Någon som programmerat med libspotify?

Permalänk
Medlem

Någon som programmerat med libspotify?

Sitter med ett litet projekt som kör fast sig, men Spotify i sig är ju inte världsomspännande så jag hittar knappt några diskussioner rörande libspotify på nätet.

Permalänk
Glömsk

Coolt! Vad är det du hackar på?

Bara fråga om du behöver hjälp. Jag har lekt lite med libspotify.

Visa signatur

...man is not free unless government is limited. There's a clear cause and effect here that is as neat and predictable as a law of physics: As government expands, liberty contracts.

Permalänk
Medlem

Häftigt, jag ska bara hämta en playlist och skriva ut den helt enkelt. Programmet är inte så stort så här är allt jag har:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#include <libspotify/api.h>

char* SpotifyURI;
static HANDLE events[2];
sp_session *g_session;
//int done;
// Engelska kommentarer från medföljande exempel till libspotify där efterföljande kod exekveras.

/**
*
*/
void notify_main_thread(sp_session *session)
{
printf( "Main thread!" );
SetEvent(events[0]);
}
/**
* This callback is called when an attempt to login has succeeded or failed.
*
* @sa sp_session_callbacks#logged_in
*/
static void logged_in(sp_session *session, sp_error error)
{
printf( "Logged in!" );
sp_user *me;
const char *my_name;

if (SP_ERROR_OK != error) {
fprintf(stderr, "failed to log in to Spotify: %s\n",
sp_error_message(error));
exit(4);
}

// Let us print the nice message...
me = sp_session_user(session);
my_name = (sp_user_is_loaded(me) ? sp_user_display_name(me) : sp_user_canonical_name(me));

fprintf(stderr, "Logged in to Spotify as user %s\n", my_name);

//start_prompt();
}

/**
* This callback is called when the user was logged in, but the connection to
* Spotify was dropped for some reason.
*
* @sa sp_session_callbacks#connection_error
*/
static void connection_error(sp_session *session, sp_error error)
{
fprintf(stderr, "Connection to Spotify failed: %s\n",
sp_error_message(error));
}

/**
* This callback is called for log messages.
*
* @sa sp_session_callbacks#log_message
*/
static void log_message(sp_session *session, const char *data)
{
fprintf(stderr, "%s", data);
}

/**
* Callback called when libspotify has new metadata available
*
* @sa sp_session_callbacks#metadata_updated
*/
static void metadata_updated(sp_session *session)
{
printf( "Job's done!" );
// done = 1;
sp_link *link = sp_link_create_from_string(SpotifyURI);

if (!link) {
fprintf(stderr, "failed to get link from a Spotify URI\n");
}

if(sp_link_type(link) != SP_LINKTYPE_PLAYLIST) {
fprintf(stderr, "Can not handle link.\n");
sp_link_release(link);
}

printf( "Playlist name: %s\n", sp_playlist_name(sp_playlist_create(session, link)) );
//browse_playlist(sp_playlist_create(session, link));
sp_link_release(link);
}

/**
* Session callbacks
*/
static sp_session_callbacks callbacks = {
&logged_in,
NULL,
&metadata_updated,
&connection_error,
NULL,
NULL,
&notify_main_thread,
NULL,
&log_message
};

