aboutsummaryrefslogtreecommitdiffstats
path: root/beetsplug/browse.py
blob: 72999b961e96523117658f153e60b2ce71fef52d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import subprocess
import uuid
import webbrowser

from beets.plugins import BeetsPlugin
from beets.ui import Subcommand, UserError

MUSICBRAINZ_LOOKUP='https://musicbrainz.org/otherlookup/mbid?other-lookup.mbid='
FIELD_NAMES = ['albumartist', 'album', 'artist', 'releasegroup', 'releasetrack', 'track', 'work']

class BrowsePlugin(BeetsPlugin):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def commands(self):
        return [BrowseCommand(self.config)]

class BrowseCommand(Subcommand):

    explorer = 'thunar'

    def __init__(self, config):
        super().__init__('browse', parser=None,
                help='browse items on MusicBrainz or the file system')

        self.parser.add_option('-f', '--field', type='string',
                help='which field to look up on MusicBrainz, '
                'e.g. album, artist, track, work, ...')
        self.parser.add_option('-o', '--open', action='store_true',
                help='open in the file browser instead of MusicBrainz')
        self.parser.add_album_option()

        if 'explorer' in config:
            self.explorer = config['explorer'].get()

    # pylint: disable=no-self-use
    def browse_musicbrainz(self, item, field):
        mbid = item.get(f'mb_{field}id')
        if not mbid:
            raise UserError(f'\'mb_{field}id\' not available for: {item}')

        try:
            uuid.UUID(mbid)
        except ValueError:
            raise UserError(f'invalid UUID "{mbid}" for: {item}') from None

        webbrowser.open(MUSICBRAINZ_LOOKUP + mbid)

    def browse_filesystem(self, item, _):
        try:
            subprocess.Popen(self.explorer.split(' ') + [item.get('path')]) # pylint: disable=consider-using-with
        except OSError as err:
            raise UserError(err) from err

    def func(self, lib, opts, args):
        queryfun = lib.albums if opts.album else lib.items
        browsefun = self.browse_filesystem if opts.open else self.browse_musicbrainz
        field = opts.field or ('album' if opts.album else 'track')

        if field not in FIELD_NAMES:
            raise UserError(f'invalid field "{field}", try one of: {", ".join(FIELD_NAMES)}')

        if not args:
            raise UserError('empty query, refusing')

        items = queryfun(args)

        if not items:
            return

        if len(items) == 1:
            browsefun(items[0], field)
            return

        print('Query returned multiple matches, please disambiguate:')
        for index, item in enumerate(items):
            if index < 5:
                print(item)
            else:
                print(f'[{len(items) - index} more]')
                return