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); }
static void process_pls(plist_t playlists, char *file) { plist_t pl; plist_t items; struct playlist_info *pli; char *name; uint64_t id; int pl_id; uint32_t alen; uint32_t i; char virtual_path[PATH_MAX]; int ret; alen = plist_array_get_size(playlists); for (i = 0; i < alen; i++) { pl = plist_array_get_item(playlists, i); if (plist_get_node_type(pl) != PLIST_DICT) continue; ret = get_dictval_int_from_key(pl, "Playlist ID", &id); if (ret < 0) { DPRINTF(E_DBG, L_SCAN, "Playlist ID not found!\n"); continue; } ret = get_dictval_string_from_key(pl, "Name", &name); if (ret < 0) { DPRINTF(E_DBG, L_SCAN, "Name not found!\n"); continue; } if (ignore_pl(pl, name)) { free(name); continue; } pli = db_pl_fetch_bytitlepath(name, file); if (pli) { pl_id = pli->id; free_pli(pli, 0); db_pl_ping(pl_id); db_pl_clear_items(pl_id); } else pl_id = 0; ret = get_dictval_array_from_key(pl, "Playlist Items", &items); if (ret < 0) { DPRINTF(E_INFO, L_SCAN, "Playlist '%s' has no items\n", name); free(name); continue; } if (pl_id == 0) { snprintf(virtual_path, PATH_MAX, "/file:%s", file); ret = db_pl_add(name, file, virtual_path, &pl_id); if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Error adding iTunes playlist '%s' (%s)\n", name, file); free(name); continue; } DPRINTF(E_INFO, L_SCAN, "Added playlist as id %d\n", pl_id); } free(name); process_pl_items(items, pl_id); } }