Picard plugin

Add two context menu options, ingest tracks, and Copy MusicBrainz ids to
the clipboard. The latest root hash of the ingest set is accessed in the
options menu or in the log output.
This commit is contained in:
Ehmry - 2019-02-16 22:48:23 +01:00
parent d0d264c42e
commit cac95e2db4
3 changed files with 148 additions and 0 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
/tests/test_set
/tests/test_http
/genode/bin/
*.pyc

View File

@ -0,0 +1,115 @@
PLUGIN_NAME = 'Blobsets'
PLUGIN_AUTHOR = 'ehmry'
PLUGIN_DESCRIPTION = '''Ingest files into a blobset.'''
PLUGIN_VERSION = '0.1'
PLUGIN_API_VERSIONS = ['2.0']
PLUGIN_LICENSE = "GPL-2.0"
PLUGIN_LICENSE_URL = "https://www.gnu.org/licenses/gpl-2.0.html"
from functools import partial
from picard.config import TextOption
from picard.file import File
from picard.album import Album
from picard.track import Track
from picard.ui.itemviews import BaseAction, register_track_action, register_album_action
from picard.ui.options import register_options_page, OptionsPage
from picard.util import thread
from .ui_options_blobsets import Ui_BlobsetsOptionsPage
import picard.tagger as tagger
from PyQt5 import QtWidgets
from subprocess import Popen, PIPE
import os.path
ROOT_HASH_KEY = "blobsets_root_hash"
# TODO the setttings thing cannot be trusted
class BlobsetsOptionsPage(OptionsPage):
NAME = 'blobsets'
TITLE = 'Blobsets'
PARENT = "plugins"
options = [
TextOption("setting", ROOT_HASH_KEY, "d59ea2fbcbf2f3d21184f21938ed7966aa480c1dea7463b3de6cae1a18bb1308")
]
def __init__(self, parent=None):
super(BlobsetsOptionsPage, self).__init__(parent)
self.ui = Ui_BlobsetsOptionsPage()
self.ui.setupUi(self)
def load(self):
self.ui.root_hash.setText(self.config.setting[ROOT_HASH_KEY])
def save(self):
self.config.setting[ROOT_HASH_KEY] = self.ui.root_hash.text()
register_options_page(BlobsetsOptionsPage)
class IngestFile(BaseAction):
NAME = 'Ingest file to blob set'
def _ingest(self, track, file):
rootHex = tagger.config.setting[ROOT_HASH_KEY]
print("rootHex is ", rootHex)
mbid = track.metadata["musicbrainz_recordingid"]
path = file.filename
self.tagger.window.set_statusbar_message(
N_('Ingesting %(path)s...'), {'path': path})
_, ext = os.path.splitext(path)
p = Popen(["blobset", "repl"], stdin=PIPE, stdout=PIPE)
cmd = '(hex (commit (insert (load {}) (blob (path "{}")) "{}{}")))'.format(rootHex, path, mbid, ext)
print(cmd)
(stdout_data, stderr_data) = p.communicate(cmd.encode(encoding='UTF-8'))
new_root = stdout_data.decode().strip().strip('"')
print(new_root)
tagger.config.setting[ROOT_HASH_KEY] = new_root
self.tagger.window.set_statusbar_message(
N_('Root hash updated to %(hash)s.'),
{'hash': tagger.config.setting[ROOT_HASH_KEY]}
)
def _ingest_callback(self, track, result=None, error=None):
if error:
self.tagger.window.set_statusbar_message(
N_('%(mbid)s ingestion failed.'),
{'mbid': track.metadata["musicbrainz_recordingid"]}
)
def callback(self, objs):
rootHex = tagger.config.setting[ROOT_HASH_KEY]
print("current hash ", rootHex)
for obj in objs:
if isinstance(obj, Track):
for file in obj.linked_files:
thread.run_task(
partial(self._ingest, obj, file),
partial(self._ingest_callback, obj),
priority=2, thread_pool=file.tagger.save_thread_pool)
break
class CopyIdToClipboard(BaseAction):
NAME = "Copy id Clipboard..."
# Copy an id to the clipboard so the playlist generator picks it up
def callback(self, objs):
if len(objs) != 1:
return
if isinstance(objs[0], Track):
track = objs[0]
mbid = track.metadata["musicbrainz_recordingid"]
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText('musicbrainz/recording/{}'.format(mbid))
elif isinstance(objs[0], Album):
album = objs[0]
mbid = album.metadata["musicbrainz_albumid"]
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText('musicbrainz/release/{}'.format(mbid))
register_track_action(IngestFile())
register_track_action(CopyIdToClipboard())
register_album_action(CopyIdToClipboard())

View File

@ -0,0 +1,32 @@
from PyQt5 import QtCore, QtWidgets
class Ui_BlobsetsOptionsPage(object):
def setupUi(self, BlobsetsOptionsPage):
BlobsetsOptionsPage.setObjectName("BlobsetsOptionsPage")
self.vboxlayout = QtWidgets.QVBoxLayout(BlobsetsOptionsPage)
self.vboxlayout.setContentsMargins(9, 9, 9, 9)
self.vboxlayout.setSpacing(6)
self.vboxlayout.setObjectName("vboxlayout")
self.root_hash_group = QtWidgets.QGroupBox(BlobsetsOptionsPage)
self.root_hash_group.setObjectName("root_hash_group")
self.vboxlayout2 = QtWidgets.QVBoxLayout(self.root_hash_group)
self.vboxlayout2.setContentsMargins(9, 9, 9, 9)
self.vboxlayout2.setSpacing(2)
self.vboxlayout2.setObjectName("vboxlayout2")
self.root_hash_label = QtWidgets.QLabel(self.root_hash_group)
self.root_hash_label.setObjectName("root_hash_label")
self.vboxlayout2.addWidget(self.root_hash_label)
self.root_hash = QtWidgets.QLineEdit(self.root_hash_group)
self.root_hash.setObjectName("root_hash")
self.vboxlayout2.addWidget(self.root_hash)
self.vboxlayout.addWidget(self.root_hash_group)
spacerItem = QtWidgets.QSpacerItem(1, 1, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.vboxlayout.addItem(spacerItem)
self.retranslateUi(BlobsetsOptionsPage)
QtCore.QMetaObject.connectSlotsByName(BlobsetsOptionsPage)
def retranslateUi(self, BlobsetsOptionsPage):
self.root_hash_label.setText(_("Current Blobset root hash"))