int app_init()
{

SpotifyURI = "spotify:user:ellenfilippamaria:playlist:2wIRCI54mfuOf6Alj3a64i";

printf( "SpotifyURI: %s\n", SpotifyURI );

sp_session_config config;
sp_error error;
sp_session *session;

/// The application key is specific to each project, and allows Spotify
/// to produce statistics on how our service is used.
extern const char g_appkey[];
/// The size of the application key.
extern const size_t g_appkey_size;

// Always do this. It allows libspotify to check for
// header/library inconsistencies.
config.api_version = SPOTIFY_API_VERSION;

// The path of the directory to store the cache. This must be specified.
// Please read the documentation on preferred values.
config.cache_location = "tmp";

// The path of the directory to store the settings.
// This must be specified.
// Please read the documentation on preferred values.
config.settings_location = "tmp";

// The key of the application. They are generated by Spotify,
// and are specific to each application using libspotify.
config.application_key = g_appkey;
config.application_key_size = g_appkey_size;

// This identifies the application using some
// free-text string [1, 255] characters.
config.user_agent = "SpotifyDavid";

// Register the callbacks.
config.callbacks = &callbacks;

error = sp_session_init(&config, &session);
if (SP_ERROR_OK != error) {
fprintf(stderr, "failed to create session: %s\n",
sp_error_message(error));
return 2;
}

// Login using the credentials given on the command line.
error = sp_session_login(session, "XXXX", "XXXX");

if (SP_ERROR_OK != error) {
fprintf(stderr, "failed to login: %s\n",
sp_error_message(error));
return 3;
}

g_session = session;
//system("PAUSE");
return 0;
}

int main()
{
int r;
int next_timeout = 0;
DWORD ev;

events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
if ((r = app_init()) != 0)
exit(r);

do {
sp_session_process_events(g_session, &next_timeout);
} while (next_timeout == 0);

//system("PAUSE");

}

Dold text

Det bygger på och är snarlikt exemplen, från sidan och senaste versionen, och när jag kompilerar programmet går den igenom allt utan att göra några callback-funktioner. I metadata-funktionen försöker jag bara skriva ut namnet på playlistan, där tänkte jag senare skriva ut all information. Skillnaden med exemplet är att de gör fler saker i main-funktionen men de verkar bara vara för att synkronisera konsol-inputen och eventen. Eftersom jag inte har någon konsol-input tänkte jag bara köra på.

Det är den här exempel-filen jag tänker på:

/**
* Copyright (c) 2006-2010 Spotify Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/

#include "spshell.h"
#include "cmd.h"

static HANDLE events[2];

static int enable_console;
static char console_buf[1024];
static int console_ptr;

/**
* Very simplistic console input
*/
static void console_input(void)
{
TCHAR inp;
DWORD read;
if(!ReadConsole(events[1], &inp, 1, &read, NULL))
return;

switch(inp) {
case 8:
if(console_ptr > 0) {
console_ptr--;
printf("\b \b");
}
break;
case 13:
printf("\n\r");
console_buf[console_ptr++] = 0;
console_ptr = 0;
enable_console = 0;
cmd_exec_unparsed(console_buf);
break;

default:
if(console_ptr < sizeof(console_buf) - 1) {
printf("%c", (char)inp);
console_buf[console_ptr++] = inp;
}
break;
}
}

static void trim(char *buf)
{
size_t l = strlen(buf);
while(l > 0 && buf[l - 1] < 32)
buf[--l] = 0;
}

int __cdecl main(int argc, char **argv)
{
const char *username = argc > 1 ? argv[1] : NULL;
const char *password = argc > 2 ? argv[2] : NULL;
char username_buf[256];
char password_buf[256];
int r;
int next_timeout = 0;
DWORD ev;

events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
events[1] = GetStdHandle(STD_INPUT_HANDLE);

if (username == NULL) {
printf("Username: ");
fflush(stdout);
fgets(username_buf, sizeof(username_buf), stdin);
trim(username_buf);
username = username_buf;
}

if (password == NULL) {
DWORD mode;
printf("Password: ");
fflush(stdout);

if (!GetConsoleMode(events[1], &mode) ||
!SetConsoleMode(events[1], mode & ~ENABLE_ECHO_INPUT)) {
printf("Unable to set console mode err=%d\n", GetLastError());
exit(1);
}

fgets(password_buf, sizeof(password_buf), stdin);
trim(password_buf);
password = password_buf;
printf("\r\n");
}

if ((r = spshell_init(username, password)) != 0)
exit(r);

if (!SetConsoleMode(events[1], ENABLE_PROCESSED_INPUT)) {
printf("Unable to set console mode err=%d\n", GetLastError());
exit(1);
}

while(1) {
ev = WaitForMultipleObjects(1 + enable_console, events, FALSE, next_timeout > 0 ? next_timeout : INFINITE);
switch (ev) {
case WAIT_OBJECT_0 + 0:
do {
sp_session_process_events(g_session, &next_timeout);
} while (next_timeout == 0);
break;

case WAIT_OBJECT_0 + 1:
console_input();
break;
}
}
}

