From 7f6457aa9b7110869e89111c42651b1055728679 Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Fri, 7 Aug 2020 08:08:10 +0200 Subject: Allow changing station mode Fixes #700. --- src/libpiano/piano.c | 12 ++++++++ src/libpiano/piano.h | 20 ++++++++++++ src/libpiano/request.c | 30 ++++++++++++++++++ src/libpiano/response.c | 60 ++++++++++++++++++++++++++++++++++++ src/ui_act.c | 82 ++++++++++++++++++++++++++++++++++++------------- src/ui_dispatch.h | 2 +- 6 files changed, 183 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/libpiano/piano.c b/src/libpiano/piano.c index a189f34..fde8789 100644 --- a/src/libpiano/piano.c +++ b/src/libpiano/piano.c @@ -183,6 +183,18 @@ static void PianoDestroyPartner (PianoPartner_t *partner) { memset (partner, 0, sizeof (*partner)); } +void PianoDestroyStationMode (PianoStationMode_t * const modes) { + PianoStationMode_t *curMode = modes; + + while (curMode != NULL) { + free (curMode->name); + free (curMode->description); + PianoStationMode_t * const lastMode = curMode; + curMode = (PianoStationMode_t *) curMode->head.next; + free (lastMode); + } +} + /* frees the whole piano handle structure * @param piano handle * @return nothing diff --git a/src/libpiano/piano.h b/src/libpiano/piano.h index 0ec6c49..f7360aa 100644 --- a/src/libpiano/piano.h +++ b/src/libpiano/piano.h @@ -155,6 +155,13 @@ typedef struct { bool explicitContentFilter; } PianoSettings_t; +typedef struct { + PianoListHead_t head; + char *name, *description; + bool isAlgorithmic, isTakeover, active; + int id; +} PianoStationMode_t; + typedef enum { /* 0 is reserved: memset (x, 0, sizeof (x)) */ PIANO_REQUEST_LOGIN = 1, @@ -179,6 +186,8 @@ typedef enum { PIANO_REQUEST_DELETE_SEED = 22, PIANO_REQUEST_GET_SETTINGS = 23, PIANO_REQUEST_CHANGE_SETTINGS = 24, + PIANO_REQUEST_GET_STATION_MODES = 25, + PIANO_REQUEST_SET_STATION_MODE = 26, } PianoRequestType_t; typedef struct PianoRequest { @@ -266,6 +275,16 @@ typedef struct { PianoTristate_t explicitContentFilter; } PianoRequestDataChangeSettings_t; +typedef struct { + PianoStation_t *station; + PianoStationMode_t *retModes; +} PianoRequestDataGetStationModes_t; + +typedef struct { + PianoStation_t *station; + unsigned int id; +} PianoRequestDataSetStationMode_t; + /* pandora error code offset */ #define PIANO_RET_OFFSET 1024 typedef enum { @@ -355,6 +374,7 @@ void PianoDestroy (PianoHandle_t *); void PianoDestroyPlaylist (PianoSong_t *); void PianoDestroySearchResult (PianoSearchResult_t *); void PianoDestroyStationInfo (PianoStationInfo_t *); +void PianoDestroyStationMode (PianoStationMode_t * const); /* pandora rpc */ PianoReturn_t PianoRequest (PianoHandle_t *, PianoRequest_t *, diff --git a/src/libpiano/request.c b/src/libpiano/request.c index 35feda9..69e49a1 100644 --- a/src/libpiano/request.c +++ b/src/libpiano/request.c @@ -369,6 +369,36 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req, break; } + case PIANO_REQUEST_GET_STATION_MODES: { + PianoRequestDataGetStationModes_t *reqData = req->data; + assert (reqData != NULL); + PianoStation_t * const station = reqData->station; + assert (station != NULL); + + json_object_object_add (j, "stationId", + json_object_new_string (station->id)); + + method = "interactiveradio.v1.getAvailableModesSimple"; + req->secure = true; + break; + } + + case PIANO_REQUEST_SET_STATION_MODE: { + PianoRequestDataSetStationMode_t *reqData = req->data; + assert (reqData != NULL); + PianoStation_t * const station = reqData->station; + assert (station != NULL); + + json_object_object_add (j, "stationId", + json_object_new_string (station->id)); + json_object_object_add (j, "modeId", + json_object_new_int (reqData->id)); + + method = "interactiveradio.v1.setAndGetAvailableModes"; + req->secure = true; + break; + } + case PIANO_REQUEST_DELETE_FEEDBACK: { PianoSong_t *song = req->data; diff --git a/src/libpiano/response.c b/src/libpiano/response.c index 4b706e2..1a0f2d5 100644 --- a/src/libpiano/response.c +++ b/src/libpiano/response.c @@ -651,6 +651,66 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { } break; } + + case PIANO_REQUEST_GET_STATION_MODES: { + PianoRequestDataGetStationModes_t *reqData = req->data; + assert (reqData != NULL); + + int active = -1; + + json_object *activeMode; + if (json_object_object_get_ex (result, "currentModeId", &activeMode)) { + active = json_object_get_int (activeMode); + } + + json_object *availableModes; + if (json_object_object_get_ex (result, "availableModes", &availableModes)) { + for (int i = 0; i < json_object_array_length (availableModes); i++) { + json_object *val = json_object_array_get_idx (availableModes, i); + + assert (json_object_is_type (val, json_type_object)); + + PianoStationMode_t *mode; + if ((mode = calloc (1, sizeof (*mode))) == NULL) { + return PIANO_RET_OUT_OF_MEMORY; + } + + json_object *modeId; + if (json_object_object_get_ex (val, "modeId", &modeId)) { + mode->id = json_object_get_int (modeId); + mode->name = PianoJsonStrdup (val, "modeName"); + mode->description = PianoJsonStrdup (val, "modeDescription"); + mode->isAlgorithmic = getBoolDefault (val, "isAlgorithmicMode", + false); + mode->isTakeover = getBoolDefault (val, "isTakeoverMode", + false); + mode->active = active == mode->id; + } + + reqData->retModes = PianoListAppendP (reqData->retModes, + mode); + } + } + break; + } + + case PIANO_REQUEST_SET_STATION_MODE: { + PianoRequestDataSetStationMode_t *reqData = req->data; + assert (reqData != NULL); + + int active = -1; + + json_object *activeMode; + if (json_object_object_get_ex (result, "currentModeId", &activeMode)) { + active = json_object_get_int (activeMode); + } + + if (active != reqData->id) { + /* this did not work */ + return PIANO_RET_ERR; + } + break; + } } cleanup: diff --git a/src/ui_act.c b/src/ui_act.c index ba340c1..43d5b6e 100644 --- a/src/ui_act.c +++ b/src/ui_act.c @@ -224,6 +224,15 @@ BarUiActCallback(BarUiActAddSharedStation) { } } +static void drainPlaylist (BarApp_t * const app) { + BarUiDoSkipSong (&app->player); + if (app->playlist != NULL) { + /* drain playlist */ + PianoDestroyPlaylist (PianoListNextP (app->playlist)); + app->playlist->head.next = NULL; + } +} + /* delete current station */ BarUiActCallback(BarUiActDeleteStation) { @@ -238,13 +247,7 @@ BarUiActCallback(BarUiActDeleteStation) { BarUiMsg (&app->settings, MSG_INFO, "Deleting station... "); if (BarUiActDefaultPianoCall (PIANO_REQUEST_DELETE_STATION, selStation) && selStation == app->curStation) { - BarUiDoSkipSong (&app->player); - if (app->playlist != NULL) { - /* drain playlist */ - PianoDestroyPlaylist (PianoListNextP (app->playlist)); - app->playlist->head.next = NULL; - selSong = NULL; - } + drainPlaylist (app); app->nextStation = NULL; /* XXX: usually we shoudn’t touch cur*, but DELETE_STATION destroys * station struct */ @@ -478,12 +481,7 @@ BarUiActCallback(BarUiActSelectStation) { "Select station: ", NULL, app->settings.autoselect); if (newStation != NULL) { app->nextStation = newStation; - BarUiDoSkipSong (&app->player); - if (app->playlist != NULL) { - /* drain playlist */ - PianoDestroyPlaylist (PianoListNextP (app->playlist)); - app->playlist->head.next = NULL; - } + drainPlaylist (app); } } @@ -773,7 +771,7 @@ BarUiActCallback(BarUiActManageStation) { CURLcode wRet; PianoRequestDataGetStationInfo_t reqData; char selectBuf[2], allowedActions[6], *allowedPos = allowedActions; - char question[64]; + char question[128]; memset (&reqData, 0, sizeof (reqData)); reqData.station = selStation; @@ -816,18 +814,17 @@ BarUiActCallback(BarUiActManageStation) { strcat (question, "[f]eedback"); *allowedPos++ = 'f'; } + /* station mode is always available */ + if (allowedPos != allowedActions) { + strcat (question, "? "); + } + strcat (question, "Manage [m]ode? "); + *allowedPos++ = 'm'; + *allowedPos = '\0'; - strcat (question, "? "); assert (strlen (question) < sizeof (question) / sizeof (*question)); - /* nothing to see? */ - if (allowedPos == allowedActions) { - BarUiMsg (&app->settings, MSG_ERR, "No seeds or feedback available yet.\n"); - PianoDestroyStationInfo (&reqData.info); - return; - } - BarUiMsg (&app->settings, MSG_QUESTION, "%s", question); if (BarReadline (selectBuf, sizeof (selectBuf), allowedActions, &app->input, BAR_RL_FULLRETURN, -1)) { @@ -879,6 +876,47 @@ BarUiActCallback(BarUiActManageStation) { BarUiActDefaultPianoCall (PIANO_REQUEST_DELETE_FEEDBACK, song); BarUiActDefaultEventcmd ("stationdeletefeedback"); } + } else if (selectBuf[0] == 'm') { + PianoRequestDataGetStationModes_t subReqData = + { .station = selStation }; + BarUiMsg (&app->settings, MSG_INFO, "Fetching modes... "); + BarUiActDefaultPianoCall (PIANO_REQUEST_GET_STATION_MODES, + &subReqData); + BarUiActDefaultEventcmd ("stationgetmodes"); + + const PianoStationMode_t *curMode = subReqData.retModes; + unsigned int i = 0; + PianoListForeachP (curMode) { + BarUiMsg (&app->settings, MSG_LIST, "%2i) %s: %s%s\n", i, + curMode->name, curMode->description, + curMode->active ? " (active)" : ""); + i++; + } + + BarUiMsg (&app->settings, MSG_QUESTION, "Pick a new mode: "); + int selected; + while (true) { + if (BarReadlineInt (&selected, &app->input) == 0) { + break; + } + + const PianoStationMode_t * const selMode = + PianoListGetP (subReqData.retModes, selected); + if (selMode != NULL) { + PianoRequestDataSetStationMode_t subReqDataSet = + {.station = selStation, .id = selected}; + BarUiMsg (&app->settings, MSG_INFO, + "Selecting mode \"%s\"... ", selMode->name); + if (BarUiActDefaultPianoCall ( + PIANO_REQUEST_SET_STATION_MODE, &subReqDataSet)) { + drainPlaylist (app); + } + BarUiActDefaultEventcmd ("stationsetmode"); + break; + } + } + + PianoDestroyStationMode (subReqData.retModes); } } diff --git a/src/ui_dispatch.h b/src/ui_dispatch.h index 7e34393..72de887 100644 --- a/src/ui_dispatch.h +++ b/src/ui_dispatch.h @@ -90,7 +90,7 @@ static const BarUiDispatchAction_t dispatchActions[BAR_KS_COUNT] = { "act_voldown"}, {')', BAR_DC_GLOBAL, BarUiActVolUp, "increase volume", "act_volup"}, - {'=', BAR_DC_STATION, BarUiActManageStation, "delete seeds/feedback", + {'=', BAR_DC_STATION, BarUiActManageStation, "manage station seeds/feedback/mode", "act_managestation"}, {' ', BAR_DC_GLOBAL | BAR_DC_STATION, BarUiActTogglePause, NULL, "act_songpausetoggle2"}, -- cgit v1.2.3