Пример #1
0
def convert(flac, output_dir, hidden):
    """Convert FLAC files."""
    for path in flac:
        info = FileInfo(path)
        summary = info.summary
        if not summary.parse_ok or not summary.cuesheet:
            continue
        album_tags = info.tags.album_tags()
        if album_tags.get('HIDE') == 'true' and not hidden:
            continue
        if 'ARTIST' not in album_tags or 'ALBUM' not in album_tags:
            continue
        tracks = fc.prepare_tracks(info, output_dir, 'ogg')
        if not tracks:
            continue
        if any(map(lambda t: os.path.exists(t.path), tracks)):
            continue
        click.echo('{} {}'.format(summary, path))
        try:
            click.echo('- Decoding tracks')
            tempdir = fc.decode_tracks(info)
            click.echo('- Encoding tracks')
            fc.encode_tracks(tracks, tempdir, 'ogg')
        except fc.ConversionError as e:
            click.echo('ERROR {}'.format(e))
            click.get_current_context().exit(1)
        front = info.get_picture(FRONT_COVER_TYPE)
        if front is not None:
            dst_base = os.path.dirname(tracks[0].path)
            fc.export_cover(front, dst_base)
Пример #2
0
 def test_export_cover(self, datadir):
     """Test the export_cover function."""
     info = FileInfo(datadir / 'tagged.flac')
     front = info.get_picture(3)
     fc.export_cover(front, str(datadir))
     path = Path(datadir / 'cover.jpg')
     assert path.is_file()
Пример #3
0
 def test_init_empty(self, datadir):
     """Call the constructor with an empty FLAC file."""
     file = FileInfo(datadir / 'empty.flac')
     assert file.parse_ok is True
     assert file.parse_exception is None
     assert file.streaminfo is not None
     assert file.cuesheet is None
     assert file.tags is not None
     assert file.pictures() is not None
Пример #4
0
 def test_init_invalid(self, datadir):
     """Call the constructor with an invalid FLAC file."""
     file = FileInfo(datadir / 'invalid.flac')
     assert file.parse_ok is False
     assert file.parse_exception is not None
     assert file.streaminfo is None
     assert file.cuesheet is None
     assert file.tags is None
     assert file.pictures() is None
Пример #5
0
 def test_set_picture(self, datadir):
     """Test setting a picture."""
     file = FileInfo(datadir / 'empty.flac')
     with open(str(datadir / 'cover.png'), 'rb') as cover:
         data = cover.read()
     picture = parse_picture(data, 3)
     assert file.set_picture(picture) is True
     assert file.set_picture(picture) is False
     file.update()
     self.assert_picture(file, 3, 'image/png', 128, 128, 24, data)
Пример #6
0
def tag(flac, mbid, hide, unhide, hide_track, unhide_track):
    """Tag FLAC files.

    Files with a cue sheet but no album/track-level tags will be tagged using
    metadata from MusicBrainz.
    """
    mb = MusicBrainz()
    for path in flac:
        info = FileInfo(path)
        summary = info.summary
        if not summary.parse_ok or not summary.cuesheet:
            continue
        tagged = summary.album_tags or summary.track_tags
        album_edit = hide or unhide
        track_edit = hide_track or unhide_track
        if tagged and not (album_edit or track_edit):
            continue
        click.echo('{} {}'.format(summary, path))
        album_changed = False
        track_changed = False
        # Query MusicBrainz
        if not tagged:
            try:
                if mbid is None:
                    release = find_release(mb, info)
                else:
                    release = mb.release_by_id(mbid, info.cuesheet)
                if release is None:
                    continue
                original_date = mb.first_release_date(release['group-id'])
            except MusicBrainzError:
                click.echo('- Error while querying MusicBrainz')
                continue
            album_changed |= update_album_tags(info, release, original_date)
            track_changed |= update_track_tags(info, release)
        # Hide or unhide album
        tags = info.tags.album_tags()
        if hide:
            tags['HIDE'] = 'true'
        if unhide:
            tags.pop('HIDE', None)
        album_changed |= info.tags.update_album(tags)
        # Hide or unhide tracks
        for number in hide_track:
            tags = info.tags.track_tags(number)
            tags['HIDE'] = 'true'
            track_changed |= info.tags.update_track(number, tags)
        for number in unhide_track:
            tags = info.tags.track_tags(number)
            tags.pop('HIDE', None)
            track_changed |= info.tags.update_track(number, tags)
        # Save any changes
        if album_changed or track_changed:
            info.update()
