struct queue_item * queueitem_make_byquery(struct query_params *qp) { struct db_media_file_info dbmfi; struct queue_item *item_head; struct queue_item *item_tail; struct queue_item *item_temp; int ret; ret = db_query_start(qp); if (ret < 0) { DPRINTF(E_LOG, L_PLAYER, "Could not start query\n"); return NULL; } DPRINTF(E_DBG, L_PLAYER, "Player queue query returned %d items\n", qp->results); item_head = NULL; item_tail = NULL; while (((ret = db_query_fetch_file(qp, &dbmfi)) == 0) && (dbmfi.id)) { item_temp = queue_item_new(&dbmfi); if (!item_temp) { DPRINTF(E_LOG, L_PLAYER, "Error creating new queue_item for id '%s'\n", dbmfi.id); continue; } if (!item_head) item_head = item_temp; if (item_tail) { item_tail->next = item_temp; item_temp->prev = item_tail; item_tail->shuffle_next = item_temp; item_temp->shuffle_prev = item_tail; } item_tail = item_temp; DPRINTF(E_DBG, L_PLAYER, "Added song id %s (%s)\n", dbmfi.id, dbmfi.title); } db_query_end(qp); if (ret < 0) { DPRINTF(E_LOG, L_PLAYER, "Error fetching results\n"); return NULL; } item_head->prev = item_tail; item_tail->next = item_head; item_head->shuffle_prev = item_tail; item_tail->shuffle_next = item_head; return item_head; }
static void rsp_reply_browse(struct evhttp_request *req, char **uri, struct evkeyvalq *query) { struct query_params qp; char *browse_item; mxml_node_t *reply; mxml_node_t *status; mxml_node_t *items; mxml_node_t *node; int records; int ret; memset(&qp, 0, sizeof(struct query_params)); if (strcmp(uri[3], "artist") == 0) qp.type = Q_BROWSE_ARTISTS; else if (strcmp(uri[3], "genre") == 0) qp.type = Q_BROWSE_GENRES; else if (strcmp(uri[3], "album") == 0) qp.type = Q_BROWSE_ALBUMS; else if (strcmp(uri[3], "composer") == 0) qp.type = Q_BROWSE_COMPOSERS; else { DPRINTF(E_LOG, L_RSP, "Unsupported browse type '%s'\n", uri[3]); rsp_send_error(req, "Unsupported browse type"); return; } ret = safe_atoi32(uri[2], &qp.id); if (ret < 0) { rsp_send_error(req, "Invalid playlist ID"); return; } ret = get_query_params(req, query, &qp); if (ret < 0) return; ret = db_query_start(&qp); if (ret < 0) { DPRINTF(E_LOG, L_RSP, "Could not start query\n"); rsp_send_error(req, "Could not start query"); if (qp.filter) free(qp.filter); return; } if (qp.offset > qp.results) records = 0; else if (qp.limit > (qp.results - qp.offset)) records = qp.results - qp.offset; else records = qp.limit; /* We'd use mxmlNewXML(), but then we can't put any attributes * on the root node and we need some. */ reply = mxmlNewElement(MXML_NO_PARENT, RSP_XML_ROOT); node = mxmlNewElement(reply, "response"); status = mxmlNewElement(node, "status"); items = mxmlNewElement(node, "items"); /* Status block */ node = mxmlNewElement(status, "errorcode"); mxmlNewText(node, 0, "0"); node = mxmlNewElement(status, "errorstring"); mxmlNewText(node, 0, ""); node = mxmlNewElement(status, "records"); mxmlNewTextf(node, 0, "%d", records); node = mxmlNewElement(status, "totalrecords"); mxmlNewTextf(node, 0, "%d", qp.results); /* Items block (all items) */ while (((ret = db_query_fetch_string(&qp, &browse_item)) == 0) && (browse_item)) { node = mxmlNewElement(items, "item"); mxmlNewText(node, 0, browse_item); } if (qp.filter) free(qp.filter); if (ret < 0) { DPRINTF(E_LOG, L_RSP, "Error fetching results\n"); mxmlDelete(reply); db_query_end(&qp); rsp_send_error(req, "Error fetching query results"); return; } /* HACK * Add a dummy empty string to the items element if there is no data * to return - this prevents mxml from sending out an empty <items/> * tag that the SoundBridge does not handle. It's hackish, but it works. */ if (qp.results == 0) mxmlNewText(items, 0, ""); db_query_end(&qp); rsp_send_reply(req, reply); }
static void rsp_reply_playlist(struct evhttp_request *req, char **uri, struct evkeyvalq *query) { struct query_params qp; struct db_media_file_info dbmfi; const char *param; char **strval; mxml_node_t *reply; mxml_node_t *status; mxml_node_t *items; mxml_node_t *item; mxml_node_t *node; int mode; int records; int transcode; int32_t bitrate; int i; int ret; memset(&qp, 0, sizeof(struct query_params)); ret = safe_atoi32(uri[2], &qp.id); if (ret < 0) { rsp_send_error(req, "Invalid playlist ID"); return; } if (qp.id == 0) qp.type = Q_ITEMS; else qp.type = Q_PLITEMS; mode = F_FULL; param = evhttp_find_header(query, "type"); if (param) { if (strcasecmp(param, "full") == 0) mode = F_FULL; else if (strcasecmp(param, "browse") == 0) mode = F_BROWSE; else if (strcasecmp(param, "id") == 0) mode = F_ID; else if (strcasecmp(param, "detailed") == 0) mode = F_DETAILED; else DPRINTF(E_LOG, L_RSP, "Unknown browse mode %s\n", param); } ret = get_query_params(req, query, &qp); if (ret < 0) return; ret = db_query_start(&qp); if (ret < 0) { DPRINTF(E_LOG, L_RSP, "Could not start query\n"); rsp_send_error(req, "Could not start query"); if (qp.filter) free(qp.filter); return; } if (qp.offset > qp.results) records = 0; else if (qp.limit > (qp.results - qp.offset)) records = qp.results - qp.offset; else records = qp.limit; /* We'd use mxmlNewXML(), but then we can't put any attributes * on the root node and we need some. */ reply = mxmlNewElement(MXML_NO_PARENT, RSP_XML_ROOT); node = mxmlNewElement(reply, "response"); status = mxmlNewElement(node, "status"); items = mxmlNewElement(node, "items"); /* Status block */ node = mxmlNewElement(status, "errorcode"); mxmlNewText(node, 0, "0"); node = mxmlNewElement(status, "errorstring"); mxmlNewText(node, 0, ""); node = mxmlNewElement(status, "records"); mxmlNewTextf(node, 0, "%d", records); node = mxmlNewElement(status, "totalrecords"); mxmlNewTextf(node, 0, "%d", qp.results); /* Items block (all items) */ while (((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id)) { transcode = transcode_needed(req->input_headers, dbmfi.codectype); /* Item block (one item) */ item = mxmlNewElement(items, "item"); for (i = 0; rsp_fields[i].field; i++) { if (!(rsp_fields[i].flags & mode)) continue; strval = (char **) ((char *)&dbmfi + rsp_fields[i].offset); if (!(*strval) || (strlen(*strval) == 0)) continue; node = mxmlNewElement(item, rsp_fields[i].field); if (!transcode) mxmlNewText(node, 0, *strval); else { switch (rsp_fields[i].offset) { case dbmfi_offsetof(type): mxmlNewText(node, 0, "wav"); break; case dbmfi_offsetof(bitrate): bitrate = 0; ret = safe_atoi32(dbmfi.samplerate, &bitrate); if ((ret < 0) || (bitrate == 0)) bitrate = 1411; else bitrate = (bitrate * 8) / 250; mxmlNewTextf(node, 0, "%d", bitrate); break; case dbmfi_offsetof(description): mxmlNewText(node, 0, "wav audio file"); break; case dbmfi_offsetof(codectype): mxmlNewText(node, 0, "wav"); node = mxmlNewElement(item, "original_codec"); mxmlNewText(node, 0, *strval); break; default: mxmlNewText(node, 0, *strval); break; } } } } if (qp.filter) free(qp.filter); if (ret < 0) { DPRINTF(E_LOG, L_RSP, "Error fetching results\n"); mxmlDelete(reply); db_query_end(&qp); rsp_send_error(req, "Error fetching query results"); return; } /* HACK * Add a dummy empty string to the items element if there is no data * to return - this prevents mxml from sending out an empty <items/> * tag that the SoundBridge does not handle. It's hackish, but it works. */ if (qp.results == 0) mxmlNewText(items, 0, ""); db_query_end(&qp); rsp_send_reply(req, reply); }
static void rsp_reply_db(struct evhttp_request *req, char **uri, struct evkeyvalq *query) { struct query_params qp; struct db_playlist_info dbpli; char **strval; mxml_node_t *reply; mxml_node_t *status; mxml_node_t *pls; mxml_node_t *pl; mxml_node_t *node; int i; int ret; memset(&qp, 0, sizeof(struct db_playlist_info)); qp.type = Q_PL; qp.idx_type = I_NONE; ret = db_query_start(&qp); if (ret < 0) { DPRINTF(E_LOG, L_RSP, "Could not start query\n"); rsp_send_error(req, "Could not start query"); return; } /* We'd use mxmlNewXML(), but then we can't put any attributes * on the root node and we need some. */ reply = mxmlNewElement(MXML_NO_PARENT, RSP_XML_ROOT); node = mxmlNewElement(reply, "response"); status = mxmlNewElement(node, "status"); pls = mxmlNewElement(node, "playlists"); /* Status block */ node = mxmlNewElement(status, "errorcode"); mxmlNewText(node, 0, "0"); node = mxmlNewElement(status, "errorstring"); mxmlNewText(node, 0, ""); node = mxmlNewElement(status, "records"); mxmlNewTextf(node, 0, "%d", qp.results); node = mxmlNewElement(status, "totalrecords"); mxmlNewTextf(node, 0, "%d", qp.results); /* Playlists block (all playlists) */ while (((ret = db_query_fetch_pl(&qp, &dbpli)) == 0) && (dbpli.id)) { /* Playlist block (one playlist) */ pl = mxmlNewElement(pls, "playlist"); for (i = 0; pl_fields[i].field; i++) { if (pl_fields[i].flags & F_FULL) { strval = (char **) ((char *)&dbpli + pl_fields[i].offset); node = mxmlNewElement(pl, pl_fields[i].field); mxmlNewText(node, 0, *strval); } } } if (ret < 0) { DPRINTF(E_LOG, L_RSP, "Error fetching results\n"); mxmlDelete(reply); db_query_end(&qp); rsp_send_error(req, "Error fetching query results"); return; } /* HACK * Add a dummy empty string to the playlists element if there is no data * to return - this prevents mxml from sending out an empty <playlists/> * tag that the SoundBridge does not handle. It's hackish, but it works. */ if (qp.results == 0) mxmlNewText(pls, 0, ""); db_query_end(&qp); rsp_send_reply(req, reply); }
static int process_regular_file(int pl_id, char *path) { struct query_params qp; char filter[PATH_MAX]; const char *a; const char *b; char *dbpath; char *winner; int score; int i; int ret; // Playlist might be from Windows so we change backslash to forward slash for (i = 0; i < strlen(path); i++) { if (path[i] == '\\') path[i] = '/'; } ret = db_snprintf(filter, sizeof(filter), "f.fname = '%q' COLLATE NOCASE", filename_from_path(path)); if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Path in playlist is too long: '%s'\n", path); return -1; } memset(&qp, 0, sizeof(struct query_params)); qp.type = Q_BROWSE_PATH; qp.sort = S_NONE; qp.filter = filter; ret = db_query_start(&qp); if (ret < 0) { db_query_end(&qp); return -1; } winner = NULL; score = 0; while ((db_query_fetch_string(&qp, &dbpath) == 0) && dbpath) { if (qp.results == 1) { winner = strdup(dbpath); break; } for (i = 0, a = NULL, b = NULL; (parent_dir(&a, path) == 0) && (parent_dir(&b, dbpath) == 0) && (strcasecmp(a, b) == 0); i++) ; DPRINTF(E_SPAM, L_SCAN, "Comparison of '%s' and '%s' gave score %d\n", dbpath, path, i); if (i > score) { free(winner); winner = strdup(dbpath); score = i; } else if (i == score) { free(winner); winner = NULL; } } db_query_end(&qp); if (!winner) { DPRINTF(E_LOG, L_SCAN, "No file in the library matches playlist entry '%s'\n", path); return -1; } DPRINTF(E_DBG, L_SCAN, "Adding '%s' to playlist %d (results %d)\n", winner, pl_id, qp.results); db_pl_add_item_bypath(pl_id, winner); free(winner); return 0; }
static int mfi_id_find(const char *path) { struct query_params qp; char filter[PATH_MAX]; const char *a; const char *b; char *dbpath; char *winner; int score; int i; int ret; ret = db_snprintf(filter, sizeof(filter), "f.fname = '%q' COLLATE NOCASE", filename_from_path(path)); if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Location in iTunes XML is too long: '%s'\n", path); return -1; } memset(&qp, 0, sizeof(struct query_params)); qp.type = Q_BROWSE_PATH; qp.sort = S_NONE; qp.filter = filter; ret = db_query_start(&qp); if (ret < 0) { db_query_end(&qp); return -1; } winner = NULL; score = 0; while ((db_query_fetch_string(&qp, &dbpath) == 0) && dbpath) { if (qp.results == 1) { winner = strdup(dbpath); break; } for (i = 0, a = NULL, b = NULL; (parent_dir(&a, path) == 0) && (parent_dir(&b, dbpath) == 0) && (strcasecmp(a, b) == 0); i++) ; DPRINTF(E_SPAM, L_SCAN, "Comparison of '%s' and '%s' gave score %d\n", dbpath, path, i); if (i > score) { free(winner); winner = strdup(dbpath); score = i; } else if (i == score) { free(winner); winner = NULL; } } db_query_end(&qp); if (!winner) { DPRINTF(E_LOG, L_SCAN, "No file matches iTunes XML entry '%s'\n", path); return -1; } DPRINTF(E_DBG, L_SCAN, "Found '%s' from iTunes XML (results %d)\n", path, qp.results); ret = db_file_id_bypath(winner); free(winner); return ret; }
int artwork_get_group(int id, int max_w, int max_h, int format, struct evbuffer *evbuf) { struct query_params qp; struct db_media_file_info dbmfi; char *dir; int got_art; int ret; DPRINTF(E_DBG, L_ART, "Artwork request for group %d\n", id); /* Try directory artwork first */ memset(&qp, 0, sizeof(struct query_params)); qp.type = Q_GROUP_DIRS; qp.id = id; ret = db_query_start(&qp); if (ret < 0) { DPRINTF(E_LOG, L_ART, "Could not start Q_GROUP_DIRS query\n"); /* Skip to invidual files artwork */ goto files_art; } got_art = -1; while ((got_art < 0) && ((ret = db_query_fetch_string(&qp, &dir)) == 0) && (dir)) { got_art = artwork_get_dir_image(dir, 1, max_w, max_h, format, evbuf); if (got_art < 0) got_art = artwork_get_parentdir_image(dir, 1, max_w, max_h, format, evbuf); } db_query_end(&qp); if (ret < 0) DPRINTF(E_LOG, L_ART, "Error fetching Q_GROUP_DIRS results\n"); else if (got_art > 0) return got_art; /* Then try individual files */ files_art: memset(&qp, 0, sizeof(struct query_params)); qp.type = Q_GROUP_ITEMS; qp.id = id; ret = db_query_start(&qp); if (ret < 0) { DPRINTF(E_LOG, L_ART, "Could not start Q_GROUP_ITEMS query\n"); return -1; } got_art = -1; while ((got_art < 0) && ((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id)) { got_art = artwork_get_own_image(dbmfi.path, max_w, max_h, format, evbuf); } db_query_end(&qp); if (ret < 0) DPRINTF(E_LOG, L_ART, "Error fetching Q_GROUP_ITEMS results\n"); else if (got_art > 0) return got_art; return -1; }