/**
*
*/
void cmd_done(void)
{
enable_console = 1;
printf("> ");
fflush(stdout);
}

/**
*
*/
void start_prompt(void)
{
cmd_done();
}

/**
*
*/
void notify_main_thread(sp_session *session)
{
SetEvent(events[0]);
}

Dold text

Saken är nog att jag inte förstår vad notify_main_thread och sp_session_process_events ska vara bra för. Jag tänkte mig att man sätter upp alla callback-funktioner, skapar sessionen och så körs de när jag får svar från Spotify. Två saker fick mig att använda dem överhuvudtaget, först så var notify_main_thread den enda funktionen som kördes men fick programmet att krasha (vilket enligt dokumentatione skulle åtgärdas med sp_session_process_events), men den verkar inte kallas på längre, sen när jag kommenterar in "system(PAUSE)" för jag det här meddelandet i konsolen:

23:03:03.328 I [ap:1368] Connecting to AP A1.spotify.com:4070

I exemplet som gick att kompilera stod det så när jag loggade in, så jag tänkte mig att jag måste ha någon form av väntlop, kanske köar den upp några kallelser till Spotify eller liknande. Från exemplen på sidan hanterade de notify_main_thread med lås, men jag tycker ju liksom att jag inte behöver synkornisera något. Jag vill bara läsa in en Spotify URI och köra på, men programmet fungerar likadant utan notify_main_thread och sp_session_process_events numera.

EDIT:

Tänkte bara lägga till att jag programmerar i Dev-CPP i Windows, jag lyckades kompilera exemplet som följde med och starta det i Visual C++ genom .vproj-filen, sen logga in med mina uppgifter och nyckel. Dock kunde jag inte kompilera och köra exemplet i Dev-CPP förut men jag har ändrat det lite, bara kommenterade ut snprintf på ett ställe, och nu verkar jag få samma fel eller ett liknande som i mitt egna program. Alltså jag loggar in med mina uppgifter sen avslutas programmet, som om den inte får svar från några callback-funktioner.

Det är lite konstigt, det är svårt att tyda vad Visual C++ gör eftersom den skapar jättemånga filer, men samtidigt kanske mitt problem blir enklare eftersom det är samma fel med exemplet. Jag har ju lagt include- och lib-filen där Dev-CPP vill ha dem och jag får inga länkningsfel eller varningar att den inte hittar funktioner, borde inte allt vara som det ska då? Det känns ju som vproj-filen ger någon rättighet eller öppnar någon port, tror det var de här Visual C++-filerna som följde med:

spshell.sln:

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual C++ Express 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spshell", "spshell.vcxproj", "{6C49596C-977E-4BB6-BC94-123AEB5E2724}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6C49596C-977E-4BB6-BC94-123AEB5E2724}.Debug|Win32.ActiveCfg = Debug|Win32
{6C49596C-977E-4BB6-BC94-123AEB5E2724}.Debug|Win32.Build.0 = Debug|Win32
{6C49596C-977E-4BB6-BC94-123AEB5E2724}.Release|Win32.ActiveCfg = Release|Win32
{6C49596C-977E-4BB6-BC94-123AEB5E2724}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

Dold text

spshell.vcproj:

