Exemplo n.º 1
0
static int
process_url(int pl_id, const char *path, time_t mtime, int extinf, struct media_file_info *mfi)
{
  char virtual_path[PATH_MAX];
  char *pos;
  int ret;

  if (extinf)
    DPRINTF(E_INFO, L_SCAN, "Playlist has EXTINF metadata, artist is '%s', title is '%s'\n", mfi->artist, mfi->title);

  mfi->id = db_file_id_bypath(path);
  mfi->path = strdup(path);

  pos = strchr(path, '#');
  if (pos)
    mfi->fname = strdup(pos+1);
  else
    mfi->fname = strdup(filename_from_path(mfi->path));

  mfi->data_kind = DATA_KIND_HTTP;
  mfi->time_modified = mtime;
  mfi->directory_id = DIR_HTTP;

  ret = scan_metadata_ffmpeg(path, mfi);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_SCAN, "Playlist URL '%s' is unavailable for probe/metadata, assuming MP3 encoding\n", path);
      mfi->type = strdup("mp3");
      mfi->codectype = strdup("mpeg");
      mfi->description = strdup("MPEG audio file");
    }

  if (!mfi->title)
    mfi->title = strdup(mfi->fname);

  snprintf(virtual_path, sizeof(virtual_path), "/%s", mfi->path);
  mfi->virtual_path = strdup(virtual_path);

  library_add_media(mfi);

  return db_pl_add_item_bypath(pl_id, path);
}
Exemplo n.º 2
0
static int
mfi_id_find(const char *path)
{
  struct query_params qp;
  char filter[PATH_MAX];
  const char *a;
  const char *b;
  char *dbpath;
  char *winner;
  int score;
  int i;
  int ret;

  ret = db_snprintf(filter, sizeof(filter), "f.fname = '%q' COLLATE NOCASE", filename_from_path(path));
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_SCAN, "Location in iTunes XML is too long: '%s'\n", path);
      return -1;
    }

  memset(&qp, 0, sizeof(struct query_params));

  qp.type = Q_BROWSE_PATH;
  qp.sort = S_NONE;
  qp.filter = filter;

  ret = db_query_start(&qp);
  if (ret < 0)
    {
      db_query_end(&qp);
      return -1;
    }

  winner = NULL;
  score = 0;
  while ((db_query_fetch_string(&qp, &dbpath) == 0) && dbpath)
    {
      if (qp.results == 1)
	{
	  winner = strdup(dbpath);
	  break;
	}

      for (i = 0, a = NULL, b = NULL; (parent_dir(&a, path) == 0) && (parent_dir(&b, dbpath) == 0) && (strcasecmp(a, b) == 0); i++)
	;

      DPRINTF(E_SPAM, L_SCAN, "Comparison of '%s' and '%s' gave score %d\n", dbpath, path, i);

      if (i > score)
	{
	  free(winner);
	  winner = strdup(dbpath);
	  score = i;
	}
      else if (i == score)
	{
	  free(winner);
	  winner = NULL;
	}
    }

  db_query_end(&qp);

  if (!winner)
    {
      DPRINTF(E_LOG, L_SCAN, "No file matches iTunes XML entry '%s'\n", path);
      return -1;
    }

  DPRINTF(E_DBG, L_SCAN, "Found '%s' from iTunes XML (results %d)\n", path, qp.results);

  ret = db_file_id_bypath(winner);
  free(winner);

  return ret;
}
Exemplo n.º 3
0
void
filescanner_process_media(char *path, time_t mtime, off_t size, int type, struct media_file_info *external_mfi)
{
  struct media_file_info *mfi;
  char *filename;
  time_t stamp;
  int id;
  int ret;

  filename = strrchr(path, '/');
  if ((!filename) || (strlen(filename) == 1))
    filename = path;
  else
    filename++;

  db_file_stamp_bypath(path, &stamp, &id);

  if (stamp && (stamp >= mtime))
    {
      db_file_ping(id);
      return;
    }

  if (!external_mfi)
    {
      mfi = (struct media_file_info*)malloc(sizeof(struct media_file_info));
      if (!mfi)
	{
	  DPRINTF(E_LOG, L_SCAN, "Out of memory for mfi\n");
	  return;
	}

      memset(mfi, 0, sizeof(struct media_file_info));
    }
  else
    mfi = external_mfi;

  if (stamp)
    mfi->id = db_file_id_bypath(path);

  mfi->fname = strdup(filename);
  if (!mfi->fname)
    {
      DPRINTF(E_LOG, L_SCAN, "Out of memory for fname\n");
      goto out;
    }

  mfi->path = strdup(path);
  if (!mfi->path)
    {
      DPRINTF(E_LOG, L_SCAN, "Out of memory for path\n");
      goto out;
    }

  mfi->time_modified = mtime;
  mfi->file_size = size;

  if (type & F_SCAN_TYPE_FILE)
    {
      mfi->data_kind = 0; /* real file */
      ret = scan_metadata_ffmpeg(path, mfi);
    }
  else if (type & F_SCAN_TYPE_URL)
    {
      mfi->data_kind = 1; /* url/stream */
#if LIBAVFORMAT_VERSION_MAJOR >= 56 || (LIBAVFORMAT_VERSION_MAJOR == 55 && LIBAVFORMAT_VERSION_MINOR >= 13)
      ret = scan_metadata_ffmpeg(path, mfi);
#else
      ret = scan_metadata_icy(path, mfi);
#endif
    }
  else if (type & F_SCAN_TYPE_SPOTIFY)
    {
      mfi->data_kind = 2; /* iTunes has no spotify data kind, but we use 2 */
      ret = mfi->artist && mfi->album && mfi->title;
    }
  else if (type & F_SCAN_TYPE_PIPE)
    {
      mfi->data_kind = 3; /* iTunes has no pipe data kind, but we use 3 */
      mfi->type = strdup("wav");
      mfi->codectype = strdup("wav");
      mfi->description = strdup("PCM16 pipe");
      ret = 1;
    }
  else
    {
      DPRINTF(E_LOG, L_SCAN, "Unknown scan type for %s, this error should not occur\n", path);
      ret = -1;
    }

  if (ret < 0)
    {
      DPRINTF(E_INFO, L_SCAN, "Could not extract metadata for %s\n", path);
      goto out;
    }

  if (type & F_SCAN_TYPE_COMPILATION)
    mfi->compilation = 1;
  if (type & F_SCAN_TYPE_PODCAST)
    mfi->media_kind = 4; /* podcast */
  if (type & F_SCAN_TYPE_AUDIOBOOK)
    mfi->media_kind = 8; /* audiobook */

  if (!mfi->item_kind)
    mfi->item_kind = 2; /* music */
  if (!mfi->media_kind)
    mfi->media_kind = 1; /* music */

  unicode_fixup_mfi(mfi);

  fixup_tags(mfi);

  if (mfi->id == 0)
    db_file_add(mfi);
  else
    db_file_update(mfi);

 out:
  if (!external_mfi)
    free_mfi(mfi, 0);
}
Exemplo n.º 4
0
/* Thread: scan */
static void
process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie)
{
  struct stat sb;
  char *deref = NULL;
  char *file = path;
  int type;
  int ret;

  DPRINTF(E_SPAM, L_SCAN, "File event: 0x%x, cookie 0x%x, wd %d\n", ie->mask, ie->cookie, wi->wd);

  if (ie->mask & IN_DELETE)
    {
      DPRINTF(E_DBG, L_SCAN, "File deleted: %s\n", path);

      db_file_delete_bypath(path);
      db_pl_delete_bypath(path);
    }

  if (ie->mask & IN_MOVED_FROM)
    {
      DPRINTF(E_DBG, L_SCAN, "File moved from: %s\n", path);

      db_file_disable_bypath(path, path, ie->cookie);
      db_pl_disable_bypath(path, path, ie->cookie);
    }

  if (ie->mask & IN_ATTRIB)
    {
      DPRINTF(E_DBG, L_SCAN, "File permissions changed: %s\n", path);

#ifdef HAVE_EUIDACCESS
      if (euidaccess(path, R_OK) < 0)
#else
      if (access(path, R_OK) < 0)
#endif
	{
	  DPRINTF(E_LOG, L_SCAN, "File access to '%s' failed: %s\n", path, strerror(errno));

	  db_file_delete_bypath(path);;
	}
      else if ((file_type_get(path) == FILE_REGULAR) && (db_file_id_bypath(path) <= 0)) // TODO Playlists
	{
	  DPRINTF(E_LOG, L_SCAN, "File access to '%s' achieved\n", path);

	  ie->mask |= IN_CLOSE_WRITE;
	}
    }

  if (ie->mask & IN_MOVED_TO)
    {
      DPRINTF(E_DBG, L_SCAN, "File moved to: %s\n", path);

      ret = db_file_enable_bycookie(ie->cookie, path);

      if (ret <= 0)
	{
	  /* It's not a known media file, so it's either a new file
	   * or a playlist, known or not.
	   * We want to scan the new file and we want to rescan the
	   * playlist to update playlist items (relative items).
	   */
	  ie->mask |= IN_CLOSE_WRITE;
	  db_pl_enable_bycookie(ie->cookie, path);
	}
    }

  if (ie->mask & IN_CREATE)
    {
      DPRINTF(E_DBG, L_SCAN, "File created: %s\n", path);

      ret = lstat(path, &sb);
      if (ret < 0)
	{
	  DPRINTF(E_LOG, L_SCAN, "Could not lstat() '%s': %s\n", path, strerror(errno));

	  return;
	}

      if (S_ISFIFO(sb.st_mode))
	ie->mask |= IN_CLOSE_WRITE;
    }

  if (ie->mask & IN_CLOSE_WRITE)
    {
      DPRINTF(E_DBG, L_SCAN, "File closed: %s\n", path);

      ret = lstat(path, &sb);
      if (ret < 0)
	{
	  DPRINTF(E_LOG, L_SCAN, "Could not lstat() '%s': %s\n", path, strerror(errno));

	  return;
	}

      if (S_ISLNK(sb.st_mode))
	{
	  deref = m_realpath(path);
	  if (!deref)
	    {
	      DPRINTF(E_LOG, L_SCAN, "Could not dereference symlink '%s': %s\n", path, strerror(errno));

	      return;
	    }

	  file = deref;

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

	      free(deref);
	      return;
	    }

	  if (S_ISDIR(sb.st_mode))
	    {
	      process_inotify_dir(wi, deref, ie);

	      free(deref);
	      return;
	    }
	}

      type = 0;
      if (check_speciallib(path, "compilations"))
	type |= F_SCAN_TYPE_COMPILATION;
      if (check_speciallib(path, "podcasts"))
	type |= F_SCAN_TYPE_PODCAST;
      if (check_speciallib(path, "audiobooks"))
	type |= F_SCAN_TYPE_AUDIOBOOK;

      if (S_ISREG(sb.st_mode))
	process_file(file, sb.st_mtime, sb.st_size, F_SCAN_TYPE_FILE | type, 0);
      else if (S_ISFIFO(sb.st_mode))
	process_file(file, sb.st_mtime, sb.st_size, F_SCAN_TYPE_PIPE | type, 0);

      if (deref)
	free(deref);
    }
}
Exemplo n.º 5
0
static void
process_media_file(char *file, time_t mtime, off_t size, int compilation)
{
  struct media_file_info mfi;
  char *filename;
  char *ext;
  time_t stamp;
  int id;
  int ret;

  db_file_stamp_bypath(file, &stamp, &id);

  if (stamp >= mtime)
    {
      db_file_ping(id);
      return;
    }

  memset(&mfi, 0, sizeof(struct media_file_info));

  if (stamp)
    mfi.id = db_file_id_bypath(file);

  filename = strrchr(file, '/');
  if (!filename)
    {
      DPRINTF(E_LOG, L_SCAN, "Could not determine filename for %s\n", file);

      return;
    }

  mfi.fname = strdup(filename + 1);
  if (!mfi.fname)
    {
      DPRINTF(E_WARN, L_SCAN, "Out of memory for fname\n");

      return;
    }

  mfi.path = strdup(file);
  if (!mfi.path)
    {
      DPRINTF(E_WARN, L_SCAN, "Out of memory for path\n");

      free(mfi.fname);
      return;
    }

  mfi.time_modified = mtime;
  mfi.file_size = size;

  ret = -1;

  /* Special cases */
  ext = strrchr(file, '.');
  if (ext)
    {
      if ((strcmp(ext, ".pls") == 0)
	  || (strcmp(ext, ".url") == 0))
	{
	  mfi.data_kind = 1; /* url/stream */

	  ret = scan_url_file(file, &mfi);
	  if (ret < 0)
	    goto out;
	}
      else if ((strcmp(ext, ".png") == 0)
	       || (strcmp(ext, ".jpg") == 0))
	{
	  /* Artwork - don't scan */
	  goto out;
	}
    }

  /* General case */
  if (ret < 0)
    {
      ret = scan_metadata_ffmpeg(file, &mfi);
      mfi.data_kind = 0; /* real file */
    }

  if (ret < 0)
    {
      DPRINTF(E_INFO, L_SCAN, "Could not extract metadata for %s\n", file);

      goto out;
    }

  mfi.compilation = compilation;

  if (!mfi.item_kind)
    mfi.item_kind = 2; /* music */
  if (!mfi.media_kind)
    mfi.media_kind = 1; /* music */

  unicode_fixup_mfi(&mfi);

  fixup_tags(&mfi);

  if (mfi.id == 0)
    db_file_add(&mfi);
  else
    db_file_update(&mfi);

 out:
  free_mfi(&mfi, 1);
}
Exemplo n.º 6
0
void
filescanner_process_media(char *path, time_t mtime, off_t size, int type, struct media_file_info *external_mfi, int dir_id)
{
  struct media_file_info *mfi;
  char *filename;
  time_t stamp;
  int id;
  char virtual_path[PATH_MAX];
  int ret;

  filename = strrchr(path, '/');
  if ((!filename) || (strlen(filename) == 1))
    filename = path;
  else
    filename++;

  db_file_stamp_bypath(path, &stamp, &id);

  if (stamp && (stamp >= mtime))
    {
      db_file_ping(id);
      return;
    }

  if (!external_mfi)
    {
      mfi = (struct media_file_info*)malloc(sizeof(struct media_file_info));
      if (!mfi)
	{
	  DPRINTF(E_LOG, L_SCAN, "Out of memory for mfi\n");
	  return;
	}

      memset(mfi, 0, sizeof(struct media_file_info));
    }
  else
    mfi = external_mfi;

  if (stamp)
    mfi->id = db_file_id_bypath(path);

  mfi->fname = strdup(filename);
  if (!mfi->fname)
    {
      DPRINTF(E_LOG, L_SCAN, "Out of memory for fname\n");
      goto out;
    }

  mfi->path = strdup(path);
  if (!mfi->path)
    {
      DPRINTF(E_LOG, L_SCAN, "Out of memory for path\n");
      goto out;
    }

  mfi->time_modified = mtime;
  mfi->file_size = size;

  if (type & F_SCAN_TYPE_COMPILATION)
    mfi->compilation = 1;
  if (type & F_SCAN_TYPE_PODCAST)
    mfi->media_kind = MEDIA_KIND_PODCAST; /* podcast */
  if (type & F_SCAN_TYPE_AUDIOBOOK)
    mfi->media_kind = MEDIA_KIND_AUDIOBOOK; /* audiobook */

  if (type & F_SCAN_TYPE_FILE)
    {
      mfi->data_kind = DATA_KIND_FILE;
      ret = scan_metadata_ffmpeg(path, mfi);
    }
  else if (type & F_SCAN_TYPE_URL)
    {
      mfi->data_kind = DATA_KIND_HTTP;
      ret = scan_metadata_ffmpeg(path, mfi);
      if (ret < 0)
	{
	  DPRINTF(E_LOG, L_SCAN, "Playlist URL is unavailable for probe/metadata, assuming MP3 encoding\n");
	  mfi->type = strdup("mp3");
	  mfi->codectype = strdup("mpeg");
	  mfi->description = strdup("MPEG audio file");
	  ret = 1;
	}
    }
  else if (type & F_SCAN_TYPE_SPOTIFY)
    {
      mfi->data_kind = DATA_KIND_SPOTIFY;
      ret = mfi->artist && mfi->album && mfi->title;
    }
  else if (type & F_SCAN_TYPE_PIPE)
    {
      mfi->data_kind = DATA_KIND_PIPE;
      mfi->type = strdup("wav");
      mfi->codectype = strdup("wav");
      mfi->description = strdup("PCM16 pipe");
      ret = 1;
    }
  else
    {
      DPRINTF(E_LOG, L_SCAN, "Unknown scan type for %s, this error should not occur\n", path);
      ret = -1;
    }

  if (ret < 0)
    {
      DPRINTF(E_INFO, L_SCAN, "Could not extract metadata for %s\n", path);
      goto out;
    }

  if (!mfi->item_kind)
    mfi->item_kind = 2; /* music */
  if (!mfi->media_kind)
    mfi->media_kind = MEDIA_KIND_MUSIC; /* music */

  unicode_fixup_mfi(mfi);

  fixup_tags(mfi);

  if (type & F_SCAN_TYPE_URL)
    {
      snprintf(virtual_path, PATH_MAX, "/http:/%s", mfi->title);
      mfi->virtual_path = strdup(virtual_path);
    }
  else if (type & F_SCAN_TYPE_SPOTIFY)
    {
      snprintf(virtual_path, PATH_MAX, "/spotify:/%s/%s/%s", mfi->album_artist, mfi->album, mfi->title);
      mfi->virtual_path = strdup(virtual_path);
    }
  else
    {
      snprintf(virtual_path, PATH_MAX, "/file:%s", mfi->path);
      mfi->virtual_path = strdup(virtual_path);
    }

  mfi->directory_id = dir_id;

  if (mfi->id == 0)
    db_file_add(mfi);
  else
    db_file_update(mfi);

 out:
  if (!external_mfi)
    free_mfi(mfi, 0);
}
Exemplo n.º 7
0
/* Thread: scan */
static void
process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie)
{
  struct stat sb;
  uint32_t path_hash;
  char *deref = NULL;
  char *file = path;
  char *dir;
  char dir_vpath[PATH_MAX];
  int type;
  int i;
  int dir_id;
  char *ptr;
  int ret;

  DPRINTF(E_DBG, L_SCAN, "File event: 0x%x, cookie 0x%x, wd %d\n", ie->mask, ie->cookie, wi->wd);

  path_hash = djb_hash(path, strlen(path));

  if (ie->mask & IN_DELETE)
    {
      DPRINTF(E_DBG, L_SCAN, "File deleted: %s\n", path);

      db_file_delete_bypath(path);
      db_pl_delete_bypath(path);
      cache_artwork_delete_by_path(path);
    }

  if (ie->mask & IN_MOVED_FROM)
    {
      DPRINTF(E_DBG, L_SCAN, "File moved from: %s\n", path);

      db_file_disable_bypath(path, path, ie->cookie);
      db_pl_disable_bypath(path, path, ie->cookie);
    }

  if (ie->mask & IN_ATTRIB)
    {
      DPRINTF(E_DBG, L_SCAN, "File attributes changed: %s\n", path);

      // Ignore the IN_ATTRIB if we just got an IN_CREATE
      for (i = 0; i < INCOMINGFILES_BUFFER_SIZE; i++)
	{
	  if (incomingfiles_buffer[i] == path_hash)
	    return;
	}

#ifdef HAVE_EUIDACCESS
      if (euidaccess(path, R_OK) < 0)
#else
      if (access(path, R_OK) < 0)
#endif
	{
	  DPRINTF(E_LOG, L_SCAN, "File access to '%s' failed: %s\n", path, strerror(errno));

	  db_file_delete_bypath(path);
	  cache_artwork_delete_by_path(path);
	}
      else if ((file_type_get(path) == FILE_REGULAR) && (db_file_id_bypath(path) <= 0)) // TODO Playlists
	{
	  DPRINTF(E_LOG, L_SCAN, "File access to '%s' achieved\n", path);

	  ie->mask |= IN_CLOSE_WRITE;
	}
    }

  if (ie->mask & IN_MOVED_TO)
    {
      DPRINTF(E_DBG, L_SCAN, "File moved to: %s\n", path);

      ret = db_file_enable_bycookie(ie->cookie, path);

      if (ret > 0)
	{
	  // If file was successfully enabled, update the directory id
	  dir = strdup(path);
	  ptr = strrchr(dir, '/');
	  dir[(ptr - dir)] = '\0';

	  ret = create_virtual_path(dir, dir_vpath, sizeof(dir_vpath));
	  if (ret >= 0)
	    {
	      dir_id = db_directory_id_byvirtualpath(dir_vpath);
	      if (dir_id > 0)
		{
		  ret = db_file_update_directoryid(path, dir_id);
		  if (ret < 0)
		    DPRINTF(E_LOG, L_SCAN, "Error updating directory id for file: %s\n", path);
		}
	    }

	  free(dir);
	}
      else
	{
	  /* It's not a known media file, so it's either a new file
	   * or a playlist, known or not.
	   * We want to scan the new file and we want to rescan the
	   * playlist to update playlist items (relative items).
	   */
	  ie->mask |= IN_CLOSE_WRITE;
	  db_pl_enable_bycookie(ie->cookie, path);
	}
    }

  if (ie->mask & IN_CREATE)
    {
      DPRINTF(E_DBG, L_SCAN, "File created: %s\n", path);

      ret = lstat(path, &sb);
      if (ret < 0)
	{
	  DPRINTF(E_LOG, L_SCAN, "Could not lstat() '%s': %s\n", path, strerror(errno));

	  return;
	}

      // Add to the list of files where we ignore IN_ATTRIB until the file is closed again
      if (S_ISREG(sb.st_mode))
	{
	  DPRINTF(E_SPAM, L_SCAN, "Incoming file created '%s' (%d), index %d\n", path, (int)path_hash, incomingfiles_idx);

	  incomingfiles_buffer[incomingfiles_idx] = path_hash;
	  incomingfiles_idx = (incomingfiles_idx + 1) % INCOMINGFILES_BUFFER_SIZE;
	}
      else if (S_ISFIFO(sb.st_mode))
	ie->mask |= IN_CLOSE_WRITE;
    }

  if (ie->mask & IN_CLOSE_WRITE)
    {
      DPRINTF(E_DBG, L_SCAN, "File closed: %s\n", path);

      // File has been closed so remove from the IN_ATTRIB ignore list
      for (i = 0; i < INCOMINGFILES_BUFFER_SIZE; i++)
	if (incomingfiles_buffer[i] == path_hash)
	  {
	    DPRINTF(E_SPAM, L_SCAN, "Incoming file closed '%s' (%d), index %d\n", path, (int)path_hash, i);

	    incomingfiles_buffer[i] = 0;
	  }

      ret = lstat(path, &sb);
      if (ret < 0)
	{
	  DPRINTF(E_LOG, L_SCAN, "Could not lstat() '%s': %s\n", path, strerror(errno));

	  return;
	}

      if (S_ISLNK(sb.st_mode))
	{
	  deref = m_realpath(path);
	  if (!deref)
	    {
	      DPRINTF(E_LOG, L_SCAN, "Could not dereference symlink '%s': %s\n", path, strerror(errno));

	      return;
	    }

	  file = deref;

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

	      free(deref);
	      return;
	    }

	  if (S_ISDIR(sb.st_mode))
	    {
	      process_inotify_dir(wi, deref, ie);

	      free(deref);
	      return;
	    }
	}

      type = 0;
      if (check_speciallib(path, "compilations"))
	type |= F_SCAN_TYPE_COMPILATION;
      if (check_speciallib(path, "podcasts"))
	type |= F_SCAN_TYPE_PODCAST;
      if (check_speciallib(path, "audiobooks"))
	type |= F_SCAN_TYPE_AUDIOBOOK;

      dir_id = get_parent_dir_id(file);

      if (S_ISREG(sb.st_mode))
	{
	  process_file(file, sb.st_mtime, sb.st_size, F_SCAN_TYPE_FILE | type, 0, dir_id);
	}
      else if (S_ISFIFO(sb.st_mode))
	process_file(file, sb.st_mtime, sb.st_size, F_SCAN_TYPE_PIPE | type, 0, dir_id);

      if (deref)
	free(deref);
    }
}