/* Key/value functions */ int keyval_add_size(struct keyval *kv, const char *name, const char *value, size_t size) { struct onekeyval *okv; const char *val; /* Check for duplicate key names */ val = keyval_get(kv, name); if (val) { /* Same value, fine */ if (strcmp(val, value) == 0) return 0; else /* Different value, bad */ return -1; } okv = (struct onekeyval *)malloc(sizeof(struct onekeyval)); if (!okv) { DPRINTF(E_LOG, L_MISC, "Out of memory for new keyval\n"); return -1; } okv->name = strdup(name); if (!okv->name) { DPRINTF(E_LOG, L_MISC, "Out of memory for new keyval name\n"); free(okv); return -1; } okv->value = (char *)malloc(size + 1); if (!okv->value) { DPRINTF(E_LOG, L_MISC, "Out of memory for new keyval value\n"); free(okv->name); free(okv); return -1; } memcpy(okv->value, value, size); okv->value[size] = '\0'; okv->next = NULL; if (!kv->head) kv->head = okv; if (kv->tail) kv->tail->next = okv; kv->tail = okv; return 0; }
static int scrobble(int id) { struct media_file_info *mfi; struct keyval *kv; char duration[4]; char trackNumber[4]; char timestamp[16]; int ret; mfi = db_file_fetch_byid(id); if (!mfi) { DPRINTF(E_LOG, L_LASTFM, "Scrobble failed, track id %d is unknown\n", id); return -1; } // Don't scrobble songs which are shorter than 30 sec if (mfi->song_length < 30000) goto noscrobble; // Don't scrobble non-music and radio stations if ((mfi->media_kind != MEDIA_KIND_MUSIC) || (mfi->data_kind == DATA_KIND_URL)) goto noscrobble; // Don't scrobble songs with unknown artist if (strcmp(mfi->artist, "Unknown artist") == 0) goto noscrobble; kv = keyval_alloc(); if (!kv) goto noscrobble; snprintf(duration, sizeof(duration), "%" PRIu32, mfi->song_length); snprintf(trackNumber, sizeof(trackNumber), "%" PRIu32, mfi->track); snprintf(timestamp, sizeof(timestamp), "%" PRIi64, (int64_t)time(NULL)); ret = ( (keyval_add(kv, "api_key", lastfm_api_key) == 0) && (keyval_add(kv, "sk", lastfm_session_key) == 0) && (keyval_add(kv, "artist", mfi->artist) == 0) && (keyval_add(kv, "track", mfi->title) == 0) && (keyval_add(kv, "album", mfi->album) == 0) && (keyval_add(kv, "albumArtist", mfi->album_artist) == 0) && (keyval_add(kv, "duration", duration) == 0) && (keyval_add(kv, "trackNumber", trackNumber) == 0) && (keyval_add(kv, "timestamp", timestamp) == 0) ); free_mfi(mfi, 0); if (!ret) { keyval_clear(kv); free(kv); return -1; } DPRINTF(E_INFO, L_LASTFM, "Scrobbling '%s' by '%s'\n", keyval_get(kv, "track"), keyval_get(kv, "artist")); ret = request_post("track.scrobble", kv, 0); keyval_clear(kv); free(kv); return ret; noscrobble: free_mfi(mfi, 0); return -1; }
/* Thread: main (mdns) */ static void touch_remote_cb(const char *name, const char *type, const char *domain, const char *hostname, int family, const char *address, int port, struct keyval *txt) { const char *p; char *devname; char *paircode; int ret; if (port < 0) { /* If Remote stops advertising itself, the pairing either succeeded or * failed; any subsequent attempt will need a new pairing pin, so * we can just forget everything we know about the remote. */ pthread_mutex_lock(&remote_lck); remove_remote_address_byid(name, family); pthread_mutex_unlock(&remote_lck); } else { /* Get device name (DvNm field in TXT record) */ p = keyval_get(txt, "DvNm"); if (!p) { DPRINTF(E_LOG, L_REMOTE, "Remote %s: no DvNm in TXT record!\n", name); return; } if (*p == '\0') { DPRINTF(E_LOG, L_REMOTE, "Remote %s: DvNm has no value\n", name); return; } devname = strdup(p); if (!devname) { DPRINTF(E_LOG, L_REMOTE, "Out of memory for device name\n"); return; } /* Get pairing code (Pair field in TXT record) */ p = keyval_get(txt, "Pair"); if (!p) { DPRINTF(E_LOG, L_REMOTE, "Remote %s: no Pair in TXT record!\n", name); free(devname); return; } if (*p == '\0') { DPRINTF(E_LOG, L_REMOTE, "Remote %s: Pair has no value\n", name); free(devname); return; } paircode = strdup(p); if (!paircode) { DPRINTF(E_LOG, L_REMOTE, "Out of memory for paircode\n"); free(devname); return; } DPRINTF(E_LOG, L_REMOTE, "Discovered remote %s (id %s) at [%s]:%d, paircode %s\n", devname, name, address, port, paircode); /* Add the data to the list, adding the remote to the list if needed */ pthread_mutex_lock(&remote_lck); ret = add_remote_mdns_data(name, family, address, port, devname, paircode); if (ret < 0) { DPRINTF(E_WARN, L_REMOTE, "Could not add Remote mDNS data, id %s\n", name); free(devname); free(paircode); } else if (ret == 1) kickoff_pairing(); pthread_mutex_unlock(&remote_lck); } }
/* Earlier versions of ffmpeg/libav do not seem to allow access to the http * headers, so we must instead open the stream ourselves to get the metadata. * Sorry about the extra connections, you radio streaming people! * * It is not possible to get the packet metadata with these versions of ffmpeg */ struct http_icy_metadata * http_icy_metadata_get(AVFormatContext *fmtctx, int packet_only) { struct http_icy_metadata *metadata; struct http_client_ctx ctx; struct keyval *kv; const char *value; int got_header; int ret; /* Can only get header metadata */ if (packet_only) return NULL; kv = keyval_alloc(); if (!kv) return NULL; memset(&ctx, 0, sizeof(struct http_client_ctx)); ctx.url = fmtctx->filename; ctx.headers = kv; ctx.headers_only = 1; ctx.body = NULL; ret = http_client_request(&ctx); if (ret < 0) { DPRINTF(E_LOG, L_HTTP, "Error fetching %s\n", fmtctx->filename); free(kv); return NULL; } metadata = malloc(sizeof(struct http_icy_metadata)); if (!metadata) return NULL; memset(metadata, 0, sizeof(struct http_icy_metadata)); got_header = 0; if ( (value = keyval_get(ctx.headers, "icy-name")) ) { metadata->name = strdup(value); got_header = 1; } if ( (value = keyval_get(ctx.headers, "icy-description")) ) { metadata->description = strdup(value); got_header = 1; } if ( (value = keyval_get(ctx.headers, "icy-genre")) ) { metadata->genre = strdup(value); got_header = 1; } keyval_clear(kv); free(kv); if (!got_header) { free(metadata); return NULL; } /* DPRINTF(E_DBG, L_HTTP, "Found ICY: N %s, D %s, G %s, T %s, A %s, U %s, I %" PRIu32 "\n", metadata->name, metadata->description, metadata->genre, metadata->title, metadata->artist, metadata->artwork_url, metadata->hash );*/ return metadata; }