static PyObject*python_eval(char *functionname) {
	PyObject *ret;

	ices_log_debug("Interpreting [%s]", functionname);

	/* Call the python function */
	ret = PyObject_CallMethod(python_module, functionname, NULL);
	if (!ret)
		PyErr_Print();

	ices_log_debug("Done interpreting [%s]", functionname);
	return ret;
}
/* Update metadata on server via fork.
 * Note that the very first metadata update after connection is delayed,
 * because if we try to update our new info to the server and the server has
 * not yet accepted us as a source, the information is lost. */
void ices_metadata_update(int delay) {
	pid_t child;

	if (delay)
		ices_log_debug("Delaying metadata update...");

	if ((child = fork()) == 0) {
		metadata_update(delay ? INITDELAY : 0);
		_exit(0);
	}

	if (child == -1)
		ices_log_debug("Metadata update failed: fork");
}
Beispiel #3
0
/* Cleanup a filename so it looks more like a song name */
static char *
metadata_clean_filename (const char* path, char *buf, size_t len)
{
  char *ptr =NULL;

  if (!path || !buf) {
    ices_log ("ERROR: Polluted strings sent to filename cleanup.");
    return NULL;
  }

  /* Find last slash */
  ptr = strrchr (path, '/');

  if (ptr && strlen (ptr) > 0) {
    strncpy (buf, ptr + 1, len);
  } else {
    strncpy (buf, path, len);
  }

  if ((ptr = strrchr (buf, '.'))) {
    *ptr = '\0';
  }

  ices_log_debug ("Filename cleaned up from [%s] to [%s]", path, buf);
  return buf;
}
Beispiel #4
0
/* I'm not sure whether I'll keep this... */
static RETSIGTYPE
signals_usr1 (const int sig)
{
  ices_log_debug ("Caught SIGUSR1, skipping to next track...");
  ices_stream_next ();
#endif
}
Beispiel #5
0
static void cf_new_track(input_stream_t *source) {
  static int skipnext = 0;
  static input_stream_t lasttrack;
  int filesecs;

  if (lasttrack.samplerate && lasttrack.samplerate != source->samplerate) {
    if (resample(lasttrack.samplerate, source->samplerate) < 0)
      skipnext = 1;
  }

  memcpy(&lasttrack, source, sizeof(lasttrack));

  /* turn off crossfading for tracks less than twice the length of the fade */
  if (skipnext) {
    skipnext = 0;
    return;
  }

  if (source->filesize && source->bitrate) {
    filesecs = source->filesize / (source->bitrate * 128);
    if (filesecs < 10 || filesecs <= Fadelen * 2) {
      ices_log_debug("crossfade: not fading short track of %d secs", filesecs);
      skipnext = 1;
      return;
    }
  }

  NewTrack = FadeSamples;
}
Beispiel #6
0
/* Initialize the script playlist handler */
int
ices_playlist_script_initialize (playlist_module_t* pm)
{
  ices_log_debug ("Initializing script playlist handler...");

  if (!pm->module) {
    ices_log_error ("No playlist script defined");
    return 0;
  }
  
  cmd = pm->module;
  /* make path relative to module dir */
  if (cmd[0] != '/' && 
      !(cmd[0] == '.' && (cmd[1] == '/' || (cmd[1] == '.' && cmd[2] == '/'))))
  {
      cmd = malloc (strlen(pm->module) + strlen(ICES_MODULEDIR) + 2);
      if (cmd)
        sprintf (cmd, "%s/%s", ICES_MODULEDIR, pm->module);
  } else
    cmd = strdup(pm->module);
    
  if (!cmd) {
    ices_log_error("Could not allocate memory for playlist path");
    return 0;
  }

  pm->get_next = playlist_script_get_next;
  pm->get_metadata = playlist_script_get_metadata;
  pm->get_lineno = NULL;
  pm->shutdown = playlist_script_shutdown;

  return 1;
}
Beispiel #7
0
void
ices_id3v1_parse (input_stream_t* source)
{
  off_t pos;
  char buffer[1024];
  char title[31];
  int i;

  if (! source->filesize)
    return;

  buffer[30] = '\0';
  title[30] = '\0';
  pos = lseek (source->fd, 0, SEEK_CUR);

  lseek (source->fd, -128, SEEK_END);

  if ((read (source->fd, buffer, 3) == 3) && !strncmp (buffer, "TAG", 3)) {
    /* Don't stream the tag */
    source->filesize -= 128;

    if (read (source->fd, title, 30) != 30) {
      ices_log ("Error reading ID3v1 song title: %s",
        ices_util_strerror (errno, buffer, sizeof (buffer)));
      goto out;
    }

    for (i = strlen (title) - 1; i >= 0 && title[i] == ' '; i--)
      title[i] = '\0';
    ices_log_debug ("ID3v1: Title: %s", title);

    if (read (source->fd, buffer, 30) != 30) {
      ices_log ("Error reading ID3v1 artist: %s",
        ices_util_strerror (errno, buffer, sizeof (buffer)));
      goto out;
    }

    for (i = strlen (buffer) - 1; i >= 0 && buffer[i] == ' '; i--)
      buffer[i] = '\0';
    ices_log_debug ("ID3v1: Artist: %s", buffer);

    ices_metadata_set (buffer, title);
  }
  
out:
  lseek (source->fd, pos, SEEK_SET);
}
static const char*pl_find_func(const char* func) {
	if (hv_exists(PL_defstash, func, strlen(func))) {
		ices_log_debug("Found method: %s", func);
		return func;
	}

	return NULL;
}
Beispiel #9
0
/* Update metadata on server via fork.
 * It also does the job of cleaning up the song title to something the
 * world likes.
 * Note that the very first metadata update is delayed, because if we
 * try to update our new info to the server and the server has not yet
 * accepted us as a source, the information is lost. */
