diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d3ce601f..4194dbb85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,12 @@ This release improves the WebradioDB integration and removes the radiobrowser.in ### API changes - MYMPD_API_WEBRADIODB_UPDATE: new +- MYMPD_API_WEBRADIODB_SEARCH: new +- MYMPD_API_WEBRADIODB_LIST renamed to MYMPD_API_WEBRADIODB_SEARCH +- MYMPD_API_WEBRADIO_FAVORITE_GET_BY_NAME: new +- MYMPD_API_WEBRADIO_FAVORITE_GET_BY_URI: new +- MYMPD_API_WEBRADIODB_RADIO_GET_BY_NAME: new +- MYMPD_API_WEBRADIODB_RADIO_GET_BY_URI: new - MYMPD_API_CLOUD_WEBRADIODB_COMBINED_GET: removed - MYMPD_API_CLOUD_RADIOBROWSER_NEWEST: removed - MYMPD_API_CLOUD_RADIOBROWSER_SEARCH: removed diff --git a/docs/references/translating_status.md b/docs/references/translating_status.md index 68d030569..14195cd06 100644 --- a/docs/references/translating_status.md +++ b/docs/references/translating_status.md @@ -1,14 +1,14 @@ -- bg-BG: 1049 missing phrases -- es-AR: 3 missing phrases +- bg-BG: 1050 missing phrases +- es-AR: 5 missing phrases - es-ES: 915 missing phrases -- es-VE: 905 missing phrases -- fi-FI: 902 missing phrases -- fr-FR: 3 missing phrases -- it-IT: 33 missing phrases -- ja-JP: 3 missing phrases -- ko-KR: 3 missing phrases -- nl-NL: 3 missing phrases -- pl-PL: 78 missing phrases -- ru-RU: 149 missing phrases -- zh-Hans: 31 missing phrases -- zh-Hant: 78 missing phrases +- es-VE: 904 missing phrases +- fi-FI: 901 missing phrases +- fr-FR: 5 missing phrases +- it-IT: 35 missing phrases +- ja-JP: 5 missing phrases +- ko-KR: 5 missing phrases +- nl-NL: 5 missing phrases +- pl-PL: 80 missing phrases +- ru-RU: 151 missing phrases +- zh-Hans: 33 missing phrases +- zh-Hant: 80 missing phrases diff --git a/docs/references/webserver-uris.md b/docs/references/webserver-uris.md index a5fe2e3e1..b4edc7472 100644 --- a/docs/references/webserver-uris.md +++ b/docs/references/webserver-uris.md @@ -24,5 +24,6 @@ Reference of all webserver uris. | `/serverinfo` | Returns the ip address of myMPD | | `/stream/` | Reverse proxy for mpd http stream | | `/tagart?tag=&value=` | Returns the tagart thumbnail. | +| `/webradio?uri=` | Webradio endpoint | | `/ws/` | Websocket endpoint | {: .table .table-sm } diff --git a/htdocs/sw.js.in b/htdocs/sw.js.in index 716f32c69..6ae86ec80 100644 --- a/htdocs/sw.js.in +++ b/htdocs/sw.js.in @@ -35,6 +35,7 @@ const ignoreRequests = new RegExp(subdir + '/(' + [ 'tagart.*', 'folderart.*', 'playlistart.*', + 'webradio.*', 'proxy.*', 'browse/.*'].join('|') + ')$'); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 397836a17..600a9847f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -61,7 +61,6 @@ target_sources(mympd lib/last_played.c lib/list.c lib/log.c - lib/m3u.c lib/mg_str_utils.c lib/mimetype.c lib/mpack.c @@ -138,7 +137,9 @@ target_sources(mympd mympd_api/timer_handlers.c mympd_api/trigger.c mympd_api/volume.c - mympd_api/webradios.c + mympd_api/webradio.c + mympd_api/webradiodb.c + mympd_api/webradio_favorites.c web_server/web_server.c web_server/albumart.c web_server/folderart.c diff --git a/src/compile_time.h.in b/src/compile_time.h.in index 96e530d4c..6fa016e7a 100644 --- a/src/compile_time.h.in +++ b/src/compile_time.h.in @@ -91,6 +91,7 @@ extern struct t_mympd_queue *mympd_api_queue; #define FILENAME_TIMER "timer_list" #define FILENAME_TRIGGER "trigger_list" #define FILENAME_WEBRADIODB "webradiodb.mpack" +#define FILENAME_WEBRADIO_FAVORITES "webradio_favorites.mpack" #define FILENAME_SCRIPTVARS "scriptvars_list" #define DIR_CACHE_COVER "cover" @@ -109,7 +110,6 @@ extern struct t_mympd_queue *mympd_api_queue; #define DIR_WORK_STATE "state" #define DIR_WORK_STATE_DEFAULT DIR_WORK_STATE"/default" #define DIR_WORK_TAGS "tags" -#define DIR_WORK_WEBRADIOS "webradios" //mpd partitions #define MPD_PARTITION_DEFAULT "default" diff --git a/src/i18n/json/i18n.json b/src/i18n/json/i18n.json index dc6c147c7..1dd2aba65 100644 --- a/src/i18n/json/i18n.json +++ b/src/i18n/json/i18n.json @@ -1,14 +1,14 @@ { "default": {"desc":"Browser default", "missingPhrases": 0}, - "de-DE": {"desc":"Deutsch (de-DE)", "missingPhrases": 3}, + "de-DE": {"desc":"Deutsch (de-DE)", "missingPhrases": 5}, "en-US": {"desc":"English (en-US)", "missingPhrases": 0}, - "es-AR": {"desc":"Español (es-AR)", "missingPhrases": 3}, - "fr-FR": {"desc":"Français (fr-FR)", "missingPhrases": 3}, - "it-IT": {"desc":"Italiano (it-IT)", "missingPhrases": 33}, - "ja-JP": {"desc":"日本語 (ja-JP)", "missingPhrases": 3}, - "ko-KR": {"desc":"한국어 (ko-KR)", "missingPhrases": 3}, - "nl-NL": {"desc":"Nederlands (nl-NL)", "missingPhrases": 3}, - "pl-PL": {"desc":"Polish (pl-PL)", "missingPhrases": 78}, - "zh-Hans": {"desc":"简体中文 (zh-Hans)", "missingPhrases": 31}, - "zh-Hant": {"desc":"简体中文 (zh-Hant)", "missingPhrases": 78} + "es-AR": {"desc":"Español (es-AR)", "missingPhrases": 5}, + "fr-FR": {"desc":"Français (fr-FR)", "missingPhrases": 5}, + "it-IT": {"desc":"Italiano (it-IT)", "missingPhrases": 35}, + "ja-JP": {"desc":"日本語 (ja-JP)", "missingPhrases": 5}, + "ko-KR": {"desc":"한국어 (ko-KR)", "missingPhrases": 5}, + "nl-NL": {"desc":"Nederlands (nl-NL)", "missingPhrases": 5}, + "pl-PL": {"desc":"Polish (pl-PL)", "missingPhrases": 80}, + "zh-Hans": {"desc":"简体中文 (zh-Hans)", "missingPhrases": 33}, + "zh-Hant": {"desc":"简体中文 (zh-Hant)", "missingPhrases": 80} } diff --git a/src/i18n/json/phrases.json b/src/i18n/json/phrases.json index 0ff99bcb1..c0049824a 100644 --- a/src/i18n/json/phrases.json +++ b/src/i18n/json/phrases.json @@ -108,13 +108,11 @@ {"term":"Can not move home icon"}, {"term":"Can not open directory pics"}, {"term":"Can not open script"}, -{"term":"Can not open webradios directory"}, {"term":"Can not parse response from %{uri}"}, {"term":"Can not parse script arguments"}, {"term":"Can not parse script metadata."}, {"term":"Can not parse settings"}, {"term":"Can not parse smart playlist file"}, -{"term":"Can not parse webradio favorite file"}, {"term":"Can not read smart playlist file"}, {"term":"Can not save home icon"}, {"term":"Can't delete script variable"}, @@ -583,7 +581,7 @@ {"term":"No song positions provided"}, {"term":"No tagart found."}, {"term":"No uris provided"}, -{"term":"No webradios provided"}, +{"term":"No webradio favorites provided"}, {"term":"None"}, {"term":"Not playing"}, {"term":"Notification icon"}, @@ -976,6 +974,7 @@ {"term":"Web notifications are blocked by the browser"}, {"term":"Webradio"}, {"term":"Webradio details"}, +{"term":"Webradio favorite not found"}, {"term":"Webradio favorite successfully saved"}, {"term":"Webradio favorites"}, {"term":"WebradioDB"}, diff --git a/src/lib/api.h b/src/lib/api.h index 80eeb5383..d20ce3e93 100644 --- a/src/lib/api.h +++ b/src/lib/api.h @@ -205,10 +205,14 @@ X(MYMPD_API_TRIGGER_RM) \ X(MYMPD_API_TRIGGER_SAVE) \ X(MYMPD_API_VIEW_SAVE) \ - X(MYMPD_API_WEBRADIO_FAVORITE_GET) \ - X(MYMPD_API_WEBRADIO_FAVORITE_LIST) \ + X(MYMPD_API_WEBRADIO_FAVORITE_GET_BY_NAME) \ + X(MYMPD_API_WEBRADIO_FAVORITE_GET_BY_URI) \ X(MYMPD_API_WEBRADIO_FAVORITE_RM) \ X(MYMPD_API_WEBRADIO_FAVORITE_SAVE) \ + X(MYMPD_API_WEBRADIO_FAVORITE_SEARCH) \ + X(MYMPD_API_WEBRADIODB_RADIO_GET_BY_NAME) \ + X(MYMPD_API_WEBRADIODB_RADIO_GET_BY_URI) \ + X(MYMPD_API_WEBRADIODB_SEARCH) \ X(MYMPD_API_WEBRADIODB_UPDATE) \ X(TOTAL_API_COUNT) diff --git a/src/lib/m3u.c b/src/lib/m3u.c deleted file mode 100644 index 231d76ff8..000000000 --- a/src/lib/m3u.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - SPDX-License-Identifier: GPL-3.0-or-later - myMPD (c) 2018-2024 Juergen Mang - https://github.com/jcorporation/mympd -*/ - -#include "compile_time.h" -#include "src/lib/m3u.h" - -#include "src/lib/filehandler.h" -#include "src/lib/jsonrpc.h" -#include "src/lib/log.h" -#include "src/lib/sds_extras.h" - -#include -#include - -/** - * Private definitions - */ -static const char *m3ufields_map(sds field); - -/** - * Public functions - */ - -/** - * Appends the extm3u field value to buffer - * @param buffer already allocated sds string to append - * @param field extm3u field to read - * @param filename m3u file to open - * @return pointer to buffer - */ -sds m3u_get_field(sds buffer, const char *field, const char *filename) { - errno = 0; - FILE *fp = fopen(filename, OPEN_FLAGS_READ); - if (fp == NULL) { - MYMPD_LOG_ERROR(NULL, "Can not open file \"%s\"", filename); - MYMPD_LOG_ERRNO(NULL, errno); - return buffer; - } - size_t field_len = strlen(field); - size_t min_line_len = field_len + 2; - sds line = sdsempty(); - int nread = 0; - while ((line = sds_getline(line, fp, LINE_LENGTH_MAX, &nread)) && nread >= 0) { - if (sdslen(line) > min_line_len && - strncmp(line, field, field_len) == 0) - { - const char *ptr = line; - ptr+= field_len + 1; - buffer = sdscat(buffer, ptr); - } - } - FREE_SDS(line); - (void) fclose(fp); - return buffer; -} - -/** - * Converts the m3u to json and appends it to buffer - * @param buffer already allocated sds string to append - * @param filename m3u file to open - * @param m3ufields appends all fields values (lower case) to this sds string if not NULL - * @return pointer to buffer - */ -sds m3u_to_json(sds buffer, const char *filename, sds *m3ufields) { - errno = 0; - FILE *fp = fopen(filename, OPEN_FLAGS_READ); - if (fp == NULL) { - MYMPD_LOG_ERROR(NULL, "Can not open file \"%s\"", filename); - MYMPD_LOG_ERRNO(NULL, errno); - sdsclear(buffer); - return buffer; - } - //check ext m3u header - int nread = 0; - sds line = sds_getline(sdsempty(), fp, LINE_LENGTH_MAX, &nread); - if (strcmp(line, "#EXTM3U") != 0) { - MYMPD_LOG_WARN(NULL, "Invalid ext m3u file"); - FREE_SDS(line); - (void) fclose(fp); - sdsclear(buffer); - return buffer; - } - int line_count = 0; - sds field = sdsempty(); - nread = 0; - while ((line = sds_getline(line, fp, LINE_LENGTH_MAX, &nread)) && nread >= 0) { - if (line[0] == '\0') { - //skip blank lines - continue; - } - if (line_count++) { - buffer = sdscatlen(buffer, ",", 1); - } - if (line[0] != '#') { - //stream uri - buffer = tojson_char(buffer, "StreamUri", line, false); - continue; - } - //skip # char - int i = 1; - sdsclear(field); - while (line[i] != '\0' && - line[i] != ':') - { - field = sds_catjsonchar(field, line[i]); - i++; - } - const char *key = m3ufields_map(field); - if (key[0] == '\0') { - key = field; - } - buffer = sdscatfmt(buffer, "\"%s\":\"", key); - i++; - while (line[i] != '\0') { - buffer = sds_catjsonchar(buffer, line[i]); - if (m3ufields != NULL) { - *m3ufields = sds_catchar(*m3ufields, line[i]); - } - i++; - } - buffer = sdscatlen(buffer, "\"", 1); - } - FREE_SDS(line); - FREE_SDS(field); - (void) fclose(fp); - if (m3ufields != NULL) { - sds_utf8_tolower(*m3ufields); - } - return buffer; -} - -/** - * Private functions - */ - -/** - * Converts the extm3u field name in a better readable name - * @param field extm3u field name - * @return readable name - */ -static const char *m3ufields_map(sds field) { - if (strcmp(field, "EXTGENRE") == 0) { return "Genre"; } - if (strcmp(field, "EXTIMG") == 0) { return "Image"; } - if (strcmp(field, "HOMEPAGE") == 0) { return "Homepage"; } - if (strcmp(field, "COUNTRY") == 0) { return "Country"; } - if (strcmp(field, "STATE") == 0) { return "State"; } - if (strcmp(field, "LANGUAGE") == 0) { return "Language"; } - if (strcmp(field, "DESCRIPTION") == 0) { return "Description"; } - if (strcmp(field, "PLAYLIST") == 0) { return "Name"; } - if (strcmp(field, "CODEC") == 0) { return "Codec"; } - if (strcmp(field, "BITRATE") == 0) { return "Bitrate"; } - return ""; -} diff --git a/src/lib/m3u.h b/src/lib/m3u.h deleted file mode 100644 index 9ae04b38d..000000000 --- a/src/lib/m3u.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - SPDX-License-Identifier: GPL-3.0-or-later - myMPD (c) 2018-2024 Juergen Mang - https://github.com/jcorporation/mympd -*/ - -#ifndef MYMPD_M3U_H -#define MYMPD_M3U_H - -#include "dist/sds/sds.h" - -sds m3u_to_json(sds buffer, const char *filename, sds *m3ufields); -sds m3u_get_field(sds buffer, const char *field, const char *filename); - -#endif diff --git a/src/lib/mympd_state.c b/src/lib/mympd_state.c index ae24a6e7b..80021e127 100644 --- a/src/lib/mympd_state.c +++ b/src/lib/mympd_state.c @@ -45,6 +45,7 @@ void mympd_state_save(struct t_mympd_state *mympd_state, bool free_data) { mympd_api_home_file_save(&mympd_state->home_list, mympd_state->config->workdir); mympd_api_timer_file_save(&mympd_state->timer_list, mympd_state->config->workdir); mympd_api_trigger_file_save(&mympd_state->trigger_list, mympd_state->config->workdir); + webradios_save_to_disk(mympd_state->config, mympd_state->webradio_favorites, FILENAME_WEBRADIO_FAVORITES); if (free_data == true) { mympd_state_free(mympd_state); } @@ -126,8 +127,9 @@ void mympd_state_default(struct t_mympd_state *mympd_state, struct t_config *con mympd_state->last_played_count = MYMPD_LAST_PLAYED_COUNT; //poll fds event_pfd_init(&mympd_state->pfds); - //webradioDB + //webradios mympd_state->webradiodb = webradios_new(); + mympd_state->webradio_favorites = webradios_new(); } /** @@ -158,6 +160,7 @@ void mympd_state_free(struct t_mympd_state *mympd_state) { cache_free(&mympd_state->album_cache); //webradioDB webradios_free(mympd_state->webradiodb); + webradios_free(mympd_state->webradio_favorites); //sds FREE_SDS(mympd_state->tag_list_search); FREE_SDS(mympd_state->tag_list_browse); diff --git a/src/lib/mympd_state.h b/src/lib/mympd_state.h index c64e451fe..c8e87f6e0 100644 --- a/src/lib/mympd_state.h +++ b/src/lib/mympd_state.h @@ -263,7 +263,8 @@ struct t_mympd_state { sds info_txt_name; //!< name of album info files struct t_cache album_cache; //!< the album cache created by the mpd_worker thread unsigned last_played_count; //!< number of songs to keep in the last played list (disk + memory) - struct t_webradios *webradiodb; //!< webradioDB + struct t_webradios *webradiodb; //!< WebradioDB + struct t_webradios *webradio_favorites; //!< webradio favorites }; /** diff --git a/src/lib/utility.c b/src/lib/utility.c index f9e7c4fb8..cafa76573 100644 --- a/src/lib/utility.c +++ b/src/lib/utility.c @@ -229,8 +229,8 @@ struct t_mympd_uris { }; const struct t_mympd_uris mympd_uris[] = { - {"mympd://webradio/", "/browse/"DIR_WORK_WEBRADIOS"/"}, - {"mympd://", "/"}, + {"mympd://webradio/", "/webradio?uri=" }, + {"mympd://", "/"}, {NULL, NULL} }; diff --git a/src/lib/webradio.c b/src/lib/webradio.c index dd9ff854d..94defbabd 100644 --- a/src/lib/webradio.c +++ b/src/lib/webradio.c @@ -125,8 +125,6 @@ bool webradios_save_to_disk(struct t_config *config, struct t_webradios *webradi while (raxNext(&iter)) { mpack_build_map(&writer); struct t_webradio_data *data = (struct t_webradio_data *)iter.data; - mpack_write_cstr(&writer, "Key"); - mpack_write_str(&writer, (char *)iter.key, (uint32_t)iter.key_len); mpack_write_kv(&writer, "Name", data->name); mpack_write_kv(&writer, "Image", data->image); mpack_write_kv(&writer, "Homepage", data->homepage); @@ -212,13 +210,11 @@ bool webradios_read_from_disk(struct t_config *config, struct t_webradios *webra mpack_tree_parse(&tree); mpack_node_t root = mpack_tree_root(&tree); size_t len = mpack_node_array_length(root); - sds key = sdsempty(); sds uri = sdsempty(); sds codec = sdsempty(); for (size_t i = 0; i < len; i++) { mpack_node_t entry = mpack_node_array_at(root, i); struct t_webradio_data *data = webradio_data_new(); - key = mpackstr_sdscat(key, entry, "Key"); data->name = mpackstr_sds(entry, "Name"); data->image = mpackstr_sds(entry, "Image"); data->homepage = mpackstr_sds(entry, "Homepage"); @@ -248,7 +244,7 @@ bool webradios_read_from_disk(struct t_config *config, struct t_webradios *webra sdsclear(uri); sdsclear(codec); } - if (raxTryInsert(webradios->db, (unsigned char *)key, strlen(key), data, NULL) == 1) { + if (raxTryInsert(webradios->db, (unsigned char *)data->name, strlen(data->name), data, NULL) == 1) { // write uri index struct t_list_node *current = data->uris.head; while (current != NULL) { @@ -258,12 +254,10 @@ bool webradios_read_from_disk(struct t_config *config, struct t_webradios *webra } else { // insert error - MYMPD_LOG_ERROR(NULL, "Duplicate WebradioDB key found: %s", key); + MYMPD_LOG_ERROR(NULL, "Duplicate WebradioDB key found: %s", data->name); webradio_data_free(data); } - sdsclear(key); } - FREE_SDS(key); FREE_SDS(uri); FREE_SDS(codec); // clean up and check for errors diff --git a/src/main.c b/src/main.c index 9d86ee81b..2b42b8a89 100644 --- a/src/main.c +++ b/src/main.c @@ -272,7 +272,6 @@ static const struct t_subdirs_entry workdir_subdirs[] = { {DIR_WORK_STATE, "State dir"}, {DIR_WORK_STATE_DEFAULT, "Default partition dir"}, {DIR_WORK_TAGS, "Tags cache dir"}, - {DIR_WORK_WEBRADIOS, "Webradio dir"}, {NULL, NULL} }; diff --git a/src/mympd_api/mympd_api.c b/src/mympd_api/mympd_api.c index b29fe9568..cb511e23a 100644 --- a/src/mympd_api/mympd_api.c +++ b/src/mympd_api/mympd_api.c @@ -83,6 +83,7 @@ void *mympd_api_loop(void *arg_config) { mympd_api_timer_add(&mympd_state->timer_list, TIMER_WEBRADIODB_UPDATE_OFFSET, TIMER_WEBRADIODB_UPDATE_INTERVAL, timer_handler_by_id, TIMER_ID_WEBRADIODB_UPDATE, NULL); } + webradios_read_from_disk(mympd_state->config, mympd_state->webradio_favorites, FILENAME_WEBRADIO_FAVORITES); // set timers MYMPD_LOG_DEBUG(NULL, "Adding timer for cache cropping to execute periodic each day"); mympd_api_timer_add(&mympd_state->timer_list, TIMER_DISK_CACHE_CLEANUP_OFFSET, TIMER_DISK_CACHE_CLEANUP_INTERVAL, diff --git a/src/mympd_api/mympd_api_handler.c b/src/mympd_api/mympd_api_handler.c index bc79233be..25ceb9619 100644 --- a/src/mympd_api/mympd_api_handler.c +++ b/src/mympd_api/mympd_api_handler.c @@ -58,7 +58,8 @@ #include "src/mympd_api/timer_handlers.h" #include "src/mympd_api/trigger.h" #include "src/mympd_api/volume.h" -#include "src/mympd_api/webradios.h" +#include "src/mympd_api/webradio_favorites.h" +#include "src/mympd_api/webradiodb.h" #ifdef MYMPD_ENABLE_LUA #include "src/mympd_api/lua_mympd_state.h" @@ -168,15 +169,6 @@ void mympd_api_handler(struct t_mympd_state *mympd_state, struct t_partition_sta mympd_state->album_cache.building = false; } break; - // WebradioDB - case INTERNAL_API_WEBRADIODB_CREATED: - if (request->extra != NULL) { - if (mympd_state->webradiodb != NULL) { - webradios_free(mympd_state->webradiodb); - } - mympd_state->webradiodb = (struct t_webradios *)request->extra; - } - break; // Album cache case INTERNAL_API_ALBUMCACHE_SKIPPED: case INTERNAL_API_ALBUMCACHE_ERROR: @@ -1099,10 +1091,10 @@ void mympd_api_handler(struct t_mympd_state *mympd_state, struct t_partition_sta if (sdslen(sds_buf1) == 0 && // no search expression strcmp(sds_buf2, "Priority") == 0) // sort by priority { - response->data = mympd_api_queue_list(partition_state, mympd_state->stickerdb, response->data, request->id, uint_buf1, uint_buf2, &tagcols); + response->data = mympd_api_queue_list(mympd_state, partition_state, response->data, request->id, uint_buf1, uint_buf2, &tagcols); } else { - response->data = mympd_api_queue_search(partition_state, mympd_state->stickerdb, response->data, request->id, + response->data = mympd_api_queue_search(mympd_state, partition_state, response->data, request->id, sds_buf1, sds_buf2, bool_buf1, uint_buf1, uint_buf2, &tagcols); } } @@ -1588,53 +1580,90 @@ void mympd_api_handler(struct t_mympd_state *mympd_state, struct t_partition_sta case MYMPD_API_MOUNT_URLHANDLER_LIST: response->data = mympd_api_mounts_urlhandler_list(partition_state, response->data, request->id); break; + // WebradioDB + case INTERNAL_API_WEBRADIODB_CREATED: + if (request->extra != NULL) { + if (mympd_state->webradiodb != NULL) { + webradios_free(mympd_state->webradiodb); + } + mympd_state->webradiodb = (struct t_webradios *)request->extra; + } + break; + case MYMPD_API_WEBRADIODB_RADIO_GET_BY_NAME: + if (json_get_string(request->data, "$.params.name", 1, FILENAME_LEN_MAX, &sds_buf1, vcb_isname, &parse_error) == true) { + response->data = mympd_api_webradio_favorite_get_by_name(mympd_state->webradio_favorites, response->data, request->id, sds_buf1); + } + break; + case MYMPD_API_WEBRADIODB_RADIO_GET_BY_URI: + if (json_get_string(request->data, "$.params.uri", 1, FILENAME_LEN_MAX, &sds_buf1, vcb_isuri, &parse_error) == true) { + response->data = mympd_api_webradio_favorite_get_by_uri(mympd_state->webradio_favorites, response->data, request->id, sds_buf1); + } + break; + case MYMPD_API_WEBRADIODB_SEARCH: + if (json_get_uint(request->data, "$.params.offset", 0, MPD_PLAYLIST_LENGTH_MAX, &uint_buf1, &parse_error) == true && + json_get_uint(request->data, "$.params.limit", MPD_RESULTS_MIN, MPD_RESULTS_MAX, &uint_buf2, &parse_error) == true && + json_get_string(request->data, "$.params.expression", 0, EXPRESSION_LEN_MAX, &sds_buf1, vcb_isname, &parse_error) == true) + { + //TODO + } + break; // webradio favorites - case MYMPD_API_WEBRADIO_FAVORITE_LIST: + case MYMPD_API_WEBRADIO_FAVORITE_SEARCH: if (json_get_uint(request->data, "$.params.offset", 0, MPD_PLAYLIST_LENGTH_MAX, &uint_buf1, &parse_error) == true && json_get_uint(request->data, "$.params.limit", MPD_RESULTS_MIN, MPD_RESULTS_MAX, &uint_buf2, &parse_error) == true && json_get_string(request->data, "$.params.searchstr", 0, NAME_LEN_MAX, &sds_buf1, vcb_isname, &parse_error) == true) { - response->data = mympd_api_webradio_list(config->workdir, response->data, request->cmd_id, sds_buf1, uint_buf1, uint_buf2); + //TODO } break; - case MYMPD_API_WEBRADIO_FAVORITE_GET: - if (json_get_string(request->data, "$.params.filename", 1, FILENAME_LEN_MAX, &sds_buf1, vcb_isfilename, &parse_error) == true) { - response->data = mympd_api_webradio_get(config->workdir, response->data, request->cmd_id, sds_buf1); + case MYMPD_API_WEBRADIO_FAVORITE_GET_BY_NAME: + if (json_get_string(request->data, "$.params.name", 1, NAME_LEN_MAX, &sds_buf1, vcb_isname, &parse_error) == true) { + response->data = mympd_api_webradio_favorite_get_by_name(mympd_state->webradio_favorites, response->data, request->id, sds_buf1); } break; - case MYMPD_API_WEBRADIO_FAVORITE_SAVE: - if (json_get_string(request->data, "$.params.name", 1, FILENAME_LEN_MAX, &sds_buf1, vcb_isname, &parse_error) == true && - json_get_string(request->data, "$.params.streamUri", 1, FILEPATH_LEN_MAX, &sds_buf2, vcb_isuri, &parse_error) == true && - json_get_string(request->data, "$.params.streamUriOld", 0, FILEPATH_LEN_MAX, &sds_buf3, vcb_isuri, &parse_error) == true && - json_get_string(request->data, "$.params.genre", 0, FILENAME_LEN_MAX, &sds_buf4, vcb_isname, &parse_error) == true && - json_get_string(request->data, "$.params.image", 0, FILEPATH_LEN_MAX, &sds_buf5, vcb_isuri, &parse_error) == true && - json_get_string(request->data, "$.params.homepage", 0, FILEPATH_LEN_MAX, &sds_buf6, vcb_isuri, &parse_error) == true && - json_get_string(request->data, "$.params.country", 0, FILEPATH_LEN_MAX, &sds_buf7, vcb_isname, &parse_error) == true && - json_get_string(request->data, "$.params.language", 0, FILEPATH_LEN_MAX, &sds_buf8, vcb_isname, &parse_error) == true && - json_get_string(request->data, "$.params.codec", 0, FILEPATH_LEN_MAX, &sds_buf9, vcb_isprint, &parse_error) == true && + case MYMPD_API_WEBRADIO_FAVORITE_GET_BY_URI: + if (json_get_string(request->data, "$.params.name", 1, NAME_LEN_MAX, &sds_buf1, vcb_isuri, &parse_error) == true) { + response->data = mympd_api_webradio_favorite_get_by_uri(mympd_state->webradio_favorites, response->data, request->id, sds_buf1); + } + break; + case MYMPD_API_WEBRADIO_FAVORITE_SAVE: { + struct t_webradio_data *webradio = webradio_data_new(); + if (json_get_string(request->data, "$.params.name", 1, NAME_LEN_MAX, &webradio->name, vcb_isname, &parse_error) == true && + json_get_string(request->data, "$.params.streamUri", 1, FILEPATH_LEN_MAX, &sds_buf1, vcb_isuri, &parse_error) == true && + json_get_string(request->data, "$.params.oldName", 0, FILEPATH_LEN_MAX, &sds_buf2, vcb_isname, &parse_error) == true && + json_get_array_string(request->data, "$.params.genres", &webradio->genres, vcb_isname, 64, &parse_error) == true && + json_get_string(request->data, "$.params.image", 0, FILEPATH_LEN_MAX, &webradio->image, vcb_isuri, &parse_error) == true && + json_get_string(request->data, "$.params.homepage", 0, FILEPATH_LEN_MAX, &webradio->homepage, vcb_isuri, &parse_error) == true && + json_get_string(request->data, "$.params.country", 0, FILEPATH_LEN_MAX, &webradio->country, vcb_isname, &parse_error) == true && + json_get_array_string(request->data, "$.params.languages", &webradio->languages, vcb_isname, 64, &parse_error) == true && + json_get_string(request->data, "$.params.codec", 0, FILEPATH_LEN_MAX, &sds_buf3, vcb_isprint, &parse_error) == true && json_get_int(request->data, "$.params.bitrate", 0, 2048, &int_buf1, &parse_error) == true && - json_get_string(request->data, "$.params.description", 0, CONTENT_LEN_MAX, &sds_buf0, vcb_isname, &parse_error) == true && - json_get_string(request->data, "$.params.state", 0, CONTENT_LEN_MAX, &sds_buf10, vcb_isname, &parse_error) == true) + json_get_string(request->data, "$.params.description", 0, CONTENT_LEN_MAX, &webradio->description, vcb_isname, &parse_error) == true && + json_get_string(request->data, "$.params.state", 0, CONTENT_LEN_MAX, &webradio->state, vcb_isname, &parse_error) == true) { - rc = mympd_api_webradio_save(config->workdir, sds_buf1, sds_buf2, sds_buf3, sds_buf4, sds_buf5, sds_buf6, sds_buf7, - sds_buf8, sds_buf9, int_buf1, sds_buf0, sds_buf10); + list_push(&webradio->uris, sds_buf1, int_buf1, sds_buf3, NULL); + rc = mympd_api_webradio_favorite_save(mympd_state->webradio_favorites, webradio, sds_buf2); response->data = jsonrpc_respond_with_message_or_error(response->data, request->cmd_id, request->id, rc, JSONRPC_FACILITY_DATABASE, "Webradio favorite successfully saved", "Could not save webradio favorite"); + if (rc == false) { + webradio_data_free(webradio); + } } break; + } case MYMPD_API_WEBRADIO_FAVORITE_RM: { - struct t_list filenames; - list_init(&filenames); - if (json_get_array_string(request->data, "$.params.filenames", &filenames, vcb_isfilename, MPD_COMMANDS_MAX, &parse_error) == true) { - if (filenames.length == 0) { + struct t_list ids; + list_init(&ids); + if (json_get_array_string(request->data, "$.params.name", &ids, vcb_isname, MPD_COMMANDS_MAX, &parse_error) == true) { + if (ids.length == 0) { response->data = jsonrpc_respond_message(response->data, request->cmd_id, request->id, - JSONRPC_FACILITY_QUEUE, JSONRPC_SEVERITY_ERROR, "No webradios provided"); + JSONRPC_FACILITY_QUEUE, JSONRPC_SEVERITY_ERROR, "No webradio favorites provided"); } - rc = mympd_api_webradio_delete(config->workdir, &filenames); + rc = mympd_api_webradio_favorite_delete(mympd_state->webradio_favorites, &ids); response->data = jsonrpc_respond_with_ok_or_error(response->data, request->cmd_id, request->id, rc, JSONRPC_FACILITY_DATABASE, "Could not delete webradio favorite"); } - list_clear(&filenames); + list_clear(&ids); break; } // unhandled method diff --git a/src/mympd_api/queue.c b/src/mympd_api/queue.c index 583edbf17..9b93920c3 100644 --- a/src/mympd_api/queue.c +++ b/src/mympd_api/queue.c @@ -19,7 +19,7 @@ #include "src/mpd_client/stickerdb.h" #include "src/mpd_client/tags.h" #include "src/mympd_api/sticker.h" -#include "src/mympd_api/webradios.h" +#include "src/mympd_api/webradio.h" #include #include @@ -29,7 +29,7 @@ */ static bool add_queue_search_adv_params(struct t_partition_state *partition_state, sds sort, bool sortdesc, unsigned offset, unsigned limit); -sds print_queue_entry(struct t_partition_state *partition_state, struct t_stickerdb_state *stickerdb, +sds print_queue_entry(struct t_mympd_state *mympd_state, struct t_partition_state *partition_state, sds buffer, const struct t_fields *tagcols, struct mpd_song *song); /** @@ -595,8 +595,8 @@ sds mympd_api_queue_crop(struct t_partition_state *partition_state, sds buffer, /** * Lists the queue, this is faster for older MPD servers than the search function below. + * @param mympd_state pointer to mympd_state * @param partition_state pointer to partition state - * @param stickerdb pointer to stickerdb state * @param buffer already allocated sds string to append the response * @param request_id jsonrpc id * @param offset offset for the list @@ -604,7 +604,7 @@ sds mympd_api_queue_crop(struct t_partition_state *partition_state, sds buffer, * @param tagcols columns to print * @return pointer to buffer */ -sds mympd_api_queue_list(struct t_partition_state *partition_state, struct t_stickerdb_state *stickerdb, +sds mympd_api_queue_list(struct t_mympd_state *mympd_state, struct t_partition_state *partition_state, sds buffer, unsigned request_id, unsigned offset, unsigned limit, const struct t_fields *tagcols) { enum mympd_cmd_ids cmd_id = MYMPD_API_QUEUE_SEARCH; @@ -618,7 +618,7 @@ sds mympd_api_queue_list(struct t_partition_state *partition_state, struct t_sti if (partition_state->mpd_state->feat.stickers == true && tagcols->stickers.len > 0) { - stickerdb_exit_idle(stickerdb); + stickerdb_exit_idle(mympd_state->stickerdb); } unsigned real_limit = offset + limit; if (mpd_send_list_queue_range_meta(partition_state->conn, offset, real_limit) == true) { @@ -631,7 +631,7 @@ sds mympd_api_queue_list(struct t_partition_state *partition_state, struct t_sti if (entities_returned++) { buffer = sdscatlen(buffer, ",", 1); } - buffer = print_queue_entry(partition_state, stickerdb, buffer, tagcols, song); + buffer = print_queue_entry(mympd_state, partition_state, buffer, tagcols, song); total_time += mpd_song_get_duration(song); mpd_song_free(song); } @@ -647,7 +647,7 @@ sds mympd_api_queue_list(struct t_partition_state *partition_state, struct t_sti if (partition_state->mpd_state->feat.stickers == true && tagcols->stickers.len > 0) { - stickerdb_enter_idle(stickerdb); + stickerdb_enter_idle(mympd_state->stickerdb); } mympd_check_error_and_recover_respond(partition_state, &buffer, cmd_id, request_id, "mpd_send_list_queue_range_meta"); return buffer; @@ -655,8 +655,8 @@ sds mympd_api_queue_list(struct t_partition_state *partition_state, struct t_sti /** * Searches the queue + * @param mympd_state pointer to mympd_state * @param partition_state pointer to partition state - * @param stickerdb pointer to stickerdb state * @param buffer already allocated sds string to append the response * @param request_id jsonrpc id * @param expression mpd filter expression @@ -667,7 +667,7 @@ sds mympd_api_queue_list(struct t_partition_state *partition_state, struct t_sti * @param tagcols columns to print * @return pointer to buffer */ -sds mympd_api_queue_search(struct t_partition_state *partition_state, struct t_stickerdb_state *stickerdb, +sds mympd_api_queue_search(struct t_mympd_state *mympd_state, struct t_partition_state *partition_state, sds buffer, unsigned request_id, sds expression, sds sort, bool sortdesc, unsigned offset, unsigned limit, const struct t_fields *tagcols) { @@ -692,7 +692,7 @@ sds mympd_api_queue_search(struct t_partition_state *partition_state, struct t_s if (partition_state->mpd_state->feat.stickers == true && tagcols->stickers.len > 0) { - stickerdb_exit_idle(stickerdb); + stickerdb_exit_idle(mympd_state->stickerdb); } if (mpd_search_commit(partition_state->conn)) { buffer = jsonrpc_respond_start(buffer, cmd_id, request_id); @@ -709,7 +709,7 @@ sds mympd_api_queue_search(struct t_partition_state *partition_state, struct t_s if (entities_returned++) { buffer= sdscatlen(buffer, ",", 1); } - buffer = print_queue_entry(partition_state, stickerdb, buffer, tagcols, song); + buffer = print_queue_entry(mympd_state, partition_state, buffer, tagcols, song); total_time += mpd_song_get_duration(song); } mpd_song_free(song); @@ -739,7 +739,7 @@ sds mympd_api_queue_search(struct t_partition_state *partition_state, struct t_s if (partition_state->mpd_state->feat.stickers == true && tagcols->stickers.len > 0) { - stickerdb_enter_idle(stickerdb); + stickerdb_enter_idle(mympd_state->stickerdb); } if (mympd_check_error_and_recover_respond(partition_state, &buffer, cmd_id, request_id, "mpd_search_queue_songs") == false) { return buffer; @@ -808,6 +808,7 @@ static bool add_queue_search_adv_params(struct t_partition_state *partition_stat /** * Prints a queue entry as an json object string + * @param mympd_state pointer to mympd_state * @param partition_state pointer to partition state * @param stickerdb pointer to stickerdb state * @param buffer already allocated sds string to append the response @@ -815,7 +816,7 @@ static bool add_queue_search_adv_params(struct t_partition_state *partition_stat * @param song pointer to mpd song struct * @return pointer to buffer */ -sds print_queue_entry(struct t_partition_state *partition_state, struct t_stickerdb_state *stickerdb, +sds print_queue_entry(struct t_mympd_state *mympd_state, struct t_partition_state *partition_state, sds buffer, const struct t_fields *tagcols, struct mpd_song *song) { buffer = sdscatlen(buffer, "{", 1); @@ -829,11 +830,11 @@ sds print_queue_entry(struct t_partition_state *partition_state, struct t_sticke const char *uri = mpd_song_get_uri(song); buffer = sdscatlen(buffer, ",", 1); if (is_streamuri(uri) == true) { - sds webradio = get_webradio_from_uri(partition_state->config->workdir, uri); + sds webradio = webradio_from_uri_tojson(mympd_state, uri); if (sdslen(webradio) > 0) { - buffer = sdscat(buffer, "\"webradio\":{"); + buffer = sdscat(buffer, "\"webradio\":"); buffer = sdscatsds(buffer, webradio); - buffer = sdscatlen(buffer, "},", 2); + buffer = sdscatlen(buffer, ",", 1); buffer = tojson_char(buffer, "Type", "webradio", false); } else { @@ -847,7 +848,7 @@ sds print_queue_entry(struct t_partition_state *partition_state, struct t_sticke if (partition_state->mpd_state->feat.stickers == true && tagcols->stickers.len > 0) { - buffer = mympd_api_sticker_get_print_batch(buffer, stickerdb, uri, &tagcols->stickers); + buffer = mympd_api_sticker_get_print_batch(buffer, mympd_state->stickerdb, uri, &tagcols->stickers); } buffer = sdscatlen(buffer, "}", 1); return buffer; diff --git a/src/mympd_api/queue.h b/src/mympd_api/queue.h index ec2c5932c..3f62f64e4 100644 --- a/src/mympd_api/queue.h +++ b/src/mympd_api/queue.h @@ -11,13 +11,13 @@ #include "src/lib/mympd_state.h" bool mympd_api_queue_save(struct t_partition_state *partition_state, sds name, sds mode, sds *error); -sds mympd_api_queue_list(struct t_partition_state *partition_state, struct t_stickerdb_state *stickerdb, +sds mympd_api_queue_list(struct t_mympd_state *mympd_state, struct t_partition_state *partition_state, sds buffer, unsigned request_id, unsigned offset, unsigned limit, const struct t_fields *tagcols); -sds mympd_api_queue_crop(struct t_partition_state *partition_state, sds buffer, enum mympd_cmd_ids cmd_id, - unsigned request_id, bool or_clear); -sds mympd_api_queue_search(struct t_partition_state *partition_state, struct t_stickerdb_state *stickerdb, +sds mympd_api_queue_search(struct t_mympd_state *mympd_state, struct t_partition_state *partition_state, sds buffer, unsigned request_id, sds expression, sds sort, bool sortdesc, unsigned offset, unsigned limit, const struct t_fields *tagcols); +sds mympd_api_queue_crop(struct t_partition_state *partition_state, sds buffer, enum mympd_cmd_ids cmd_id, + unsigned request_id, bool or_clear); bool mympd_api_queue_prio_set(struct t_partition_state *partition_state, struct t_list *song_ids, unsigned priority, sds *error); bool mympd_api_queue_prio_set_highest(struct t_partition_state *partition_state, struct t_list *song_ids, sds *error); bool mympd_api_queue_rm_song_ids(struct t_partition_state *partition_state, struct t_list *song_ids, sds *error); diff --git a/src/mympd_api/status.c b/src/mympd_api/status.c index 9f5b1a235..162a9cb1f 100644 --- a/src/mympd_api/status.c +++ b/src/mympd_api/status.c @@ -21,7 +21,7 @@ #include "src/mpd_client/volume.h" #include "src/mympd_api/extra_media.h" #include "src/mympd_api/sticker.h" -#include "src/mympd_api/webradios.h" +#include "src/mympd_api/webradio.h" /** * Private definitions @@ -354,11 +354,10 @@ sds mympd_api_status_current_song(struct t_mympd_state *mympd_state, struct t_pa buffer = json_comma(buffer); buffer = mympd_api_get_extra_media(buffer, partition_state->mpd_state, mympd_state->booklet_name, mympd_state->info_txt_name, uri, false); if (is_streamuri(uri) == true) { - sds webradio = get_webradio_from_uri(partition_state->config->workdir, uri); + sds webradio = webradio_from_uri_tojson(mympd_state, uri); if (sdslen(webradio) > 0) { - buffer = sdscat(buffer, ",\"webradio\":{"); + buffer = sdscat(buffer, ",\"webradio\":"); buffer = sdscatsds(buffer, webradio); - buffer = sdscatlen(buffer, "}", 1); } FREE_SDS(webradio); } diff --git a/src/mympd_api/webradio.c b/src/mympd_api/webradio.c new file mode 100644 index 000000000..0de0314aa --- /dev/null +++ b/src/mympd_api/webradio.c @@ -0,0 +1,81 @@ +/* + SPDX-License-Identifier: GPL-3.0-or-later + myMPD (c) 2018-2024 Juergen Mang + https://github.com/jcorporation/mympd +*/ + +#include "compile_time.h" +#include "src/mympd_api/webradio.h" + +#include "src/lib/jsonrpc.h" + +#include + +/** + * Search webradio by uri in favorites and WebradioDB and print json response + * @param mympd_state pointer to mympd_state + * @param uri Uri to search for + * @return newly allocated sds string, or empty string on error + */ +sds webradio_from_uri_tojson(struct t_mympd_state *mympd_state, const char *uri) { + sds buffer = sdsempty(); + struct t_webradio_data *webradio = webradio_by_uri(mympd_state, uri); + if (webradio != NULL) { + buffer = webradio_print(webradio, buffer); + } + return buffer; +} + +/** + * Prints a webradio entry + * @param webradio webradio data struct to print + * @param buffer already allocated buffer to append the data + * @return pointer to buffer + */ +sds webradio_print(struct t_webradio_data *webradio, sds buffer) { + buffer = sdscatlen(buffer, "{", 1); + buffer = tojson_sds(buffer, "Name", webradio->name, true); + buffer = tojson_sds(buffer, "Image", webradio->homepage, true); + buffer = tojson_sds(buffer, "Homepage", webradio->homepage, true); + buffer = tojson_sds(buffer, "Country", webradio->country, true); + buffer = tojson_sds(buffer, "State", webradio->state, true); + buffer = tojson_sds(buffer, "Description", webradio->description, true); + struct t_list_node *current = webradio->uris.head; + buffer = tojson_sds(buffer, "StreamUri", current->key, true); + buffer = tojson_sds(buffer, "Codec", current->value_p, true); + buffer = tojson_int64(buffer, "Bitrate", current->value_i, true); + buffer = sdscat(buffer, "\"alternativeStreams\":{"); + current = current->next; + unsigned i = 0; + while (current != NULL) { + if (i++) { + buffer = sdscatlen(buffer, ",", 1); + } + buffer = tojson_sds(buffer, "StreamUri", current->key, true); + buffer = tojson_sds(buffer, "Codec", current->value_p, true); + buffer = tojson_int64(buffer, "Bitrate", current->value_i, true); + } + buffer = sdscat(buffer, "},\"Genres\":["); + list_to_json_array(buffer, &webradio->genres); + buffer = sdscat(buffer, "],\"Languages\":["); + list_to_json_array(buffer, &webradio->languages); + buffer = sdscatlen(buffer, "]}", 2); + return buffer; +} + +/** + * Search webradio by uri in favorites and WebradioDB + * @param mympd_state pointer to mympd_state + * @param uri Uri to search for + * @return pointer to webradio data or NULL on error + */ +struct t_webradio_data *webradio_by_uri(struct t_mympd_state *mympd_state, const char *uri) { + void *data = raxFind(mympd_state->webradio_favorites->idx_uris, (unsigned char *)uri, strlen(uri)); + if (data == raxNotFound) { + raxFind(mympd_state->webradio_favorites->idx_uris, (unsigned char *)uri, strlen(uri)); + } + if (data == raxNotFound) { + return NULL; + } + return (struct t_webradio_data *)data; +} diff --git a/src/mympd_api/webradio.h b/src/mympd_api/webradio.h new file mode 100644 index 000000000..0a6efc200 --- /dev/null +++ b/src/mympd_api/webradio.h @@ -0,0 +1,17 @@ +/* + SPDX-License-Identifier: GPL-3.0-or-later + myMPD (c) 2018-2024 Juergen Mang + https://github.com/jcorporation/mympd +*/ + +#ifndef MYMPD_API_WEBRADIO_H +#define MYMPD_API_WEBRADIO_H + +#include "dist/sds/sds.h" +#include "src/lib/mympd_state.h" + +sds webradio_from_uri_tojson(struct t_mympd_state *mympd_state, const char *uri); +sds webradio_print(struct t_webradio_data *webradio, sds buffer); +struct t_webradio_data *webradio_by_uri(struct t_mympd_state *mympd_state, const char *uri); + +#endif diff --git a/src/mympd_api/webradio_favorites.c b/src/mympd_api/webradio_favorites.c new file mode 100644 index 000000000..d575bd9b7 --- /dev/null +++ b/src/mympd_api/webradio_favorites.c @@ -0,0 +1,148 @@ +/* + SPDX-License-Identifier: GPL-3.0-or-later + myMPD (c) 2018-2024 Juergen Mang + https://github.com/jcorporation/mympd +*/ + +#include "compile_time.h" +#include "src/mympd_api/webradio_favorites.h" + +#include "dist/rax/rax.h" +#include "src/lib/api.h" +#include "src/lib/jsonrpc.h" +#include "src/lib/utility.h" +#include "src/mympd_api/webradio.h" + +#include +#include + +/** + * Prints a webradio favorite as jsonrpc response + * @param workdir working directory + * @param buffer already allocated sds string to append the response + * @param request_id jsonrpc request id + * @param name webradio name + * @return pointer to buffer + */ +sds mympd_api_webradio_favorite_get_by_name(struct t_webradios *webradio_favorites, sds buffer, unsigned request_id, sds name) { + enum mympd_cmd_ids cmd_id = MYMPD_API_WEBRADIO_FAVORITE_GET_BY_NAME; + void *data = raxFind(webradio_favorites->db, (unsigned char *)name, strlen(name)); + if (data == raxNotFound) { + buffer = jsonrpc_respond_message(buffer, cmd_id, request_id, + JSONRPC_FACILITY_DATABASE, JSONRPC_SEVERITY_ERROR, "Webradio favorite not found"); + return buffer; + } + struct t_webradio_data *webradio = (struct t_webradio_data *)data; + buffer = jsonrpc_respond_start(buffer, cmd_id, request_id); + buffer = sdscat(buffer, "\"data\":"); + buffer = webradio_print(webradio, buffer); + buffer = jsonrpc_end(buffer); + return buffer; +} + +/** + * Prints a webradio favorite as jsonrpc response + * @param workdir working directory + * @param buffer already allocated sds string to append the response + * @param request_id jsonrpc request id + * @param uri webradio stream uri + * @return pointer to buffer + */ +sds mympd_api_webradio_favorite_get_by_uri(struct t_webradios *webradio_favorites, sds buffer, unsigned request_id, sds uri) { + enum mympd_cmd_ids cmd_id = MYMPD_API_WEBRADIO_FAVORITE_GET_BY_URI; + void *data = raxFind(webradio_favorites->idx_uris, (unsigned char *)uri, strlen(uri)); + if (data == raxNotFound) { + buffer = jsonrpc_respond_message(buffer, cmd_id, request_id, + JSONRPC_FACILITY_DATABASE, JSONRPC_SEVERITY_ERROR, "Webradio favorite not found"); + return buffer; + } + struct t_webradio_data *webradio = (struct t_webradio_data *)data; + buffer = jsonrpc_respond_start(buffer, cmd_id, request_id); + buffer = sdscat(buffer, "\"data\":"); + buffer = webradio_print(webradio, buffer); + buffer = jsonrpc_end(buffer); + return buffer; +} + +/** + * Prints the webradio list as a jsonrpc response + * @param webradio_favorites Webradio favorites struct + * @param buffer already allocated sds string to append the response + * @param request_id jsonrpc request id + * @param expression string to search + * @param offset offset for the list + * @param limit maximum entries to print + * @return pointer to buffer + */ +sds mympd_api_webradio_favorites_search(struct t_webradios *webradio_favorites, sds buffer, unsigned request_id, + sds expression, unsigned offset, unsigned limit) +{ + enum mympd_cmd_ids cmd_id = MYMPD_API_WEBRADIO_FAVORITE_SEARCH; + buffer = jsonrpc_respond_start(buffer, cmd_id, request_id); + buffer = sdscat(buffer, "\"data\":["); + //TODO + (void) webradio_favorites; + (void) expression; + (void) offset; + (void) limit; + buffer = sdscatlen(buffer, "],", 2); + //buffer = tojson_uint64(buffer, "totalEntities", webradios->numele, true); + //buffer = tojson_uint(buffer, "returnedEntities", entities_returned, false); + buffer = jsonrpc_end(buffer); + return buffer; +} + +/** + * Saves a webradio favorite + * @param webradio_favorites Webradio favorites struct + * @param webradio webradio struct to save + * @param old_name old webradio name + * @return true on success, else false + */ +bool mympd_api_webradio_favorite_save(struct t_webradios *webradio_favorites, struct t_webradio_data *webradio, sds old_name) { + if (webradio->uris.head == NULL) { + return false; + } + if (sdslen(old_name) > 0) { + struct t_list old_names; + list_push(&old_names, old_name, 0, NULL, NULL); + if (mympd_api_webradio_favorite_delete(webradio_favorites, &old_names) == false) { + return false; + } + list_clear(&old_names); + } + + sds id = sdsdup(webradio->uris.head->key); + sanitize_filename(id); + if (raxTryInsert(webradio_favorites->db, (unsigned char *)id, sdslen(id), webradio, NULL) == 1) { + // write uri index + raxTryInsert(webradio_favorites->idx_uris, (unsigned char *)webradio->uris.head->key, sdslen(webradio->uris.head->key), webradio, NULL); + return true; + } + return false; +} + +/** + * Deletes webradio favorite + * @param webradio_favorites Webradio favorites struct + * @param names webradio ids to delete + * @return true on success, else false + */ +bool mympd_api_webradio_favorite_delete(struct t_webradios *webradio_favorites, struct t_list *names) { + bool rc = true; + struct t_list_node *current = names->head; + while (current != NULL) { + void *data = NULL; + if (raxRemove(webradio_favorites->db, (unsigned char *)current->key, sdslen(current->key), &data) == 1) { + struct t_webradio_data *webradio = (struct t_webradio_data *)data; + // remove uri index + raxRemove(webradio_favorites->idx_uris, (unsigned char *)webradio->uris.head->key, sdslen(webradio->uris.head->key), NULL); + webradio_data_free(webradio); + } + else { + rc = false; + } + current = current->next; + } + return rc; +} diff --git a/src/mympd_api/webradio_favorites.h b/src/mympd_api/webradio_favorites.h new file mode 100644 index 000000000..6b1eb7c5e --- /dev/null +++ b/src/mympd_api/webradio_favorites.h @@ -0,0 +1,22 @@ +/* + SPDX-License-Identifier: GPL-3.0-or-later + myMPD (c) 2018-2024 Juergen Mang + https://github.com/jcorporation/mympd +*/ + +#ifndef MYMPD_API_WEBRADIO_FAVORITES_H +#define MYMPD_API_WEBRADIO_FAVORITES_H + +#include "dist/sds/sds.h" +#include "src/lib/webradio.h" + +#include + +sds mympd_api_webradio_favorite_get_by_name(struct t_webradios *webradio_favorites, sds buffer, unsigned request_id, sds name); +sds mympd_api_webradio_favorite_get_by_uri(struct t_webradios *webradio_favorites, sds buffer, unsigned request_id, sds uri); +sds mympd_api_webradio_favorites_search(struct t_webradios *webradio_favorites, sds buffer, unsigned request_id, + sds expression, unsigned offset, unsigned limit); +bool mympd_api_webradio_favorite_save(struct t_webradios *webradio_favorites, struct t_webradio_data *webradio, sds old_name); +bool mympd_api_webradio_favorite_delete(struct t_webradios *webradio_favorites, struct t_list *ids); + +#endif diff --git a/src/mympd_api/webradiodb.c b/src/mympd_api/webradiodb.c new file mode 100644 index 000000000..cd5aeb97b --- /dev/null +++ b/src/mympd_api/webradiodb.c @@ -0,0 +1,64 @@ +/* + SPDX-License-Identifier: GPL-3.0-or-later + myMPD (c) 2018-2024 Juergen Mang + https://github.com/jcorporation/mympd +*/ + +#include "compile_time.h" +#include "src/mympd_api/webradiodb.h" + +#include "dist/rax/rax.h" +#include "src/lib/api.h" +#include "src/lib/jsonrpc.h" +#include "src/mympd_api/webradio.h" + +#include +#include + +/** + * Prints a WebradioDB entry as jsonrpc response + * @param workdir working directory + * @param buffer already allocated sds string to append the response + * @param request_id jsonrpc request id + * @param name webradio name + * @return pointer to buffer + */ +sds mympd_api_webradiodb_radio_get_by_name(struct t_webradios *webradiodb, sds buffer, unsigned request_id, sds name) { + enum mympd_cmd_ids cmd_id = MYMPD_API_WEBRADIO_FAVORITE_GET_BY_NAME; + void *data = raxFind(webradiodb->db, (unsigned char *)name, strlen(name)); + if (data == raxNotFound) { + buffer = jsonrpc_respond_message(buffer, cmd_id, request_id, + JSONRPC_FACILITY_DATABASE, JSONRPC_SEVERITY_ERROR, "Webradio favorite not found"); + return buffer; + } + struct t_webradio_data *webradio = (struct t_webradio_data *)data; + buffer = jsonrpc_respond_start(buffer, cmd_id, request_id); + buffer = sdscat(buffer, "\"data\":"); + buffer = webradio_print(webradio, buffer); + buffer = jsonrpc_end(buffer); + return buffer; +} + +/** + * Prints a WebradioDB entry as jsonrpc response + * @param workdir working directory + * @param buffer already allocated sds string to append the response + * @param request_id jsonrpc request id + * @param uri webradio stream uri + * @return pointer to buffer + */ +sds mympd_api_webradiodb_radio_get_by_uri(struct t_webradios *webradiodb, sds buffer, unsigned request_id, sds uri) { + enum mympd_cmd_ids cmd_id = MYMPD_API_WEBRADIO_FAVORITE_GET_BY_URI; + void *data = raxFind(webradiodb->idx_uris, (unsigned char *)uri, strlen(uri)); + if (data == raxNotFound) { + buffer = jsonrpc_respond_message(buffer, cmd_id, request_id, + JSONRPC_FACILITY_DATABASE, JSONRPC_SEVERITY_ERROR, "Webradio favorite not found"); + return buffer; + } + struct t_webradio_data *webradio = (struct t_webradio_data *)data; + buffer = jsonrpc_respond_start(buffer, cmd_id, request_id); + buffer = sdscat(buffer, "\"data\":"); + buffer = webradio_print(webradio, buffer); + buffer = jsonrpc_end(buffer); + return buffer; +} diff --git a/src/mympd_api/webradiodb.h b/src/mympd_api/webradiodb.h new file mode 100644 index 000000000..2a431e54c --- /dev/null +++ b/src/mympd_api/webradiodb.h @@ -0,0 +1,18 @@ +/* + SPDX-License-Identifier: GPL-3.0-or-later + myMPD (c) 2018-2024 Juergen Mang + https://github.com/jcorporation/mympd +*/ + +#ifndef MYMPD_API_WEBRADIO_FAVORITES_H +#define MYMPD_API_WEBRADIO_FAVORITES_H + +#include "dist/sds/sds.h" +#include "src/lib/webradio.h" + +#include + +sds mympd_api_webradiodb_radio_get_by_name(struct t_webradios *webradiodb, sds buffer, unsigned request_id, sds name); +sds mympd_api_webradiodb_radio_get_by_uri(struct t_webradios *webradiodb, sds buffer, unsigned request_id, sds uri); + +#endif diff --git a/src/mympd_api/webradios.c b/src/mympd_api/webradios.c deleted file mode 100644 index 3555715f7..000000000 --- a/src/mympd_api/webradios.c +++ /dev/null @@ -1,277 +0,0 @@ -/* - SPDX-License-Identifier: GPL-3.0-or-later - myMPD (c) 2018-2024 Juergen Mang - https://github.com/jcorporation/mympd -*/ - -#include "compile_time.h" -#include "src/mympd_api/webradios.h" - -#include "dist/rax/rax.h" -#include "dist/utf8/utf8.h" -#include "src/lib/api.h" -#include "src/lib/filehandler.h" -#include "src/lib/jsonrpc.h" -#include "src/lib/log.h" -#include "src/lib/m3u.h" -#include "src/lib/mem.h" -#include "src/lib/rax_extras.h" -#include "src/lib/sds_extras.h" -#include "src/lib/utility.h" - -#include -#include -#include - -/** - * Private definitions - */ - -/** - * Struct to hold a webradio entry in the rax tree - */ -struct t_webradio_entry { - sds entry; //!< json representation of the webradio m3u - sds filename; //!< filename of the webradio m3u -}; - -/** - * Public functions - */ - -/** - * Gets the webradio m3u as json object string. - * This function calculates the real filename for the m3u from the uri - * @param workdir working directory - * @param uri webradio stream uri - * @return new sds string with the json object string - */ -sds get_webradio_from_uri(sds workdir, const char *uri) { - sds filename = sdsnew(uri); - sanitize_filename(filename); - filename = sdscatlen(filename, ".m3u", 4); - sds filepath = sdscatfmt(sdsempty(), "%S/%s/%s", workdir, DIR_WORK_WEBRADIOS, filename); - sds entry = sdsempty(); - if (testfile_read(filepath) == true) { - entry = tojson_sds(entry, "filename", filename, true); - entry = m3u_to_json(entry, filepath, NULL); - FREE_SDS(filepath); - FREE_SDS(filename); - return entry; - } - FREE_SDS(filepath); - FREE_SDS(filename); - return entry; -} - -/** - * Prints a webradio m3u as jsonrpc response - * @param workdir working directory - * @param buffer already allocated sds string to append the response - * @param request_id jsonrpc request id - * @param filename webradio m3u filename - * @return pointer to buffer - */ -sds mympd_api_webradio_get(sds workdir, sds buffer, unsigned request_id, sds filename) { - enum mympd_cmd_ids cmd_id = MYMPD_API_WEBRADIO_FAVORITE_GET; - sds filepath = sdscatfmt(sdsempty(), "%S/%s/%S", workdir, DIR_WORK_WEBRADIOS, filename); - sds entry = sdsempty(); - entry = m3u_to_json(entry, filepath, NULL); - if (sdslen(entry) == 0) { - buffer = jsonrpc_respond_message(buffer, cmd_id, request_id, - JSONRPC_FACILITY_DATABASE, JSONRPC_SEVERITY_ERROR, "Can not parse webradio favorite file"); - } - else { - buffer = jsonrpc_respond_start(buffer, cmd_id, request_id); - buffer = tojson_sds(buffer, "filename", filename, true); - buffer = sdscatsds(buffer, entry); - buffer = jsonrpc_end(buffer); - } - FREE_SDS(entry); - FREE_SDS(filepath); - return buffer; -} - -/** - * Prints the webradio list as a jsonrpc response - * @param workdir working directory - * @param buffer already allocated sds string to append the response - * @param request_id jsonrpc request id - * @param searchstr string to search - * @param offset offset for the list - * @param limit maximum entries to print - * @return pointer to buffer - */ -sds mympd_api_webradio_list(sds workdir, sds buffer, unsigned request_id, sds searchstr, unsigned offset, unsigned limit) { - enum mympd_cmd_ids cmd_id = MYMPD_API_WEBRADIO_FAVORITE_GET; - buffer = jsonrpc_respond_start(buffer, cmd_id, request_id); - buffer = sdscat(buffer, "\"data\":["); - sds webradios_dirname = sdscatfmt(sdsempty(), "%S/%s", workdir, DIR_WORK_WEBRADIOS); - errno = 0; - DIR *webradios_dir = opendir(webradios_dirname); - if (webradios_dir == NULL) { - MYMPD_LOG_ERROR(NULL, "Can not open directory \"%s\"", webradios_dirname); - MYMPD_LOG_ERRNO(NULL, errno); - FREE_SDS(webradios_dirname); - buffer = jsonrpc_respond_message(buffer, cmd_id, request_id, - JSONRPC_FACILITY_DATABASE, JSONRPC_SEVERITY_ERROR, "Can not open webradios directory"); - return buffer; - } - - size_t search_len = sdslen(searchstr); - struct dirent *next_file; - rax *webradios = raxNew(); - sds key = sdsempty(); - unsigned real_limit = offset + limit; - //read dir - sds filepath = sdsempty(); - while ((next_file = readdir(webradios_dir)) != NULL ) { - const char *ext = get_extension_from_filename(next_file->d_name); - if (ext == NULL || - strcasecmp(ext, "m3u") != 0) - { - continue; - } - - sdsclear(key); - sdsclear(filepath); - filepath = sdscatfmt(filepath, "%S/%s", webradios_dirname, next_file->d_name); - sds entry = m3u_to_json(sdsempty(), filepath, &key); - if (sdslen(entry) == 0) { - //skip on parsing error - FREE_SDS(entry); - continue; - } - if (search_len == 0 || - utf8casestr(key, searchstr) != NULL) - { - struct t_webradio_entry *webradio = malloc_assert(sizeof(struct t_webradio_entry)); - webradio->filename = sdsnew(next_file->d_name); - webradio->entry = entry; - key = sdscatsds(key, next_file->d_name); //append filename to keep it unique - sds_utf8_tolower(key); - rax_insert_no_dup(webradios, key, webradio); - } - else { - FREE_SDS(entry); - } - } - closedir(webradios_dir); - FREE_SDS(filepath); - FREE_SDS(webradios_dirname); - FREE_SDS(key); - //print result - unsigned entity_count = 0; - unsigned entities_returned = 0; - raxIterator iter; - raxStart(&iter, webradios); - raxSeek(&iter, "^", NULL, 0); - while (raxNext(&iter)) { - struct t_webradio_entry *webradio = (struct t_webradio_entry *)iter.data; - if (entity_count >= offset && - entity_count < real_limit) - { - if (entities_returned++) { - buffer = sdscatlen(buffer, ",", 1); - } - buffer = sdscatlen(buffer, "{", 1); - buffer = tojson_sds(buffer, "filename", webradio->filename, true); - buffer = sdscatsds(buffer, webradio->entry); - buffer = sdscatlen(buffer, "}", 1); - } - entity_count++; - FREE_SDS(webradio->entry); - FREE_SDS(webradio->filename); - FREE_PTR(webradio); - } - raxStop(&iter); - buffer = sdscatlen(buffer, "],", 2); - buffer = tojson_uint64(buffer, "totalEntities", webradios->numele, true); - buffer = tojson_uint(buffer, "returnedEntities", entities_returned, false); - buffer = jsonrpc_end(buffer); - raxFree(webradios); - return buffer; -} - -/** - * Saves a webradio as m3u - * @param workdir working directory - * @param name webradio name - * @param uri webradio uri - * @param uri_old old webradio uri - * @param genre comma separated list of genres - * @param picture picture uri - * @param homepage homepage - * @param country country - * @param language language - * @param codec codec - * @param bitrate bitrate - * @param description short description - * @param state statea - * @return true on success, else false - */ -bool mympd_api_webradio_save(sds workdir, sds name, sds uri, sds uri_old, - sds genre, sds picture, sds homepage, sds country, sds language, sds codec, int bitrate, - sds description, sds state) -{ - sds filename = sdsdup(uri); - sanitize_filename(filename); - sds filepath = sdscatfmt(sdsempty(), "%S/%s/%S.m3u", workdir, DIR_WORK_WEBRADIOS, filename); - - sds content = sdscatfmt(sdsempty(), "#EXTM3U\n" - "#EXTINF:-1,%S\n" - "#EXTGENRE:%S\n" - "#PLAYLIST:%S\n" - "#EXTIMG:%S\n" - "#HOMEPAGE:%S\n" - "#COUNTRY:%S\n" - "#STATE:%S\n" - "#LANGUAGE:%S\n" - "#DESCRIPTION:%S\n" - "#CODEC:%S\n" - "#BITRATE:%i\n" - "%S\n", - name, genre, name, picture, homepage, country, state, language, description, codec, bitrate, uri); - - bool rc = write_data_to_file(filepath, content, sdslen(content)); - - if (rc == true && - uri_old[0] != '\0' && - strcmp(uri, uri_old) != 0) - { - sdsclear(filename); - filename = sdscatsds(filename, uri_old); - sanitize_filename(filename); - sdsclear(filepath); - filepath = sdscatfmt(filepath, "%S/%s/%S.m3u", workdir, DIR_WORK_WEBRADIOS, filename); - rc = rm_file(filepath); - } - FREE_SDS(filename); - FREE_SDS(filepath); - FREE_SDS(content); - - return rc; -} - -/** - * Deletes webradio m3u's - * @param workdir working directory - * @param filenames webradio m3u filenames to delete - * @return true on success, else false - */ -bool mympd_api_webradio_delete(sds workdir, struct t_list *filenames) { - sds filepath = sdsempty(); - bool rc = true; - struct t_list_node *current = filenames->head; - while (current != NULL) { - filepath = sdscatfmt(filepath, "%S/%s/%S", workdir, DIR_WORK_WEBRADIOS, current->key); - rc = rm_file(filepath); - if (rc == false) { - break; - } - sdsclear(filepath); - current = current->next; - } - FREE_SDS(filepath); - return rc; -} diff --git a/src/mympd_api/webradios.h b/src/mympd_api/webradios.h deleted file mode 100644 index 176536145..000000000 --- a/src/mympd_api/webradios.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - SPDX-License-Identifier: GPL-3.0-or-later - myMPD (c) 2018-2024 Juergen Mang - https://github.com/jcorporation/mympd -*/ - -#ifndef MYMPD_API_WEBRADIOS_H -#define MYMPD_API_WEBRADIOS_H - -#include "dist/sds/sds.h" -#include "src/lib/list.h" - -#include - -sds get_webradio_from_uri(sds workdir, const char *uri); -bool mympd_api_webradio_save(sds workdir, sds name, sds uri, sds uri_old, - sds genre, sds picture, sds homepage, sds country, sds language, - sds codec, int bitrate, sds description, sds state); -bool mympd_api_webradio_delete(sds workdir, struct t_list *filenames); -sds mympd_api_webradio_get(sds workdir, sds buffer, unsigned request_id, sds filename); -sds mympd_api_webradio_list(sds workdir, sds buffer, unsigned request_id, sds searchstr, - unsigned offset, unsigned limit); - -#endif diff --git a/src/web_server/albumart.c b/src/web_server/albumart.c index 0c359773b..f94fc888b 100644 --- a/src/web_server/albumart.c +++ b/src/web_server/albumart.c @@ -13,7 +13,6 @@ #include "src/lib/filehandler.h" #include "src/lib/jsonrpc.h" #include "src/lib/log.h" -#include "src/lib/m3u.h" #include "src/lib/mimetype.h" #include "src/lib/msg_queue.h" #include "src/lib/sds_extras.h" @@ -158,7 +157,7 @@ bool request_handler_albumart_by_uri(struct mg_connection *nc, struct mg_http_me return true; } - //check for cover in /pics/thumbs/ and webradio m3u + //check for cover in /pics/thumbs/ and webradios if (is_streamuri(uri) == true) { if (sdslen(uri) > FILENAME_LEN_MAX) { FREE_SDS(uri); @@ -166,49 +165,23 @@ bool request_handler_albumart_by_uri(struct mg_connection *nc, struct mg_http_me webserver_serve_placeholder_image(nc, PLACEHOLDER_STREAM); return true; } - sanitize_filename(uri); - sds coverfile = sdscatfmt(sdsempty(), "%S/%s/%S", config->workdir, DIR_WORK_PICS_THUMBS, uri); + sds sanitized_uri = sdsdup(uri); + sanitize_filename(sanitized_uri); + sds coverfile = sdscatfmt(sdsempty(), "%S/%s/%S", config->workdir, DIR_WORK_PICS_THUMBS, sanitized_uri); + FREE_SDS(sanitized_uri); MYMPD_LOG_DEBUG(NULL, "Check for stream cover \"%s\"", coverfile); coverfile = webserver_find_image_file(coverfile); - - if (sdslen(coverfile) == 0) { - //no coverfile found, next try to find a webradio m3u - sds webradio_file = sdscatfmt(sdsempty(), "%S/%s/%S.m3u", config->workdir, DIR_WORK_WEBRADIOS, uri); - MYMPD_LOG_DEBUG(NULL, "Check for webradio playlist \"%s\"", webradio_file); - if (testfile_read(webradio_file) == true) { - sds extimg = m3u_get_field(sdsempty(), "#EXTIMG", webradio_file); - if (is_streamuri(extimg) == true) { - //full uri, send redirect to covercache proxy - //use relative path to support hosting myMPD behind a reverse proxy in a subdir - sds redirect_uri = sdsnew("proxy-covercache?uri="); - redirect_uri = sds_urlencode(redirect_uri, extimg, sdslen(extimg)); - webserver_send_header_found(nc, redirect_uri, ""); - FREE_SDS(redirect_uri); - FREE_SDS(uri); - FREE_SDS(coverfile); - FREE_SDS(extimg); - FREE_SDS(webradio_file); - return true; - } - if (sdslen(extimg) > 0) { - //local coverfile - coverfile = sdscatfmt(sdsempty(), "%S/%s/%S", config->workdir, DIR_WORK_PICS_THUMBS, extimg); - } - FREE_SDS(extimg); - } - FREE_SDS(webradio_file); - } if (sdslen(coverfile) > 0) { //found a local coverfile webserver_serve_file(nc, hm, mg_user_data->browse_directory, coverfile); + FREE_SDS(uri); + FREE_SDS(coverfile); + return true; } - else { - //serve fallback image - webserver_serve_placeholder_image(nc, PLACEHOLDER_STREAM); - } + // TODO: get the webradio image + webserver_serve_placeholder_image(nc, PLACEHOLDER_STREAM); FREE_SDS(uri); - FREE_SDS(coverfile); return true; } diff --git a/src/web_server/request_handler.c b/src/web_server/request_handler.c index 4c55604b7..f9e7c3be1 100644 --- a/src/web_server/request_handler.c +++ b/src/web_server/request_handler.c @@ -186,7 +186,6 @@ void request_handler_browse(struct mg_connection *nc, struct mg_http_message *hm dirs = sdscat(dirs, "playlists/MPD playlists directory"); } dirs = sdscat(dirs, "smartplaylists/myMPD smart playlists directory"); - dirs = sdscat(dirs, "webradios/Webradio favorites"); mg_http_reply(nc, 200, "Content-Type: text/html\r\n"EXTRA_HEADERS_UNSAFE, "" "" "" diff --git a/src/web_server/web_server.c b/src/web_server/web_server.c index ffdbec8a7..2bada6d3f 100644 --- a/src/web_server/web_server.c +++ b/src/web_server/web_server.c @@ -280,7 +280,6 @@ static bool parse_internal_message(struct t_work_response *response, struct t_mg mg_user_data->browse_directory = sdscatfmt(mg_user_data->browse_directory, "%S/%s", config->workdir, DIR_WORK_EMPTY); mg_user_data->browse_directory = sdscatfmt(mg_user_data->browse_directory, ",/browse/pics=%S/%s", config->workdir, DIR_WORK_PICS); mg_user_data->browse_directory = sdscatfmt(mg_user_data->browse_directory, ",/browse/smartplaylists=%S/%s", config->workdir, DIR_WORK_SMARTPLS); - mg_user_data->browse_directory = sdscatfmt(mg_user_data->browse_directory, ",/browse/webradios=%S/%s", config->workdir, DIR_WORK_WEBRADIOS); if (sdslen(new_mg_user_data->playlist_directory) > 0) { mg_user_data->browse_directory = sdscatfmt(mg_user_data->browse_directory, ",/browse/playlists=%S", new_mg_user_data->playlist_directory); mg_user_data->publish_playlists = true; @@ -748,6 +747,9 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { else if (mg_match(hm->uri, mg_str("/browse/#"), NULL)) { request_handler_browse(nc, hm, mg_user_data); } + else if (mg_match(hm->uri, mg_str("/webradio"), NULL)) { + //TODO: deliver extm3u for MPD + } else if (mg_match(hm->uri, mg_str("/ws/*"), NULL)) { //check partition if (get_partition_from_uri(nc, hm, frontend_nc_data) == false) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8d5fbe21f..10282abfd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,7 +25,6 @@ set(TEST_SOURCES ../src/lib/last_played.c ../src/lib/list.c ../src/lib/log.c - ../src/lib/m3u.c ../src/lib/mg_str_utils.c ../src/lib/mimetype.c ../src/lib/mpack.c @@ -69,7 +68,7 @@ set(TEST_SOURCES ../src/mympd_api/timer_handlers.c ../src/mympd_api/trigger.c ../src/mympd_api/queue.c - ../src/mympd_api/webradios.c + ../src/mympd_api/webradio.c ../src/scripts/events.c tests/test_album_cache.c tests/test_api.c @@ -81,7 +80,6 @@ set(TEST_SOURCES tests/test_http_client.c tests/test_jsonrpc.c tests/test_list.c - tests/test_m3u.c tests/test_mimetype.c tests/test_mympd_queue.c tests/test_mympd_state.c diff --git a/test/tests/test_m3u.c b/test/tests/test_m3u.c deleted file mode 100644 index 7544cb49d..000000000 --- a/test/tests/test_m3u.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - SPDX-License-Identifier: GPL-3.0-or-later - myMPD (c) 2018-2024 Juergen Mang - https://github.com/jcorporation/mympd -*/ - -#include "compile_time.h" -#include "utility.h" - -#include "dist/utest/utest.h" -#include "dist/sds/sds.h" -#include "src/lib/list.h" -#include "src/lib/jsonrpc.h" -#include "src/lib/m3u.h" -#include "src/mympd_api/webradios.h" - -#include - -static bool webradio_save(void) { - sds name = sdsnew("Yumi Co. Radio"); - sds uri = sdsnew("http://yumicoradio.net:8000/stream"); - sds uri_old = sdsnew(""); - sds genre = sdsnew("Future Funk, City Pop, Anime Groove, Vaporwave, Nu Disco, Electronic"); - sds picture = sdsnew("http___yumicoradio_net_8000_stream.webp"); - sds homepage = sdsnew("http://yumicoradio.net"); - sds country = sdsnew("France"); - sds state = sdsnew("Paris"); - sds language = sdsnew("English"); - sds codec = sdsnew("MP3"); - sds description = sdsnew("24/7 webradio that plays Future Funk, City Pop, Anime Groove, Nu Disco, Electronica, a little bit of Vaporwave and some of the sub-genres derived."); - int bitrate = 256; - bool rc = mympd_api_webradio_save(workdir, name, uri, uri_old, genre, picture, homepage, country, language, codec, bitrate, description, state); - sdsfree(name); - sdsfree(uri); - sdsfree(uri_old); - sdsfree(genre); - sdsfree(picture); - sdsfree(homepage); - sdsfree(country); - sdsfree(language); - sdsfree(codec); - sdsfree(description); - sdsfree(state); - return rc; -} - -UTEST(m3u, test_m3u_webradio_save) { - init_testenv(); - - bool rc = webradio_save(); - ASSERT_TRUE(rc); - - clean_testenv(); -} - -UTEST(m3u, test_m3u_get_field) { - init_testenv(); - webradio_save(); - - sds s = sdsempty(); - s = m3u_get_field(s, "#EXTIMG", "/tmp/mympd-test/webradios/http___yumicoradio_net_8000_stream.m3u"); - ASSERT_STREQ("http___yumicoradio_net_8000_stream.webp", s); - sdsfree(s); - - clean_testenv(); -} - -UTEST(m3u, test_m3u_to_json) { - init_testenv(); - webradio_save(); - - sds s = sdsempty(); - sds m3ufields = sdsempty(); - s = m3u_to_json(s, "/tmp/mympd-test/webradios/http___yumicoradio_net_8000_stream.m3u", &m3ufields); - const char *e = "-1,yumi co. radiofuture funk, city pop, anime groove, vaporwave, nu disco, electronicyumi co. radiohttp___yumicoradio_net_8000_stream.webphttp://yumicoradio.netfranceparisenglish24/7 webradio that plays future funk, city pop, anime groove, nu disco, electronica, a little bit of vaporwave and some of the sub-genres derived.mp3256"; - ASSERT_STREQ(e, m3ufields); - sdsfree(s); - sdsfree(m3ufields); - - clean_testenv(); -} - -UTEST(m3u, test_get_webradio_from_uri) { - init_testenv(); - webradio_save(); - - sds m3u = get_webradio_from_uri(workdir, "http://yumicoradio.net:8000/stream"); - ASSERT_GT(sdslen(m3u), (size_t)0); - sdsfree(m3u); - - clean_testenv(); -} - -UTEST(m3u, test_mympd_api_webradio_list) { - init_testenv(); - webradio_save(); - - sds searchstr = sdsempty(); - sds buffer = mympd_api_webradio_list(workdir, sdsempty(), 0, searchstr, 0, 10); - struct t_jsonrpc_parse_error parse_error; - jsonrpc_parse_error_init(&parse_error); - int result; - bool rc = json_get_int_max(buffer, "$.result.totalEntities", &result, &parse_error); - ASSERT_TRUE(rc); - ASSERT_EQ(result, 1); - jsonrpc_parse_error_clear(&parse_error); - sdsfree(searchstr); - sdsfree(buffer); - - clean_testenv(); -} - -UTEST(m3u, test_mympd_api_webradio_delete) { - init_testenv(); - webradio_save(); - - struct t_list filenames; - list_init(&filenames); - list_push(&filenames, "http___yumicoradio_net_8000_stream.m3u", 0, NULL, NULL); - bool rc = mympd_api_webradio_delete(workdir, &filenames); - list_clear(&filenames); - ASSERT_TRUE(rc); - - clean_testenv(); -}