void scan_playlist(char *file, time_t mtime) { FILE *fp; struct media_file_info mfi; struct playlist_info *pli; struct stat sb; char buf[PATH_MAX]; char *path; char *entry; char *filename; char *ptr; size_t len; int extinf; int pl_id; int pl_format; int mfi_id; int ret; char virtual_path[PATH_MAX]; int i; DPRINTF(E_LOG, L_SCAN, "Processing static playlist: %s\n", file); ptr = strrchr(file, '.'); if (!ptr) return; if (strcasecmp(ptr, ".m3u") == 0) pl_format = PLAYLIST_M3U; else if (strcasecmp(ptr, ".pls") == 0) pl_format = PLAYLIST_PLS; else return; filename = strrchr(file, '/'); if (!filename) filename = file; else filename++; ret = stat(file, &sb); if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Could not stat() '%s': %s\n", file, strerror(errno)); return; } fp = fopen(file, "r"); if (!fp) { DPRINTF(E_LOG, L_SCAN, "Could not open playlist '%s': %s\n", file, strerror(errno)); return; } /* Fetch or create playlist */ pli = db_pl_fetch_bypath(file); if (pli) { DPRINTF(E_DBG, L_SCAN, "Found playlist '%s', updating\n", file); pl_id = pli->id; db_pl_ping(pl_id); db_pl_clear_items(pl_id); } else { pli = (struct playlist_info *)malloc(sizeof(struct playlist_info)); if (!pli) { DPRINTF(E_LOG, L_SCAN, "Out of memory\n"); return; } memset(pli, 0, sizeof(struct playlist_info)); pli->type = PL_PLAIN; /* Get only the basename, to be used as the playlist title */ ptr = strrchr(filename, '.'); if (ptr) *ptr = '\0'; pli->title = strdup(filename); /* Restore the full filename */ if (ptr) *ptr = '.'; pli->path = strdup(file); snprintf(virtual_path, PATH_MAX, "/file:%s", file); pli->virtual_path = strdup(virtual_path); ret = db_pl_add(pli, &pl_id); if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Error adding playlist '%s'\n", file); free_pli(pli, 0); return; } DPRINTF(E_INFO, L_SCAN, "Added playlist as id %d\n", pl_id); } free_pli(pli, 0); extinf = 0; memset(&mfi, 0, sizeof(struct media_file_info)); while (fgets(buf, sizeof(buf), fp) != NULL) { len = strlen(buf); /* rtrim and check that length is sane (ignore blank lines) */ while ((len > 0) && isspace(buf[len - 1])) { len--; buf[len] = '\0'; } if (len < 1) continue; /* Saves metadata in mfi if EXTINF metadata line */ if ((pl_format == PLAYLIST_M3U) && extinf_get(buf, &mfi, &extinf)) continue; /* For pls files we are only interested in the part after the FileX= entry */ path = NULL; if ((pl_format == PLAYLIST_PLS) && (strncasecmp(buf, "file", strlen("file")) == 0)) path = strchr(buf, '=') + 1; else if (pl_format == PLAYLIST_M3U) path = buf; if (!path) continue; /* Check that first char is sane for a path */ if ((!isalnum(path[0])) && (path[0] != '/') && (path[0] != '.')) continue; /* Check if line is an URL, will be added to library */ if (strncasecmp(path, "http://", strlen("http://")) == 0) { DPRINTF(E_DBG, L_SCAN, "Playlist contains URL entry\n"); filename = strdup(path); if (!filename) { DPRINTF(E_LOG, L_SCAN, "Out of memory for playlist filename\n"); continue; } if (extinf) DPRINTF(E_INFO, L_SCAN, "Playlist has EXTINF metadata, artist is '%s', title is '%s'\n", mfi.artist, mfi.title); filescanner_process_media(filename, mtime, 0, F_SCAN_TYPE_URL, &mfi); } /* Regular file, should already be in library */ else { /* Playlist might be from Windows so we change backslash to forward slash */ for (i = 0; i < strlen(path); i++) { if (path[i] == '\\') path[i] = '/'; } /* Now search for the library item where the path has closest match to playlist item */ /* Succes is when we find an unambiguous match, or when we no longer can expand the */ /* the path to refine our search. */ entry = NULL; do { ptr = strrchr(path, '/'); if (entry) *(entry - 1) = '/'; if (ptr) { *ptr = '\0'; entry = ptr + 1; } else entry = path; DPRINTF(E_SPAM, L_SCAN, "Playlist entry is now %s\n", entry); ret = db_files_get_count_bymatch(entry); } while (ptr && (ret > 1)); if (ret > 0) { mfi_id = db_file_id_bymatch(entry); DPRINTF(E_DBG, L_SCAN, "Found playlist entry match, id is %d, entry is %s\n", mfi_id, entry); filename = db_file_path_byid(mfi_id); if (!filename) { DPRINTF(E_LOG, L_SCAN, "Playlist entry %s matches file id %d, but file path is missing.\n", entry, mfi_id); continue; } } else { DPRINTF(E_DBG, L_SCAN, "No match for playlist entry %s\n", entry); continue; } } ret = db_pl_add_item_bypath(pl_id, filename); if (ret < 0) DPRINTF(E_WARN, L_SCAN, "Could not add %s to playlist\n", filename); /* Clean up in preparation for next item */ extinf = 0; free_mfi(&mfi, 1); free(filename); } /* We had some extinf that we never got to use, free it now */ if (extinf) free_mfi(&mfi, 1); if (!feof(fp)) { DPRINTF(E_LOG, L_SCAN, "Error reading playlist '%s': %s\n", file, strerror(errno)); fclose(fp); return; } fclose(fp); DPRINTF(E_INFO, L_SCAN, "Done processing playlist\n"); }
int library_add_playlist_info(const char *path, const char *title, const char *virtual_path, enum pl_type type, int parent_pl_id, int dir_id) { struct playlist_info *pli; int plid; int ret; pli = db_pl_fetch_bypath(path); if (pli) { DPRINTF(E_DBG, L_LIB, "Playlist found ('%s', link %s), updating\n", title, path); plid = pli->id; pli->type = type; free(pli->title); pli->title = strdup(title); if (pli->virtual_path) free(pli->virtual_path); pli->virtual_path = safe_strdup(virtual_path); pli->directory_id = dir_id; ret = db_pl_update(pli); if (ret < 0) { DPRINTF(E_LOG, L_LIB, "Error updating playlist ('%s', link %s)\n", title, path); free_pli(pli, 0); return -1; } db_pl_clear_items(plid); } else { DPRINTF(E_DBG, L_LIB, "Adding playlist ('%s', link %s)\n", title, path); pli = (struct playlist_info *)malloc(sizeof(struct playlist_info)); if (!pli) { DPRINTF(E_LOG, L_LIB, "Out of memory\n"); return -1; } memset(pli, 0, sizeof(struct playlist_info)); pli->type = type; pli->title = strdup(title); pli->path = strdup(path); pli->virtual_path = safe_strdup(virtual_path); pli->parent_id = parent_pl_id; pli->directory_id = dir_id; ret = db_pl_add(pli, &plid); if ((ret < 0) || (plid < 1)) { DPRINTF(E_LOG, L_LIB, "Error adding playlist ('%s', link %s, ret %d, plid %d)\n", title, path, ret, plid); free_pli(pli, 0); return -1; } } free_pli(pli, 0); return plid; }
void scan_playlist(const char *file, time_t mtime, int dir_id) { FILE *fp; struct media_file_info mfi; struct playlist_info *pli; struct stat sb; char buf[PATH_MAX]; char *path; const char *filename; char *ptr; size_t len; int extinf; int pl_id; int pl_format; int ntracks; int nadded; int ret; ptr = strrchr(file, '.'); if (!ptr) return; if (strcasecmp(ptr, ".m3u") == 0) pl_format = PLAYLIST_M3U; else if (strcasecmp(ptr, ".pls") == 0) pl_format = PLAYLIST_PLS; else return; filename = filename_from_path(file); /* Fetch or create playlist */ pli = db_pl_fetch_bypath(file); if (pli) { db_pl_ping(pli->id); if (mtime && (pli->db_timestamp >= mtime)) { DPRINTF(E_LOG, L_SCAN, "Unchanged playlist found, not processing '%s'\n", file); // Protect this playlist's radio stations from purge after scan db_pl_ping_items_bymatch("http://", pli->id); free_pli(pli, 0); return; } DPRINTF(E_LOG, L_SCAN, "Modified playlist found, processing '%s'\n", file); pl_id = pli->id; db_pl_clear_items(pl_id); } else { DPRINTF(E_LOG, L_SCAN, "New playlist found, processing '%s'\n", file); CHECK_NULL(L_SCAN, pli = calloc(1, sizeof(struct playlist_info))); pli->type = PL_PLAIN; /* Get only the basename, to be used as the playlist title */ pli->title = strip_extension(filename); 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, &pl_id); if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Error adding playlist '%s'\n", file); free_pli(pli, 0); return; } DPRINTF(E_INFO, L_SCAN, "Added new playlist as id %d\n", pl_id); } free_pli(pli, 0); ret = stat(file, &sb); if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Could not stat() '%s': %s\n", file, strerror(errno)); return; } fp = fopen(file, "r"); if (!fp) { DPRINTF(E_LOG, L_SCAN, "Could not open playlist '%s': %s\n", file, strerror(errno)); return; } db_transaction_begin(); extinf = 0; memset(&mfi, 0, sizeof(struct media_file_info)); ntracks = 0; nadded = 0; while (fgets(buf, sizeof(buf), fp) != NULL) { len = strlen(buf); /* rtrim and check that length is sane (ignore blank lines) */ while ((len > 0) && isspace(buf[len - 1])) { len--; buf[len] = '\0'; } if (len < 1) continue; /* Saves metadata in mfi if EXTINF metadata line */ if ((pl_format == PLAYLIST_M3U) && extinf_get(buf, &mfi, &extinf)) continue; /* For pls files we are only interested in the part after the FileX= entry */ path = NULL; if ((pl_format == PLAYLIST_PLS) && (strncasecmp(buf, "file", strlen("file")) == 0)) path = strchr(buf, '=') + 1; else if (pl_format == PLAYLIST_M3U) path = buf; if (!path) continue; /* Check that first char is sane for a path */ if ((!isalnum(path[0])) && (path[0] != '/') && (path[0] != '.')) continue; /* Check if line is an URL, will be added to library, otherwise it should already be there */ if (strncasecmp(path, "http://", 7) == 0) ret = process_url(pl_id, path, sb.st_mtime, extinf, &mfi); else ret = process_regular_file(pl_id, path); ntracks++; if (ntracks % 200 == 0) { DPRINTF(E_LOG, L_SCAN, "Processed %d items...\n", ntracks); db_transaction_end(); db_transaction_begin(); } if (ret == 0) nadded++; /* Clean up in preparation for next item */ extinf = 0; free_mfi(&mfi, 1); } db_transaction_end(); /* We had some extinf that we never got to use, free it now */ if (extinf) free_mfi(&mfi, 1); if (!feof(fp)) DPRINTF(E_LOG, L_SCAN, "Error reading playlist '%s' (only added %d tracks): %s\n", file, nadded, strerror(errno)); else DPRINTF(E_LOG, L_SCAN, "Done processing playlist, added/modified %d items\n", nadded); fclose(fp); }
void scan_smartpl(char *file, time_t mtime) { struct playlist_info *pli; int pl_id; char virtual_path[PATH_MAX]; char *ptr; int ret; /* Fetch or create playlist */ pli = db_pl_fetch_bypath(file); if (!pli) { pli = (struct playlist_info *) malloc(sizeof(struct playlist_info)); if (!pli) { DPRINTF(E_LOG, L_SCAN, "Out of memory\n"); return; } memset(pli, 0, sizeof(struct playlist_info)); pli->path = strdup(file); snprintf(virtual_path, PATH_MAX, "/file:%s", file); ptr = strrchr(virtual_path, '.'); if (ptr) *ptr = '\0'; pli->virtual_path = strdup(virtual_path); pli->type = PL_SMART; } else pl_id = pli->id; ret = smartpl_parse_file(file, pli); if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Error parsing smart playlist '%s'\n", file); free_pli(pli, 0); return; } if (pli->id) { ret = db_pl_update(pli); } else { ret = db_pl_add(pli, &pl_id); } if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Error adding smart playlist '%s'\n", file); free_pli(pli, 0); return; } DPRINTF(E_INFO, L_SCAN, "Added smart playlist as id %d\n", pl_id); free_pli(pli, 0); DPRINTF(E_INFO, L_SCAN, "Done processing smart playlist\n"); }