/** * Import a "Comment frame" (ID3v2.4.0 section 4.10). It * contains 4 fields: * * - encoding * - language * - string * - full string (we use this one) */ static void tag_id3_import_comment(struct tag *dest, struct id3_tag *tag, const char *id, enum tag_type type) { struct id3_frame const *frame; id3_ucs4_t const *ucs4; id3_utf8_t *utf8; union id3_field const *field; frame = id3_tag_findframe(tag, id, 0); if (frame == NULL || frame->nfields != 4) return; /* for now I only read the 4th field, with the fullstring */ field = id3_frame_field(frame, 3); if (field == NULL) return; ucs4 = id3_field_getfullstring(field); if (ucs4 == NULL) return; utf8 = import_id3_string(tag_is_id3v1(tag), ucs4); if (utf8 == NULL) return; tag_add_item(dest, type, (char *)utf8); g_free(utf8); }
/** * Import all known MusicBrainz tags from TXXX frames. */ static void tag_id3_import_musicbrainz(struct tag *mpd_tag, struct id3_tag *id3_tag) { for (unsigned i = 0;; ++i) { const struct id3_frame *frame; id3_utf8_t *name, *value; enum tag_type type; frame = id3_tag_findframe(id3_tag, "TXXX", i); if (frame == NULL) break; name = tag_id3_getstring(frame, 1); if (name == NULL) continue; type = tag_id3_parse_txxx_name((const char*)name); free(name); if (type == TAG_NUM_OF_ITEM_TYPES) continue; value = tag_id3_getstring(frame, 2); if (value == NULL) continue; tag_add_item(mpd_tag, type, (const char*)value); free(value); } }
static struct tag * modplug_stream_tag(struct input_stream *is) { ModPlugFile *f; struct tag *ret = NULL; GByteArray *bdatas; char *title; bdatas = mod_loadfile(NULL, is); if (!bdatas) return NULL; f = ModPlug_Load(bdatas->data, bdatas->len); g_byte_array_free(bdatas, TRUE); if (f == NULL) return NULL; ret = tag_new(); ret->time = ModPlug_GetLength(f) / 1000; title = g_strdup(ModPlug_GetName(f)); if (title) tag_add_item(ret, TAG_TITLE, title); g_free(title); ModPlug_Unload(f); return ret; }
static int handle_end_map(void *ctx) { struct parse_data *data = (struct parse_data *) ctx; if (data->got_url > 1) { data->got_url--; return 1; } if (data->got_url == 0) return 1; /* got_url == 1, track finished, make it into a song */ data->got_url = 0; struct song *s; struct tag *t; char *u; u = g_strconcat(data->stream_url, "?client_id=", soundcloud_config.apikey, NULL); s = song_remote_new(u); g_free(u); t = tag_new(); t->time = data->duration / 1000; if (data->title != NULL) tag_add_item(t, TAG_NAME, data->title); s->tag = t; data->songs = g_slist_prepend(data->songs, s); return 1; }
static struct tag * sndfile_tag_dup(const char *path_fs) { SNDFILE *sf; SF_INFO info; struct tag *tag; const char *p; info.format = 0; sf = sf_open(path_fs, SFM_READ, &info); if (sf == NULL) return NULL; if (!audio_valid_sample_rate(info.samplerate)) { sf_close(sf); g_warning("Invalid sample rate in %s\n", path_fs); return NULL; } tag = tag_new(); tag->time = info.frames / info.samplerate; p = sf_get_string(sf, SF_STR_TITLE); if (p != NULL) tag_add_item(tag, TAG_TITLE, p); p = sf_get_string(sf, SF_STR_ARTIST); if (p != NULL) tag_add_item(tag, TAG_ARTIST, p); p = sf_get_string(sf, SF_STR_DATE); if (p != NULL) tag_add_item(tag, TAG_DATE, p); sf_close(sf); return tag; }
/* * Reads metainfo from the specified file. */ static struct tag * wavpack_tagdup(const char *fname) { WavpackContext *wpc; struct tag *tag; char error[ERRORLEN]; char *s; int size, allocated_size; wpc = WavpackOpenFileInput(fname, error, OPEN_TAGS, 0); if (wpc == NULL) { g_warning( "failed to open WavPack file \"%s\": %s\n", fname, error ); return NULL; } tag = tag_new(); tag->time = WavpackGetNumSamples(wpc); tag->time /= WavpackGetSampleRate(wpc); allocated_size = 0; s = NULL; for (unsigned i = 0; i < G_N_ELEMENTS(tagtypes); ++i) { size = WavpackGetTagItem(wpc, tagtypes[i].name, NULL, 0); if (size > 0) { ++size; /* EOS */ if (s == NULL) { s = g_malloc(size); allocated_size = size; } else if (size > allocated_size) { char *t = (char *)g_realloc(s, size); allocated_size = size; s = t; } WavpackGetTagItem(wpc, tagtypes[i].name, s, size); tag_add_item(tag, tagtypes[i].type, s); } } g_free(s); WavpackCloseFile(wpc); return tag; }
static void copy_icy_tag(struct input_curl *c) { struct tag *tag = icy_tag(&c->icy_metadata); if (tag == NULL) return; if (c->tag != NULL) tag_free(c->tag); if (c->meta_name != NULL && !tag_has_type(tag, TAG_NAME)) tag_add_item(tag, TAG_NAME, c->meta_name); c->tag = tag; }
/** * Import a "Text information frame" (ID3v2.4.0 section 4.2). It * contains 2 fields: * * - encoding * - string list */ static void tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id, enum tag_type type) { struct id3_frame const *frame; id3_ucs4_t const *ucs4; id3_utf8_t *utf8; union id3_field const *field; unsigned int nstrings, i; frame = id3_tag_findframe(tag, id, 0); if (frame == NULL || frame->nfields != 2) return; /* check the encoding field */ field = id3_frame_field(frame, 0); if (field == NULL || field->type != ID3_FIELD_TYPE_TEXTENCODING) return; /* process the value(s) */ field = id3_frame_field(frame, 1); if (field == NULL || field->type != ID3_FIELD_TYPE_STRINGLIST) return; /* Get the number of strings available */ nstrings = id3_field_getnstrings(field); for (i = 0; i < nstrings; i++) { ucs4 = id3_field_getstrings(field, i); if (ucs4 == NULL) continue; if (type == TAG_GENRE) ucs4 = id3_genre_name(ucs4); utf8 = import_id3_string(tag_is_id3v1(tag), ucs4); if (utf8 == NULL) continue; tag_add_item(dest, type, (char *)utf8); g_free(utf8); } }
static struct tag * cue_cue_tag_load(const char *file, const unsigned int tnum) { struct tag* tag = NULL; char* char_tnum = NULL; char* ptr = NULL; // unsigned int sample_rate = 0; #ifdef HAVE_CUE /* libcue */ FILE* cs_file; #endif /* libcue */ ptr = g_strdup(file); remove_suffix(ptr,'/'); #ifdef HAVE_CUE /* libcue */ cs_file = fopen(ptr, "rt"); g_free(ptr); if (cs_file != NULL) { tag = cue_tag_file(cs_file, tnum); fclose(cs_file); } #else g_free(ptr); #endif /* libcue */ if (tag == NULL) return NULL; char_tnum = g_strdup_printf("%u", tnum); if (char_tnum != NULL) { tag_add_item(tag, TAG_TRACK, char_tnum); g_free(char_tnum); } // if (sample_rate != 0) // { // tag->time = (unsigned int)(track_time/sample_rate); // } return tag; }
static void parse_exif_entry(ExifEntry * e, void *data) { MediaScanResult *r = (MediaScanResult *)data; const char *key; char val[1024]; // Get orientation if (e->tag == 0x112) { ExifByteOrder o = exif_data_get_byte_order(e->parent->parent); r->image->orientation = exif_get_short(e->data, o); LOG_DEBUG("Exif orientation: %d\n", r->image->orientation); } // Get key and value key = exif_tag_get_name_in_ifd(e->tag, exif_entry_get_ifd(e)); exif_entry_get_value(e, val, sizeof(val)); LOG_DEBUG("Saving Exif entry: %s: %s\n", key, val); tag_add_item(r->_tag, key, (const char *)val); }
static void pls_parser(GKeyFile *keyfile, struct pls_playlist *playlist) { gchar *key; gchar *value; int length; GError *error = NULL; int num_entries = g_key_file_get_integer(keyfile, "playlist", "NumberOfEntries", &error); if (error) { g_debug("Invalid PLS file: '%s'", error->message); g_error_free(error); error = NULL; /* Hack to work around shoutcast failure to comform to spec */ num_entries = g_key_file_get_integer(keyfile, "playlist", "numberofentries", &error); if (error) { g_error_free(error); error = NULL; } } while (num_entries > 0) { struct song *song; key = g_strdup_printf("File%i", num_entries); value = g_key_file_get_string(keyfile, "playlist", key, &error); if(error) { g_debug("Invalid PLS entry %s: '%s'",key, error->message); g_error_free(error); g_free(key); return; } g_free(key); song = song_remote_new(value); g_free(value); key = g_strdup_printf("Title%i", num_entries); value = g_key_file_get_string(keyfile, "playlist", key, &error); g_free(key); if(error == NULL && value){ if (song->tag == NULL) song->tag = tag_new(); tag_add_item(song->tag,TAG_TITLE, value); } /* Ignore errors? Most likely value not present */ if(error) g_error_free(error); error = NULL; g_free(value); key = g_strdup_printf("Length%i", num_entries); length = g_key_file_get_integer(keyfile, "playlist", key, &error); g_free(key); if(error == NULL && length > 0){ if (song->tag == NULL) song->tag = tag_new(); song->tag->time = length; } /* Ignore errors? Most likely value not present */ if(error) g_error_free(error); error = NULL; playlist->songs = g_slist_prepend(playlist->songs, song); num_entries--; } }
/** called by curl when new data is available */ static size_t input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream) { struct input_curl *c = (struct input_curl *)stream; const char *header = ptr, *end, *value; char name[64]; size *= nmemb; end = header + size; value = memchr(header, ':', size); if (value == NULL || (size_t)(value - header) >= sizeof(name)) return size; memcpy(name, header, value - header); name[value - header] = 0; /* skip the colon */ ++value; /* strip the value */ while (value < end && g_ascii_isspace(*value)) ++value; while (end > value && g_ascii_isspace(end[-1])) --end; if (g_ascii_strcasecmp(name, "accept-ranges") == 0) { /* a stream with icy-metadata is not seekable */ if (!icy_defined(&c->icy_metadata)) c->base.seekable = true; } else if (g_ascii_strcasecmp(name, "content-length") == 0) { char buffer[64]; if ((size_t)(end - header) >= sizeof(buffer)) return size; memcpy(buffer, value, end - value); buffer[end - value] = 0; c->base.size = c->base.offset + g_ascii_strtoull(buffer, NULL, 10); } else if (g_ascii_strcasecmp(name, "content-type") == 0) { g_free(c->base.mime); c->base.mime = g_strndup(value, end - value); } else if (g_ascii_strcasecmp(name, "icy-name") == 0 || g_ascii_strcasecmp(name, "ice-name") == 0 || g_ascii_strcasecmp(name, "x-audiocast-name") == 0) { g_free(c->meta_name); c->meta_name = g_strndup(value, end - value); if (c->tag != NULL) tag_free(c->tag); c->tag = tag_new(); tag_add_item(c->tag, TAG_NAME, c->meta_name); } else if (g_ascii_strcasecmp(name, "icy-metaint") == 0) { char buffer[64]; size_t icy_metaint; if ((size_t)(end - header) >= sizeof(buffer) || icy_defined(&c->icy_metadata)) return size; memcpy(buffer, value, end - value); buffer[end - value] = 0; icy_metaint = g_ascii_strtoull(buffer, NULL, 10); g_debug("icy-metaint=%zu", icy_metaint); if (icy_metaint > 0) { icy_start(&c->icy_metadata, icy_metaint); /* a stream with icy-metadata is not seekable */ c->base.seekable = false; } } return size; }
struct song * song_load(FILE *fp, struct directory *parent, const char *uri, GString *buffer, GError **error_r) { struct song *song = parent != NULL ? song_file_new(uri, parent) : song_remote_new(uri); char *line, *colon; enum tag_type type; const char *value; while ((line = read_text_line(fp, buffer)) != NULL && strcmp(line, SONG_END) != 0) { colon = strchr(line, ':'); if (colon == NULL || colon == line) { if (song->tag != NULL) tag_end_add(song->tag); song_free(song); g_set_error(error_r, song_save_quark(), 0, "unknown line in db: %s", line); return NULL; } *colon++ = 0; value = g_strchug(colon); if ((type = tag_name_parse(line)) != TAG_NUM_OF_ITEM_TYPES) { if (!song->tag) { song->tag = tag_new(); tag_begin_add(song->tag); } tag_add_item(song->tag, type, value); } else if (strcmp(line, "Time") == 0) { if (!song->tag) { song->tag = tag_new(); tag_begin_add(song->tag); } song->tag->time = atoi(value); } else if (strcmp(line, SONG_MTIME) == 0) { song->mtime = atoi(value); } else if (strcmp(line, "Range") == 0) { char *endptr; song->start_ms = strtoul(value, &endptr, 10); if (*endptr == '-') song->end_ms = strtoul(endptr + 1, NULL, 10); } else { if (song->tag != NULL) tag_end_add(song->tag); song_free(song); g_set_error(error_r, song_save_quark(), 0, "unknown line in db: %s", line); return NULL; } } if (song->tag != NULL) tag_end_add(song->tag); return song; }
static void getID3Info(struct id3_tag *tag, const char *id, int type, struct tag *mpdTag) { struct id3_frame const *frame; id3_ucs4_t const *ucs4; id3_utf8_t *utf8; union id3_field const *field; unsigned int nstrings, i; enum id3_field_textencoding id3_encoding = 0xff; frame = id3_tag_findframe(tag, id, 0); /* Check frame */ if (!frame) { return; } /* Check fields in frame */ if(frame->nfields == 0) { g_debug("Frame has no fields"); return; } /* Starting with T is a stringlist */ if (id[0] == 'T') { /* This one contains 2 fields: * 1st: Text encoding * 2: Stringlist * Shamefully this isn't the RL case. * But I am going to enforce it anyway. */ if(frame->nfields != 2) { g_debug("Invalid number '%i' of fields for TXX frame", frame->nfields); return; } field = &frame->fields[0]; /** * First field is encoding field. * This is ignored by mpd. */ if(field->type != ID3_FIELD_TYPE_TEXTENCODING) { g_debug("Expected encoding, found: %i", field->type); } else id3_encoding = field->number.value; /* Process remaining fields, should be only one */ field = &frame->fields[1]; /* Encoding field */ if(field->type == ID3_FIELD_TYPE_STRINGLIST) { /* Get the number of strings available */ nstrings = id3_field_getnstrings(field); for (i = 0; i < nstrings; i++) { ucs4 = id3_field_getstrings(field,i); if(!ucs4) continue; utf8 = processID3FieldString(isId3v1(tag),ucs4, type, id3_encoding); if(!utf8) continue; tag_add_item(mpdTag, type, (char *)utf8); g_free(utf8); } } else { g_warning("Field type not processed: %i", (int)id3_field_gettextencoding(field)); } } /* A comment frame */ else if(!strcmp(ID3_FRAME_COMMENT, id)) { /* A comment frame is different... */ /* 1st: encoding * 2nd: Language * 3rd: String * 4th: FullString. * The 'value' we want is in the 4th field */ if(frame->nfields == 4) { /* for now I only read the 4th field, with the fullstring */ field = &frame->fields[3]; if(field->type == ID3_FIELD_TYPE_STRINGFULL) { ucs4 = id3_field_getfullstring(field); if(ucs4) { utf8 = processID3FieldString(isId3v1(tag),ucs4, type, id3_encoding); if(utf8) { tag_add_item(mpdTag, type, (char *)utf8); g_free(utf8); } } } else { g_debug("4th field in comment frame differs from expected, got '%i': ignoring", field->type); } } else { g_debug("Invalid 'comments' tag, got '%i' fields instead of 4", frame->nfields); } } /* Unsupported */ else g_debug("Unsupported tag type requrested"); }