From 900886ab3f47c5caeee279e017cf11b79e68d8f5 Mon Sep 17 00:00:00 2001 From: moritz Date: Thu, 8 Mar 2007 14:39:00 +0000 Subject: [PATCH] Iterate towards optional TagLib support, as well as scripted metadata support. This is just the first step and equivalent to current functionality. git-svn-id: https://svn.xiph.org/trunk/ezstream@12680 0101bb08-14d6-0310-b084-bc0e0c8e3800 --- configure.in | 2 +- src/Makefile.am | 6 +- src/ezstream.c | 272 ++++++++-------------------- src/metadata.c | 457 ++++++++++++++++++++++++++++++++++++++++++++++++ src/metadata.h | 96 ++++++++++ 5 files changed, 631 insertions(+), 202 deletions(-) create mode 100644 src/metadata.c create mode 100644 src/metadata.h diff --git a/configure.in b/configure.in index a9698d1..e3bf028 100644 --- a/configure.in +++ b/configure.in @@ -64,7 +64,7 @@ AC_TYPE_SIZE_T dnl USEFUL HEADERS -AC_CHECK_HEADERS(sys/time.h paths.h signal.h) +AC_CHECK_HEADERS(sys/time.h paths.h signal.h libgen.h) dnl LIBRARY FUNCTIONS diff --git a/src/Makefile.am b/src/Makefile.am index ceaf4ee..0ed9339 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,12 +2,14 @@ AUTOMAKE_OPTIONS = 1.9 foreign bin_PROGRAMS = ezstream -ezstream_SOURCES = ezstream.c compat.c configfile.c playlist.c util.c +ezstream_SOURCES = compat.c configfile.c ezstream.c metadata.c playlist.c \ + util.c ezstream_LDADD = @LIBOBJS@ @XIPH_LIBS@ AM_CFLAGS = @XIPH_CFLAGS@ AM_CPPFLAGS = @XIPH_CPPFLAGS@ -EXTRA_DIST = compat.h configfile.h getopt.h playlist.h strfctns.h util.h +EXTRA_DIST = compat.h configfile.h getopt.h metadata.h playlist.h \ + strfctns.h util.h CLEANFILES = core *.core *~ .*~ diff --git a/src/ezstream.c b/src/ezstream.c index 9845437..b1f550f 100644 --- a/src/ezstream.c +++ b/src/ezstream.c @@ -33,6 +33,10 @@ #include #include #include +#ifdef HAVE_LIBGEN_H +# include +#endif +#include #ifdef HAVE_PATHS_H # include #endif @@ -42,22 +46,21 @@ #include #include #include -#include #ifdef WIN32 # include # include -#else -# include -# include #endif /* WIN32 */ +#ifdef HAVE_UNISTD_H +# include +#endif #include -#include #include "compat.h" #include "configfile.h" #ifndef HAVE_GETOPT # include "getopt.h" #endif +#include "metadata.h" #include "playlist.h" #include "strfctns.h" #include "util.h" @@ -105,9 +108,8 @@ typedef struct tag_ID3Tag { int urlParse(const char *, char **, int *, char **); void replaceString(const char *, char *, size_t, const char *, const char *); -void setMetadata(shout_t *, const char *); char * buildCommandString(const char *, const char *, const char *); -char * processMetadata(shout_t *, const char *, const char *); +char * processMetadata(shout_t *, const char *); FILE * openResource(shout_t *, const char *, int *, char **, int *); int reconnectServer(shout_t *, int); int sendStream(shout_t *, FILE *, const char *, int, void *); @@ -214,17 +216,7 @@ replaceString(const char *source, char *dest, size_t size, strlcat(dest, p1, size); } -void -setMetadata(shout_t *shout, const char *metadata) -{ - shout_metadata_t *shoutMetadata = shout_metadata_new(); - - shout_metadata_add(shoutMetadata, "song", metadata); - shout_set_metadata(shout, shoutMetadata); - shout_metadata_free(shoutMetadata); -} - -char* +char * buildCommandString(const char *extension, const char *fileName, const char *metadata) { @@ -293,154 +285,33 @@ buildCommandString(const char *extension, const char *fileName, } char * -processMetadata(shout_t *shout, const char *extension, const char *fileName) +processMetadata(shout_t *shout, const char *fileName) { - FILE *filepstream = NULL; char *songInfo = NULL; - size_t songLen = 0; - ID3Tag id3tag; - shout_metadata_t *pmetadata = NULL; + shout_metadata_t *shout_mdata = NULL; + metadata_t *mdata = NULL; - if ((filepstream = fopen(fileName, "rb")) == NULL) { - printf("%s: processMetadata(): %s: %s\n", - __progname, fileName, strerror(errno)); - return (xstrdup(blankString)); + if ((mdata = metadata_file(fileName)) == NULL) { + songInfo = xstrdup(blankString); + return (songInfo); } - if (strcmp(extension, ".mp3") == 0) { - /* Look for the ID3 tag */ - memset(&id3tag, '\000', sizeof(id3tag)); - fseek(filepstream, -128L, SEEK_END); - fread(&id3tag, 1, 127, filepstream); - if (strncmp(id3tag.tag, "TAG", strlen("TAG")) == 0) { - char tempTrackName[31]; - char tempArtistName[31]; - - snprintf(tempTrackName, sizeof(tempTrackName), "%s", - id3tag.trackName); - snprintf(tempArtistName, sizeof(tempArtistName), "%s", - id3tag.artistName); - - if (strlen(tempTrackName) > 0 || - strlen(tempArtistName) > 0) { - songLen = strlen(tempArtistName) + - strlen(" - ") + strlen(tempTrackName) - + 1; - songInfo = xmalloc(songLen); - strlcpy(songInfo, tempArtistName, songLen); - if (strlen(songInfo) > 0 && - strlen(tempTrackName) > 0) - strlcat(songInfo, " - ", songLen); - strlcat(songInfo, tempTrackName, songLen); - } - } - } else if (strcmp(extension, ".ogg") == 0) { - OggVorbis_File vf; - int ret; - - if ((ret = ov_open(filepstream, &vf, NULL, 0)) != 0) { - switch (ret) { - case OV_EREAD: - printf("%s: No metadata support: %s: Media read error\n", - __progname, fileName); - break; - case OV_ENOTVORBIS: - printf("%s: No metadata support: %s: Invalid Vorbis bitstream\n", - __progname, fileName); - break; - case OV_EVERSION: - printf("%s: No metadata support: %s: Vorbis version mismatch\n", - __progname, fileName); - break; - case OV_EBADHEADER: - printf("%s: No metadata support: %s: Invalid Vorbis bitstream header\n", - __progname, fileName); - break; - case OV_EFAULT: - printf("%s: Fatal: Internal libvorbisfile fault\n", - __progname); - abort(); - default: - printf("%s: No metadata support: %s: ov_read() returned unknown error\n", - __progname, fileName); - break; - } - } else { - char **ptr = ov_comment(&vf, -1)->user_comments; - char *artist = NULL; - char *title = NULL; - - while(*ptr){ - if (artist == NULL && - strncasecmp(*ptr, "ARTIST", strlen("ARTIST")) == 0) - artist = xstrdup(*ptr + strlen("ARTIST=")); - if (title == NULL && - strncasecmp(*ptr, "TITLE", strlen("TITLE")) == 0) - title = xstrdup(*ptr + strlen("TITLE=")); - ++ptr; - } - - if (artist != NULL || title != NULL) { - songLen = 0; - if (artist != NULL) - songLen += strlen(artist); - if (title != NULL) - songLen += strlen(title); - songLen += strlen(" - ") + 1; - songInfo = xcalloc(1, songLen); - - if (artist != NULL) - strlcpy(songInfo, artist, songLen); - if (title != NULL) { - if (artist != NULL) - strlcat(songInfo, " - ", songLen); - strlcat(songInfo, title, songLen); - xfree(title); - } - if (artist != NULL) - xfree(artist); - } - - ov_clear(&vf); - filepstream = NULL; - } + if (!metadata_file_update(mdata)) { + metadata_free(mdata); + songInfo = xstrdup(blankString); + return (songInfo); } + songInfo = xstrdup(metadata_get_string(mdata)); + metadata_free(mdata); - if (filepstream != NULL) - fclose(filepstream); - - if (songInfo == NULL) { - /* - * If we didn't get any song info via tags or comments, then - * let's just use the filename. - */ - char *p1 = NULL; - char *p2 = NULL; - char *filename_copy = NULL; - - filename_copy = xstrdup(fileName); - p2 = basename(filename_copy); - if (p2 == NULL) { - /* Assert that basename() cannot fail. */ - printf("%s: Internal error: basename() failed with '%s'\n", - __progname, filename_copy); - exit(1); - } - songInfo = xstrdup(p2); - xfree(filename_copy); - p1 = strrchr(songInfo, '.'); - if (p1 != NULL) - *p1 = '\000'; - } - - if ((pmetadata = shout_metadata_new()) == NULL) { + if ((shout_mdata = shout_metadata_new()) == NULL) { printf("%s: shout_metadata_new(): %s\n", __progname, strerror(ENOMEM)); exit(1); } - shout_metadata_add(pmetadata, "song", songInfo); - shout_set_metadata(shout, pmetadata); - shout_metadata_free(pmetadata); + shout_metadata_add(shout_mdata, "song", songInfo); + shout_set_metadata(shout, shout_mdata); + shout_metadata_free(shout_mdata); return (songInfo); } @@ -451,7 +322,7 @@ openResource(shout_t *shout, const char *fileName, int *popenFlag, { FILE *filep = NULL; char extension[25]; - char *p1 = NULL; + char *p = NULL; char *pMetadata = NULL; char *pCommandString = NULL; @@ -471,62 +342,65 @@ openResource(shout_t *shout, const char *fileName, int *popenFlag, *isStdin = 0; extension[0] = '\0'; - p1 = strrchr(fileName, '.'); - if (p1 != NULL) - strlcpy(extension, p1, sizeof(extension)); - for (p1 = extension; *p1 != '\0'; p1++) - *p1 = tolower((int)*p1); - pMetadata = processMetadata(shout, extension, fileName); + p = strrchr(fileName, '.'); + if (p != NULL) + strlcpy(extension, p, sizeof(extension)); + for (p = extension; *p != '\0'; p++) + *p = tolower((int)*p); + + if (strlen(extension) == 0) { + printf("%s: Error: Cannot determine file type of '%s'\n", + __progname, fileName); + return (filep); + } + + pMetadata = processMetadata(shout, fileName); if (metaCopy != NULL) *metaCopy = xstrdup(pMetadata); *popenFlag = 0; if (pezConfig->reencode) { - if (strlen(extension) > 0) { - int stderr_fd = dup(fileno(stderr)); + int stderr_fd = dup(fileno(stderr)); - pCommandString = buildCommandString(extension, fileName, pMetadata); - if (vFlag > 1) - printf("%s: Running command `%s`\n", __progname, - pCommandString); + pCommandString = buildCommandString(extension, fileName, pMetadata); + if (vFlag > 1) + printf("%s: Running command `%s`\n", __progname, + pCommandString); - if (qFlag) { - int fd; + if (qFlag) { + int fd; - if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) { - printf("%s: Cannot open %s for redirecting STDERR output: %s\n", - __progname, _PATH_DEVNULL, strerror(errno)); - exit(1); - } - - dup2(fd, fileno(stderr)); - if (fd > 2) - close(fd); + if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) { + printf("%s: Cannot open %s for redirecting STDERR output: %s\n", + __progname, _PATH_DEVNULL, strerror(errno)); + exit(1); } - fflush(NULL); - errno = 0; - if ((filep = popen(pCommandString, "r")) == NULL) { - printf("%s: popen(): Error while executing '%s'", - __progname, pCommandString); - /* popen() does not set errno reliably ... */ - if (errno) - printf(": %s\n", strerror(errno)); - else - printf("\n"); - } else { - *popenFlag = 1; + dup2(fd, fileno(stderr)); + if (fd > 2) + close(fd); + } + + fflush(NULL); + errno = 0; + if ((filep = popen(pCommandString, "r")) == NULL) { + printf("%s: popen(): Error while executing '%s'", + __progname, pCommandString); + /* popen() does not set errno reliably ... */ + if (errno) + printf(": %s\n", strerror(errno)); + else + printf("\n"); + } else { + *popenFlag = 1; #ifdef WIN32 - _setmode(_fileno(filep), _O_BINARY ); + _setmode(_fileno(filep), _O_BINARY ); #endif - } - xfree(pCommandString); + } + xfree(pCommandString); - if (qFlag) - dup2(stderr_fd, fileno(stderr)); - } else - printf("%s: Error: Cannot determine file type of '%s'\n", - __progname, fileName); + if (qFlag) + dup2(stderr_fd, fileno(stderr)); xfree(pMetadata); return (filep); diff --git a/src/metadata.c b/src/metadata.c new file mode 100644 index 0000000..02816c9 --- /dev/null +++ b/src/metadata.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2007 Moritz Grimm + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#include +#include +#ifdef HAVE_LIBGEN_H +# include +#endif +#include +#include +#include + +#include + +#include "compat.h" +#include "metadata.h" +#include "strfctns.h" +#include "util.h" + +extern char *__progname; + +struct metadata { + char *filename; + char *string; + char *artist; + char *title; + int program; +}; + +struct ID3Tag { + char tag[3]; + char trackName[30]; + char artistName[30]; + char albumName[30]; + char year[3]; + char comment[30]; + char genre; +}; + +metadata_t * metadata_create(const char *); +void metadata_use_taglib(metadata_t *, FILE **); +void metadata_use_self(metadata_t *, FILE **); +void metadata_clean_md(metadata_t *); +void metadata_get_extension(char *, size_t, const char *); +char * metadata_get_name(const char *); +void metadata_process_md(metadata_t *); + +metadata_t * +metadata_create(const char *filename) +{ + metadata_t *md; + + md = xcalloc(1, sizeof(metadata_t)); + md->filename = xstrdup(filename); + + return (md); +} + +void +metadata_use_taglib(metadata_t *md, FILE **filep) +#ifdef HAVE_TAG_C +{ + TagLib_File *tf; + TagLib_Tag *tt; + char *str; + + if (md == NULL || md->filename == NULL) { + printf("%s: metadata_use_taglib(): Internal error: Bad arguments\n", + __progname); + abort(); + } + + if (filep != NULL) + fclose(*filep); + + metadata_clean_md(md); + taglib_set_string_management_enabled(0); + + if (md->string != NULL) + xfree(md->string); + + if ((tf = taglib_file_new(md->filename)) == NULL) { + md->string = metadata_get_name(md->filename); + return; + } + tt = taglib_file_tag(tf); + + str = taglib_tag_artist(tt); + if (str != NULL) { + if (strlen(str) > 0) + md->artist = xstrdup(str); + xfree(str); + } + + str = taglib_tag_title(tt); + if (str != NULL) { + if (strlen(str) > 0) + md->title = xstrdup(str); + xfree(str); + } + + taglib_file_free(tf); +} +#else +{ + printf("%s: Internal error: metadata_use_taglib() called without TagLib support\n", + __progname); + abort(); +} +#endif /* HAVE_TAG_C */ + +void +metadata_use_self(metadata_t *md, FILE **filep) +#ifdef HAVE_TAG_C +{ + printf("%s: Internal error: metadata_use_self() called with TagLib support\n", + __progname); + abort(); +} +#else +{ + char extension[25]; + struct ID3Tag id3tag; + + if (md == NULL || filep == NULL || *filep == NULL || + md->filename == NULL) { + printf("%s: metadata_use_self(): Internal error: Bad arguments\n", + __progname); + abort(); + } + + metadata_clean_md(md); + metadata_get_extension(extension, sizeof(extension), md->filename); + + if (strcmp(extension, ".mp3") == 0) { + memset(&id3tag, 0, sizeof(id3tag)); + fseek(*filep, -128L, SEEK_END); + fread(&id3tag, 1, 127, *filep); + if (strncmp(id3tag.tag, "TAG", strlen("TAG")) == 0) { + if (strlen(id3tag.artistName) > 0) + md->artist = xstrdup(id3tag.artistName); + if (strlen(id3tag.trackName) > 0) + md->title = xstrdup(id3tag.trackName); + } + } else if (strcmp(extension, ".ogg") == 0) { + OggVorbis_File vf; + int ret; + + if ((ret = ov_open(*filep, &vf, NULL, 0)) != 0) { + switch (ret) { + case OV_EREAD: + printf("%s: ov_open(): %s: Media read error\n", + __progname, md->filename); + break; + case OV_ENOTVORBIS: + printf("%s: ov_open(): %s: Invalid Vorbis bitstream\n", + __progname, md->filename); + break; + case OV_EVERSION: + printf("%s: ov_open(): %s: Vorbis version mismatch\n", + __progname, md->filename); + break; + case OV_EBADHEADER: + printf("%s: ov_open(): %s: Invalid Vorbis bitstream header\n", + __progname, md->filename); + break; + case OV_EFAULT: + printf("%s: Fatal: Internal libvorbisfile fault\n", + __progname); + abort(); + default: + printf("%s: ov_open(): %s: ov_read() returned unknown error\n", + __progname, md->filename); + break; + } + } else { + char **ptr; + + for (ptr = ov_comment(&vf, -1)->user_comments; *ptr != NULL; ptr++) { + if (md->artist == NULL && + strncasecmp(*ptr, "ARTIST", strlen("ARTIST")) == 0) { + if (strlen(*ptr + strlen("ARTIST=")) > 0) + md->artist = xstrdup(*ptr + strlen("ARTIST=")); + } + if (md->title == NULL && + strncasecmp(*ptr, "TITLE", strlen("TITLE")) == 0) { + if (strlen(*ptr + strlen("TITLE=")) > 0) + md->title = xstrdup(*ptr + strlen("TITLE=")); + } + } + + ov_clear(&vf); + *filep = NULL; + } + } + + if (*filep != NULL) + fclose(*filep); + + if (md->artist == NULL && md->title == NULL) + md->string = metadata_get_name(md->filename); +} +#endif /* HAVE_TAG_C */ + +void +metadata_clean_md(metadata_t *md) +{ + if (md == NULL) { + printf("%s: Internal error: metadata_clean_md(): NULL argument\n", + __progname); + abort(); + } + + if (md->string != NULL) + xfree(md->string); + if (md->artist != NULL) + xfree(md->artist); + if (md->title != NULL) + xfree(md->title); +} + +void +metadata_get_extension(char *buf, size_t siz, const char *filename) +{ + char *p; + + if (buf == NULL || siz == 0 || filename == NULL) { + printf("%s: metadata_get_extension(): Internal error: Bad arguments\n", + __progname); + abort(); + } + + if ((p = strrchr(filename, '.')) != NULL) + strlcpy(buf, p, siz); + else + buf[0] = '\0'; + for (p = buf; *p != '\0'; p++) + *p = tolower((int)*p); +} + +char * +metadata_get_name(const char *file) +{ + char *filename = xstrdup(file); + char *p1, *p2, *name; + + if (file == NULL) { + printf("%s: metadata_get_name(): Internal error: Bad arguments\n", + __progname); + abort(); + } + + if ((p1 = basename(filename)) == NULL) { + printf("%s: Internal error: basename() failed with '%s'\n", + __progname, filename); + exit(1); + } + + if ((p2 = strrchr(p1, '.')) != NULL) + *p2 = '\0'; + + if (strlen(p1) == 0) + name = xstrdup("[unknown]"); + else + name = xstrdup(p1); + + xfree(filename); + return (name); +} + +void +metadata_process_md(metadata_t *md) +{ + size_t siz = 0; + + if (md == NULL) { + printf("%s: metadata_process_md(): Internal error: Bad arguments\n", + __progname); + abort(); + } + + if (md->string != NULL) + return; + + if (md->artist != NULL) + siz += strlen(md->artist); + if (md->title != NULL) { + if (siz > 0) + siz += strlen(" - "); + siz += strlen(md->title); + } + siz++; + md->string = xcalloc(1, siz); + + if (md->artist != NULL) + strlcpy(md->string, md->artist, siz); + if (md->title != NULL) { + if (md->artist != NULL) + strlcat(md->string, " - ", siz); + strlcat(md->string, md->title, siz); + } +} + +metadata_t * +metadata_file(const char *filename) +{ + metadata_t *md; + + if (filename == NULL || strlen(filename) == 0) { + printf("%s: metadata_file(): Internal error: Bad arguments\n", + __progname); + abort(); + } + + md = metadata_create(filename); + if (!metadata_file_update(md)) { + metadata_free(md); + return (NULL); + } + + return (md); +} + +metadata_t * +metadata_program(const char *program) +{ + metadata_t *md; + + if (program == NULL || strlen(program) == 0) { + printf("%s: metadata_program(): Internal error: Bad arguments\n", + __progname); + abort(); + } + + md = metadata_create(program); + md->program = 1; + + return (md); +} + +void +metadata_free(metadata_t *md) +{ + if (md == NULL) + return; + + if (md->filename != NULL) + xfree(md->filename); + metadata_clean_md(md); + xfree(md); +} + + +int +metadata_file_update(metadata_t *md) +{ + FILE *filep; + + if (md == NULL) { + printf("%s: metadata_file_update(): Internal error: NULL argument\n", + __progname); + abort(); + } + + if (md->program) { + printf("%s: metadata_file_update(): Internal error: Called with program handle\n", + __progname); + abort(); + } + + if ((filep = fopen(md->filename, "rb")) == NULL) { + printf("%s: %s: %s\n", __progname, md->filename, strerror(errno)); + return (0); + } + +#ifdef HAVE_TAG_C + metadata_use_taglib(md, &filep); +#else + metadata_use_self(md, &filep); +#endif /* HAVE_TAG_C */ + + metadata_process_md(md); + + return (1); +} + +int +metadata_program_update(metadata_t *md, enum metadata_request md_req) +{ + /* XXX not implemented */ + return (0); +} + +const char * +metadata_get_string(metadata_t *md) +{ + if (md == NULL) { + printf("%s: metadata_get_string(): Internal error: Bad arguments\n", + __progname); + abort(); + } + + if (md->string == NULL) { + printf("%s: metadata_get_string(): Internal error: md->string cannot be NULL\n", + __progname); + abort(); + } + + return (md->string); +} + +const char * +metadata_get_artist(metadata_t *md) +{ + if (md == NULL) { + printf("%s: metadata_get_artist(): Internal error: Bad arguments\n", + __progname); + abort(); + } + + return (md->artist); +} + +const char * +metadata_get_title(metadata_t *md) +{ + if (md == NULL) { + printf("%s: metadata_get_title(): Internal error: Bad arguments\n", + __progname); + abort(); + } + + return (md->title); +} diff --git a/src/metadata.h b/src/metadata.h new file mode 100644 index 0000000..516cb39 --- /dev/null +++ b/src/metadata.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2007 Moritz Grimm + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __METADATA_H__ +#define __METADATA_H__ + +#define METADATA_MAX 1023 + +enum metadata_request { + METADATA_ALL = 0, + METADATA_STRING, + METADATA_ARTIST, + METADATA_TITLE +}; + +typedef struct metadata metadata_t; + +/* + * Read the metadata of a media file and return a new metadata handle on + * success, or NULL on failure. The returned handle is "branded" for reading + * metadata from media files. + */ +metadata_t * metadata_file(const char * /* filename */); + +/* + * Create a metadata handle that is "branded" for acquiring metadata from an + * external program. The handle is returned on success, or NULL on failure. + * The program is NOT YET being queried, use metadata_program_update() for + * that. Also, the program (or script) needs to follow these rules: + * + * - Print one line to standard output and exit. + * - Accept no command line parameter and return a complete metadata string + * (for metadata_get_string()). The program *should* always return + * something in this case (e.g. something based on the filename in case no + * metadata is available.) This string will default to "[unknown]" + * otherwise. + * - Accept the command line parameter "artist" and return only the artist + * metadata, or an empty string if no artist information is available. + * - Accept the command line parameter "title" and return only the song title + * metadata, or an empty string if no artist information is available. + * - Return at most METADATA_MAX characters, or the result will be truncated. + */ +metadata_t * metadata_program(const char * /* program name */); + +/* + * Free all memory used by a metadata handle that has been created with + * metadata_file() or metadata_program(). + */ +void metadata_free(metadata_t *); + +/* + * Update/read the metadata for the given handle. Returns 1 on success, and 0 + * on failure. + */ +int metadata_file_update(metadata_t *); + +/* + * Update/read the specified metadata for the given program-handle. Returns 1 + * on success, and 0 on failure. + */ +int metadata_program_update(metadata_t *, enum metadata_request); + +/* + * Returns a pointer to a metadata string ``artist - title'', or just + * ``artist'' or ``title'' if one of the two is not available. If neither + * are present, it usually returns the filename without the extension. + * This function never returns NULL. + */ +const char * metadata_get_string(metadata_t *); + +/* + * Returns a pointer to the artist string, or NULL if the file did not + * contain any artist information. + */ +const char * metadata_get_artist(metadata_t *); + +/* + * Returns a pointer to the title string, or NULL if the file did not + * contain any title information. + */ +const char * metadata_get_title(metadata_t *); + +#endif /* __METADATA_H__ */