Exemplo n.º 1
0
def scan_music_folder(music_folder):
    bytes_to_minutes = 8 / (1024 * 128 * 60)
    os.chdir(music_folder)
    exclude_prefixes = tuple(open('not_cd_folders.txt').read().split('\n')[1:])  # first one is "_Copied" - this is OK

    def is_included(walk_tuple):
        folder_name = walk_tuple[0]
        return not folder_name[len(music_folder) + 1:].startswith(exclude_prefixes)

    albums = {}
    for folder, folder_list, file_list in filter(is_included, os.walk(music_folder)):
        show_progress = len(albums) % 30 == 0
        if show_progress:
            print(folder)
        for file in filter(is_media_file, file_list):
            filename = os.path.join(folder, file)
            try:
                tags = phrydy.MediaFile(filename)
            except Exception:
                print(f'No media info for {file_list[0]}')
                continue
            # use album artist (if available) so we can compare 'Various Artist' albums
            artist = tags.albumartist or tags.artist
            album_name = tags.album
            # some buggy mp3s - assume 128kbps
            duration = tags.length / 60 if tags.length else os.path.getsize(filename) * bytes_to_minutes
            key = (folder, artist, album_name)
            # albums is a dict of dicts: each subdict stores (file, duration) as (key, value) pairs
            albums.setdefault(key, {})[file] = duration
    # remove albums with only one track
    return {key: file_list for key, file_list in albums.items() if len(file_list) > 1}
Exemplo n.º 2
0
def copy_album(album, files, existing_folder=None):
    """Copy a given album to the copy folder."""
    bad_chars = str.maketrans({char: None for char in '*?/\\<>:|"'})  # can't use these in filenames

    def remove_bad_chars(filename: str):
        return filename.translate(bad_chars)

    folder, artist, title = album
    if title:
        no_artist = artist in (None, '', 'Various', 'Various Artists')
        album_filename = remove_bad_chars(title if no_artist else f'{artist} - {title}')
    else:
        album_filename = os.path.basename(folder)
    album_filename = album_filename[:60].strip()  # shorten path names (Windows limit: 260 chars)
    if existing_folder is None:  # making a new folder
        copied_name = datetime.strftime(datetime.now(), '%Y-%m-%d ') + album_filename
        os.mkdir(copied_name)
        n = 0
    else:  # copying into an existing folder
        copied_name = f'{existing_folder}; {album_filename}'
        os.rename(existing_folder, copied_name)
        n = len(os.listdir(copied_name))
    os.chdir(copied_name)
    for j, f in enumerate(files.keys(), start=1):
        media_info = phrydy.MediaFile(os.path.join(folder, f))
        name, ext = os.path.splitext(f)
        try:
            copy_filename = remove_bad_chars(f'{int(media_info.track) + n:02d} {media_info.title}{ext}')
        except (ValueError, TypeError):  # e.g. couldn't get track name or number
            copy_filename = f'{j + 1 + n:02d} {f}'  # fall back to original name
        copy2(os.path.join(folder, f), copy_filename)
    os.chdir('..')
    return copied_name