Пример #7
0
 def test_init(self, datadir):
     """Call the constructor with a valid FLAC file."""
     file = FileInfo(datadir / 'test.flac')
     assert file.parse_ok is True
     assert file.parse_exception is None
     assert file.streaminfo is not None
     assert file.cuesheet is not None
     assert file.tags is not None
     assert file.pictures() is not None
     streaminfo = file.streaminfo
     assert streaminfo.channels == 2
     assert streaminfo.sample_bits == 16
     assert streaminfo.sample_rate == 44100
     assert streaminfo.sample_count == 132300
Пример #8
0
 def test_hide(self, datadir):
     """Test the --hide option."""
     path = datadir / 'tagged.flac'
     runner = CliRunner()
     result = runner.invoke(flackup, ['tag', '--hide', str(path)])
     info = FileInfo(path)
     assert result.exit_code == 0
     assert info.tags.album_tags().get('HIDE') == 'true'
Пример #9
0
 def test_unhide_track(self, datadir):
     """Test the --unhide-track option."""
     path = datadir / 'tagged.flac'
     runner = CliRunner()
     result = runner.invoke(flackup, ['tag', '-T', 3, str(path)])
     info = FileInfo(path)
     assert result.exit_code == 0
     assert info.tags.track_tags(3).get('HIDE') is None
Пример #10
0
 def test_prepare_tracks(self, datadir):
     """Test the prepare_tracks function."""
     info = FileInfo(datadir / 'tagged.flac')
     tracks = fc.prepare_tracks(info, str(datadir), 'ogg')
     assert len(tracks) == 2
     for track in tracks:
         assert len(track.tags) == 5
         assert track.path.endswith('.ogg')
Пример #11
0
 def test_untagged(self, datadir):
     """Test reading an untagged FLAC file."""
     file = FileInfo(datadir / 'test.flac')
     tags = file.tags
     assert tags.album_tags() == {}
     track_numbers = [t.number for t in file.cuesheet.audio_tracks]
     for number in track_numbers:
         assert tags.track_tags(number) == {}
Пример #12
0
 def test_empty(self, datadir):
     """Test with an empty FLAC file."""
     file = FileInfo(datadir / 'empty.flac')
     summary = file.summary
     assert summary.parse_ok is True
     assert summary.cuesheet is False
     assert summary.album_tags is False
     assert summary.track_tags is False
     assert summary.pictures is False
Пример #13
0
 def test_tagged(self, datadir):
     """Test with a tagged FLAC file."""
     file = FileInfo(datadir / 'invalid.flac')
     summary = file.summary
     assert summary.parse_ok is False
     assert summary.cuesheet is False
     assert summary.album_tags is False
     assert summary.track_tags is False
     assert summary.pictures is False
Пример #14
0
 def test_decode_tracks(self, datadir):
     """Test the prepare_tracks function."""
     info = FileInfo(datadir / 'tagged.flac')
     tempdir = fc.decode_tracks(info)
     assert tempdir is not None
     path = Path(tempdir.name)
     files = list(path.iterdir())
     assert len(files) == 2
     for file in files:
         assert TRACK_WAV_RE.match(file.name) is not None
Пример #15
0
 def test_encode_tracks(self, datadir):
     """Test the encode_tracks function."""
     info = FileInfo(datadir / 'tagged.flac')
     tracks = fc.prepare_tracks(info, str(datadir), 'ogg')
     tempdir = fc.decode_tracks(info)
     fc.encode_tracks(tracks, tempdir, 'ogg')
     path = Path(tracks[0].path).parent
     files = list(path.iterdir())
     assert len(files) == 2
     for file in files:
         assert file.name.endswith('.ogg')
Пример #16
0
    def test_tagged(self, datadir):
        """Test reading a tagged FLAC file."""
        file = FileInfo(datadir / 'tagged.flac')
        tags = file.tags

        album = tags.album_tags()
        assert 'FOO' not in album
        self.assert_album(album, 'Test Album', 'Test Artist', 'Test', '2018')
        self.assert_track(tags.track_tags(1), 'Track 1', None, None)
        self.assert_track(tags.track_tags(2), 'Track 2', None, None)
        self.assert_track(tags.track_tags(3), 'Track 3', 'true', 'Terrible')
Пример #17
0
 def test_cuesheet(self, datadir):
     """Test with a valid FLAC file."""
     file = FileInfo(datadir / 'test.flac')
     cuesheet = file.cuesheet
     assert cuesheet is not None
     assert cuesheet.is_cd is True
     assert cuesheet.lead_in == 88200
     track_numbers = [t.number for t in cuesheet.tracks]
     assert track_numbers == [1, 2, 3, 170]
     audio_track_numbers = [t.number for t in cuesheet.audio_tracks]
     assert audio_track_numbers == [1, 2, 3]
