Ejemplo n.º 1
0
void
scan_itunes_itml(const char *file, time_t mtime, int dir_id)
{
  struct playlist_info *pli;
  struct stat sb;
  char buf[PATH_MAX];
  char *itml_xml;
  plist_t itml;
  plist_t node;
  int fd;
  int ret;

  // This is special playlist that is disabled and only used for saving a timestamp
  pli = db_pl_fetch_bytitlepath(file, file);
  if (pli)
    {
      // mtime == db_timestamp is also treated as a modification because some editors do
      // stuff like 1) close the file with no changes (leading us to update db_timestamp),
      // 2) copy over a modified version from a tmp file (which may result in a mtime that
      // is equal to the newly updated db_timestamp)
      if (mtime && (pli->db_timestamp > mtime))
	{
	  DPRINTF(E_LOG, L_SCAN, "Unchanged iTunes XML found, not processing '%s'\n", file);

	  // TODO Protect the radio stations from purge after scan
	  db_pl_ping_bymatch(file, 0);
	  free_pli(pli, 0);
	  return;
	}

      DPRINTF(E_LOG, L_SCAN, "Modified iTunes XML found, processing '%s'\n", file);

      // Clear out everything, we will recreate
      db_pl_delete_bypath(file);
      free_pli(pli, 0);
    }
  else
    {
      DPRINTF(E_LOG, L_SCAN, "New iTunes XML found, processing: '%s'\n", file);
    }

  CHECK_NULL(L_SCAN, pli = calloc(1, sizeof(struct playlist_info)));

  pli->type = PL_PLAIN;
  pli->title = strdup(file);
  pli->path = strdup(file);
  snprintf(buf, sizeof(buf), "/file:%s", file);
  pli->virtual_path = strip_extension(buf);
  pli->directory_id = dir_id;

  ret = db_pl_add(pli, (int *)&pli->id);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_SCAN, "Error adding iTunes XML meta playlist '%s'\n", file);

      free_pli(pli, 0);
      return;
    }

  // Disable, only used for saving timestamp
  db_pl_disable_bypath(file, STRIP_NONE, 0);

  free_pli(pli, 0);

  fd = open(file, O_RDONLY);
  if (fd < 0)
    {
      DPRINTF(E_LOG, L_SCAN, "Could not open iTunes library '%s': %s\n", file, strerror(errno));

      return;
    }

  ret = fstat(fd, &sb);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_SCAN, "Could not stat iTunes library '%s': %s\n", file, strerror(errno));

      close(fd);
      return;
    }

  itml_xml = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
  if (itml_xml == MAP_FAILED)
    {
      DPRINTF(E_LOG, L_SCAN, "Could not map iTunes library '%s': %s\n", file, strerror(errno));

      close(fd);
      return;
    }

  itml = NULL;
  plist_from_xml(itml_xml, sb.st_size, &itml);

  ret = munmap(itml_xml, sb.st_size);
  if (ret < 0)
    DPRINTF(E_LOG, L_SCAN, "Could not unmap iTunes library '%s': %s\n", file, strerror(errno));

  close(fd);

  if (!itml)
    {
      DPRINTF(E_LOG, L_SCAN, "iTunes XML playlist '%s' failed to parse\n", file);

      return;
    }

  if (plist_get_node_type(itml) != PLIST_DICT)
    {
      DPRINTF(E_LOG, L_SCAN, "Malformed iTunes XML playlist '%s'\n", file);

      plist_free(itml);
      return;
    }

  /* Meta data */
  ret = check_meta(itml);
  if (ret < 0)
    {
      plist_free(itml);
      return;
    }

  /* Tracks */
  ret = get_dictval_dict_from_key(itml, "Tracks", &node);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_SCAN, "Could not find Tracks dict in '%s'\n", file);

      plist_free(itml);
      return;
    }

  id_map = calloc(ID_MAP_SIZE, sizeof(struct itml_to_db_map *));
  if (!id_map)
    {
      DPRINTF(E_FATAL, L_SCAN, "iTunes library parser could not allocate ID map\n");

      plist_free(itml);
      return;
    }

  ret = process_tracks(node);
  if (ret <= 0)
    {
      DPRINTF(E_LOG, L_SCAN, "No tracks loaded from iTunes XML '%s'\n", file);

      id_map_free();
      plist_free(itml);
      return;
    }

  DPRINTF(E_LOG, L_SCAN, "Loaded %d tracks from iTunes XML '%s'\n", ret, file);

  /* Playlists */
  ret = get_dictval_array_from_key(itml, "Playlists", &node);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_SCAN, "Could not find Playlists dict in '%s'\n", file);

      id_map_free();
      plist_free(itml);
      return;
    }

  process_pls(node, file);

  id_map_free();
  plist_free(itml);
}
Ejemplo n.º 2
0
/* Thread: scan */
static void
bulk_scan(int flags)
{
  cfg_t *lib;
  int ndirs;
  char *path;
  char *deref;
  time_t start;
  time_t end;
  int i;

  start = time(NULL);

  playlists = NULL;
  dirstack = NULL;

  lib = cfg_getsec(cfg, "library");

  ndirs = cfg_size(lib, "directories");
  for (i = 0; i < ndirs; i++)
    {
      path = cfg_getnstr(lib, "directories", i);

      deref = m_realpath(path);
      if (!deref)
	{
	  DPRINTF(E_LOG, L_SCAN, "Skipping library directory %s, could not dereference: %s\n", path, strerror(errno));

	  /* Assume dir is mistakenly not mounted, so just disable everything and update timestamps */
	  db_file_disable_bymatch(path, "", 0);
	  db_pl_disable_bymatch(path, "", 0);

	  db_file_ping_bymatch(path, 1);
	  db_pl_ping_bymatch(path, 1);

	  continue;
	}

      counter = 0;
      db_transaction_begin();
      process_directories(deref, flags);
      db_transaction_end();

      free(deref);

      if (scan_exit)
	return;
    }

  if (!(flags & F_SCAN_FAST) && playlists)
    process_deferred_playlists();

  if (scan_exit)
    return;

  if (dirstack)
    DPRINTF(E_LOG, L_SCAN, "WARNING: unhandled leftover directories\n");

  end = time(NULL);

  if (flags & F_SCAN_FAST)
    {
      DPRINTF(E_LOG, L_SCAN, "Bulk library scan completed in %.f sec (with file scan disabled)\n", difftime(end, start));
    }
  else
    {
      /* Protect spotify from the imminent purge if rescanning */
      if (flags & F_SCAN_RESCAN)
	{
	  db_file_ping_bymatch("spotify:", 0);
	  db_pl_ping_bymatch("spotify:", 0);
	}

      DPRINTF(E_DBG, L_SCAN, "Purging old database content\n");
      db_purge_cruft(start);

      DPRINTF(E_LOG, L_SCAN, "Bulk library scan completed in %.f sec\n", difftime(end, start));

      DPRINTF(E_DBG, L_SCAN, "Running post library scan jobs\n");
      db_hook_post_scan();
    }
}