Exemplo n.º 3
0
def check_radio_files(lastfm_user):
    """Find and remove recently-played tracks from the Radio folder. Fix missing titles in tags."""
    # get recently played tracks (as reported by Last.fm)
    played_tracks = lastfm_user.get_recent_tracks(limit=400)
    scrobbled_titles = [
        f'{track.track.artist.name} - {track.track.title}'.lower()
        for track in played_tracks
    ]
    scrobbled_radio = []
    os.chdir(os.path.join(user_profile, 'Radio'))
    radio_files = os.listdir()
    # loop over radio files - first check if they've been scrobbled, then try to correct tags where titles aren't set
    checking_scrobbles = True
    file_count = 0
    min_date = None
    total_hours = 0
    for file in sorted(radio_files):
        try:
            file_date = datetime.strptime(file[:10], '%Y-%m-%d')
        except ValueError:
            continue  # not a date-based filename

        file_count += 1
        min_date = min_date or file_date  # set to first one
        weeks = (file_date - min_date).days // 7

        tags = phrydy.MediaFile(file)
        tags_changed = False
        total_hours += tags.length / 3600
        if checking_scrobbles:
            track_title = media.artist_title(tags)
            if track_title.lower() in scrobbled_titles:
                print(f'Found: {track_title}')
                scrobbled_radio.append(file)
            else:
                print(f'Not found: {track_title}')
                checking_scrobbles = False  # stop here - don't keep searching
        if tags.title in ('', 'Untitled Episode', None):
            print(f'Set {file} title to {file[11:-4]}')
            tags.title = file[
                11:
                -4]  # the bit between the date and the extension (assumes 3-char ext)
            tags_changed = True
        if not tags.albumartist:
            print(f'Set {file} album artist to {tags.artist}')
            tags.albumartist = tags.artist
            tags_changed = True
        if tags_changed and not test_mode:
            tags.save()
    toast = ''
    print('\nTo delete:')
    for file in scrobbled_radio[:
                                -1]:  # don't delete the last one - we might not have finished it
        toast += f'🗑️ {os.path.splitext(file)[0]}\n'
        print(file)
        if not test_mode and os.path.exists(file):
            send2trash(file)
    toast += f'📻 {file_count} files; {weeks} weeks; {total_hours:.0f} hours\n'
    return toast
Exemplo n.º 4
0
def album_artist(files):
    """Replace a blank album artist field with something populated from the artist field."""
    if all(phrydy.MediaFile(file).albumartist for file in files):
        return  # nothing to do
    artists = [phrydy.MediaFile(file).artist for file in files]
    unique_artists = set(artists)
    if len(unique_artists
           ) == 1:  # only one for all of them - apply this to album
        artist = artists[0]
        print(
            f'Setting album artist to {artist} for {len(files)} files starting with {files[0]}'
        )
        for file in files:
            tags = phrydy.MediaFile(file)
            if not tags.albumartist:
                tags.albumartist = artist
                tags.save()
    else:
        print(unique_artists)
Exemplo n.º 5
0
def get_album_info(media_files):
    for file in media_files:
        try:
            tags = phrydy.MediaFile(file)
            break
        except phrydy.mediafile.FileTypeError:
            print(f'No media info for {file}')
    else:  # no media info for any (or empty list)
        return None, None
    # use album artist (if available) so we can compare 'Various Artist' albums
    artist = tags.albumartist or tags.artist
    title = str('' or tags.album)  # use empty string if album is None
    return artist, title
Exemplo n.º 6
0
def apostrophes(files):
    """Look for Opus files tagged with bad apostrophes (’), and replace them with normal ones (')."""
    for file in files:
        basename, ext = os.path.splitext(file)
        if ext.lower() != '.opus':
            continue
        tags = phrydy.MediaFile(file)
        for tag_name in tags.readable_fields():
            if tag_name == 'art':
                continue
            tag_text = getattr(tags, tag_name)
            if not isinstance(tag_text, str) or '’' not in tag_text:
                continue
            setattr(tags, tag_name, tag_text.replace('’', "'"))
            tags.save()
            print(file, tag_name, tag_text)
Exemplo n.º 7
0
def artist_title(file, separator=' - '):
    """Return {artist} - {title} string for a given file, converted to lowercase for easy comparison.
    Pass file as a filename or a MediaFile object from phrydy."""
    media_info = phrydy.MediaFile(file) if isinstance(file, str) else file
    return f'{media_info.artist}{separator}{media_info.title}'.lower()
Exemplo n.º 8
0
def artist_title(filename):
    """Return {artist} - {title} string for a given file."""
    media_info = phrydy.MediaFile(filename)
    return f'{media_info.artist} - {media_info.title}'.lower()