mirror of
https://gitlab.xiph.org/xiph/ezstream.git
synced 2024-12-04 14:46:31 -05:00
Add new <metadata_progname> configuration option, which specifies an external
program/script to get metadata from. Also include SIGUSR2 handling that triggers metadata updates from the external program mid-stream. git-svn-id: https://svn.xiph.org/trunk/ezstream@12693 0101bb08-14d6-0310-b084-bc0e0c8e3800
This commit is contained in:
parent
163b7ca187
commit
304908bff4
6
NEWS
6
NEWS
@ -19,6 +19,12 @@ Changes in 0.4.0, (SVN trunk):
|
|||||||
* various:
|
* various:
|
||||||
- [ADD] Allow ezstream to use TagLib for reading metadata from media
|
- [ADD] Allow ezstream to use TagLib for reading metadata from media
|
||||||
files. TagLib (libtag_c) is now an optional dependency.
|
files. TagLib (libtag_c) is now an optional dependency.
|
||||||
|
- [ADD] New <metadata_progname> configuration option, which causes
|
||||||
|
metadata to be read from the output of an external program or
|
||||||
|
script.
|
||||||
|
- [ADD] New runtime control via the SIGUSR2 signal, which triggers reading
|
||||||
|
of fresh metadata information from <metadata_progname> (metadata
|
||||||
|
is always read at song changes.)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
.Nm ezstream
|
.Nm ezstream
|
||||||
.Nd source client for Icecast with external en-/decoder support
|
.Nd source client for Icecast with external de-/encoder support
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Bk -words
|
.Bk -words
|
||||||
@ -59,8 +59,7 @@ and bitrate \(em is displayed.
|
|||||||
.Ss Runtime control
|
.Ss Runtime control
|
||||||
On POSIX systems,
|
On POSIX systems,
|
||||||
.Nm
|
.Nm
|
||||||
offers limited runtime control via signals when it is not streaming data from
|
offers limited runtime control via signals.
|
||||||
standard input.
|
|
||||||
By sending a signal to the ezstream process, e.g. with the
|
By sending a signal to the ezstream process, e.g. with the
|
||||||
.Xr kill 1
|
.Xr kill 1
|
||||||
utility, a certain action will be triggered.
|
utility, a certain action will be triggered.
|
||||||
@ -74,6 +73,11 @@ following it, or restarts from the beginning of the list otherwise.
|
|||||||
.It Cd SIGUSR1
|
.It Cd SIGUSR1
|
||||||
Skips the currently playing track and moves on to the next in playlist mode, or
|
Skips the currently playing track and moves on to the next in playlist mode, or
|
||||||
restarts the current track when streaming a single file.
|
restarts the current track when streaming a single file.
|
||||||
|
.It Cd SIGUSR2
|
||||||
|
Triggers rereading of metadata for the stream by running the program or script
|
||||||
|
specified in \&<metadata_progname/\&>
|
||||||
|
.Pq see below.
|
||||||
|
This is the only meaningful signal when streaming from standard input.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
.Sh CONFIGURATION FILE SYNTAX
|
.Sh CONFIGURATION FILE SYNTAX
|
||||||
@ -103,7 +107,7 @@ In the configuration file, they need to be used as
|
|||||||
.Em start tag + content + end tag ,
|
.Em start tag + content + end tag ,
|
||||||
like in the introductory example shown above.
|
like in the introductory example shown above.
|
||||||
.Ss Root element
|
.Ss Root element
|
||||||
.Bl -ohang
|
.Bl -tag -width -Ds
|
||||||
.It Sy \&<ezstream\ /\&>
|
.It Sy \&<ezstream\ /\&>
|
||||||
.Pq Mandatory.
|
.Pq Mandatory.
|
||||||
The configuration file's root element.
|
The configuration file's root element.
|
||||||
@ -112,7 +116,7 @@ It contains all other configuration elements.
|
|||||||
.Ss Global configuration elements
|
.Ss Global configuration elements
|
||||||
Each of the global configuration elements have the \&<ezstream/\&> element as
|
Each of the global configuration elements have the \&<ezstream/\&> element as
|
||||||
their parent.
|
their parent.
|
||||||
.Bl -ohang
|
.Bl -tag -width -Ds
|
||||||
.It Sy \&<url\ /\&>
|
.It Sy \&<url\ /\&>
|
||||||
.Pq Mandatory.
|
.Pq Mandatory.
|
||||||
Specifies the location and mountpoint of the Icecast server, to which the
|
Specifies the location and mountpoint of the Icecast server, to which the
|
||||||
@ -170,10 +174,6 @@ Set to
|
|||||||
.Pq one
|
.Pq one
|
||||||
to indicate that the file in \&<filename/\&> is actually an executable program
|
to indicate that the file in \&<filename/\&> is actually an executable program
|
||||||
or script.
|
or script.
|
||||||
This program is supposed to print
|
|
||||||
.Pq to standard output
|
|
||||||
one line with the name of a file that should be streamed next and then exit.
|
|
||||||
.Pp
|
|
||||||
If set to
|
If set to
|
||||||
.Sy 0
|
.Sy 0
|
||||||
.Pq zero ,
|
.Pq zero ,
|
||||||
@ -181,6 +181,10 @@ If set to
|
|||||||
keyword
|
keyword
|
||||||
.Pa stdin
|
.Pa stdin
|
||||||
.Pq the default .
|
.Pq the default .
|
||||||
|
.Pp
|
||||||
|
See the
|
||||||
|
.Sy SCRIPTING
|
||||||
|
section for details on how the playlist program must behave.
|
||||||
.It Sy \&<shuffle\ /\&>
|
.It Sy \&<shuffle\ /\&>
|
||||||
.Pq Optional.
|
.Pq Optional.
|
||||||
Set to
|
Set to
|
||||||
@ -193,6 +197,24 @@ Files are played sequentially if set to
|
|||||||
or when the \&<shuffle/\&> element is absent.
|
or when the \&<shuffle/\&> element is absent.
|
||||||
This option will be ignored if \&<playlist_program/\&> is set to 1
|
This option will be ignored if \&<playlist_program/\&> is set to 1
|
||||||
.Pq one.
|
.Pq one.
|
||||||
|
.It Sy \&<metadata_progname\ /\&>
|
||||||
|
.Pq Optional.
|
||||||
|
Set the path and name of an executable program or script that should be used by
|
||||||
|
.Nm
|
||||||
|
to set the metadata of the stream.
|
||||||
|
The program is automatically queried when a new track is streamed, or whenever
|
||||||
|
the
|
||||||
|
.Sy SIGUSR2
|
||||||
|
signal is received.
|
||||||
|
.Pp
|
||||||
|
If the \&<metadata_progname/\&> element is present in the configuration, no
|
||||||
|
attempts will be made to read metadata from files that are being streamed.
|
||||||
|
If this behavior is not desired, it should be removed or commented out in the
|
||||||
|
configuration file.
|
||||||
|
.Pp
|
||||||
|
See the
|
||||||
|
.Sy SCRIPTING
|
||||||
|
section for details on how the metadata program must behave.
|
||||||
.It Sy \&<stream_once\ /\&>
|
.It Sy \&<stream_once\ /\&>
|
||||||
Set to
|
Set to
|
||||||
.Sy 1
|
.Sy 1
|
||||||
@ -268,7 +290,7 @@ should be done.
|
|||||||
.Ss Reencoding settings
|
.Ss Reencoding settings
|
||||||
Each of the reencoding configuration elements have the \&<reencode/\&>
|
Each of the reencoding configuration elements have the \&<reencode/\&>
|
||||||
element as their parent.
|
element as their parent.
|
||||||
.Bl -ohang
|
.Bl -tag -width -Ds
|
||||||
.It Sy \&<enable\ /\&>
|
.It Sy \&<enable\ /\&>
|
||||||
Set to
|
Set to
|
||||||
.Sy 1
|
.Sy 1
|
||||||
@ -287,7 +309,7 @@ Each format is described by a separate \&<encdec/\&> element.
|
|||||||
.Ss Decoder/Encoder settings
|
.Ss Decoder/Encoder settings
|
||||||
Each of the decoder/encoder configuration elements have the \&<encdec/\&>
|
Each of the decoder/encoder configuration elements have the \&<encdec/\&>
|
||||||
element as their parent.
|
element as their parent.
|
||||||
.Bl -ohang
|
.Bl -tag -width -Ds
|
||||||
.It Sy \&<format\ /\&>
|
.It Sy \&<format\ /\&>
|
||||||
This element is used by
|
This element is used by
|
||||||
.Nm
|
.Nm
|
||||||
@ -353,6 +375,54 @@ utility:
|
|||||||
.Pp
|
.Pp
|
||||||
.Dl \&<encode\&>oggenc -r -q 1.5 -t \&"@M@\&" -\&</encode\&>
|
.Dl \&<encode\&>oggenc -r -q 1.5 -t \&"@M@\&" -\&</encode\&>
|
||||||
.El
|
.El
|
||||||
|
.Sh SCRIPTING
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
utility provides hooks for externally controlled playlist and metadata
|
||||||
|
management.
|
||||||
|
This is done by running external programs or scripts that need to behave in
|
||||||
|
ways explained here.
|
||||||
|
.Ss Common Rules
|
||||||
|
.Bl -dash -compact
|
||||||
|
.It
|
||||||
|
The program must be an executable file.
|
||||||
|
.It
|
||||||
|
The program must write one line to standard output and exit.
|
||||||
|
.It
|
||||||
|
The program must not require arbitary command line options to function.
|
||||||
|
A wrapper script must be used if there is no other way.
|
||||||
|
.El
|
||||||
|
.Ss Playlist Programs
|
||||||
|
.Bl -dash -compact
|
||||||
|
.It
|
||||||
|
The program must return only filenames, with one filename per execution.
|
||||||
|
.It
|
||||||
|
The program should not return an empty line unless
|
||||||
|
.Nm
|
||||||
|
is supposed to know that the end of the playlist has been reached.
|
||||||
|
This is significant when the \&<stream_once/\&> option is enabled.
|
||||||
|
.El
|
||||||
|
.Ss Metadata Programs
|
||||||
|
.Bl -dash -compact
|
||||||
|
.It
|
||||||
|
The program must not return anything (just a newline character is okay) if it
|
||||||
|
is called by
|
||||||
|
.Nm
|
||||||
|
with a command line parameter that the program does not support.
|
||||||
|
.It
|
||||||
|
When called without command line parameters, the program should return a
|
||||||
|
complete string that should be used for metadata.
|
||||||
|
.It
|
||||||
|
When called with the command line parameter
|
||||||
|
.Qq artist ,
|
||||||
|
the program should return only the artist information of the metadata.
|
||||||
|
.Pq Optional.
|
||||||
|
.It
|
||||||
|
When called with the command line parameter
|
||||||
|
.Qq title ,
|
||||||
|
the program should return only the title information of the metadata.
|
||||||
|
.Pq Optional.
|
||||||
|
.El
|
||||||
.Sh FILES
|
.Sh FILES
|
||||||
.Bl -tag -width "!!EXAMPLES_DIR!!" -compact
|
.Bl -tag -width "!!EXAMPLES_DIR!!" -compact
|
||||||
.It Pa !!EXAMPLES_DIR!!
|
.It Pa !!EXAMPLES_DIR!!
|
||||||
|
@ -98,6 +98,8 @@ freeConfig(EZCONFIG *cfg)
|
|||||||
xfree(cfg->format);
|
xfree(cfg->format);
|
||||||
if (cfg->fileName != NULL)
|
if (cfg->fileName != NULL)
|
||||||
xfree(cfg->fileName);
|
xfree(cfg->fileName);
|
||||||
|
if (cfg->metadataProgram != NULL)
|
||||||
|
xfree(cfg->metadataProgram);
|
||||||
if (cfg->serverName != NULL)
|
if (cfg->serverName != NULL)
|
||||||
xfree(cfg->serverName);
|
xfree(cfg->serverName);
|
||||||
if (cfg->serverURL != NULL)
|
if (cfg->serverURL != NULL)
|
||||||
@ -228,6 +230,25 @@ parseConfig(const char *fileName)
|
|||||||
xmlFree(ls_xmlContentPtr);
|
xmlFree(ls_xmlContentPtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!xmlStrcmp(cur->name, BAD_CAST "metadata_progname")) {
|
||||||
|
if (ezConfig.metadataProgram != NULL) {
|
||||||
|
printf("%s[%ld]: Error: Cannot have multiple <metadata_progname> elements\n",
|
||||||
|
fileName, xmlGetLineNo(cur));
|
||||||
|
config_error++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cur->xmlChildrenNode != NULL) {
|
||||||
|
ls_xmlContentPtr = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
|
||||||
|
if (strlen(ls_xmlContentPtr) > PATH_MAX - 1) {
|
||||||
|
printf("%s[%ld]: Error: Path or filename in <metadata_progname> is too long\n",
|
||||||
|
fileName, xmlGetLineNo(cur));
|
||||||
|
config_error++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ezConfig.metadataProgram = xstrdup(ls_xmlContentPtr);
|
||||||
|
xmlFree(ls_xmlContentPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!xmlStrcmp(cur->name, BAD_CAST "playlist_program")) {
|
if (!xmlStrcmp(cur->name, BAD_CAST "playlist_program")) {
|
||||||
if (program_set) {
|
if (program_set) {
|
||||||
printf("%s[%ld]: Error: Cannot have multiple <playlist_program> elements\n",
|
printf("%s[%ld]: Error: Cannot have multiple <playlist_program> elements\n",
|
||||||
|
@ -44,6 +44,7 @@ typedef struct tag_EZCONFIG {
|
|||||||
char *password;
|
char *password;
|
||||||
char *format;
|
char *format;
|
||||||
char *fileName;
|
char *fileName;
|
||||||
|
char *metadataProgram;
|
||||||
char *serverName;
|
char *serverName;
|
||||||
char *serverURL;
|
char *serverURL;
|
||||||
char *serverGenre;
|
char *serverGenre;
|
||||||
|
@ -69,6 +69,7 @@
|
|||||||
#define STREAM_CONT 1
|
#define STREAM_CONT 1
|
||||||
#define STREAM_SKIP 2
|
#define STREAM_SKIP 2
|
||||||
#define STREAM_SERVERR 3
|
#define STREAM_SERVERR 3
|
||||||
|
#define STREAM_UPDMDATA 4
|
||||||
|
|
||||||
#ifdef HAVE___PROGNAME
|
#ifdef HAVE___PROGNAME
|
||||||
extern char *__progname;
|
extern char *__progname;
|
||||||
@ -78,6 +79,7 @@ char *__progname;
|
|||||||
|
|
||||||
int qFlag;
|
int qFlag;
|
||||||
int vFlag;
|
int vFlag;
|
||||||
|
int metadataFromProgram;
|
||||||
|
|
||||||
EZCONFIG *pezConfig = NULL;
|
EZCONFIG *pezConfig = NULL;
|
||||||
static const char *blankString = "";
|
static const char *blankString = "";
|
||||||
@ -85,15 +87,17 @@ playlist_t *playlist = NULL;
|
|||||||
int playlistMode = 0;
|
int playlistMode = 0;
|
||||||
|
|
||||||
#ifdef HAVE_SIGNALS
|
#ifdef HAVE_SIGNALS
|
||||||
const int ezstream_signals[] = { SIGHUP, SIGUSR1 };
|
const int ezstream_signals[] = { SIGHUP, SIGUSR1, SIGUSR2 };
|
||||||
|
|
||||||
volatile sig_atomic_t rereadPlaylist = 0;
|
volatile sig_atomic_t rereadPlaylist = 0;
|
||||||
volatile sig_atomic_t rereadPlaylist_notify = 0;
|
volatile sig_atomic_t rereadPlaylist_notify = 0;
|
||||||
volatile sig_atomic_t skipTrack = 0;
|
volatile sig_atomic_t skipTrack = 0;
|
||||||
|
volatile sig_atomic_t queryMetadata = 0;
|
||||||
#else
|
#else
|
||||||
int rereadPlaylist = 0;
|
int rereadPlaylist = 0;
|
||||||
int rereadPlaylist_notify = 0;
|
int rereadPlaylist_notify = 0;
|
||||||
int skipTrack = 0;
|
int skipTrack = 0;
|
||||||
|
int queryMetadata = 0;
|
||||||
#endif /* HAVE_SIGNALS */
|
#endif /* HAVE_SIGNALS */
|
||||||
|
|
||||||
typedef struct tag_ID3Tag {
|
typedef struct tag_ID3Tag {
|
||||||
@ -133,6 +137,9 @@ sig_handler(int sig)
|
|||||||
case SIGUSR1:
|
case SIGUSR1:
|
||||||
skipTrack = 1;
|
skipTrack = 1;
|
||||||
break;
|
break;
|
||||||
|
case SIGUSR2:
|
||||||
|
queryMetadata = 1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -291,16 +298,26 @@ processMetadata(shout_t *shout, const char *fileName)
|
|||||||
shout_metadata_t *shout_mdata = NULL;
|
shout_metadata_t *shout_mdata = NULL;
|
||||||
metadata_t *mdata = NULL;
|
metadata_t *mdata = NULL;
|
||||||
|
|
||||||
if ((mdata = metadata_file(fileName)) == NULL) {
|
if (metadataFromProgram) {
|
||||||
|
if ((mdata = metadata_program(fileName)) == NULL)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
if (!metadata_program_update(mdata, METADATA_STRING)) {
|
||||||
|
metadata_free(&mdata);
|
||||||
songInfo = xstrdup(blankString);
|
songInfo = xstrdup(blankString);
|
||||||
return (songInfo);
|
return (songInfo);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if ((mdata = metadata_file(fileName)) == NULL)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
if (!metadata_file_update(mdata)) {
|
if (!metadata_file_update(mdata)) {
|
||||||
metadata_free(&mdata);
|
metadata_free(&mdata);
|
||||||
songInfo = xstrdup(blankString);
|
songInfo = xstrdup(blankString);
|
||||||
return (songInfo);
|
return (songInfo);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
songInfo = xstrdup(metadata_get_string(mdata));
|
songInfo = xstrdup(metadata_get_string(mdata));
|
||||||
metadata_free(&mdata);
|
metadata_free(&mdata);
|
||||||
|
|
||||||
@ -327,6 +344,10 @@ openResource(shout_t *shout, const char *fileName, int *popenFlag,
|
|||||||
char *pCommandString = NULL;
|
char *pCommandString = NULL;
|
||||||
|
|
||||||
if (strcmp(fileName, "stdin") == 0) {
|
if (strcmp(fileName, "stdin") == 0) {
|
||||||
|
if (metadataFromProgram &&
|
||||||
|
processMetadata(shout, pezConfig->metadataProgram) == NULL)
|
||||||
|
return (filep);
|
||||||
|
|
||||||
if (vFlag)
|
if (vFlag)
|
||||||
printf("%s: Reading from standard input\n",
|
printf("%s: Reading from standard input\n",
|
||||||
__progname);
|
__progname);
|
||||||
@ -354,7 +375,12 @@ openResource(shout_t *shout, const char *fileName, int *popenFlag,
|
|||||||
return (filep);
|
return (filep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (metadataFromProgram)
|
||||||
|
pMetadata = processMetadata(shout, pezConfig->metadataProgram);
|
||||||
|
else
|
||||||
pMetadata = processMetadata(shout, fileName);
|
pMetadata = processMetadata(shout, fileName);
|
||||||
|
if (pMetadata == NULL)
|
||||||
|
return (filep);
|
||||||
if (metaCopy != NULL)
|
if (metaCopy != NULL)
|
||||||
*metaCopy = xstrdup(pMetadata);
|
*metaCopy = xstrdup(pMetadata);
|
||||||
|
|
||||||
@ -487,6 +513,19 @@ sendStream(shout_t *shout, FILE *filepstream, const char *fileName,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shout_sync(shout);
|
||||||
|
|
||||||
|
if (shout_send(shout, buff, read) != SHOUTERR_SUCCESS) {
|
||||||
|
printf("%s: shout_send(): %s\n", __progname,
|
||||||
|
shout_get_error(shout));
|
||||||
|
if (reconnectServer(shout, 1))
|
||||||
|
break;
|
||||||
|
else {
|
||||||
|
ret = STREAM_SERVERR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (rereadPlaylist_notify) {
|
if (rereadPlaylist_notify) {
|
||||||
rereadPlaylist_notify = 0;
|
rereadPlaylist_notify = 0;
|
||||||
if (!pezConfig->fileNameIsProgram)
|
if (!pezConfig->fileNameIsProgram)
|
||||||
@ -498,16 +537,10 @@ sendStream(shout_t *shout, FILE *filepstream, const char *fileName,
|
|||||||
ret = STREAM_SKIP;
|
ret = STREAM_SKIP;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (queryMetadata) {
|
||||||
shout_sync(shout);
|
queryMetadata = 0;
|
||||||
|
if (metadataFromProgram) {
|
||||||
if (shout_send(shout, buff, read) != SHOUTERR_SUCCESS) {
|
ret = STREAM_UPDMDATA;
|
||||||
printf("%s: shout_send(): %s\n", __progname,
|
|
||||||
shout_get_error(shout));
|
|
||||||
if (reconnectServer(shout, 1))
|
|
||||||
break;
|
|
||||||
else {
|
|
||||||
ret = STREAM_SERVERR;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -609,9 +642,14 @@ streamFile(shout_t *shout, const char *fileName)
|
|||||||
ret = sendStream(shout, filepstream, fileName, isStdin, NULL);
|
ret = sendStream(shout, filepstream, fileName, isStdin, NULL);
|
||||||
#endif
|
#endif
|
||||||
if (ret != STREAM_DONE) {
|
if (ret != STREAM_DONE) {
|
||||||
if (skipTrack && rereadPlaylist) {
|
if ((skipTrack && rereadPlaylist) ||
|
||||||
|
(skipTrack && queryMetadata)) {
|
||||||
skipTrack = 0;
|
skipTrack = 0;
|
||||||
ret = 1;
|
ret = STREAM_CONT;
|
||||||
|
}
|
||||||
|
if (queryMetadata && rereadPlaylist) {
|
||||||
|
queryMetadata = 0;
|
||||||
|
ret = STREAM_CONT;
|
||||||
}
|
}
|
||||||
if (ret == STREAM_SKIP || skipTrack) {
|
if (ret == STREAM_SKIP || skipTrack) {
|
||||||
skipTrack = 0;
|
skipTrack = 0;
|
||||||
@ -621,13 +659,30 @@ streamFile(shout_t *shout, const char *fileName)
|
|||||||
retval = 1;
|
retval = 1;
|
||||||
ret = STREAM_DONE;
|
ret = STREAM_DONE;
|
||||||
}
|
}
|
||||||
|
if (ret == STREAM_UPDMDATA || queryMetadata) {
|
||||||
|
queryMetadata = 0;
|
||||||
|
if (metadataFromProgram) {
|
||||||
|
char *mdataStr;
|
||||||
|
|
||||||
|
if (vFlag > 1)
|
||||||
|
printf("%s: Querying '%s' for fresh metadata\n",
|
||||||
|
__progname, pezConfig->metadataProgram);
|
||||||
|
if ((mdataStr = processMetadata(shout, pezConfig->metadataProgram)) == NULL) {
|
||||||
|
retval = 0;
|
||||||
|
ret = STREAM_DONE;
|
||||||
|
}
|
||||||
|
printf("%s: New metadata: ``%s''\n",
|
||||||
|
__progname, mdataStr);
|
||||||
|
xfree(mdataStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (ret == STREAM_SERVERR) {
|
if (ret == STREAM_SERVERR) {
|
||||||
retval = 0;
|
retval = 0;
|
||||||
ret = STREAM_DONE;
|
ret = STREAM_DONE;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
retval = 1;
|
retval = 1;
|
||||||
} while (ret);
|
} while (ret != STREAM_DONE);
|
||||||
|
|
||||||
if (popenFlag)
|
if (popenFlag)
|
||||||
pclose(filepstream);
|
pclose(filepstream);
|
||||||
@ -683,9 +738,6 @@ streamPlaylist(shout_t *shout, const char *fileName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pezConfig->streamOnce)
|
|
||||||
return (0);
|
|
||||||
else
|
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -976,6 +1028,11 @@ main(int argc, char *argv[])
|
|||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pezConfig->metadataProgram != NULL)
|
||||||
|
metadataFromProgram = 1;
|
||||||
|
else
|
||||||
|
metadataFromProgram = 0;
|
||||||
|
|
||||||
#ifdef HAVE_SIGNALS
|
#ifdef HAVE_SIGNALS
|
||||||
memset(&act, 0, sizeof(act));
|
memset(&act, 0, sizeof(act));
|
||||||
act.sa_handler = sig_handler;
|
act.sa_handler = sig_handler;
|
||||||
|
130
src/metadata.c
130
src/metadata.c
@ -352,6 +352,11 @@ metadata_t *
|
|||||||
metadata_program(const char *program)
|
metadata_program(const char *program)
|
||||||
{
|
{
|
||||||
metadata_t *md;
|
metadata_t *md;
|
||||||
|
#ifdef HAVE_STAT
|
||||||
|
struct stat st;
|
||||||
|
#else
|
||||||
|
FILE *filep;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (program == NULL || strlen(program) == 0) {
|
if (program == NULL || strlen(program) == 0) {
|
||||||
printf("%s: metadata_program(): Internal error: Bad arguments\n",
|
printf("%s: metadata_program(): Internal error: Bad arguments\n",
|
||||||
@ -361,6 +366,27 @@ metadata_program(const char *program)
|
|||||||
|
|
||||||
md = metadata_create(program);
|
md = metadata_create(program);
|
||||||
md->program = 1;
|
md->program = 1;
|
||||||
|
md->string = xstrdup("");
|
||||||
|
|
||||||
|
#ifdef HAVE_STAT
|
||||||
|
if (stat(program, &st) == -1) {
|
||||||
|
printf("%s: %s: %s\n", __progname, program, strerror(errno));
|
||||||
|
metadata_free(&md);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
if (!(st.st_mode & (S_IEXEC | S_IXGRP | S_IXOTH))) {
|
||||||
|
printf("%s: %s: Not an executable program\n", __progname, program);
|
||||||
|
metadata_free(&md);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if ((filep = fopen(program, "r")) == NULL) {
|
||||||
|
printf("%s: %s: %s\n", __progname, program, strerror(errno));
|
||||||
|
metadata_free(&md);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
fclose(filep);
|
||||||
|
#endif /* HAVE_STAT */
|
||||||
|
|
||||||
return (md);
|
return (md);
|
||||||
}
|
}
|
||||||
@ -418,8 +444,110 @@ metadata_file_update(metadata_t *md)
|
|||||||
int
|
int
|
||||||
metadata_program_update(metadata_t *md, enum metadata_request md_req)
|
metadata_program_update(metadata_t *md, enum metadata_request md_req)
|
||||||
{
|
{
|
||||||
/* XXX not implemented */
|
FILE *filep;
|
||||||
|
char buf[METADATA_MAX + 1];
|
||||||
|
char command[PATH_MAX + strlen(" artist") + 1];
|
||||||
|
|
||||||
|
if (md == NULL) {
|
||||||
|
printf("%s: metadata_program_update(): Internal error: NULL argument\n",
|
||||||
|
__progname);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!md->program) {
|
||||||
|
printf("%s: metadata_program_update(): Internal error: Received file handle\n",
|
||||||
|
__progname);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (md_req) {
|
||||||
|
case METADATA_ALL:
|
||||||
|
metadata_clean_md(md);
|
||||||
|
if (!metadata_program_update(md, METADATA_STRING) ||
|
||||||
|
!metadata_program_update(md, METADATA_ARTIST) ||
|
||||||
|
!metadata_program_update(md, METADATA_TITLE))
|
||||||
return (0);
|
return (0);
|
||||||
|
break;
|
||||||
|
case METADATA_STRING:
|
||||||
|
strlcpy(command, md->filename, sizeof(command));
|
||||||
|
if (md->string != NULL)
|
||||||
|
xfree(md->string);
|
||||||
|
break;
|
||||||
|
case METADATA_ARTIST:
|
||||||
|
snprintf(command, sizeof(command), "%s artist", md->filename);
|
||||||
|
if (md->artist != NULL)
|
||||||
|
xfree(md->artist);
|
||||||
|
break;
|
||||||
|
case METADATA_TITLE:
|
||||||
|
snprintf(command, sizeof(command), "%s title", md->filename);
|
||||||
|
if (md->title != NULL)
|
||||||
|
xfree(md->title);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("%s: metadata_program_update(): Internal error: Unknown md_req\n",
|
||||||
|
__progname);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(NULL);
|
||||||
|
errno = 0;
|
||||||
|
if ((filep = popen(command, "r")) == NULL) {
|
||||||
|
printf("%s: playlist_run_program(): Error while executing '%s'",
|
||||||
|
__progname, command);
|
||||||
|
/* popen() does not set errno reliably ... */
|
||||||
|
if (errno)
|
||||||
|
printf(": %s\n", strerror(errno));
|
||||||
|
else
|
||||||
|
printf("\n");
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fgets(buf, sizeof(buf), filep) == NULL) {
|
||||||
|
if (ferror(filep))
|
||||||
|
printf("%s: Error while reading output from program '%s': %s\n",
|
||||||
|
__progname, md->filename, strerror(errno));
|
||||||
|
pclose(filep);
|
||||||
|
printf("%s: FATAL: External program '%s' not (or no longer) usable.\n",
|
||||||
|
__progname, md->filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose(filep);
|
||||||
|
|
||||||
|
if (strlen(buf) == sizeof(buf) - 1)
|
||||||
|
printf("%s: Warning: Metadata string received via '%s' is too long and has been truncated\n",
|
||||||
|
__progname, command);
|
||||||
|
|
||||||
|
if (buf[0] != '\0' && buf[strlen(buf) - 1] == '\n')
|
||||||
|
buf[strlen(buf) - 1] = '\0';
|
||||||
|
if (buf[0] != '\0' && buf[strlen(buf) - 1] == '\r')
|
||||||
|
buf[strlen(buf) - 1] = '\0';
|
||||||
|
|
||||||
|
switch (md_req) {
|
||||||
|
case METADATA_STRING:
|
||||||
|
if (strlen(buf) == 0) {
|
||||||
|
printf("%s: Warning: Empty metadata string received from '%s'\n",
|
||||||
|
__progname, md->filename);
|
||||||
|
md->string = xstrdup("");
|
||||||
|
} else
|
||||||
|
md->string = xstrdup(buf);
|
||||||
|
break;
|
||||||
|
case METADATA_ARTIST:
|
||||||
|
if (strlen(buf) > 0)
|
||||||
|
md->artist = xstrdup(buf);
|
||||||
|
break;
|
||||||
|
case METADATA_TITLE:
|
||||||
|
if (strlen(buf) > 0)
|
||||||
|
md->title = xstrdup(buf);
|
||||||
|
break;
|
||||||
|
case METADATA_ALL:
|
||||||
|
default:
|
||||||
|
printf("%s: metadata_program_update(): Internal error: METADATA_ALL in code unreachable by METADATA_ALL\n",
|
||||||
|
__progname);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
|
@ -45,8 +45,7 @@ metadata_t * metadata_file(const char * /* filename */);
|
|||||||
* - Accept no command line parameter and return a complete metadata string
|
* - Accept no command line parameter and return a complete metadata string
|
||||||
* (for metadata_get_string()). The program *should* always return
|
* (for metadata_get_string()). The program *should* always return
|
||||||
* something in this case (e.g. something based on the filename in case no
|
* something in this case (e.g. something based on the filename in case no
|
||||||
* metadata is available.) This string will default to "[unknown]"
|
* metadata is available.)
|
||||||
* otherwise.
|
|
||||||
* - Accept the command line parameter "artist" and return only the artist
|
* - Accept the command line parameter "artist" and return only the artist
|
||||||
* metadata, or an empty string if no artist information is available.
|
* metadata, or an empty string if no artist information is available.
|
||||||
* - Accept the command line parameter "title" and return only the song title
|
* - Accept the command line parameter "title" and return only the song title
|
||||||
|
Loading…
Reference in New Issue
Block a user