diff --git a/.gitignore b/.gitignore index 8cd3b1c..f8e1d1b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /tests/test_set /tests/test_http /genode/bin/ +*.pyc diff --git a/extras/picard_plugin/__init__.py b/extras/picard_plugin/__init__.py new file mode 100644 index 0000000..c93410e --- /dev/null +++ b/extras/picard_plugin/__init__.py @@ -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()) diff --git a/extras/picard_plugin/ui_options_blobsets.py b/extras/picard_plugin/ui_options_blobsets.py new file mode 100644 index 0000000..8080da2 --- /dev/null +++ b/extras/picard_plugin/ui_options_blobsets.py @@ -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"))