Пример #18
0
def cover(flac):
    """Add cover images to tagged FLAC files."""
    mb = MusicBrainz()
    for path in flac:
        info = FileInfo(path)
        if not info.parse_ok:
            continue
        album_tags = info.tags.album_tags()
        mbid = album_tags.get('RELEASE_MBID')
        if mbid is None:
            continue
        front = info.get_picture(FRONT_COVER_TYPE)
        if front is not None:
            continue
        click.echo('{} {}'.format(info.summary, path))
        try:
            release = mb.release_by_id(mbid)
            data = mb.front_cover(release)
            if data is not None:
                front = fc.parse_picture(data, FRONT_COVER_TYPE)
                if info.set_picture(front):
                    info.update()
                width = front.width
                height = front.height
                type_ = fc.picture_ext(front).upper()
                click.echo('- {} x {} {}'.format(width, height, type_))
            else:
                click.echo('- No image found')
        except MusicBrainzError:
            click.echo('- Error while querying MusicBrainz')
            continue
        except Exception as e:
            click.echo('- Error while processing image ({})'.format(e))
            continue
Пример #19
0
 def test_remove_picture(self, datadir):
     """Test removing a picture."""
     file = FileInfo(datadir / 'tagged.flac')
     assert file.remove_picture(3) is True
     assert file.remove_picture(3) is False
     file.update()
     assert file.get_picture(3) is None
Пример #20
0
    def test_update_tagged(self, datadir):
        """Test updating a tagged FLAC file."""
        file = FileInfo(datadir / 'tagged.flac')
        tags = file.tags

        album = tags.album_tags()
        album['ALBUM'] = 'Album'
        album['ARTIST'] = 'Artist'
        assert tags.update_album(album) is True

        track = tags.track_tags(1)
        track['TITLE'] = 'Track One'
        assert tags.update_track(1, track) is True

        track = tags.track_tags(2)
        assert tags.update_track(2, track) is False

        track = tags.track_tags(3)
        del track['TITLE']
        assert tags.update_track(3, track) is True

        file.update()
        file = FileInfo(datadir / 'tagged.flac')
        tags = file.tags
        self.assert_album(tags.album_tags(), 'Album', 'Artist', 'Test', '2018')
        self.assert_track(tags.track_tags(1), 'Track One', None, None)
        self.assert_track(tags.track_tags(2), 'Track 2', None, None)
        self.assert_track(tags.track_tags(3), None, 'true', 'Terrible')
Пример #21
0
def analyze(flac, verbose, hidden):
    """Analyze FLAC files.

    For each file, shows a list of flags followed by the filename.

    \b
    Flags:
    - O: The file parsed successfully.
    - C: A cue sheet is present.
    - A: Album-level tags are present (any number).
    - T: Track-level tags are present (any number).
    - P: Pictures are present (any number).
    """
    for path in flac:
        info = FileInfo(path)
        if info.parse_ok:
            album_tags = info.tags.album_tags()
        else:
            album_tags = {}
        if album_tags.get('HIDE') != 'true' and hidden:
            continue
        if not verbose:
            click.echo('{} {}'.format(info.summary, path))
        else:
            img = '|                 |'
            url = ''
            if info.parse_ok:
                front = info.get_picture(FRONT_COVER_TYPE)
                if front is not None:
                    width = front.width
                    height = front.height
                    type_ = fc.picture_ext(front).upper()
                    img = '| {:4d} x {:4d} {} |'.format(width, height, type_)
                mbid = album_tags.get('RELEASE_MBID')
                if mbid is not None:
                    url = ' | ' + RELEASE_URL.format(mbid)
            click.echo('{} {} {}{}'.format(info.summary, img, path, url))
Пример #22
0
 def test_prepare_tracks_date_original(self, datadir):
     """Test the prepare_tracks function with a DATE_ORIGINAL tag."""
     info = FileInfo(datadir / 'date_original.flac')
     tracks = fc.prepare_tracks(info, str(datadir), 'ogg')
     for track in tracks:
         assert track.tags['DATE'] == '1970'
Пример #23
0
 def test_read_picture(self, datadir):
     """Test reading a picture."""
     file = FileInfo(datadir / 'tagged.flac')
     with open(str(datadir / 'cover.png'), 'rb') as cover:
         data = cover.read()
     self.assert_picture(file, 3, 'image/png', 128, 128, 24, data)