void
ices_metadata_update (input_stream_t* source)
{
  static int delay = INITDELAY;
  pid_t child;

  if (delay)
    ices_log_debug ("Initially delaying metadata update...");

  if ((child = fork()) == 0) {
    metadata_update (source, delay);
    _exit (0);
  }

  if (child == -1)
    ices_log_debug ("Metadata update failed: fork");

  delay = 0;
}
static int pl_perl_init_perl(void) {
	static char *my_argv[4] = { "", "-I" ICES_MODULEDIR, "-e", NULL };
	static char module_space[255];

	if ((my_perl = perl_alloc()) == NULL) {
		ices_log_debug("perl_alloc() error: (no memory!)");
		return -1;
	}

	snprintf(module_space, sizeof(module_space), "use %s",
		 ices_config.pm.module);
	my_argv[3] = module_space;

	perl_construct(my_perl);

	ices_log_debug("Importing perl module: %s", my_argv[3] + 4);

	if (perl_parse(my_perl, xs_init, 4, my_argv, NULL)) {
		ices_log_debug("perl_parse() error: parse problem");
		return -1;
	}

	if (!(pl_init_hook = pl_find_func("ices_init")))
		pl_init_hook = pl_find_func("ices_perl_initialize");
	if (!(pl_shutdown_hook = pl_find_func("ices_shutdown")))
		pl_shutdown_hook = pl_find_func("ices_perl_shutdown");
	if (!(pl_get_next_hook = pl_find_func("ices_get_next")))
		pl_get_next_hook = pl_find_func("ices_perl_get_next");
	if (!(pl_get_metadata_hook = pl_find_func("ices_get_metadata")))
		pl_get_metadata_hook = pl_find_func("ices_perl_get_metadata");
	if (!(pl_get_lineno_hook = pl_find_func("ices_get_lineno")))
		pl_get_lineno_hook = pl_find_func("ices_perl_get_current_lineno");

	if (!pl_get_next_hook) {
		ices_log_error("The playlist module must define at least the ices_get_next method");
		return -1;
	}

	return 0;
}
/*
 *  Here be magic...
 *  man perlcall gave me the following steps, to be
 *  able to handle getting return values from the
 *  embedded perl calls.
 */
