mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 16:42:42 +01:00
Merge branch 'master' of github.com:beetbox/beets into deinterlace
This commit is contained in:
commit
4d94bf8fad
7 changed files with 41 additions and 206 deletions
|
|
@ -757,15 +757,21 @@ def show_path_changes(path_changes):
|
|||
if max_width > col_width:
|
||||
# Print every change over two lines
|
||||
for source, dest in zip(sources, destinations):
|
||||
log.info('{0} \n -> {1}', source, dest)
|
||||
color_source, color_dest = colordiff(source, dest)
|
||||
print_('{0} \n -> {1}'.format(color_source, color_dest))
|
||||
else:
|
||||
# Print every change on a single line, and add a header
|
||||
title_pad = max_width - len('Source ') + len(' -> ')
|
||||
|
||||
log.info('Source {0} Destination', ' ' * title_pad)
|
||||
print_('Source {0} Destination'.format(' ' * title_pad))
|
||||
for source, dest in zip(sources, destinations):
|
||||
pad = max_width - len(source)
|
||||
log.info('{0} {1} -> {2}', source, ' ' * pad, dest)
|
||||
color_source, color_dest = colordiff(source, dest)
|
||||
print_('{0} {1} -> {2}'.format(
|
||||
color_source,
|
||||
' ' * pad,
|
||||
color_dest,
|
||||
))
|
||||
|
||||
|
||||
# Helper functions for option parsing.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import sys
|
|||
import errno
|
||||
import locale
|
||||
import re
|
||||
import tempfile
|
||||
import shutil
|
||||
import fnmatch
|
||||
import functools
|
||||
|
|
@ -478,6 +479,11 @@ def move(path, dest, replace=False):
|
|||
instead, in which case metadata will *not* be preserved. Paths are
|
||||
translated to system paths.
|
||||
"""
|
||||
if os.path.isdir(path):
|
||||
raise FilesystemError(u'source is directory', 'move', (path, dest))
|
||||
if os.path.isdir(dest):
|
||||
raise FilesystemError(u'destination is directory', 'move',
|
||||
(path, dest))
|
||||
if samefile(path, dest):
|
||||
return
|
||||
path = syspath(path)
|
||||
|
|
@ -487,15 +493,23 @@ def move(path, dest, replace=False):
|
|||
|
||||
# First, try renaming the file.
|
||||
try:
|
||||
os.rename(path, dest)
|
||||
os.replace(path, dest)
|
||||
except OSError:
|
||||
# Otherwise, copy and delete the original.
|
||||
tmp = tempfile.mktemp(suffix='.beets',
|
||||
prefix=py3_path(b'.' + os.path.basename(dest)),
|
||||
dir=py3_path(os.path.dirname(dest)))
|
||||
tmp = syspath(tmp)
|
||||
try:
|
||||
shutil.copyfile(path, dest)
|
||||
shutil.copyfile(path, tmp)
|
||||
os.replace(tmp, dest)
|
||||
tmp = None
|
||||
os.remove(path)
|
||||
except OSError as exc:
|
||||
raise FilesystemError(exc, 'move', (path, dest),
|
||||
traceback.format_exc())
|
||||
finally:
|
||||
if tmp is not None:
|
||||
os.remove(tmp)
|
||||
|
||||
|
||||
def link(path, dest, replace=False):
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
# This file is part of beets.
|
||||
# Copyright 2017, Tigran Kostandyan.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
|
|
@ -12,124 +11,15 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
"""Upload files to Google Play Music and list songs in its library."""
|
||||
|
||||
import os.path
|
||||
"""Deprecation warning for the removed gmusic plugin."""
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets import ui
|
||||
from beets import config
|
||||
from beets.ui import Subcommand
|
||||
from gmusicapi import Musicmanager, Mobileclient
|
||||
from gmusicapi.exceptions import NotLoggedIn
|
||||
import gmusicapi.clients
|
||||
|
||||
|
||||
class Gmusic(BeetsPlugin):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.m = Musicmanager()
|
||||
|
||||
# OAUTH_FILEPATH was moved in gmusicapi 12.0.0.
|
||||
if hasattr(Musicmanager, 'OAUTH_FILEPATH'):
|
||||
oauth_file = Musicmanager.OAUTH_FILEPATH
|
||||
else:
|
||||
oauth_file = gmusicapi.clients.OAUTH_FILEPATH
|
||||
|
||||
self.config.add({
|
||||
'auto': False,
|
||||
'uploader_id': '',
|
||||
'uploader_name': '',
|
||||
'device_id': '',
|
||||
'oauth_file': oauth_file,
|
||||
})
|
||||
if self.config['auto']:
|
||||
self.import_stages = [self.autoupload]
|
||||
|
||||
def commands(self):
|
||||
gupload = Subcommand('gmusic-upload',
|
||||
help='upload your tracks to Google Play Music')
|
||||
gupload.func = self.upload
|
||||
|
||||
search = Subcommand('gmusic-songs',
|
||||
help='list of songs in Google Play Music library')
|
||||
search.parser.add_option('-t', '--track', dest='track',
|
||||
action='store_true',
|
||||
help='Search by track name')
|
||||
search.parser.add_option('-a', '--artist', dest='artist',
|
||||
action='store_true',
|
||||
help='Search by artist')
|
||||
search.func = self.search
|
||||
return [gupload, search]
|
||||
|
||||
def authenticate(self):
|
||||
if self.m.is_authenticated():
|
||||
return
|
||||
# Checks for OAuth2 credentials,
|
||||
# if they don't exist - performs authorization
|
||||
oauth_file = self.config['oauth_file'].as_filename()
|
||||
if os.path.isfile(oauth_file):
|
||||
uploader_id = self.config['uploader_id']
|
||||
uploader_name = self.config['uploader_name']
|
||||
self.m.login(oauth_credentials=oauth_file,
|
||||
uploader_id=uploader_id.as_str().upper() or None,
|
||||
uploader_name=uploader_name.as_str() or None)
|
||||
else:
|
||||
self.m.perform_oauth(oauth_file)
|
||||
|
||||
def upload(self, lib, opts, args):
|
||||
items = lib.items(ui.decargs(args))
|
||||
files = self.getpaths(items)
|
||||
self.authenticate()
|
||||
ui.print_('Uploading your files...')
|
||||
self.m.upload(filepaths=files)
|
||||
ui.print_('Your files were successfully added to library')
|
||||
|
||||
def autoupload(self, session, task):
|
||||
items = task.imported_items()
|
||||
files = self.getpaths(items)
|
||||
self.authenticate()
|
||||
self._log.info('Uploading files to Google Play Music...', files)
|
||||
self.m.upload(filepaths=files)
|
||||
self._log.info('Your files were successfully added to your '
|
||||
+ 'Google Play Music library')
|
||||
|
||||
def getpaths(self, items):
|
||||
return [x.path for x in items]
|
||||
|
||||
def search(self, lib, opts, args):
|
||||
password = config['gmusic']['password']
|
||||
email = config['gmusic']['email']
|
||||
uploader_id = config['gmusic']['uploader_id']
|
||||
device_id = config['gmusic']['device_id']
|
||||
password.redact = True
|
||||
email.redact = True
|
||||
# Since Musicmanager doesn't support library management
|
||||
# we need to use mobileclient interface
|
||||
mobile = Mobileclient()
|
||||
try:
|
||||
new_device_id = (device_id.as_str()
|
||||
or uploader_id.as_str().replace(':', '')
|
||||
or Mobileclient.FROM_MAC_ADDRESS).upper()
|
||||
mobile.login(email.as_str(), password.as_str(), new_device_id)
|
||||
files = mobile.get_all_songs()
|
||||
except NotLoggedIn:
|
||||
ui.print_(
|
||||
'Authentication error. Please check your email and password.'
|
||||
)
|
||||
return
|
||||
if not args:
|
||||
for i, file in enumerate(files, start=1):
|
||||
print(i, ui.colorize('blue', file['artist']),
|
||||
file['title'], ui.colorize('red', file['album']))
|
||||
else:
|
||||
if opts.track:
|
||||
self.match(files, args, 'title')
|
||||
else:
|
||||
self.match(files, args, 'artist')
|
||||
|
||||
@staticmethod
|
||||
def match(files, args, search_by):
|
||||
for file in files:
|
||||
if ' '.join(ui.decargs(args)) in file[search_by]:
|
||||
print(file['artist'], file['title'], file['album'])
|
||||
self._log.warning("The 'gmusic' plugin has been removed following the"
|
||||
" shutdown of Google Play Music. Remove the plugin"
|
||||
" from your configuration to silence this warning.")
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ For packagers:
|
|||
:bug:`4037` :bug:`4038`
|
||||
* This version of beets no longer depends on the `six`_ library.
|
||||
:bug:`4030`
|
||||
* The `gmusic` plugin was removed since Google Play Music has been shut down.
|
||||
Thus, the optional dependency on `gmusicapi` does not exist anymore.
|
||||
:bug:`4089`
|
||||
|
||||
Major new features:
|
||||
|
||||
|
|
@ -37,6 +40,12 @@ Other new things:
|
|||
subdirectories in library.
|
||||
* :doc:`/plugins/info`: Support ``--album`` flag.
|
||||
* :doc:`/plugins/export`: Support ``--album`` flag.
|
||||
* ``beet move`` path differences are now highlighted in color (when enabled).
|
||||
* When moving files and a direct rename of a file is not possible, beets now
|
||||
copies to a temporary file in the target folder first instead of directly
|
||||
using the target path. This gets us closer to always updating files
|
||||
atomically. Thanks to :user:`catap`.
|
||||
:bug:`4060`
|
||||
* :doc:`/plugins/fetchart`: A new option to store cover art as non-progressive
|
||||
image. Useful for DAPs that support progressive images. Set ``deinterlace:
|
||||
yes`` in your configuration to enable.
|
||||
|
|
|
|||
|
|
@ -1,87 +1,5 @@
|
|||
Gmusic Plugin
|
||||
=============
|
||||
|
||||
The ``gmusic`` plugin lets you upload songs to Google Play Music and query
|
||||
songs in your library.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The plugin requires :pypi:`gmusicapi`. You can install it using ``pip``::
|
||||
|
||||
pip install gmusicapi
|
||||
|
||||
.. _gmusicapi: https://github.com/simon-weber/gmusicapi/
|
||||
|
||||
Then, you can enable the ``gmusic`` plugin in your configuration (see
|
||||
:ref:`using-plugins`).
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
Configuration is required before use. Below is an example configuration::
|
||||
|
||||
gmusic:
|
||||
email: user@example.com
|
||||
password: seekrit
|
||||
auto: yes
|
||||
uploader_id: 00:11:22:33:AA:BB
|
||||
device_id: 00112233AABB
|
||||
oauth_file: ~/.config/beets/oauth.cred
|
||||
|
||||
|
||||
To upload tracks to Google Play Music, use the ``gmusic-upload`` command::
|
||||
|
||||
beet gmusic-upload [QUERY]
|
||||
|
||||
If you don't include a query, the plugin will upload your entire collection.
|
||||
|
||||
To list your music collection, use the ``gmusic-songs`` command::
|
||||
|
||||
beet gmusic-songs [-at] [ARGS]
|
||||
|
||||
Use the ``-a`` option to search by artist and ``-t`` to search by track. For
|
||||
example::
|
||||
|
||||
beet gmusic-songs -a John Frusciante
|
||||
beet gmusic-songs -t Black Hole Sun
|
||||
|
||||
For a list of all songs in your library, run ``beet gmusic-songs`` without any
|
||||
arguments.
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
To configure the plugin, make a ``gmusic:`` section in your configuration file.
|
||||
The available options are:
|
||||
|
||||
- **email**: Your Google account email address.
|
||||
Default: none.
|
||||
- **password**: Password to your Google account. Required to query songs in
|
||||
your collection.
|
||||
For accounts with 2-step-verification, an
|
||||
`app password <https://support.google.com/accounts/answer/185833?hl=en>`__
|
||||
will need to be generated. An app password for an account without
|
||||
2-step-verification is not required but is recommended.
|
||||
Default: none.
|
||||
- **auto**: Set to ``yes`` to automatically upload new imports to Google Play
|
||||
Music.
|
||||
Default: ``no``
|
||||
- **uploader_id**: Unique id as a MAC address, eg ``00:11:22:33:AA:BB``.
|
||||
This option should be set before the maximum number of authorized devices is
|
||||
reached.
|
||||
If provided, use the same id for all future runs on this, and other, beets
|
||||
installations as to not reach the maximum number of authorized devices.
|
||||
Default: device's MAC address.
|
||||
- **device_id**: Unique device ID for authorized devices. It is usually
|
||||
the same as your MAC address with the colons removed, eg ``00112233AABB``.
|
||||
This option only needs to be set if you receive an `InvalidDeviceId`
|
||||
exception. Below the exception will be a list of valid device IDs.
|
||||
Default: none.
|
||||
- **oauth_file**: Filepath for oauth credentials file.
|
||||
Default: `{user_data_dir} <https://pypi.org/project/appdirs/>`__/gmusicapi/oauth.cred
|
||||
|
||||
Refer to the `Google Play Music Help
|
||||
<https://support.google.com/googleplaymusic/answer/3139562?hl=en>`__
|
||||
page for more details on authorized devices.
|
||||
The ``gmusic`` plugin interfaced beets to Google Play Music. It has been
|
||||
removed after the shutdown of this service.
|
||||
|
|
|
|||
|
|
@ -231,7 +231,6 @@ Miscellaneous
|
|||
* :doc:`filefilter`: Automatically skip files during the import process based
|
||||
on regular expressions.
|
||||
* :doc:`fuzzy`: Search albums and tracks with fuzzy string matching.
|
||||
* :doc:`gmusic`: Search and upload files to Google Play Music.
|
||||
* :doc:`hook`: Run a command when an event is emitted by beets.
|
||||
* :doc:`ihate`: Automatically skip albums and tracks during the import process.
|
||||
* :doc:`info`: Print music files' tags to the console.
|
||||
|
|
|
|||
1
setup.py
1
setup.py
|
|
@ -126,7 +126,6 @@ setup(
|
|||
'embedart': ['Pillow'],
|
||||
'embyupdate': ['requests'],
|
||||
'chroma': ['pyacoustid'],
|
||||
'gmusic': ['gmusicapi'],
|
||||
'discogs': ['python3-discogs-client>=2.3.10'],
|
||||
'beatport': ['requests-oauthlib>=0.6.1'],
|
||||
'kodiupdate': ['requests'],
|
||||
|
|
|
|||
Loading…
Reference in a new issue