Add id3 based auto playlist generation

This commit is contained in:
Nimesh Ghelani 2016-06-08 05:14:58 +05:30
parent 44fc42a2e4
commit f22fdee042

View file

@ -67,6 +67,30 @@ def get_relpath(path, basepath):
def is_path_prefix(prefix, path): def is_path_prefix(prefix, path):
return prefix == os.sep.join(os.path.commonprefix(map(splitpath, [prefix, path]))) return prefix == os.sep.join(os.path.commonprefix(map(splitpath, [prefix, path])))
def group_tracks_by_id3_template(tracks, template):
grouped_tracks_dict = {}
template_vars = set(re.findall(r'{.*?}', template))
for track in tracks:
try:
id3_dict = mutagen.File(track, easy=True)
except:
id3_dict = {}
key = template
single_var_present = False
for var in template_vars:
val = id3_dict.get(var[1:-1], [''])[0]
if len(val) > 0:
single_var_present = True
key = key.replace(var, val)
if single_var_present:
if key not in grouped_tracks_dict:
grouped_tracks_dict[key] = []
grouped_tracks_dict[key].append(track)
return sorted(grouped_tracks_dict.items())
class Text2Speech(object): class Text2Speech(object):
valid_tts = {'pico2wave': True, 'RHVoice': True, 'espeak': True} valid_tts = {'pico2wave': True, 'RHVoice': True, 'espeak': True}
@ -395,7 +419,7 @@ class PlaylistHeader(Record):
playlistcount = 1 playlistcount = 1
for i in self.lists: for i in self.lists:
playlist = Playlist(self) playlist = Playlist(self)
print "[+] Adding playlist", i print "[+] Adding playlist", (i[0] if type(i) == type(()) else i)
playlist.populate(i) playlist.populate(i)
construction = playlist.construct(tracks) construction = playlist.construct(tracks)
if playlist["number_of_songs"] > 0: if playlist["number_of_songs"] > 0:
@ -492,13 +516,18 @@ class Playlist(Record):
fullPath = relative fullPath = relative
return fullPath return fullPath
def populate(self, filename): def populate(self, obj):
# Create a playlist of the folder and all subfolders # Create a playlist of the folder and all subfolders
if type(obj) == type(()):
self.listtracks = obj[1]
text = obj[0]
else:
filename = obj
if os.path.isdir(filename): if os.path.isdir(filename):
self.listtracks = self.populate_directory(filename) self.listtracks = self.populate_directory(filename)
text = os.path.splitext(os.path.basename(filename))[0]
# Read the playlist file
else: else:
# Read the playlist file
with open(filename, 'rb') as f: with open(filename, 'rb') as f:
data = f.readlines() data = f.readlines()
@ -513,9 +542,9 @@ class Playlist(Record):
# Ensure all paths are not relative to the playlist file # Ensure all paths are not relative to the playlist file
for i in range(len(self.listtracks)): for i in range(len(self.listtracks)):
self.listtracks[i] = self.remove_relatives(self.listtracks[i], filename) self.listtracks[i] = self.remove_relatives(self.listtracks[i], filename)
text = os.path.splitext(os.path.basename(filename))[0]
# Handle the VoiceOverData # Handle the VoiceOverData
text = os.path.splitext(os.path.basename(filename))[0]
self["dbid"] = hashlib.md5(text).digest()[:8] #pylint: disable-msg=E1101 self["dbid"] = hashlib.md5(text).digest()[:8] #pylint: disable-msg=E1101
self.text_to_speech(text, self["dbid"], True) self.text_to_speech(text, self["dbid"], True)
@ -543,7 +572,7 @@ class Playlist(Record):
return output + chunks return output + chunks
class Shuffler(object): class Shuffler(object):
def __init__(self, path, voiceover=False, playlist_voiceover=False, rename=False, trackgain=0, auto_playlists=None): def __init__(self, path, voiceover=False, playlist_voiceover=False, rename=False, trackgain=0, auto_dir_playlists=None, auto_id3_playlists=None):
self.path = os.path.abspath(path) self.path = os.path.abspath(path)
self.tracks = [] self.tracks = []
self.albums = [] self.albums = []
@ -554,7 +583,8 @@ class Shuffler(object):
self.playlist_voiceover = playlist_voiceover self.playlist_voiceover = playlist_voiceover
self.rename = rename self.rename = rename
self.trackgain = trackgain self.trackgain = trackgain
self.auto_playlists = auto_playlists self.auto_dir_playlists = auto_dir_playlists
self.auto_id3_playlists = auto_id3_playlists
def initialize(self): def initialize(self):
# remove existing voiceover files (they are either useless or will be overwritten anyway) # remove existing voiceover files (they are either useless or will be overwritten anyway)
@ -650,24 +680,40 @@ def handle_interrupt(signal, frame):
if __name__ == '__main__': if __name__ == '__main__':
signal.signal(signal.SIGINT, handle_interrupt) signal.signal(signal.SIGINT, handle_interrupt)
parser = argparse.ArgumentParser(description= parser = argparse.ArgumentParser(description=
'Python script for building the Track and Playlist database ' 'Python script for building the Track and Playlist database '
'for the newer gen IPod Shuffle. Version 1.3') 'for the newer gen IPod Shuffle. Version 1.3')
parser.add_argument('--voiceover', action='store_true', parser.add_argument('--voiceover', action='store_true',
help='Enable track voiceover feature') help='Enable track voiceover feature')
parser.add_argument('--playlist-voiceover', action='store_true', parser.add_argument('--playlist-voiceover', action='store_true',
help='Enable playlist voiceover feature') help='Enable playlist voiceover feature')
parser.add_argument('--rename-unicode', action='store_true', parser.add_argument('--rename-unicode', action='store_true',
help='Rename files causing unicode errors, will do minimal required renaming') help='Rename files causing unicode errors, will do minimal required renaming')
parser.add_argument('--track-gain', type=nonnegative_int, default='0', parser.add_argument('--track-gain', type=nonnegative_int, default='0',
help='Specify volume gain (0-99) for all tracks; ' help='Specify volume gain (0-99) for all tracks; '
'0 (default) means no gain and is usually fine; ' '0 (default) means no gain and is usually fine; '
'e.g. 60 is very loud even on minimal player volume') 'e.g. 60 is very loud even on minimal player volume')
parser.add_argument('--auto-playlists', type=int, default=None, const=-1, nargs='?',
parser.add_argument('--auto-dir-playlists', type=int, default=None, const=-1, nargs='?',
help='Generate automatic playlists for each folder recursively inside ' help='Generate automatic playlists for each folder recursively inside '
'"IPod_Control/Music/". You can optionally limit the depth: ' '"IPod_Control/Music/". You can optionally limit the depth: '
'0=root, 1=artist, 2=album, n=subfoldername, default=-1 (No Limit).') '0=root, 1=artist, 2=album, n=subfoldername, default=-1 (No Limit).')
parser.add_argument('--auto-id3-playlists', type=str, default=None, metavar='ID3_TEMPLATE', const='{artist}', nargs='?',
help='Generate automatic playlists based on the id3 tags of any music '
'added to the iPod. You can optionally specify a template string '
'based on which id3 tags are used to generate playlists. For eg. '
'\'{artist} - {album}\' will use the pair of artist and album to group '
'tracks under one playlist. Similarly \'{genre}\' will group tracks based '
'on their genre tag. Default template used is \'{artist}\'')
parser.add_argument('path', help='Path to the IPod\'s root directory') parser.add_argument('path', help='Path to the IPod\'s root directory')
result = parser.parse_args() result = parser.parse_args()
checkPathValidity(result.path) checkPathValidity(result.path)
@ -675,11 +721,15 @@ if __name__ == '__main__':
if result.rename_unicode: if result.rename_unicode:
check_unicode(result.path) check_unicode(result.path)
if result.voiceover and not Text2Speech.check_support(): if result.auto_id3_playlists != None or result.auto_dir_playlists != None:
result.playlist_voiceover = True
if (result.voiceover or result.playlist_voiceover) and not Text2Speech.check_support():
print "Error: Did not find any voiceover program. Voiceover disabled." print "Error: Did not find any voiceover program. Voiceover disabled."
result.voiceover = False result.voiceover = False
result.playlist_voiceover = False
shuffle = Shuffler(result.path, voiceover=result.voiceover, playlist_voiceover=result.playlist_voiceover, rename=result.rename_unicode, trackgain=result.track_gain, auto_playlists=result.auto_playlists) shuffle = Shuffler(result.path, voiceover=result.voiceover, playlist_voiceover=result.playlist_voiceover, rename=result.rename_unicode, trackgain=result.track_gain, auto_dir_playlists=result.auto_dir_playlists, auto_id3_playlists=result.auto_id3_playlists)
shuffle.initialize() shuffle.initialize()
shuffle.populate() shuffle.populate()
shuffle.write_database() shuffle.write_database()