<?xml version="1.0" encoding="UTF-8"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="spshell"
ProjectGUID="{6C49596C-977E-4BB6-BC94-123AEB5E2724}"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="Debug"
IntermediateDirectory="Debug"
ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""$(LIBSPOTIFY)\include";..\..\..\include"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
CallingConvention="2"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="libspotify.lib"
LinkIncremental="2"
AdditionalLibraryDirectories=""$(LIBSPOTIFY)\lib";..\..\..\lib"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="Release"
IntermediateDirectory="Release"
ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\cmd.h"
>
</File>
<File
RelativePath="..\spshell.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\..\appkey.c"
>
</File>
<File
RelativePath="..\browse.c"
>
</File>
<File
RelativePath="..\cmd.c"
>
</File>
<File
RelativePath="..\search.c"
>
</File>
<File
RelativePath="..\star.c"
>
</File>
<File
RelativePath="..\spshell.c"
>
</File>
<File
RelativePath="..\spshell_win32.c"
>
</File>
<File
RelativePath="..\toplist.c"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

Dold text

Jag är ju ingen MicroSoft-människa men det ser bara ut som om den säger vart filerna ska läggas och finns, sen att den kompilerar allting som ett 32-bitars program, vilket jag gör i Dev-CPP.

Tillägg.
Permalänk
Medlem

Nu har jag gjort så här, jag tog exemplet som gick att kompilera och tog bort allt som jag inte behövde och bytte namn på resten. Nu fungerar det i Visual Studio iallafall. När jag försökte skapa ett helt nytt projekt i Visual Studio kunde jag inte heller kompilera programmet, så det är nog något den gör. Jag skulle uppskatta om någon har någon tankte där

Annars ska det se ut typ så här i main:

int r; int next_timeout = 0; if ((r = sbdhell_init(username, password, uri)) != 0) exit(r); while(1) { do { sp_session_process_events(g_session, &next_timeout); } while (next_timeout == 0); }

Jag kunde också helt ta bort notify_main_thread, den var nog som jag trodde bara nödvändig när man vill synkronisera input och utskrift, eller som i ett exempel för att synkronisera låtar som spelas upp.

Det är ett problem som kvarstår dock, min metadata-funktion kallas på en gång för mycket. I min metadata-funktion skriver jag helt enkelt ut en playlist nu, men efter att jag loggat in kallas den utan att kunna skriva ut någon information. Jag har försökt med saker som if(!link) eller if(sp_playlist_name(playlist) != NULL) men den envisas med att gå igenom hela funktionen och skriva ut mina rubriker.

Hoppas det var tydligt vad problemet var, jag tror att metadata-funktionen helt enkelt kallas en gång då man loggat in och då innan jag fått någon information att skriva ut. Jag är inte helt säker på hur jag ska hantera det på bästa sätt. I exemplen har de någon konstigt deklarering så här:

void (*metadata_updated_fn)(void);

Sen i metadata-funktionen:

static void metadata_updated(sp_session *sess) { if(metadata_updated_fn) metadata_updated_fn(); }

Och i t.ex. browse-funktionen:

metadata_updated_fn = playlist_browse_try;

Det verkar ju som de väljer vilken funktion som ska köras i metadata, så jag försökte med samma sak fast ha en print_playlsit-funktion i min main-funktion fast den kallade aldrig på print_playlist.

EDIT: Fixat med en global int som heter g_done, så får den köra metadata-funktnionen hur många gånger den vill utan att skriva ut inforamnen mer än en gång. Det konstiga va bara att jag gör en session_logout för den globala session-variabeln i slutet av metadata-funktionen, men jag tror att han köar upp metadata-callbacks med en kopia av sessionen i varje, eller nått?

Permalänk

Måste man alltid logga in?

Jag har kollat lite på libspotify under dagen och undrar... Måste man alltid logga in?
Går det inte att göra ett program med det här API:t som kan manipulera och använda spotify för den som är inloggad för tillfället?

T.ex att jag vill göra ett program som byter låt eller hämtar info från spotify som körs samtidigt på datorn?

Håller med för övrigt, inte många trådar vad man kan hitta inom ämnet. Så det var kul att man hittade den här tråden

Visa signatur

PC: ASUS P5Q Delux DDR2, E8600@4400, TRUE+Noctua 120mm, 4GB Corsair DOMINATOR 1066, PowerColor HD4870X2
MacBookPro 13"
Iphone4
Canon500D