mirror of
https://github.com/beetbox/beets.git
synced 2025-12-26 18:43:38 +01:00
Merge branch 'master' of github.com:sampsyo/beets into prompthook
Conflicts: docs/changelog.rst
This commit is contained in:
commit
75636a2503
11 changed files with 182 additions and 17 deletions
|
|
@ -1172,7 +1172,7 @@ def _raw_main(args, lib=None):
|
|||
parser.add_option('-d', '--directory', dest='directory',
|
||||
help="destination music directory")
|
||||
parser.add_option('-v', '--verbose', dest='verbose', action='count',
|
||||
help='print debugging information')
|
||||
help='log more details (use twice for even more)')
|
||||
parser.add_option('-c', '--config', dest='config',
|
||||
help='path to configuration file')
|
||||
parser.add_option('-h', '--help', dest='help', action='store_true',
|
||||
|
|
|
|||
|
|
@ -1428,10 +1428,18 @@ def move_items(lib, dest, query, copy, album, pretend):
|
|||
items, albums = _do_query(lib, query, album, False)
|
||||
objs = albums if album else items
|
||||
|
||||
# Filter out files that don't need to be moved.
|
||||
isitemmoved = lambda item: item.path != item.destination(basedir=dest)
|
||||
isalbummoved = lambda album: any(isitemmoved(i) for i in album.items())
|
||||
objs = [o for o in objs if (isalbummoved if album else isitemmoved)(o)]
|
||||
|
||||
action = 'Copying' if copy else 'Moving'
|
||||
entity = 'album' if album else 'item'
|
||||
log.info(u'{0} {1} {2}{3}.', action, len(objs), entity,
|
||||
's' if len(objs) > 1 else '')
|
||||
's' if len(objs) != 1 else '')
|
||||
if not objs:
|
||||
return
|
||||
|
||||
if pretend:
|
||||
if album:
|
||||
show_path_changes([(item.path, item.destination(basedir=dest))
|
||||
|
|
|
|||
106
beetsplug/acousticbrainz.py
Normal file
106
beetsplug/acousticbrainz.py
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# This file is part of beets.
|
||||
# Copyright 2015-2016, Ohm Patel.
|
||||
#
|
||||
# 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.
|
||||
|
||||
""" Fetch various AcousticBrainz metadata using MBID
|
||||
"""
|
||||
from __future__ import (division, absolute_import, print_function,
|
||||
unicode_literals)
|
||||
|
||||
import requests
|
||||
|
||||
from beets import plugins, ui
|
||||
|
||||
ACOUSTIC_URL = "http://acousticbrainz.org/"
|
||||
LEVEL = "/high-level"
|
||||
|
||||
|
||||
class AcousticPlugin(plugins.BeetsPlugin):
|
||||
def __init__(self):
|
||||
super(AcousticPlugin, self).__init__()
|
||||
|
||||
def commands(self):
|
||||
cmd = ui.Subcommand('acousticbrainz',
|
||||
help="fetch metadata from AcousticBrainz")
|
||||
|
||||
def func(lib, opts, args):
|
||||
items = lib.items(ui.decargs(args))
|
||||
fetch_info(self._log, items)
|
||||
|
||||
cmd.func = func
|
||||
return [cmd]
|
||||
|
||||
|
||||
def fetch_info(log, items):
|
||||
"""Currently outputs MBID and corresponding request status code
|
||||
"""
|
||||
for item in items:
|
||||
if item.mb_trackid:
|
||||
log.info('getting data for: {}', item)
|
||||
|
||||
# Fetch the data from the AB API.
|
||||
url = generate_url(item.mb_trackid)
|
||||
log.debug('fetching URL: {}', url)
|
||||
try:
|
||||
rs = requests.get(url)
|
||||
except requests.RequestException as exc:
|
||||
log.info('request error: {}', exc)
|
||||
continue
|
||||
|
||||
# Check for missing tracks.
|
||||
if rs.status_code == 404:
|
||||
log.info('recording ID {} not found', item.mb_trackid)
|
||||
continue
|
||||
|
||||
# Parse the JSON response.
|
||||
try:
|
||||
data = rs.json()
|
||||
except ValueError:
|
||||
log.debug('Invalid Response: {}', rs.text)
|
||||
|
||||
# Get each field and assign it on the item.
|
||||
item.danceable = get_value(
|
||||
log,
|
||||
data,
|
||||
["highlevel", "danceability", "all", "danceable"],
|
||||
)
|
||||
item.mood_happy = get_value(
|
||||
log,
|
||||
data,
|
||||
["highlevel", "mood_happy", "all", "happy"],
|
||||
)
|
||||
item.mood_party = get_value(
|
||||
log,
|
||||
data,
|
||||
["highlevel", "mood_party", "all", "party"],
|
||||
)
|
||||
|
||||
# Store the data. We only update flexible attributes, so we
|
||||
# don't call `item.try_write()` here.
|
||||
item.store()
|
||||
|
||||
|
||||
def generate_url(mbid):
|
||||
"""Generates url of AcousticBrainz end point for given MBID
|
||||
"""
|
||||
return ACOUSTIC_URL + mbid + LEVEL
|
||||
|
||||
|
||||
def get_value(log, data, map_path):
|
||||
"""Allows traversal of dictionary with cleaner formatting
|
||||
"""
|
||||
try:
|
||||
return reduce(lambda d, k: d[k], map_path, data)
|
||||
except KeyError:
|
||||
log.debug('Invalid Path: {}', map_path)
|
||||
|
|
@ -465,7 +465,7 @@ class EchonestMetadataPlugin(plugins.BeetsPlugin):
|
|||
|
||||
def commands(self):
|
||||
fetch_cmd = ui.Subcommand('echonest',
|
||||
help='Fetch metadata from the EchoNest')
|
||||
help='fetch metadata from The Echo Nest')
|
||||
fetch_cmd.parser.add_option(
|
||||
'-f', '--force', dest='force', action='store_true', default=False,
|
||||
help='(re-)download information from the EchoNest'
|
||||
|
|
|
|||
|
|
@ -129,13 +129,8 @@ class PlayPlugin(BeetsPlugin):
|
|||
try:
|
||||
util.interactive_open(open_args, command_str)
|
||||
except OSError as exc:
|
||||
raise ui.UserError("Could not play the music playlist: "
|
||||
raise ui.UserError("Could not play the query: "
|
||||
"{0}".format(exc))
|
||||
finally:
|
||||
if not raw:
|
||||
self._log.debug('Removing temporary playlist: {}',
|
||||
open_args[0])
|
||||
util.remove(open_args[0])
|
||||
|
||||
def _create_tmp_playlist(self, paths_list):
|
||||
"""Create a temporary .m3u file. Return the filename.
|
||||
|
|
|
|||
|
|
@ -9,10 +9,26 @@ New:
|
|||
* :doc:`/plugins/fetchart`: The Google Images backend has been restored. It
|
||||
now requires an API key from Google. Thanks to :user:`lcharlick`.
|
||||
:bug:`1778`
|
||||
* A new :doc:`/plugins/acousticbrainz` fetches acoustic-analysis information
|
||||
from the `AcousticBrainz`_ project. Thanks to :user:`opatel99`. :bug:`1784`
|
||||
* A new :doc:`/plugins/mbsubmit` lets you print the tracks of an album in a
|
||||
format parseable by MusicBrainz track parser during an interactive import
|
||||
session. :bug:`1779`
|
||||
|
||||
.. _AcousticBrainz: http://acousticbrainz.org/
|
||||
|
||||
Fixes:
|
||||
|
||||
* :doc:`/plugins/play`: Remove dead code. From this point on, beets isn't
|
||||
supposed and won't try to delete the playlists generated by ``beet play``
|
||||
(Note that although it was supposed to, beet didn't actually remove the
|
||||
generated ``.m3u`` files beforehand either.). If this is an issue for you, you
|
||||
might want to take a look at the ``raw`` config option of the
|
||||
:doc:`/plugins/play`. :bug:`1785`, :bug:`1600`
|
||||
* The :ref:`move-cmd` command does not display files whose path does not change
|
||||
anymore. :bug:`1583`
|
||||
|
||||
|
||||
|
||||
1.3.16 (December 28, 2015)
|
||||
--------------------------
|
||||
|
|
|
|||
|
|
@ -517,16 +517,18 @@ str.format-style string formatting. So you can write logging calls like this::
|
|||
When beets is in verbose mode, plugin messages are prefixed with the plugin
|
||||
name to make them easier to see.
|
||||
|
||||
What messages will be logged depends on the logging level and the action
|
||||
Which messages will be logged depends on the logging level and the action
|
||||
performed:
|
||||
|
||||
* On import stages and event handlers, the default is ``WARNING`` messages and
|
||||
above.
|
||||
* On direct actions, the default is ``INFO`` or above, as with the rest of
|
||||
beets.
|
||||
* Inside import stages and event handlers, the default is ``WARNING`` messages
|
||||
and above.
|
||||
* Everywhere else, the default is ``INFO`` or above.
|
||||
|
||||
The verbosity can be increased with ``--verbose`` flags: each flags lowers the
|
||||
level by a notch.
|
||||
The verbosity can be increased with ``--verbose`` (``-v``) flags: each flags
|
||||
lowers the level by a notch. That means that, with a single ``-v`` flag, event
|
||||
handlers won't have their ``DEBUG`` messages displayed, but command functions
|
||||
(for example) will. With ``-vv`` on the command line, ``DEBUG`` messages will
|
||||
be displayed everywhere.
|
||||
|
||||
This addresses a common pattern where plugins need to use the same code for a
|
||||
command and an import stage, but the command needs to print more messages than
|
||||
|
|
|
|||
22
docs/plugins/acousticbrainz.rst
Normal file
22
docs/plugins/acousticbrainz.rst
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
AcousticBrainz Plugin
|
||||
=====================
|
||||
|
||||
The ``acoustricbrainz`` plugin gets acoustic-analysis information from the
|
||||
`AcousticBrainz`_ project. The spirit is similar to the
|
||||
:doc:`/plugins/echonest`.
|
||||
|
||||
.. _AcousticBrainz: http://acousticbrainz.org/
|
||||
|
||||
Enable the ``acousticbrainz`` plugin in your configuration (see :ref:`using-plugins`) and run it by typing::
|
||||
|
||||
$ beet acousticbrainz [QUERY]
|
||||
|
||||
For all tracks with a MusicBrainz recording ID, the plugin currently sets
|
||||
these fields:
|
||||
|
||||
* ``danceable``: Predicts how easy the track is to dance to.
|
||||
* ``mood_happy``: Predicts the probability this track will evoke happiness.
|
||||
* ``mood_party``: Predicts the probability this track should be played at a
|
||||
party.
|
||||
|
||||
These three fields are all numbers between 0.0 and 1.0.
|
||||
|
|
@ -31,6 +31,7 @@ Each plugin has its own set of options that can be defined in a section bearing
|
|||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
acousticbrainz
|
||||
badfiles
|
||||
bpd
|
||||
bpm
|
||||
|
|
@ -95,6 +96,7 @@ Autotagger Extensions
|
|||
Metadata
|
||||
--------
|
||||
|
||||
* :doc:`acousticbrainz`: Fetch various AcousticBrainz metadata
|
||||
* :doc:`bpm`: Measure tempo using keystrokes.
|
||||
* :doc:`echonest`: Automatically fetch `acoustic attributes`_ from
|
||||
`the Echo Nest`_ (tempo, energy, danceability, ...).
|
||||
|
|
|
|||
|
|
@ -82,3 +82,17 @@ example::
|
|||
|
||||
indicates that you need to insert extra arguments before specifying the
|
||||
playlist.
|
||||
|
||||
Note on the Leakage of the Generated Playlists
|
||||
_______________________________________________
|
||||
|
||||
Because the command that will open the generated ``.m3u`` files can be
|
||||
arbitrarily configured by the user, beets won't try to delete those files. For
|
||||
this reason, using this plugin will leave one or several playlist(s) in the
|
||||
directory selected to create temporary files (Most likely ``/tmp/`` on Unix-like
|
||||
systems. See `tempfile.tempdir`_.). Leaking those playlists until they are
|
||||
externally wiped could be an issue for privacy or storage reasons. If this is
|
||||
the case for you, you might want to use the ``raw`` config option described
|
||||
above.
|
||||
|
||||
.. _tempfile.tempdir: https://docs.python.org/2/library/tempfile.html#tempfile.tempdir
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ anywhere in your filesystem. The ``-c`` option copies files instead of moving
|
|||
them. As with other commands, the ``-a`` option matches albums instead of items.
|
||||
|
||||
To perform a "dry run", just use the ``-p`` (for "pretend") flag. This will
|
||||
show you all how the files would be moved but won't actually change anything
|
||||
show you a list of files that would be moved but won't actually change anything
|
||||
on disk.
|
||||
|
||||
.. _update-cmd:
|
||||
|
|
|
|||
Loading…
Reference in a new issue