/* piano wrapper: prepare/execute http request and pass result back to * libpiano (updates data structures) * @param piano handle * @param request type * @param waitress handle * @param data pointer (used as request data) * @return 1 on success, 0 otherwise */ int BarUiPianoCall (PianoHandle_t *ph, PianoRequestType_t type, WaitressHandle_t *waith, void *data, PianoReturn_t *pRet, WaitressReturn_t *wRet) { PianoRequest_t req; memset (&req, 0, sizeof (req)); /* repeat as long as there are http requests to do */ do { req.data = data; *pRet = PianoRequest (ph, &req, type); if (*pRet != PIANO_RET_OK) { BarUiMsg (MSG_NONE, "Error: %s\n", PianoErrorToStr (*pRet)); PianoDestroyRequest (&req); return 0; } *wRet = BarPianoHttpRequest (waith, &req); if (*wRet != WAITRESS_RET_OK) { BarUiMsg (MSG_NONE, "Network error: %s\n", WaitressErrorToStr (*wRet)); PianoDestroyRequest (&req); if (req.responseData != NULL) { free (req.responseData); } return 0; } *pRet = PianoResponse (ph, &req); if (*pRet != PIANO_RET_CONTINUE_REQUEST) { if (*pRet != PIANO_RET_OK) { BarUiMsg (MSG_NONE, "Error: %s\n", PianoErrorToStr (*pRet)); PianoDestroyRequest (&req); if (req.responseData != NULL) { free (req.responseData); } return 0; } else { BarUiMsg (MSG_NONE, "Ok.\n"); } } /* we can destroy the request at this point, even when this call needs * more than one http request. persistend data (step counter, e.g.) is * stored in req.data */ if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); } while (*pRet == PIANO_RET_CONTINUE_REQUEST); return 1; }
/* authenticate user */ static bool BarMainLoginUser (BarApp_t *app) { PianoReturn_t pRet; WaitressReturn_t wRet; PianoRequestDataLogin_t reqData; bool ret; #if 0 WaitressHandle_t waithSync; char *syncTime; unsigned long int syncTimeInt; /* skip sync step by fetching time from somewhere else */ WaitressInit (&waithSync); WaitressSetUrl (&waithSync, "http://ridetheclown.com/s2/synctime.php"); if (app->settings.proxy != NULL && strlen (app->settings.proxy) > 0) { WaitressSetProxy (&waithSync, app->settings.proxy); } wRet = WaitressFetchBuf (&waithSync, &syncTime); WaitressFree (&waithSync); if (wRet != WAITRESS_RET_OK) { BarUiMsg (&app->settings, MSG_ERR, "Unable to sync: %s\n", WaitressErrorToStr (wRet)); return false; } syncTimeInt = strtoul (syncTime, NULL, 0); app->ph.timeOffset = time (NULL) - syncTimeInt; free (syncTime); #endif app->ph.timeOffset = -30239998; /* woo! magic number */ reqData.user = app->settings.username; reqData.password = app->settings.password; reqData.step = 0; BarUiMsg (&app->settings, MSG_INFO, "Login... "); ret = BarUiPianoCall (app, PIANO_REQUEST_LOGIN, &reqData, &pRet, &wRet); BarUiStartEventCmd (&app->settings, "userlogin", NULL, NULL, &app->player, NULL, pRet, wRet); return ret; }
/* Excute external event handler * @param settings containing the cmdline * @param event type * @param current station * @param current song * @param piano error-code (PIANO_RET_OK if not applicable) * @param waitress error-code (WAITRESS_RET_OK if not applicable) */ void BarUiStartEventCmd (const BarSettings_t *settings, const char *type, const PianoStation_t *curStation, const PianoSong_t *curSong, const struct audioPlayer *player, PianoStation_t *stations, PianoReturn_t pRet, WaitressReturn_t wRet) { pid_t chld; int pipeFd[2]; if (settings->eventCmd == NULL) { /* nothing to do... */ return; } if (pipe (pipeFd) == -1) { BarUiMsg (settings, MSG_ERR, "Cannot create eventcmd pipe. (%s)\n", strerror (errno)); return; } chld = fork (); if (chld == 0) { /* child */ close (pipeFd[1]); dup2 (pipeFd[0], fileno (stdin)); execl (settings->eventCmd, settings->eventCmd, type, (char *) NULL); BarUiMsg (settings, MSG_ERR, "Cannot start eventcmd. (%s)\n", strerror (errno)); close (pipeFd[0]); exit (1); } else if (chld == -1) { BarUiMsg (settings, MSG_ERR, "Cannot fork eventcmd. (%s)\n", strerror (errno)); } else { /* parent */ int status; PianoStation_t *songStation = NULL; FILE *pipeWriteFd; close (pipeFd[0]); pipeWriteFd = fdopen (pipeFd[1], "w"); if (curSong != NULL && stations != NULL && curStation->isQuickMix) { songStation = PianoFindStationById (stations, curSong->stationId); } fprintf (pipeWriteFd, "artist=%s\n" "title=%s\n" "album=%s\n" "coverArt=%s\n" "stationName=%s\n" "songStationName=%s\n" "pRet=%i\n" "pRetStr=%s\n" "wRet=%i\n" "wRetStr=%s\n" "songDuration=%lu\n" "songPlayed=%lu\n" "rating=%i\n" "detailUrl=%s\n", curSong == NULL ? "" : curSong->artist, curSong == NULL ? "" : curSong->title, curSong == NULL ? "" : curSong->album, curSong == NULL ? "" : curSong->coverArt, curStation == NULL ? "" : curStation->name, songStation == NULL ? "" : songStation->name, pRet, PianoErrorToStr (pRet), wRet, WaitressErrorToStr (wRet), player->songDuration, player->songPlayed, curSong == NULL ? PIANO_RATE_NONE : curSong->rating, curSong == NULL ? "" : curSong->detailUrl ); if (stations != NULL) { /* send station list */ PianoStation_t **sortedStations = NULL; size_t stationCount; sortedStations = BarSortedStations (stations, &stationCount, settings->sortOrder); assert (sortedStations != NULL); fprintf (pipeWriteFd, "stationCount=%zd\n", stationCount); for (size_t i = 0; i < stationCount; i++) { const PianoStation_t *currStation = sortedStations[i]; fprintf (pipeWriteFd, "station%zd=%s\n", i, currStation->name); } free (sortedStations); } else { const char * const msg = "stationCount=0\n"; fwrite (msg, sizeof (*msg), strlen (msg), pipeWriteFd); } /* closes pipeFd[1] as well */ fclose (pipeWriteFd); /* wait to get rid of the zombie */ waitpid (chld, &status, 0); } }
/* piano wrapper: prepare/execute http request and pass result back to * libpiano (updates data structures) * @param app handle * @param request type * @param request data * @param stores piano return code * @param stores waitress return code * @return 1 on success, 0 otherwise */ int BarUiPianoCall (BarApp_t * const app, PianoRequestType_t type, void *data, PianoReturn_t *pRet, WaitressReturn_t *wRet) { PianoRequest_t req; memset (&req, 0, sizeof (req)); /* repeat as long as there are http requests to do */ do { req.data = data; *pRet = PianoRequest (&app->ph, &req, type); if (*pRet != PIANO_RET_OK) { BarUiMsg (&app->settings, MSG_NONE, "Error: %s\n", PianoErrorToStr (*pRet)); PianoDestroyRequest (&req); return 0; } *wRet = BarPianoHttpRequest (&app->waith, &req); if (*wRet != WAITRESS_RET_OK) { BarUiMsg (&app->settings, MSG_NONE, "Network error: %s\n", WaitressErrorToStr (*wRet)); if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); return 0; } *pRet = PianoResponse (&app->ph, &req); if (*pRet != PIANO_RET_CONTINUE_REQUEST) { /* checking for request type avoids infinite loops */ if (*pRet == PIANO_RET_P_INVALID_AUTH_TOKEN && type != PIANO_REQUEST_LOGIN) { /* reauthenticate */ PianoReturn_t authpRet; WaitressReturn_t authwRet; PianoRequestDataLogin_t reqData; reqData.user = app->settings.username; reqData.password = app->settings.password; reqData.step = 0; BarUiMsg (&app->settings, MSG_NONE, "Reauthentication required... "); if (!BarUiPianoCall (app, PIANO_REQUEST_LOGIN, &reqData, &authpRet, &authwRet)) { *pRet = authpRet; *wRet = authwRet; if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); return 0; } else { /* try again */ *pRet = PIANO_RET_CONTINUE_REQUEST; BarUiMsg (&app->settings, MSG_INFO, "Trying again... "); } } else if (*pRet != PIANO_RET_OK) { BarUiMsg (&app->settings, MSG_NONE, "Error: %s\n", PianoErrorToStr (*pRet)); if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); return 0; } else { BarUiMsg (&app->settings, MSG_NONE, "Ok.\n"); } } /* we can destroy the request at this point, even when this call needs * more than one http request. persistent data (step counter, e.g.) is * stored in req.data */ if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); } while (*pRet == PIANO_RET_CONTINUE_REQUEST); return 1; }
int MythPianoService::PianoCall(PianoRequestType_t type, void *data, PianoReturn_t *pRet, WaitressReturn_t *wRet) { if (!m_Piano) return -1; PianoRequest_t req; memset (&req, 0, sizeof (req)); /* repeat as long as there are http requests to do */ do { req.data = data; *pRet = PianoRequest (m_Piano, &req, type); if (*pRet != PIANO_RET_OK) { BroadcastMessage("Error: %s\n", PianoErrorToStr (*pRet)); PianoDestroyRequest (&req); return 0; } *wRet = PianoHttpRequest(&m_Waith, &req); if (*wRet != WAITRESS_RET_OK) { BroadcastMessage ("Network error: %s\n", WaitressErrorToStr (*wRet)); if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); return 0; } *pRet = PianoResponse (m_Piano, &req); if (*pRet != PIANO_RET_CONTINUE_REQUEST) { /* checking for request type avoids infinite loops */ if (*pRet == PIANO_RET_AUTH_TOKEN_INVALID && type != PIANO_REQUEST_LOGIN) { /* reauthenticate */ PianoReturn_t authpRet; WaitressReturn_t authwRet; PianoRequestDataLogin_t reqData; QString username = gCoreContext->GetSetting("pandora-username"); QString password = gCoreContext->GetSetting("pandora-password"); //wtf really? char* usernameBuff = strndup(username.toUtf8().data(), 1024); char* passwordBuff = strndup(password.toUtf8().data(), 1024); reqData.user = usernameBuff; reqData.password = passwordBuff; reqData.step = 0; BroadcastMessage ("Reauthentication required... "); if (!PianoCall(PIANO_REQUEST_LOGIN, &reqData, &authpRet, &authwRet)) { *pRet = authpRet; *wRet = authwRet; if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); return 0; } else { /* try again */ *pRet = PIANO_RET_CONTINUE_REQUEST; BroadcastMessage("Trying again... "); } // wtf free(usernameBuff); free(passwordBuff); } else if (*pRet != PIANO_RET_OK) { BroadcastMessage("Error: %s\n", PianoErrorToStr (*pRet)); if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); return 0; } else { BroadcastMessage("Login Ok.\n"); } } /* we can destroy the request at this point, even when this call needs * more than one http request. persistent data (step counter, e.g.) is * stored in req.data */ if (req.responseData != NULL) { free (req.responseData); } PianoDestroyRequest (&req); } while (*pRet == PIANO_RET_CONTINUE_REQUEST); return 1; }
/* Excute external event handler * @param settings containing the cmdline * @param event type * @param current station * @param current song * @param piano error-code (PIANO_RET_OK if not applicable) * @param waitress error-code (WAITRESS_RET_OK if not applicable) */ void BarUiStartEventCmd (const BarSettings_t *settings, const char *type, const PianoStation_t *curStation, const PianoSong_t *curSong, const struct audioPlayer *player, PianoStation_t *stations, PianoReturn_t pRet, WaitressReturn_t wRet) { int status; PianoStation_t *songStation = NULL; if (curSong != NULL && stations != NULL && curStation->isQuickMix) { songStation = PianoFindStationById (stations, curSong->stationId); } printf ("current\tartist=%s\n" "current\ttitle=%s\n" "current\talbum=%s\n" "current\tcoverArt=%s\n" "current\tstationName=%s\n" "songStationName=%s\n" "pRet=%i\n" "pRetStr=%s\n" "wRet=%i\n" "wRetStr=%s\n" "songDuration=%lu\n" "songPlayed=%lu\n" "rating=%i\n" "detailUrl=%s\n", curSong == NULL ? "" : curSong->artist, curSong == NULL ? "" : curSong->title, curSong == NULL ? "" : curSong->album, curSong == NULL ? "" : curSong->coverArt, curStation == NULL ? "" : curStation->name, songStation == NULL ? "" : songStation->name, pRet, PianoErrorToStr (pRet), wRet, WaitressErrorToStr (wRet), player->songDuration, player->songPlayed, curSong == NULL ? PIANO_RATE_NONE : curSong->rating, curSong == NULL ? "" : curSong->detailUrl ); if (stations != NULL) { /* send station list */ PianoStation_t **sortedStations = NULL; size_t stationCount; sortedStations = BarSortedStations (stations, &stationCount, settings->sortOrder); assert (sortedStations != NULL); printf ("station\tstationCount=%zd\n", stationCount); for (size_t i = 0; i < stationCount; i++) { const PianoStation_t *currStation = sortedStations[i]; printf ("station\tstation%zd=%s\n", i, currStation->name); } free (sortedStations); } }
/* player thread; for every song a new thread is started * @param audioPlayer structure * @return PLAYER_RET_* */ void *BarPlayerThread (void *data) { struct audioPlayer *player = data; char extraHeaders[32]; void *ret = PLAYER_RET_OK; #ifdef ENABLE_FAAD NeAACDecConfigurationPtr conf; #endif WaitressReturn_t wRet = WAITRESS_RET_ERR; /* init handles */ pthread_mutex_init (&player->pauseMutex, NULL); player->waith.data = (void *) player; /* extraHeaders will be initialized later */ player->waith.extraHeaders = extraHeaders; player->buffer = malloc (BAR_PLAYER_BUFSIZE); switch (player->audioFormat) { #ifdef ENABLE_FAAD case PIANO_AF_AACPLUS: player->aacHandle = NeAACDecOpen(); /* set aac conf */ conf = NeAACDecGetCurrentConfiguration(player->aacHandle); conf->outputFormat = FAAD_FMT_16BIT; conf->downMatrix = 1; NeAACDecSetConfiguration(player->aacHandle, conf); player->waith.callback = BarPlayerAACCb; break; #endif /* ENABLE_FAAD */ #ifdef ENABLE_MAD case PIANO_AF_MP3: mad_stream_init (&player->mp3Stream); mad_frame_init (&player->mp3Frame); mad_synth_init (&player->mp3Synth); player->waith.callback = BarPlayerMp3Cb; break; #endif /* ENABLE_MAD */ default: BarUiMsg (player->settings, MSG_ERR, "Unsupported audio format!\n"); ret = (void *) PLAYER_RET_HARDFAIL; goto cleanup; break; } player->mode = PLAYER_INITIALIZED; /* This loop should work around song abortions by requesting the * missing part of the song */ do { snprintf (extraHeaders, sizeof (extraHeaders), "Range: bytes=%zu-\r\n", player->bytesReceived); wRet = WaitressFetchCall (&player->waith); } while (wRet == WAITRESS_RET_PARTIAL_FILE || wRet == WAITRESS_RET_TIMEOUT || wRet == WAITRESS_RET_READ_ERR); switch (player->audioFormat) { #ifdef ENABLE_FAAD case PIANO_AF_AACPLUS: NeAACDecClose(player->aacHandle); free (player->sampleSize); break; #endif /* ENABLE_FAAD */ #ifdef ENABLE_MAD case PIANO_AF_MP3: mad_synth_finish (&player->mp3Synth); mad_frame_finish (&player->mp3Frame); mad_stream_finish (&player->mp3Stream); break; #endif /* ENABLE_MAD */ default: /* this should never happen */ assert (0); break; } if (player->aoError) { ret = (void *) PLAYER_RET_HARDFAIL; } /* Pandora sends broken audio url’s sometimes (“bad request”). ignore them. */ if (wRet != WAITRESS_RET_OK && wRet != WAITRESS_RET_CB_ABORT) { BarUiMsg (player->settings, MSG_ERR, "Cannot access audio file: %s\n", WaitressErrorToStr (wRet)); ret = (void *) PLAYER_RET_SOFTFAIL; } cleanup: ao_close (player->audioOutDevice); WaitressFree (&player->waith); free (player->buffer); player->mode = PLAYER_FINISHED_PLAYBACK; return ret; }
/* Excute external event handler * @param settings containing the cmdline * @param event type * @param current station * @param current song * @param piano error-code (PIANO_RET_OK if not applicable) * @param waitress error-code (WAITRESS_RET_OK if not applicable) */ void BarUiStartEventCmd (const BarSettings_t *settings, const char *type, const PianoStation_t *curStation, const PianoSong_t *curSong, const struct audioPlayer *player, PianoReturn_t pRet, WaitressReturn_t wRet) { pid_t chld; char pipeBuf[1024]; int pipeFd[2]; if (settings->eventCmd == NULL) { /* nothing to do... */ return; } /* prepare stdin content */ memset (pipeBuf, 0, sizeof (pipeBuf)); snprintf (pipeBuf, sizeof (pipeBuf), "artist=%s\n" "title=%s\n" "album=%s\n" "stationName=%s\n" "pRet=%i\n" "pRetStr=%s\n" "wRet=%i\n" "wRetStr=%s\n" "songDuration=%lu\n" "songPlayed=%lu\n" "rating=%i\n", curSong == NULL ? "" : curSong->artist, curSong == NULL ? "" : curSong->title, curSong == NULL ? "" : curSong->album, curStation == NULL ? "" : curStation->name, pRet, PianoErrorToStr (pRet), wRet, WaitressErrorToStr (wRet), player->songDuration, player->songPlayed, curSong == NULL ? PIANO_RATE_NONE : curSong->rating ); if (pipe (pipeFd) == -1) { BarUiMsg (MSG_ERR, "Cannot create eventcmd pipe. (%s)\n", strerror (errno)); return; } chld = fork (); if (chld == 0) { /* child */ close (pipeFd[1]); dup2 (pipeFd[0], fileno (stdin)); execl (settings->eventCmd, settings->eventCmd, type, (char *) NULL); BarUiMsg (MSG_ERR, "Cannot start eventcmd. (%s)\n", strerror (errno)); close (pipeFd[0]); exit (1); } else if (chld == -1) { BarUiMsg (MSG_ERR, "Cannot fork eventcmd. (%s)\n", strerror (errno)); } else { /* parent */ int status; close (pipeFd[0]); write (pipeFd[1], pipeBuf, strlen (pipeBuf)); close (pipeFd[1]); /* wait to get rid of the zombie */ waitpid (chld, &status, 0); } }