mirror of
https://github.com/clinton-hall/nzbToMedia.git
synced 2024-11-14 17:40:24 -08:00
56c6773c6b
Updates colorama to 0.4.6 Adds confuse version 1.7.0 Updates jellyfish to 0.9.0 Adds mediafile 0.10.1 Updates munkres to 1.1.4 Updates musicbrainzngs to 0.7.1 Updates mutagen to 1.46.0 Updates pyyaml to 6.0 Updates unidecode to 1.3.6
296 lines
10 KiB
Python
296 lines
10 KiB
Python
# This file is part of beets.
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining
|
|
# a copy of this software and associated documentation files (the
|
|
# "Software"), to deal in the Software without restriction, including
|
|
# without limitation the rights to use, copy, modify, merge, publish,
|
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
# permit persons to whom the Software is furnished to do so, subject to
|
|
# the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be
|
|
# included in all copies or substantial portions of the Software.
|
|
|
|
"""Adds support for ipfs. Requires go-ipfs and a running ipfs daemon
|
|
"""
|
|
|
|
|
|
from beets import ui, util, library, config
|
|
from beets.plugins import BeetsPlugin
|
|
|
|
import subprocess
|
|
import shutil
|
|
import os
|
|
import tempfile
|
|
|
|
|
|
class IPFSPlugin(BeetsPlugin):
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.config.add({
|
|
'auto': True,
|
|
'nocopy': False,
|
|
})
|
|
|
|
if self.config['auto']:
|
|
self.import_stages = [self.auto_add]
|
|
|
|
def commands(self):
|
|
cmd = ui.Subcommand('ipfs',
|
|
help='interact with ipfs')
|
|
cmd.parser.add_option('-a', '--add', dest='add',
|
|
action='store_true',
|
|
help='Add to ipfs')
|
|
cmd.parser.add_option('-g', '--get', dest='get',
|
|
action='store_true',
|
|
help='Get from ipfs')
|
|
cmd.parser.add_option('-p', '--publish', dest='publish',
|
|
action='store_true',
|
|
help='Publish local library to ipfs')
|
|
cmd.parser.add_option('-i', '--import', dest='_import',
|
|
action='store_true',
|
|
help='Import remote library from ipfs')
|
|
cmd.parser.add_option('-l', '--list', dest='_list',
|
|
action='store_true',
|
|
help='Query imported libraries')
|
|
cmd.parser.add_option('-m', '--play', dest='play',
|
|
action='store_true',
|
|
help='Play music from remote libraries')
|
|
|
|
def func(lib, opts, args):
|
|
if opts.add:
|
|
for album in lib.albums(ui.decargs(args)):
|
|
if len(album.items()) == 0:
|
|
self._log.info('{0} does not contain items, aborting',
|
|
album)
|
|
|
|
self.ipfs_add(album)
|
|
album.store()
|
|
|
|
if opts.get:
|
|
self.ipfs_get(lib, ui.decargs(args))
|
|
|
|
if opts.publish:
|
|
self.ipfs_publish(lib)
|
|
|
|
if opts._import:
|
|
self.ipfs_import(lib, ui.decargs(args))
|
|
|
|
if opts._list:
|
|
self.ipfs_list(lib, ui.decargs(args))
|
|
|
|
if opts.play:
|
|
self.ipfs_play(lib, opts, ui.decargs(args))
|
|
|
|
cmd.func = func
|
|
return [cmd]
|
|
|
|
def auto_add(self, session, task):
|
|
if task.is_album:
|
|
if self.ipfs_add(task.album):
|
|
task.album.store()
|
|
|
|
def ipfs_play(self, lib, opts, args):
|
|
from beetsplug.play import PlayPlugin
|
|
|
|
jlib = self.get_remote_lib(lib)
|
|
player = PlayPlugin()
|
|
config['play']['relative_to'] = None
|
|
player.album = True
|
|
player.play_music(jlib, player, args)
|
|
|
|
def ipfs_add(self, album):
|
|
try:
|
|
album_dir = album.item_dir()
|
|
except AttributeError:
|
|
return False
|
|
try:
|
|
if album.ipfs:
|
|
self._log.debug('{0} already added', album_dir)
|
|
# Already added to ipfs
|
|
return False
|
|
except AttributeError:
|
|
pass
|
|
|
|
self._log.info('Adding {0} to ipfs', album_dir)
|
|
|
|
if self.config['nocopy']:
|
|
cmd = "ipfs add --nocopy -q -r".split()
|
|
else:
|
|
cmd = "ipfs add -q -r".split()
|
|
cmd.append(album_dir)
|
|
try:
|
|
output = util.command_output(cmd).stdout.split()
|
|
except (OSError, subprocess.CalledProcessError) as exc:
|
|
self._log.error('Failed to add {0}, error: {1}', album_dir, exc)
|
|
return False
|
|
length = len(output)
|
|
|
|
for linenr, line in enumerate(output):
|
|
line = line.strip()
|
|
if linenr == length - 1:
|
|
# last printed line is the album hash
|
|
self._log.info("album: {0}", line)
|
|
album.ipfs = line
|
|
else:
|
|
try:
|
|
item = album.items()[linenr]
|
|
self._log.info("item: {0}", line)
|
|
item.ipfs = line
|
|
item.store()
|
|
except IndexError:
|
|
# if there's non music files in the to-add folder they'll
|
|
# get ignored here
|
|
pass
|
|
|
|
return True
|
|
|
|
def ipfs_get(self, lib, query):
|
|
query = query[0]
|
|
# Check if query is a hash
|
|
# TODO: generalize to other hashes; probably use a multihash
|
|
# implementation
|
|
if query.startswith("Qm") and len(query) == 46:
|
|
self.ipfs_get_from_hash(lib, query)
|
|
else:
|
|
albums = self.query(lib, query)
|
|
for album in albums:
|
|
self.ipfs_get_from_hash(lib, album.ipfs)
|
|
|
|
def ipfs_get_from_hash(self, lib, _hash):
|
|
try:
|
|
cmd = "ipfs get".split()
|
|
cmd.append(_hash)
|
|
util.command_output(cmd)
|
|
except (OSError, subprocess.CalledProcessError) as err:
|
|
self._log.error('Failed to get {0} from ipfs.\n{1}',
|
|
_hash, err.output)
|
|
return False
|
|
|
|
self._log.info('Getting {0} from ipfs', _hash)
|
|
imp = ui.commands.TerminalImportSession(lib, loghandler=None,
|
|
query=None, paths=[_hash])
|
|
imp.run()
|
|
shutil.rmtree(_hash)
|
|
|
|
def ipfs_publish(self, lib):
|
|
with tempfile.NamedTemporaryFile() as tmp:
|
|
self.ipfs_added_albums(lib, tmp.name)
|
|
try:
|
|
if self.config['nocopy']:
|
|
cmd = "ipfs add --nocopy -q ".split()
|
|
else:
|
|
cmd = "ipfs add -q ".split()
|
|
cmd.append(tmp.name)
|
|
output = util.command_output(cmd).stdout
|
|
except (OSError, subprocess.CalledProcessError) as err:
|
|
msg = f"Failed to publish library. Error: {err}"
|
|
self._log.error(msg)
|
|
return False
|
|
self._log.info("hash of library: {0}", output)
|
|
|
|
def ipfs_import(self, lib, args):
|
|
_hash = args[0]
|
|
if len(args) > 1:
|
|
lib_name = args[1]
|
|
else:
|
|
lib_name = _hash
|
|
lib_root = os.path.dirname(lib.path)
|
|
remote_libs = os.path.join(lib_root, b"remotes")
|
|
if not os.path.exists(remote_libs):
|
|
try:
|
|
os.makedirs(remote_libs)
|
|
except OSError as e:
|
|
msg = f"Could not create {remote_libs}. Error: {e}"
|
|
self._log.error(msg)
|
|
return False
|
|
path = os.path.join(remote_libs, lib_name.encode() + b".db")
|
|
if not os.path.exists(path):
|
|
cmd = f"ipfs get {_hash} -o".split()
|
|
cmd.append(path)
|
|
try:
|
|
util.command_output(cmd)
|
|
except (OSError, subprocess.CalledProcessError):
|
|
self._log.error(f"Could not import {_hash}")
|
|
return False
|
|
|
|
# add all albums from remotes into a combined library
|
|
jpath = os.path.join(remote_libs, b"joined.db")
|
|
jlib = library.Library(jpath)
|
|
nlib = library.Library(path)
|
|
for album in nlib.albums():
|
|
if not self.already_added(album, jlib):
|
|
new_album = []
|
|
for item in album.items():
|
|
item.id = None
|
|
new_album.append(item)
|
|
added_album = jlib.add_album(new_album)
|
|
added_album.ipfs = album.ipfs
|
|
added_album.store()
|
|
|
|
def already_added(self, check, jlib):
|
|
for jalbum in jlib.albums():
|
|
if jalbum.mb_albumid == check.mb_albumid:
|
|
return True
|
|
return False
|
|
|
|
def ipfs_list(self, lib, args):
|
|
fmt = config['format_album'].get()
|
|
try:
|
|
albums = self.query(lib, args)
|
|
except OSError:
|
|
ui.print_("No imported libraries yet.")
|
|
return
|
|
|
|
for album in albums:
|
|
ui.print_(format(album, fmt), " : ", album.ipfs.decode())
|
|
|
|
def query(self, lib, args):
|
|
rlib = self.get_remote_lib(lib)
|
|
albums = rlib.albums(args)
|
|
return albums
|
|
|
|
def get_remote_lib(self, lib):
|
|
lib_root = os.path.dirname(lib.path)
|
|
remote_libs = os.path.join(lib_root, b"remotes")
|
|
path = os.path.join(remote_libs, b"joined.db")
|
|
if not os.path.isfile(path):
|
|
raise OSError
|
|
return library.Library(path)
|
|
|
|
def ipfs_added_albums(self, rlib, tmpname):
|
|
""" Returns a new library with only albums/items added to ipfs
|
|
"""
|
|
tmplib = library.Library(tmpname)
|
|
for album in rlib.albums():
|
|
try:
|
|
if album.ipfs:
|
|
self.create_new_album(album, tmplib)
|
|
except AttributeError:
|
|
pass
|
|
return tmplib
|
|
|
|
def create_new_album(self, album, tmplib):
|
|
items = []
|
|
for item in album.items():
|
|
try:
|
|
if not item.ipfs:
|
|
break
|
|
except AttributeError:
|
|
pass
|
|
item_path = os.path.basename(item.path).decode(
|
|
util._fsencoding(), 'ignore'
|
|
)
|
|
# Clear current path from item
|
|
item.path = f'/ipfs/{album.ipfs}/{item_path}'
|
|
|
|
item.id = None
|
|
items.append(item)
|
|
if len(items) < 1:
|
|
return False
|
|
self._log.info("Adding '{0}' to temporary library", album)
|
|
new_album = tmplib.add_album(items)
|
|
new_album.ipfs = album.ipfs
|
|
new_album.store()
|