aboutsummaryrefslogtreecommitdiffstats
path: root/beetsplug/browse.py
blob: c1d37c72bd00d2b07417d2578751c468d631d03e (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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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()

    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, _):
        path = item.get("path")

        if not path:
            return

        try:
            subprocess.Popen(
                self.explorer.split() + [path.decode()],
                stderr=subprocess.DEVNULL,
                stdout=subprocess.DEVNULL,
            )
        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