def Scan(path, files, mediaList, subdirs, language=None, root=None): """Handle .cue files. Find any Cue files in the input list. Remove any files handled by the Cue file. Handle any remaining files normally. """ FLACCueParse(path, files, mediaList, subdirs, language=language, root=root) # Scan for other audio files, including those that failed to process correctly. AudioFiles.Scan(path, files, mediaList, subdirs, root=root) # Read tags, etc. and build up the mediaList AudioFiles.Process(path, files, mediaList, subdirs, language=language, root=root)
def Scan(path, files, mediaList, subdirs, language=None, root=None): if len(path) == 0: # Top level, albums. dom = minidom.parse(urllib.urlopen('http://127.0.0.1:32400/music/iTunes/Albums')) for album in sorted(dom.getElementsByTagName('Album'), key=lambda album: album.getAttribute('artist').lower() + album.getAttribute('album').lower()): # Tracks. path = album.getAttribute('key') dom = minidom.parse(urllib.urlopen('http://127.0.0.1:32400/music/iTunes/Albums/%s' % path)) artist_album_map = {} compilation_count = 0 for track in dom.getElementsByTagName('Track'): # Figure out album artist. album_artist = track.getAttribute('albumArtist').strip() artist_album_map[album_artist] = True if len(album_artist) == 0: album_artist = None else: album_artist = album_artist.encode('utf-8') # Compilation handling. if track.getAttribute('compilation') == '1': album_artist = 'Various Artists' title = track.getAttribute('artist').encode('utf-8') + ' - ' + track.getAttribute('track').encode('utf-8') else: title = track.getAttribute('track').encode('utf-8') # Track index. index = int(track.getAttribute('index')) path = AudioFiles.cleanPass(track.getAttribute('file')) if index == 0: try: index = int(re.findall('[.\-]+[ ]*([0-9]{2})[ ]*[.\-]', os.path.basename(path))[0]) except: try: index = int(re.findall('^([0-9]{2})[ .\-]', os.path.basename(path))[0]) except: pass # Add the track. t = Media.Track(artist = track.getAttribute('artist').encode('utf-8'), album = track.getAttribute('album').encode('utf-8'), title = track.getAttribute('track').encode('utf-8'), index = index, album_artist = album_artist, disc = int(track.getAttribute('disc'))) t.parts.append(urllib.unquote(path)) mediaList.append(t)
def Scan(path, files, media_list, subdirs, language=None, root=None, respect_tags=False): # Scan for audio files. AudioFiles.Scan(path, files, media_list, subdirs, root) root_str = root or '' loc_str = os.path.join(root_str, path) Log('Scanning: ' + loc_str) Log('Files: ' + str(files)) Log('Subdirs: ' + str(subdirs)) # Look at the files and determine whether we can do a quick match (minimal tag parsing). do_quick_match = True mixed = False # Make sure we're looking at a leaf directory (no audio files below here). if len(subdirs) > 0: Log('Found directories below this one; won\'t attempt quick matching.') do_quick_match = False if files: # Make sure we're not sitting in the section root. parent_path = os.path.split(files[0])[0] if parent_path == root: Log('File(s) are in section root; doing expensive matching with mixed content.') do_quick_match = False mixed = True # Make sure we have reliable track indices for all files and there are no dupes. tracks = {} for f in files: try: index = re.search(r'^([0-9]{1,2})[^0-9].*', os.path.split(f)[-1]).groups(0)[0] except: do_quick_match = False Log('Couldn\'t find track indices in all filenames; doing expensive matching.') break if tracks.get(index): do_quick_match = False mixed = True Log('Found duplicate track index: %s; doing expensive matching with mixed content.' % index) break else: tracks[index] = True # Read the first track's tags to check for milti-disc and VA. if do_quick_match: disc = album_artist = None try: (artist, album, title, track, disc, album_artist, compil) = AudioFiles.getInfoFromTag(files[0], language) except: Log('Exception reading tags from first file; doing expensive matching.') do_quick_match = False # Make sure we are on the first disc. if disc is not None and disc > 1: Log('Skipping quick match because of non-first disc.') do_quick_match = False # We want to read all the tags for VA albums to pick up track artists. if album_artist is not None and album_artist == 'Various Artists': Log('Skipping quick match for Various Artists album.') do_quick_match = False artist = None album = None if do_quick_match: Log('Doing quick match') # See if we have some consensus on artist/album by reading a few tags. for i in range(3): if i < len(files): this_artist = this_album = tags = None try: tags = mutagen.File(files[i], easy=True) except: Log('There was an exception thrown reading tags.') if tags: # See if there's an album artist tag. album_artist_tags = [t for t in ['albumartist', 'TPE2', 'performer'] if t in tags] album_artist_tag = album_artist_tags[0] if len(album_artist_tags) else None this_artist = tags[album_artist_tag][0] if album_artist_tag else tags['artist'][0] if 'artist' in tags else None this_album = tags['album'][0] if 'album' in tags else None if artist and artist != this_artist: Log('Found different artists in tags (%s vs. %s); doing expensive matching.' % (artist, this_artist)) do_quick_match = False break if album and album != this_album: Log('Found different albums in tags (%s vs. %s); doing expensive matching.' % (artist, this_artist)) do_quick_match = False break artist = this_artist album = this_album if not artist or not album: Log('Couldn\'t determine unique artist or album from tags; doing expensive matching.') do_quick_match = False query_list = [] result_list = [] fingerprint = False # Directory looks clean, let's build a query list directly from info gleaned from file names. if do_quick_match: Log('Building query list for quickmatch with artist: %s, album: %s' % (artist, album)) # Determine if the artist and/or album appears in all filenames, since we'll want to strip these out for clean titles. strip_artist = True if len([f for f in files if artist.lower() in Unicodize(os.path.basename(f), language).lower()]) == len(files) else False strip_album = True if len([f for f in files if album.lower() in Unicodize(os.path.basename(f), language).lower()]) == len(files) else False for f in files: try: filename = os.path.splitext(os.path.split(f)[1])[0] (head, index, title) = re.split(r'^([0-9]{1,2})', filename) # Replace underscores and dots with spaces. title = re.sub(r'[_\. ]+', ' ', title) # Things in parens seem to confuse Gracenote, so let's strip them out. title = re.sub(r' ?\(.*\)', '', title) # Remove artist name from title if it appears in all of them. if strip_artist and len(files) > 2: title = re.sub(r'(?i)' + artist, '', title) # Remove album title from title if it appears in all of them. if strip_album and len(files) > 2: title = re.sub(r'(?i)' + album, '', title) # Remove any remaining index-, artist-, and album-related cruft from the head of the track title. title = re.sub(r'^[\W\-]+', '', title).strip() # Last chance for artist or album prefix. if not strip_artist and Unicodize(title, language).lower().find(artist.lower()) == 0: title = title[len(artist):] if not strip_album and Unicodize(title, language).lower().find(album.lower()) == 0: title = title[len(album):] t = Media.Track(artist=toBytes(artist), album=toBytes(album), title=toBytes(title), index=int(index)) t.parts.append(f) Log(' - Adding: %s - %s' % (index, title)) query_list.append(t) except Exception as e: Log('Error preparing tracks for quick matching: ' + str(e)) # Otherwise, let's do old school directory crawling and tag reading. else: AudioFiles.Process(path, files, media_list, subdirs, root) query_list = list(media_list) # Try as-is first (ask for everything at once). discs = [query_list] final_match = run_queries(discs, result_list, language, fingerprint, mixed, do_quick_match) # If the match was still shitty, and it looks like we have multiple discs, try splitting. if final_match < 75: discs = group_tracks_by_disc(query_list) if len(discs) > 1: Log('Result still looked bad, we will try splitting into separate per-disc queries.') other_result_list = [] other_match = run_queries(discs, other_result_list, language, fingerprint, mixed, do_quick_match) if other_match > final_match: Log('The split result was best, we will use it.') result_list = other_result_list final_match = other_match # If we have a crappy match, don't use it. if final_match < 50.0: Log('That was terrible, let us not use it.') result_list = [] # Finalize the results. used_tags = False del media_list[:] if len(result_list) > 0: # Gracenote results. for result in result_list: media_list.append(result) else: # We bailed during the GN lookup, fall back to tags. used_tags = True AudioFiles.Process(path, files, media_list, subdirs, root) # If we wanted to respect tags, then make sure we used tags. if not used_tags and respect_tags: # Let's grab tag results, and then set GUIDs we found. tag_media_list = [] AudioFiles.Process(path, files, tag_media_list, subdirs, root) # Now suck GN data out. path_map = {} for track in media_list: path_map[track.parts[0]] = track for track in tag_media_list: if track.parts[0] in path_map: gn_track = path_map[track.parts[0]] track.guid = gn_track.guid track.album_guid = gn_track.album_guid track.artist_guid = gn_track.artist_guid track.album_thumb_url = gn_track.album_thumb_url track.artist_thumb_url = gn_track.artist_thumb_url # If the tags failed, fill in key data from Gracenote. if track.album == '[Unknown Album]': track.album = gn_track.album if track.artist == '[Unknown Artist]': track.artist = gn_track.artist media_list[:] = tag_media_list
def Scan(path, files, mediaList, subdirs, language=None, root=None): """Handle FLAC with Cue. Find any Cue files in the input list. """ cues = [] for f in files: if(os.path.splitext(f)[1] == '.cue'): cues.append(f) for cue in cues: log(cue) try: # Get the Cue sheet info. info = read_cue(cue) log(info) # Get all files mentioned in the Cue sheet. cuefiles = info['Files'] # Get the directory the files should be in. This should be # the same as the Cue sheet directory. folder = os.path.dirname(cue) for file in cuefiles: # Get the full FLAC file path. full_file = os.path.join(folder, file) # My FLAC files include "Disc 1", "Disc 2", and similar as the # final part of the title for multi-disk sets. Something like: # "Artist - Album Title Disc 3.flac" # The scanner is designed to pull disc information from this # and to group albums together. # Get the name of the FLAC file without the extension. # Split it for white space. try: flac_details = os.path.splitext(file)[-2].split() # Check for disc numbering. if(flac_details[-2] == 'Disc'): disc = int(flac_details[-1]) else: disc = 1 except IndexError: disc = 1 try: # Add the full FLAC file. # Number it as track -1. track = -1 # Get the file info for the full FLAC file. flac_info = mutagen.flac.FLAC(full_file) # Add the additional disc number information. flac_info['discnumber'] = ['{0:0d}'.format(disc)] # Make this track -1. flac_info['tracknumber'] = ['{0:02d}'.format(track)] # Call this "Album Name - Full Album" in the track listing # Ensure the info is added cleanly. if('artist' not in flac_info or flac_info['artist'][0] == ''): flac_info['artist'] = [info['PERFORMER']] if('album' not in flac_info or flac_info['album'][0] == ''): flac_info['album'] = [info['TITLE']] if('albumartist' not in flac_info or flac_info['albumartist'][0] == ''): flac_info['albumartist'] = [info['PERFORMER']] flac_info['title'] = ['{0} - Full album'.format(flac_info['album'][0])] artist = AudioFiles.cleanPass(flac_info['artist'][0]) album = AudioFiles.cleanPass(flac_info['album'][0]) title = AudioFiles.cleanPass(flac_info['title'][0]) album_artist = AudioFiles.cleanPass(flac_info['albumartist'][0]) # Create the track object. track_object = Media.Track(artist, album, title, track, disc=disc, album_artist=album_artist, guid=None, album_guid=None) # Use the FLAC file for playback. track_object.parts.append(full_file) log(track_object) # Add the track object to the output list. mediaList.append(track_object) # Split into tracks. track_info = cuefiles[file]['Tracks'] start_time = '00:00:00' end_time = '00:00:00' # Handle each track. for track in track_info: title = AudioFiles.cleanPass(track_info[track]['TITLE']) try: # Get the start time of the track. start_time = track_info[track]['INDEX'][1] except KeyError: # If none is listed, use the previous end time. start_time = end_time try: # Get the start time of the following track. # Use this as the end time for the current track. end_time = track_info[track+1]['INDEX'][1] except (IndexError, KeyError): # For the last track, we just take the end time of the # FLAC file as the end of the song. file_end = flac_info.info.length minutes = int(file_end / 60) seconds = int(file_end % 60) # 75 frames per second. frames = int(round(75 * (file_end % 1))) end_time = '{0:02d}:{1:02d}:{2:02d}'.format(minutes, seconds, frames) # Create the track object. track_object = Media.Track(artist, album, title, track, disc=disc, album_artist=album_artist, guid=None, album_guid=None) # Use a file format for compatibility with the FLACCue # FUSE (Filesystem in Userspace) code. # This will be something like: # /flaccue/Music/Artist/Album/Artist - Album Disc 1.flac.10:25:17.12:55:20 track_object.parts.append('/flaccue'+full_file+'.{0}.{1}'.format(start_time, end_time)) log(track_object) # Add the track to the returned object list. mediaList.append(track_object) log('') # Remove the FLAC file from the list to parse. files.remove(full_file) except: log(traceback.format_exc()) except: log(traceback.format_exc()) finally: files.remove(cue) # Scan for other audio files, including those that failed to process correctly. AudioFiles.Scan(path, files, mediaList, subdirs, root=root) # Read tags, etc. and build up the mediaList AudioFiles.Process(path, files, mediaList, subdirs, language=language, root=root)
def read_cue(file): """Parse the Cue sheet to get the desired info. """ # Read the full Cue file. with open(file, 'r') as f: lines = f.readlines() cue = {} cue['Files'] = {} # Line index. We don't use a for loop as we will # read multiple lines for information. i = 0 lenlines = len(lines) try: while(True): # We have a FILE specification in the Cue sheet. if(lines[i].startswith('FILE')): # Get the filename. filename = AudioFiles.cleanPass(lines[i].split('"')[1]) # Now we will parse the tracks from the file. # Use a local variable name for clarity. file_details = {} # But store that variable in the cue sheet parse dictionary. cue['Files'][filename] = file_details # Create the Track entry to store tracks from the file. file_details['Tracks'] = {} # Start at the next line. i += 1 # Use the Cue sheet indentation for sectioning. 2 spaces for # TRACK entries in the FILE entry. while(lines[i].startswith(' '*2)): # Get rid of extra white space. line = lines[i].strip() # Handle TRACK entries. if(line.startswith('TRACK')): # Get the track number. track = int(line.split()[1]) # Use a local variable name for clarity. track_details = {} # But store that variable in the cue sheet parse dictionary. file_details['Tracks'][track] = track_details # Create the INDEX dictionary to store track indices. track_details['INDEX'] = {} # Start at the next line. i += 1 # Use the Cue sheet indentation for sectioning. 4 spaces # for INDEX entries in the TRACK entry. while(lines[i].startswith(' '*4)): # Get rid of extra white space. line = lines[i].strip() # Find the index entries. if(line.startswith('INDEX')): # Remove the INDEX text and extra white space. line = line[5:].strip() # Get the INDEX number and the rest of the line. # The rest of the line should be the time information. key, value = line.split(None, 1) # Store the time information for this index. track_details['INDEX'][int(key)] = value.strip().replace('"', '') i += 1 else: # Store all the other entries as text. Use the first # word as the access key. key, value = line.split(None, 1) # Also remove quotes from track names and similar. track_details[key] = value.strip().replace('"', '') i += 1 else: # Store all the other entries as text. Use the first # word as the access key. key, value = lines[i].split(None, 1) # Also remove quotes from track names and similar. file_details[key] = value.strip().replace('"', '') i += 1 else: # Store all the other entries as text. Use the first # word as the access key. key, value = lines[i].split(None, 1) # Also remove quotes from track names and similar. cue[key] = value.strip().replace('"', '') i += 1 except IndexError: # We're done. pass return cue
def Scan(path, files, media_list, subdirs, language=None, root=None, respect_tags=False): # Scan for audio files. AudioFiles.Scan(path, files, media_list, subdirs, root) root_str = root or '' loc_str = os.path.join(root_str, path) Log('Scanning: ' + loc_str) Log('Files: ' + str(files)) Log('Subdirs: ' + str(subdirs)) # Look at the files and determine whether we can do a quick match (minimal tag parsing). do_quick_match = True mixed = False # Make sure we're looking at a leaf directory (no audio files below here). if len(subdirs) > 0: Log('Found directories below this one; won\'t attempt quick matching.') do_quick_match = False if files: # Make sure we're not sitting in the section root. parent_path = os.path.split(files[0])[0] if parent_path == root: Log('File(s) are in section root; doing expensive matching with mixed content.' ) do_quick_match = False mixed = True # Make sure we have reliable track indices for all files and there are no dupes. tracks = {} for f in files: try: index = re.search(r'^([0-9]{1,2})[^0-9].*', os.path.split(f)[-1]).groups(0)[0] except: do_quick_match = False Log('Couldn\'t find track indices in all filenames; doing expensive matching.' ) break if tracks.get(index): do_quick_match = False mixed = True Log('Found duplicate track index: %s; doing expensive matching with mixed content.' % index) break else: tracks[index] = True # Read the first track's tags to check for milti-disc and VA. if do_quick_match: disc = album_artist = None try: (artist, album, title, track, disc, album_artist, compil) = AudioFiles.getInfoFromTag(files[0], language) except: Log('Exception reading tags from first file; doing expensive matching.' ) do_quick_match = False # Make sure we are on the first disc. if disc is not None and disc > 1: Log('Skipping quick match because of non-first disc.') do_quick_match = False # We want to read all the tags for VA albums to pick up track artists. if album_artist is not None and album_artist == 'Various Artists': Log('Skipping quick match for Various Artists album.') do_quick_match = False artist = None album = None if do_quick_match: Log('Doing quick match') # See if we have some consensus on artist/album by reading a few tags. for i in range(3): if i < len(files): this_artist = this_album = tags = None try: tags = mutagen.File(files[i], easy=True) except: Log('There was an exception thrown reading tags.') if tags: # See if there's an album artist tag. album_artist_tags = [ t for t in ['albumartist', 'TPE2', 'performer'] if t in tags ] album_artist_tag = album_artist_tags[0] if len( album_artist_tags) else None this_artist = tags[album_artist_tag][ 0] if album_artist_tag else tags['artist'][ 0] if 'artist' in tags else None this_album = tags['album'][ 0] if 'album' in tags else None if artist and artist != this_artist: Log('Found different artists in tags (%s vs. %s); doing expensive matching.' % (artist, this_artist)) do_quick_match = False break if album and album != this_album: Log('Found different albums in tags (%s vs. %s); doing expensive matching.' % (album, this_album)) do_quick_match = False break artist = this_artist album = this_album if not artist or not album: Log('Couldn\'t determine unique artist or album from tags; doing expensive matching.' ) do_quick_match = False query_list = [] result_list = [] fingerprint = False # Directory looks clean, let's build a query list directly from info gleaned from file names. if do_quick_match: Log('Building query list for quickmatch with artist: %s, album: %s' % (artist, album)) # Determine if the artist and/or album appears in all filenames, since we'll want to strip these out for clean titles. strip_artist = True if len([ f for f in files if artist.lower() in Unicodize( os.path.basename(f), language).lower() ]) == len(files) else False strip_album = True if len([ f for f in files if album.lower() in Unicodize( os.path.basename(f), language).lower() ]) == len(files) else False for f in files: try: filename = os.path.splitext(os.path.split(f)[1])[0] (head, index, title) = re.split(r'^([0-9]{1,2})', filename) # Replace underscores and dots with spaces. title = re.sub(r'[_\. ]+', ' ', title) # Things in parens seem to confuse Gracenote, so let's strip them out. title = re.sub(r' ?\(.*\)', '', title) # Remove artist name from title if it appears in all of them. if strip_artist and len(files) > 2: title = re.sub(r'(?i)' + artist, '', title) # Remove album title from title if it appears in all of them. if strip_album and len(files) > 2: title = re.sub(r'(?i)' + album, '', title) # Remove any remaining index-, artist-, and album-related cruft from the head of the track title. title = re.sub(r'^[\W\-]+', '', title).strip() # Last chance for artist or album prefix. if not strip_artist and Unicodize( title, language).lower().find(artist.lower()) == 0: title = title[len(artist):] if not strip_album and Unicodize( title, language).lower().find(album.lower()) == 0: title = title[len(album):] t = Media.Track(artist=toBytes(artist), album=toBytes(album), title=toBytes(title), index=int(index)) t.parts.append(f) Log(' - Adding: %s - %s' % (index, title)) query_list.append(t) except Exception as e: Log('Error preparing tracks for quick matching: ' + str(e)) # Otherwise, let's do old school directory crawling and tag reading. else: AudioFiles.Process(path, files, media_list, subdirs, root) query_list = list(media_list) # Try as-is first (ask for everything at once). discs = [query_list] final_match = run_queries(discs, result_list, language, fingerprint, mixed, do_quick_match) # If the match was still shitty, and it looks like we have multiple discs, try splitting. if final_match < 75: discs = group_tracks_by_disc(query_list) if len(discs) > 1: Log('Result still looked bad, we will try splitting into separate per-disc queries.' ) other_result_list = [] other_match = run_queries(discs, other_result_list, language, fingerprint, mixed, do_quick_match) if other_match > final_match: Log('The split result was best, we will use it.') result_list = other_result_list final_match = other_match # If we have a crappy match, don't use it. if final_match < 50.0: Log('That was terrible, let us not use it.') result_list = [] # Finalize the results. used_tags = False del media_list[:] if len(result_list) > 0: # Gracenote results. for result in result_list: media_list.append(result) else: # We bailed during the GN lookup, fall back to tags. used_tags = True AudioFiles.Process(path, files, media_list, subdirs, root) # If we wanted to respect tags, then make sure we used tags. if not used_tags and respect_tags: # Let's grab tag results, and then set GUIDs we found. tag_media_list = [] AudioFiles.Process(path, files, tag_media_list, subdirs, root) # Now suck GN data out. path_map = {} for track in media_list: path_map[track.parts[0]] = track for track in tag_media_list: if track.parts[0] in path_map: gn_track = path_map[track.parts[0]] track.guid = gn_track.guid track.album_guid = gn_track.album_guid track.artist_guid = gn_track.artist_guid track.album_thumb_url = gn_track.album_thumb_url track.artist_thumb_url = gn_track.artist_thumb_url # If the tags failed, fill in key data from Gracenote. if track.album == '[Unknown Album]': track.album = gn_track.album if track.artist == '[Unknown Artist]': track.artist = gn_track.artist media_list[:] = tag_media_list
def FLACCueParse(path, files, mediaList, subdirs, language=None, root=None): cues = [] for f in files: if (os.path.splitext(f)[1] == '.cue'): cues.append(f) for cue in cues: log(cue) try: # Get the Cue sheet info. info = read_cue(cue) log(info) # Get all files mentioned in the Cue sheet. cuefiles = info['Files'] # Get the album information. album = info['TITLE'] album_artist = AudioFiles.cleanPass(info['PERFORMER']) if (album_artist == ''): # No listed album artist. Use the artist from the first # track of the first file. try: first_file = list(cuefiles.keys())[0] album_artist = cuefiles[first_file]['Tracks'][1][ 'PERFORMER'] except KeyError: album_artist = 'Unknown' # Get the directory the files should be in. This should be # the same as the Cue sheet directory. folder = os.path.dirname(cue) for file in cuefiles: # Get the full file path. full_file = os.path.join(folder, file) if (not os.path.exists(full_file)): continue # My cue files include "Disc 1", "Disc 2", and similar as the # final part of the title for multi-disk sets. Something like: # "Artist - Album Title Disc 3.cue" # The scanner is designed to pull disc information from this # and to group albums together. # Get the name of the cue file without the extension. # Split it for white space. try: file_details = os.path.splitext(file)[-2].split() # Check for disc numbering. if (file_details[-2] == 'Disc'): disc = int(file_details[-1]) else: disc = 1 except IndexError: disc = 1 try: # Add the full file. # Number it as track -1. track = -1 # Call this "Album Name - Disc #" in the track listing # Ensure the info is added cleanly. title = '{0} - Disc {1}'.format(album, disc) title = AudioFiles.cleanPass(title) artist = AudioFiles.cleanPass(album_artist) # Create the track object. # Use disc 9999 to group all the full discs together. Use # the disc number as the track number. track_object = Media.Track(artist, album, title, disc, disc=9999, album_artist=album_artist, guid=None, album_guid=None) # Use the file for playback. track_object.parts.append(full_file) log(track_object) # Add the track object to the output list. mediaList.append(track_object) # Split into tracks. track_info = cuefiles[file]['Tracks'] start_time = '00:00:00' end_time = '00:00:00' # Handle each track. for track in track_info: title = AudioFiles.cleanPass( track_info[track]['TITLE']) try: artist = AudioFiles.cleanPass( track_info[track]['PERFORMER']) except KeyError: # No track artist specified. Use the album artist. artist = album_artist try: # Get the start time of the track. start_time = track_info[track]['INDEX'][1] except KeyError: # If none is listed, use the previous end time. start_time = end_time try: # Get the start time of the following track. # Use this as the end time for the current track. end_time = track_info[track + 1]['INDEX'][1] except (IndexError, KeyError): # For the last track, we specify -1 to indicate the end of file. end_time = '-1' # Create the track object. track_object = Media.Track(artist, album, title, track, disc=disc, album_artist=album_artist, guid=None, album_guid=None) # Use a file format for compatibility with the FLACCue # FUSE (Filesystem in Userspace) code. # This will be something like: # /flaccue/Music/Artist/Album/Artist - Album Disc 1.flaccuesplit.10:25:17.12:55:20.flac parsed_filename = '/flaccue' + full_file extension = os.path.splitext(parsed_filename)[1] parsed_filename = parsed_filename.replace( extension, '.flaccuesplit.{0}.{1}{2}'.format( start_time, end_time, extension)) track_object.parts.append(parsed_filename) log(track_object) # Add the track to the returned object list. mediaList.append(track_object) log('') # Remove the FLAC file from the list to parse. files.remove(full_file) except: log(traceback.format_exc()) except: log(traceback.format_exc()) finally: files.remove(cue)
def Scan(path, files, mediaList, subdirs, language=None, root=None): # Scan for audio files. AudioFiles.Scan(path, files, mediaList, subdirs, root) if len(files) < 1: return albumTracks = [] for f in files: try: artist = None (artist, album, title, track, disc, album_artist, compil) = getInfoFromTag(f, language) #print 'artist: ', artist, ' | album_artist: ', album_artist, ' | album: ', album, ' | disc: ', str(disc), ' | title: ', title, ' | compilation: ' + str(compil) if album_artist and album_artist.lower( ) in various_artists: #(compil == '1' and (album_artist is None or len(album_artist.strip()) == 0)) or ( album_artist = 'Various Artists' if artist == None or len(artist.strip()) == 0: artist = '[Unknown Artist]' if album == None or len(album.strip()) == 0: album = '[Unknown Album]' if title == None or len( title) == 0: #use the filename for the title title = os.path.splitext(os.path.split(f)[1])[0] if track == None: # See if we have a tracknumber in the title; if so, extract and strip it. m = re.match("^([0-9]{1,3})[ .-]+(.*)$", title) if m: track, title = int(m.group(1)), m.group(2) else: # Check to see if the title starts with the track number and whack it. title = re.sub("^[ 0]*%s[ ]+" % track, '', title) title = title.strip(' -.') (allbutParentDir, parentDir) = os.path.split(os.path.dirname(f)) if title.count( ' - ' ) == 1 and artist == '[Unknown Artist]': # see if we can parse the title for artist - title (artist, title) = title.split(' - ') if len(artist) == 0: artist = '[Unknown Artist]' elif parentDir and parentDir.count(' - ') == 1 and ( artist == '[Unknown Artist]' or album == '[Unknown Album]' ): #see if we can parse the folder dir for artist - album (pathArtist, pathAlbum) = parentDir.split(' - ') if artist == '[Unknown Artist]': artist = pathArtist if album == '[Unknown Album]': album = pathAlbum #make sure our last move is to encode to utf-8 before handing text back. t = Media.Track(cleanPass(artist), cleanPass(album), cleanPass(title), track, disc=disc, album_artist=cleanPass(album_artist)) t.parts.append(f) albumTracks.append(t) #print 'Adding: [Artist: ' + artist + '] [Album: ' + album + '] [Title: ' + title + '] [Tracknumber: ' + str(track) + '] [Disk: ' + str(disc) + '] [Album Artist: ' + album_artist + '] [File: ' + f + ']' except: pass #print "Skipping (Metadata tag issue): ", f #add all tracks in dir, but first see if this might be a Various Artist album #first, let's group the albums in this folder together albumsDict = {} artistDict = {} for t in albumTracks: #add all the album names to a dictionary if albumsDict.has_key(t.album.lower()): albumsDict[t.album.lower()].append(t) else: albumsDict[t.album.lower()] = [t] #count instances of same artist names if artistDict.has_key(t.artist): artistDict[t.artist] += 1 else: artistDict[t.artist] = 1 try: (maxArtistName, maxArtistCount) = sorted(artistDict.items(), key=lambda (k, v): (v, k))[-1] except: maxArtistCount = 0 percentSameArtist = float(maxArtistCount) / len(albumTracks) #next, iterate through the album keys, and look at the tracks inside each album for a in albumsDict.keys(): sameAlbum = True sameArtist = True prevAlbum = None prevArtist = None blankAlbumArtist = True for t in albumsDict[a]: if prevAlbum == None: prevAlbum = t.album if prevArtist == None: prevArtist = t.artist if prevAlbum.lower() != t.album.lower(): sameAlbum = False if prevArtist.lower() != t.artist.lower(): sameArtist = False prevAlbum = t.album prevArtist = t.artist if t.album_artist and len(t.album_artist.strip()) > 0: blankAlbumArtist = False if sameAlbum == True and sameArtist == False and blankAlbumArtist: if percentSameArtist < .9: #if the number of the same artist is less than X%, let's VA it (else, let's use the most common artist) newArtist = 'Various Artists' else: newArtist = maxArtistName for tt in albumsDict[a]: tt.album_artist = newArtist for t in albumTracks: mediaList.append(t) return
def Scan(path, files, mediaList, subdirs, language=None, root=None): # Scan for audio files. AudioFiles.Scan(path, files, mediaList, subdirs, root) # Read tags, etc. and build up the mediaList AudioFiles.Process(path, files, mediaList, subdirs, root)