static char*pl_perl_eval(const char *functionname) {
	int retcount = 0;
	char *retstr = NULL;

	dSP;                            /* initialize stack pointer      */

	ices_log_debug("Interpreting [%s]", functionname);

	ENTER;                          /* everything created after here */
	SAVETMPS;                       /* ...is a temporary variable.   */
	PUSHMARK(SP);                   /* remember the stack pointer    */
	PUTBACK;                        /* make local stack pointer global */

	/* G_SCALAR: get a scalar return | G_EVAL: Trap errors */
	retcount = perl_call_pv(functionname, G_SCALAR | G_EVAL);

	SPAGAIN;                        /* refresh stack pointer         */

	/* Check for errors in execution */
	if (SvTRUE(ERRSV)) {
		STRLEN n_a;
		ices_log_debug("perl error: %s", SvPV(ERRSV, n_a));
		(void) POPs;
	} else if (retcount) {
		/* we're calling strdup here, free() this later! */
		retstr = ices_util_strdup(POPp); /* pop the return value from stack */
		ices_log_debug("perl [%s] returned %d values, last [%s]", functionname, retcount, retstr);
	} else
		ices_log_debug("Perl call returned nothing");

	PUTBACK;
	FREETMPS;                       /* free that return value        */
	LEAVE;                          /* ...and the XPUSHed "mortal" args.*/

	ices_log_debug("Done interpreting [%s]", functionname);

	return retstr;
}
static void metadata_update(int delay) {
	ices_stream_t* stream;
	shout_metadata_t* metadata;
	char song[1024];
	char* playlist_metadata;
	char* value;
	int rc;

	if (delay)
		usleep(delay);

	if (!(playlist_metadata = ices_playlist_get_metadata())) {
		if (Title) {
			if (Artist)
				snprintf(song, sizeof(song), "%s - %s", Artist, Title);
			else
				snprintf(song, sizeof(song), "%s", Title);
		} else
			snprintf(song, sizeof(song), "%s", Filename);

		value = song;
	} else
		value = playlist_metadata;

	if (!(metadata = shout_metadata_new())) {
		ices_log_error("Error allocating metadata structure");
		ices_util_free(playlist_metadata);
		return;
	}

	if (shout_metadata_add(metadata, "song", value) != SHOUTERR_SUCCESS) {
		ices_log_error_output("Error adding info to metadata structure");
		ices_util_free(playlist_metadata);
		shout_metadata_free(metadata);
		return;
	}

	for (stream = ices_config.streams; stream; stream = stream->next) {
		rc = shout_set_metadata(stream->conn, metadata);

		if (rc != SHOUTERR_SUCCESS)
			ices_log_error_output("Updating metadata on %s failed.", stream->mount);
		else
			ices_log_debug("Updated metadata on %s to: %s", stream->mount, value);
	}

	ices_util_free(playlist_metadata);
	shout_metadata_free(metadata);
}
/* Attempt to reload the playlist module */
static int playlist_python_reload(void) {
	PyObject* new_module;

	if (!(new_module = PyImport_ReloadModule(python_module))) {
		ices_log_error("Playlist module reload failed");
		PyErr_Print();

		return -1;
	}

	python_module = new_module;
	ices_log_debug("Playlist module reloaded");

	return 0;
}
static char*python_find_attr(PyObject* module, char* f1, char* f2) {
	char* rc;

	if (PyObject_HasAttrString(module, f1))
		rc = f1;
	else if (PyObject_HasAttrString(module, f2))
		rc = f2;
	else
		rc = NULL;

	if (rc)
		ices_log_debug("Found method: %s", rc);

	return rc;
}
Beispiel #15
0
static void cf_shutdown(void) {
  if (FL) {
    free(FL);
    FL = NULL;
  }
  if (FR) {
    free(FR);
    FR = NULL;
  }
  if (Swap) {
    free(Swap);
    Swap = NULL;
  }

  ices_log_debug("Crossfader shutting down");
}
Beispiel #16
0
/* private functions */
static int cf_init(void) {
  if (!(FL = malloc(FadeSamples * 2)))
    goto err;
  if (!(FR = malloc(FadeSamples * 2)))
    goto err;
  if (!(Swap = malloc(FadeSamples * 2)))
    goto err;

  ices_log_debug("Crossfading %d seconds between tracks", Fadelen);
  return 0;

  err:
  ices_log_error("Crossfader could not allocate memory");
  cf_shutdown();
  return -1;
}
Beispiel #17
0
static int
id3v2_read_exthdr (input_stream_t* source, id3v2_tag* tag)
{
  char hdr[6];
  size_t len;

  if (source->read (source, hdr, 6) != 6) {
    ices_log ("Error reading ID3v2 extended header");

    return -1;
  }
  tag->pos += 6;

  len = id3v2_decode_synchsafe (hdr);
  ices_log_debug ("ID3v2: %d byte extended header found, skipping.", len);

  if (len > 6)
    return id3v2_skip_data (source, tag, len - 6);
  else
    return 0;
}
Beispiel #18
0
/* Function to initialize the python interpreter */
static int
python_init (void)
{
    /* For some reason, python refuses to look in the
     * current directory for modules */
    if (python_setup_path () < 0)
        return -1;

    Py_Initialize ();

    ices_log_debug ("Importing %s.py module...", ices_config.pm.module);

    /* Call the python api code to import the module */
    if (!(python_module = PyImport_ImportModule (ices_config.pm.module))) {
        ices_log ("Error: Could not import module %s", ices_config.pm.module);
        PyErr_Print();
        return -1;
    }

    /* Find defined methods */
    pl_init_hook = python_find_attr (python_module, "ices_init",
                                     "ices_python_initialize");
    pl_shutdown_hook = python_find_attr (python_module, "ices_shutdown",
                                         "ices_python_shutdown");
    pl_get_next_hook = python_find_attr (python_module, "ices_get_next",
                                         "ices_python_get_next");
    pl_get_metadata_hook = python_find_attr (python_module, "ices_get_metadata",
                           "ices_python_get_metadata");
    pl_get_lineno_hook = python_find_attr (python_module, "ices_get_lineno",
                                           "ices_python_get_current_lineno");

    if (! pl_get_next_hook) {
        ices_log_error ("The playlist module must define at least the ices_get_next method");
        return -1;
    }

    return 0;
}
Beispiel #19
0
static void
in_vorbis_set_metadata (ices_vorbis_in_t* vorbis_data)
{
  vorbis_comment* comment;
  char* key;
  char* artist = NULL;
  char* title = NULL;
  int i;

  if (! (comment = ov_comment (vorbis_data->vf, -1)))
    return;

  for (i = 0; i < comment->comments; i++) {
    key = comment->user_comments[i];
    ices_log_debug ("Vorbis comment found: %s", key);
    if (! strncasecmp ("artist", key, 6))
      artist = key+7;
    else if (! strncasecmp ("title", key, 5))
      title = key+6;
  }

  ices_metadata_set (artist, title);
}
Beispiel #20
0
static void
metadata_update (input_stream_t* source, int delay)
{
  ices_stream_t* stream;
  char song[1024];
  char* playlist_metadata;
  char* metadata;
  int rc;

  if (delay)
    usleep (delay);

  if (! (playlist_metadata = ices_playlist_get_metadata ())) {
    if (Title) {
      if (Artist)
	snprintf (song, sizeof (song), "%s - %s", Artist, Title);
      else
	snprintf (song, sizeof (song), "%s", Title);
    } else
      metadata_clean_filename (source->path, song, sizeof (song));
    
    metadata = song;
  } else
    metadata = playlist_metadata;

  for (stream = ices_config.streams; stream; stream = stream->next) {
    rc = shout_update_metadata (&stream->conn, metadata);
	
    if (rc != 1)
      ices_log_error ("Updating metadata on %s failed.", stream->mount);
    else
      ices_log_debug ("Updated metadata on %s to: %s", stream->mount, metadata);
  }

  ices_util_free (playlist_metadata);
}
Beispiel #21
0
void
ices_id3v2_parse (input_stream_t* source)
{
  unsigned char buf[1024];
  id3v2_tag tag;
  size_t remaining;
  ssize_t rv;

  if (source->read (source, buf, 10) != 10) {
    ices_log ("Error reading ID3v2");

    return;
  }

  tag.artist = tag.title = NULL;
  tag.pos = 0;
  tag.major_version = *(buf + 3);
  tag.minor_version = *(buf + 4);
  tag.flags = *(buf + 5);
  tag.len = id3v2_decode_synchsafe (buf + 6);
  ices_log_debug ("ID3v2: version %d.%d. Tag size is %d bytes.",
                  tag.major_version, tag.minor_version, tag.len);
  if (tag.major_version > 4) {
    ices_log_debug ("ID3v2: Version greater than maximum supported (4), skipping");
    id3v2_skip_data (source, &tag, tag.len);

    return;
  }

  if ((tag.major_version > 2) &&
      (tag.flags & ID3V2_FLAG_EXTHDR) && id3v2_read_exthdr (source, &tag) < 0) {
    ices_log ("Error reading ID3v2 extended header");

    return;
  }

  remaining = tag.len - tag.pos;
  if ((tag.major_version > 3) && (tag.flags & ID3V2_FLAG_FOOTER))
    remaining -= 10;

  while (remaining > ID3V2_FRAME_LEN(&tag) && (tag.artist == NULL || tag.title == NULL)) {
    if ((rv = id3v2_read_frame (source, &tag)) < 0) {
      ices_log ("Error reading ID3v2 frames, skipping to end of ID3v2 tag");
      break;
    }
    /* found padding */
    if (rv == 0)
      break;

    remaining -= rv;
  }

  /* allow fallback to ID3v1 */
  if (tag.artist || tag.title)
    ices_metadata_set (tag.artist, tag.title);
  ices_util_free (tag.artist);
  ices_util_free (tag.title);

  remaining = tag.len - tag.pos;
  if (remaining)
    id3v2_skip_data (source, &tag, remaining);
}
Beispiel #22
0
ssize_t
id3v2_read_frame (input_stream_t* source, id3v2_tag* tag)
{
  char hdr[10];
  size_t len, len2;
  ssize_t rlen;
  char* buf;

  if (source->read (source, hdr, ID3V2_FRAME_LEN(tag)) != ID3V2_FRAME_LEN(tag)) {
    ices_log ("Error reading ID3v2 frame");

    return -1;
  }
  tag->pos += ID3V2_FRAME_LEN(tag);

  if (hdr[0] == '\0')
    return 0;

  if (tag->major_version < 3) {
    len = id3v2_decode_synchsafe3 (hdr + 3);
    hdr[3] = '\0';
  } else if (tag->major_version == 3) {
    len = id3v2_decode_unsafe (hdr + 4);
    hdr[4] = '\0';
  } else {
    len = id3v2_decode_synchsafe (hdr + 4);
    hdr[4] = '\0';
  }
  if (len > tag->len - tag->pos) {
    ices_log ("Error parsing ID3v2 frame header: Frame too large (%d bytes)", len);
    
    return -1;
  }

  /* ices_log_debug("ID3v2: Frame type [%s] found, %d bytes", hdr, len); */
  if (!strcmp (hdr, ID3V2_ARTIST_TAG(tag)) || !strcmp (hdr, ID3V2_TITLE_TAG(tag))) {
    if (! (buf = malloc(len+1))) {
      ices_log ("Error allocating memory while reading ID3v2 frame");
      
      return -1;
    }
    len2 = len;
    while (len2) {
      if ((rlen = source->read (source, buf, len)) < 0) {
        ices_log ("Error reading ID3v2 frame data");
        free (buf);
      
        return -1;
      }
      tag->pos += rlen;
      len2 -= rlen;
    }

    /* skip encoding */
    if (!strcmp (hdr, ID3V2_TITLE_TAG(tag))) {
      buf[len] = '\0';
      ices_log_debug ("ID3v2: Title found: %s", buf + 1);
      tag->title = ices_util_strdup (buf + 1);
    } else {
      buf[len] = '\0';
      ices_log_debug ("ID3v2: Artist found: %s", buf + 1);
      tag->artist = ices_util_strdup (buf + 1);
    }

    free (buf);
  } else if (id3v2_skip_data (source, tag, len))
    return -1;

  return len + ID3V2_FRAME_LEN(tag);
}
Beispiel #23
0
/* SIGHUP caught, let's cycle logfiles */
static RETSIGTYPE
signals_hup (const int sig)
{
  ices_log_debug ("Caught SIGHUP, cycling logfiles...");
  ices_log_reopen_logfile ();
}
Beispiel #24
0
/* SIGINT, ok, let's be nice and just drop dead */
static RETSIGTYPE
signals_int (const int sig)
{
  ices_log_debug ("Caught signal, shutting down...");
  ices_setup_shutdown ();
}
Beispiel #25
0
/* try to open a vorbis file for decoding. Returns:
 *   0: success
 *   1: not a vorbis file
 *  -1: error opening
 */
