def update_playlist(self, filename, base_dir): """Find M3U playlists in the specified directory.""" changes = 0 deletions = 0 with tempfile.NamedTemporaryFile(mode='w+b', delete=False) as tempfp: new_playlist = tempfp.name with open(filename, mode='rb') as fp: for line in fp: original_path = line.rstrip(b'\r\n') # Ensure that path from playlist is absolute is_relative = not os.path.isabs(line) if is_relative: lookup = os.path.join(base_dir, original_path) else: lookup = original_path try: new_path = self.changes[beets.util.normpath(lookup)] except KeyError: if self.config['forward_slash']: line = path_as_posix(line) tempfp.write(line) else: if new_path is None: # Item has been deleted deletions += 1 continue changes += 1 if is_relative: new_path = os.path.relpath(new_path, base_dir) line = line.replace(original_path, new_path) if self.config['forward_slash']: line = path_as_posix(line) tempfp.write(line) if changes or deletions: self._log.info( 'Updated playlist {0} ({1} changes, {2} deletions)'.format( filename, changes, deletions)) beets.util.copy(new_playlist, filename, replace=True) beets.util.remove(new_playlist)
def update_playlists(self, lib): self._log.info("Updating {0} smart playlists...", len(self._matched_playlists)) playlist_dir = self.config['playlist_dir'].as_filename() playlist_dir = bytestring_path(playlist_dir) relative_to = self.config['relative_to'].get() if relative_to: relative_to = normpath(relative_to) # Maps playlist filenames to lists of track filenames. m3us = {} for playlist in self._matched_playlists: name, (query, q_sort), (album_query, a_q_sort) = playlist self._log.debug("Creating playlist {0}", name) items = [] if query: items.extend(lib.items(query, q_sort)) if album_query: for album in lib.albums(album_query, a_q_sort): items.extend(album.items()) # As we allow tags in the m3u names, we'll need to iterate through # the items and generate the correct m3u file names. for item in items: m3u_name = item.evaluate_template(name, True) m3u_name = sanitize_path(m3u_name, lib.replacements) if m3u_name not in m3us: m3us[m3u_name] = [] item_path = item.path if relative_to: item_path = os.path.relpath(item.path, relative_to) if item_path not in m3us[m3u_name]: m3us[m3u_name].append(item_path) prefix = bytestring_path(self.config['prefix'].as_str()) # Write all of the accumulated track lists to files. for m3u in m3us: m3u_path = normpath( os.path.join(playlist_dir, bytestring_path(m3u))) mkdirall(m3u_path) with open(syspath(m3u_path), 'wb') as f: for path in m3us[m3u]: if self.config['forward_slash'].get(): path = path_as_posix(path) if self.config['urlencode']: path = bytestring_path(pathname2url(path)) f.write(prefix + path + b'\n') self._log.info("{0} playlists updated", len(self._matched_playlists))
def write_ratings_file(self, lib): """ Android Poweramp supports importing ratings from playlist files This function writes a playlist file containing all songs with metadata tag #EXT-X-RATING:<n>, where n is 1-5. """ if not self.config["ratings_file"]: return # FIXME: Unsanitized rating file location rating_file = os.path.expanduser( bytestring_path(self.config["ratings_file"].get())) rating_dir = os.path.dirname(rating_file) if not os.path.exists(rating_dir): os.makedirs(rating_dir) # If the file has %s use a timestamp if "%s" in str(rating_file): old_files = glob.glob(rating_file % b"*") if len(old_files): # Remove up to one file to avoid surprises os.remove(old_files[0]) rating_file = rating_file % bytearray(str(int(time.time())), "utf-8") ratings = {} for item in lib.items(""): # Grab rating userrating = None if 'userrating' in item: userrating = item.userrating elif 'externalrating' in item: userrating = item.externalrating # Add to dict if userrating is not None: item_path = os.path.relpath(item.path, rating_dir) ratings[item_path] = userrating # Write ratings to rating file with open(rating_file, 'wb') as f: for fn, rating in ratings.items(): if self.config['forward_slash'].get(): fn = path_as_posix(fn) f.write(b"#EXT-X-RATING:" + bytes("%d" % (rating // 2), 'utf-8') + b"\n" + fn + b"\n") self._log.info(u"Wrote ratings to {0}", rating_file)
def test_forward_slash(self): p = br'C:\a\b\c' a = br'C:/a/b/c' self.assertEqual(util.path_as_posix(p), a)