int
ices_vorbis_open (input_stream_t* self, char* buf, size_t len)
{
  ices_vorbis_in_t* vorbis_data;
  OggVorbis_File* vf;
  FILE* fin;
  char errbuf[128];
  int rc;

  if (! (fin = fdopen (self->fd, "rb"))) {
    ices_util_strerror (errno, errbuf, sizeof (errbuf));
    ices_log_error ("Error opening %s: %s", self->path, errbuf);

    return -1;
  }

  if (! (vf = (OggVorbis_File*) malloc (sizeof (OggVorbis_File)))) {
    ices_log_error ("Malloc failed in ices_vorbis_open");
    return -1;
  }

  if ((rc = ov_open (fin, vf, buf, len)) != 0) {
    free (vf);
    fclose (fin);

    if (rc == OV_ENOTVORBIS)
      return 1;

    if (rc == OV_EREAD)
      ices_log_error ("Read error opening vorbis file");
    else if (rc == OV_EVERSION)
      ices_log_error ("Vorbis version mismatch");
    else if (rc == OV_EBADHEADER)
      ices_log_error ("Invalid vorbis header");
    else
      ices_log_error ("Error in ov_open: %d", rc);

    return -1;
  }

  if (!(vorbis_data = (ices_vorbis_in_t*)malloc (sizeof (ices_vorbis_in_t)))) {
    ices_log_error ("Malloc failed in ices_vorbis_open");
    ov_clear (vf);
    free (vf);
    return -1;
  }

  if (!(vorbis_data->info = ov_info(vf, -1))) {
    ices_log_error ("Vorbis: error reading vorbis info");
    ices_vorbis_close (self);

    return -1;
  }
  
  if (vorbis_data->info->channels < 1) {
    ices_log_error ("Vorbis: Cannot decode, %d channels of audio data!",
                    vorbis_data->info->channels);
    ices_vorbis_close (self);

    return -1;
  }

  self->bitrate = vorbis_data->info->bitrate_nominal / 1000;
  if (! self->bitrate)
    self->bitrate = ov_bitrate (vf, -1) / 1000;
  self->samplerate = (unsigned int) vorbis_data->info->rate;

  ices_log_debug("Ogg vorbis file found, version %d, %d kbps, %d channels, %ld Hz",
                 vorbis_data->info->version, self->bitrate, vorbis_data->info->channels,
		 self->samplerate);

  vorbis_data->vf = vf;
  vorbis_data->samples = 0;

  self->type = ICES_INPUT_VORBIS;
  self->data = vorbis_data;

  self->read = NULL;
  self->readpcm = ices_vorbis_readpcm;
  self->close = ices_vorbis_close;

  in_vorbis_set_metadata (vorbis_data);
  
  return 0;
}
Beispiel #26
0
static char *
playlist_script_get_next (void)
{
  char *filename = NULL, *metadata = NULL;
  FILE *pipe;
  int i = 0;

  filename = malloc(STR_BUFFER);
  metadata = malloc(STR_BUFFER);
	
  pipe = popen(cmd, "r");

  if (!pipe) {
	  ices_log_error ("Couldn't open pipe to program \"%s\"", cmd);
	  return NULL;
  }

  if (fgets(filename, STR_BUFFER, pipe) == NULL) {
	  ices_log_error ("Couldn't read filename from pipe to program \"%s\"", cmd);
	  free(filename); filename = NULL;
	  free(metadata); metadata = NULL;
	  pclose(pipe);
	  return NULL;
  }

  if (fgets(metadata, STR_BUFFER, pipe) == NULL) {
	  /* This is non-fatal. */
	  ices_log_debug ("No metadata received from pipe to program \"%s\"", cmd);
	  free(metadata); metadata = NULL;
  }
	  
  pclose(pipe);

  if (filename[0] == '\n' || (filename[0] == '\r' && filename[1] == '\n')) {
		ices_log_error ("Got newlines instead of filename from program \"%s\"", cmd);
		free(filename); filename = NULL;
		free(metadata); metadata = NULL;
		pclose(pipe);
		return NULL;
  }

  /* Remove linefeeds etc. */
  i = 0;
  while (filename[i]) {
	  if (filename[i] == '\r' || filename[i] == '\n') {
		  filename[i] = '\0';
		  break;
	  }
	  i++;
  }
  i = 0;
  while (metadata && metadata[i]) {
	  if (metadata[i] == '\r' || metadata[i] == '\n') {
		  metadata[i] = '\0';
		  break;
	  }
	  i++;
  }

  if (playlist_metadata)
	  free(playlist_metadata);
  if (metadata) 
	  playlist_metadata = metadata;
  else
	  playlist_metadata = NULL;
  
  ices_log_debug ("Script playlist handler serving: %s [%s]", ices_util_nullcheck (filename), ices_util_nullcheck(playlist_metadata));

  return filename;
}