mirror of
https://github.com/beetbox/beets.git
synced 2025-12-15 21:14:19 +01:00
Merge remote-tracking branch 'refs/remotes/beetbox/master'
This commit is contained in:
commit
84b8cf0ab5
18 changed files with 68 additions and 51 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -84,3 +84,8 @@ ENV/
|
|||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# PyDev and Eclipse project settings
|
||||
/.project
|
||||
/.pydevproject
|
||||
/.settings
|
||||
|
|
|
|||
|
|
@ -17,14 +17,13 @@ matrix:
|
|||
env: {TOX_ENV: py35-test}
|
||||
# - python: pypy
|
||||
# - env: {TOX_ENV: pypy-test}
|
||||
- python: 2.7
|
||||
env: {TOX_ENV: py27-flake8}
|
||||
- python: 3.4
|
||||
env: {TOX_ENV: py34-flake8}
|
||||
- python: 2.7
|
||||
env: {TOX_ENV: docs}
|
||||
allow_failures:
|
||||
- python: 3.4
|
||||
env: {TOX_ENV: py34-test}
|
||||
- python: 3.5
|
||||
|
||||
# Non-Python dependencies.
|
||||
|
|
|
|||
|
|
@ -327,6 +327,11 @@ def _recommendation(results):
|
|||
return rec
|
||||
|
||||
|
||||
def _sort_candidates(candidates):
|
||||
"""Sort candidates by distance."""
|
||||
return sorted(candidates, key=lambda match: match.distance)
|
||||
|
||||
|
||||
def _add_candidate(items, results, info):
|
||||
"""Given a candidate AlbumInfo object, attempt to add the candidate
|
||||
to the output dictionary of AlbumMatch objects. This involves
|
||||
|
|
@ -443,7 +448,7 @@ def tag_album(items, search_artist=None, search_album=None,
|
|||
_add_candidate(items, candidates, info)
|
||||
|
||||
# Sort and get the recommendation.
|
||||
candidates = sorted(candidates.values())
|
||||
candidates = _sort_candidates(candidates.values())
|
||||
rec = _recommendation(candidates)
|
||||
return cur_artist, cur_album, candidates, rec
|
||||
|
||||
|
|
@ -471,16 +476,16 @@ def tag_item(item, search_artist=None, search_title=None,
|
|||
candidates[track_info.track_id] = \
|
||||
hooks.TrackMatch(dist, track_info)
|
||||
# If this is a good match, then don't keep searching.
|
||||
rec = _recommendation(sorted(candidates.values()))
|
||||
rec = _recommendation(_sort_candidates(candidates.values()))
|
||||
if rec == Recommendation.strong and \
|
||||
not config['import']['timid']:
|
||||
log.debug(u'Track ID match.')
|
||||
return sorted(candidates.values()), rec
|
||||
return _sort_candidates(candidates.values()), rec
|
||||
|
||||
# If we're searching by ID, don't proceed.
|
||||
if search_ids:
|
||||
if candidates:
|
||||
return sorted(candidates.values()), rec
|
||||
return _sort_candidates(candidates.values()), rec
|
||||
else:
|
||||
return [], Recommendation.none
|
||||
|
||||
|
|
@ -496,6 +501,6 @@ def tag_item(item, search_artist=None, search_title=None,
|
|||
|
||||
# Sort by distance and return with recommendation.
|
||||
log.debug(u'Found {0} candidates.', len(candidates))
|
||||
candidates = sorted(candidates.values())
|
||||
candidates = _sort_candidates(candidates.values())
|
||||
rec = _recommendation(candidates)
|
||||
return candidates, rec
|
||||
|
|
|
|||
|
|
@ -944,7 +944,7 @@ class ArchiveImportTask(SentinelImportTask):
|
|||
return False
|
||||
|
||||
for path_test, _ in cls.handlers():
|
||||
if path_test(path):
|
||||
if path_test(util.py3_path(path)):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
@ -990,7 +990,7 @@ class ArchiveImportTask(SentinelImportTask):
|
|||
|
||||
try:
|
||||
extract_to = mkdtemp()
|
||||
archive = handler_class(self.toppath, mode='r')
|
||||
archive = handler_class(util.py3_path(self.toppath), mode='r')
|
||||
archive.extractall(extract_to)
|
||||
finally:
|
||||
archive.close()
|
||||
|
|
|
|||
|
|
@ -443,8 +443,7 @@ def move(path, dest, replace=False):
|
|||
path = syspath(path)
|
||||
dest = syspath(dest)
|
||||
if os.path.exists(dest) and not replace:
|
||||
raise FilesystemError(u'file exists', 'rename', (path, dest),
|
||||
traceback.format_exc())
|
||||
raise FilesystemError(u'file exists', 'rename', (path, dest))
|
||||
|
||||
# First, try renaming the file.
|
||||
try:
|
||||
|
|
@ -469,13 +468,19 @@ def link(path, dest, replace=False):
|
|||
path = syspath(path)
|
||||
dest = syspath(dest)
|
||||
if os.path.exists(dest) and not replace:
|
||||
raise FilesystemError(u'file exists', 'rename', (path, dest),
|
||||
traceback.format_exc())
|
||||
raise FilesystemError(u'file exists', 'rename', (path, dest))
|
||||
try:
|
||||
os.symlink(path, dest)
|
||||
except OSError:
|
||||
raise FilesystemError(u'Operating system does not support symbolic '
|
||||
u'links.', 'link', (path, dest),
|
||||
except NotImplementedError:
|
||||
# raised on python >= 3.2 and Windows versions before Vista
|
||||
raise FilesystemError(u'OS does not support symbolic links.'
|
||||
'link', (path, dest), traceback.format_exc())
|
||||
except OSError as exc:
|
||||
# TODO: Windows version checks can be removed for python 3
|
||||
if hasattr('sys', 'getwindowsversion'):
|
||||
if sys.getwindowsversion()[0] < 6: # is before Vista
|
||||
exc = u'OS does not support symbolic links.'
|
||||
raise FilesystemError(exc, 'link', (path, dest),
|
||||
traceback.format_exc())
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -640,8 +640,8 @@ class Command(object):
|
|||
"""A command issued by the client for processing by the server.
|
||||
"""
|
||||
|
||||
command_re = re.compile(br'^([^ \t]+)[ \t]*')
|
||||
arg_re = re.compile(br'"((?:\\"|[^"])+)"|([^ \t"]+)')
|
||||
command_re = re.compile(r'^([^ \t]+)[ \t]*')
|
||||
arg_re = re.compile(r'"((?:\\"|[^"])+)"|([^ \t"]+)')
|
||||
|
||||
def __init__(self, s):
|
||||
"""Creates a new `Command` from the given string, `s`, parsing
|
||||
|
|
@ -656,11 +656,10 @@ class Command(object):
|
|||
if match[0]:
|
||||
# Quoted argument.
|
||||
arg = match[0]
|
||||
arg = arg.replace(b'\\"', b'"').replace(b'\\\\', b'\\')
|
||||
arg = arg.replace('\\"', '"').replace('\\\\', '\\')
|
||||
else:
|
||||
# Unquoted argument.
|
||||
arg = match[1]
|
||||
arg = arg.decode('utf8')
|
||||
self.args.append(arg)
|
||||
|
||||
def run(self, conn):
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ from six.moves import urllib
|
|||
from beets import ui
|
||||
|
||||
import gi
|
||||
from gi.repository import GLib, Gst
|
||||
|
||||
gi.require_version('Gst', '1.0')
|
||||
from gi.repository import GLib, Gst # noqa ignore=E402
|
||||
|
||||
|
||||
Gst.init(None)
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ def replace_ext(path, ext):
|
|||
|
||||
The new extension must not contain a leading dot.
|
||||
"""
|
||||
ext_dot = util.bytestring_path('.' + ext)
|
||||
ext_dot = b'.' + ext
|
||||
return os.path.splitext(path)[0] + ext_dot
|
||||
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ def get_format(fmt=None):
|
|||
.format(fmt)
|
||||
)
|
||||
except ConfigTypeError:
|
||||
command = config['convert']['formats'][fmt].get(bytes)
|
||||
command = config['convert']['formats'][fmt].get(str)
|
||||
extension = fmt
|
||||
|
||||
# Convenience and backwards-compatibility shortcuts.
|
||||
|
|
@ -428,7 +428,9 @@ class ConvertPlugin(BeetsPlugin):
|
|||
|
||||
# Create a temporary file for the conversion.
|
||||
tmpdir = self.config['tmpdir'].get()
|
||||
fd, dest = tempfile.mkstemp('.' + ext, dir=tmpdir)
|
||||
if tmpdir:
|
||||
tmpdir = util.py3_path(util.bytestring_path(tmpdir))
|
||||
fd, dest = tempfile.mkstemp(util.py3_path(b'.' + ext), dir=tmpdir)
|
||||
os.close(fd)
|
||||
dest = util.bytestring_path(dest)
|
||||
_temp_files.append(dest) # Delete the transcode later.
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import os
|
|||
import re
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.util import mkdirall, normpath, syspath, bytestring_path
|
||||
from beets.util import mkdirall, normpath, syspath, bytestring_path, link
|
||||
from beets import config
|
||||
|
||||
M3U_DEFAULT_NAME = 'imported.m3u'
|
||||
|
|
@ -131,7 +131,7 @@ class ImportFeedsPlugin(BeetsPlugin):
|
|||
for path in paths:
|
||||
dest = os.path.join(feedsdir, os.path.basename(path))
|
||||
if not os.path.exists(syspath(dest)):
|
||||
os.symlink(syspath(path), syspath(dest))
|
||||
link(path, dest)
|
||||
|
||||
if 'echo' in formats:
|
||||
self._log.info(u"Location of imported music:")
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ The scraper script used is available here:
|
|||
https://gist.github.com/1241307
|
||||
"""
|
||||
import pylast
|
||||
import codecs
|
||||
import os
|
||||
import yaml
|
||||
import traceback
|
||||
|
|
@ -140,7 +141,8 @@ class LastGenrePlugin(plugins.BeetsPlugin):
|
|||
c14n_filename = C14N_TREE
|
||||
if c14n_filename:
|
||||
c14n_filename = normpath(c14n_filename)
|
||||
genres_tree = yaml.load(open(c14n_filename, 'r'))
|
||||
genres_file = codecs.open(c14n_filename, 'r', encoding='utf-8')
|
||||
genres_tree = yaml.load(genres_file)
|
||||
flatten_tree(genres_tree, [], self.c14n_branches)
|
||||
|
||||
@property
|
||||
|
|
|
|||
|
|
@ -137,8 +137,7 @@ class PlayPlugin(BeetsPlugin):
|
|||
else:
|
||||
open_args = [self._create_tmp_playlist(paths)]
|
||||
|
||||
self._log.debug(u'executing command: {} {}', command_str,
|
||||
util.text_string(b' '.join(open_args)))
|
||||
self._log.debug(u'executing command: {} {!r}', command_str, open_args)
|
||||
try:
|
||||
util.interactive_open(open_args, command_str)
|
||||
except OSError as exc:
|
||||
|
|
|
|||
|
|
@ -308,9 +308,9 @@ class CommandBackend(Backend):
|
|||
def format_supported(self, item):
|
||||
"""Checks whether the given item is supported by the selected tool.
|
||||
"""
|
||||
if 'mp3gain' in self.command and item.format != 'MP3':
|
||||
if b'mp3gain' in self.command and item.format != 'MP3':
|
||||
return False
|
||||
elif 'aacgain' in self.command and item.format not in ('MP3', 'AAC'):
|
||||
elif b'aacgain' in self.command and item.format not in ('MP3', 'AAC'):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ log.setLevel(logging.DEBUG)
|
|||
_item_ident = 0
|
||||
|
||||
# OS feature test.
|
||||
HAVE_SYMLINK = hasattr(os, 'symlink')
|
||||
HAVE_SYMLINK = sys.platform != 'win32'
|
||||
|
||||
|
||||
def item(lib=None):
|
||||
|
|
|
|||
|
|
@ -422,6 +422,7 @@ class TestHelper(object):
|
|||
# Running beets commands
|
||||
|
||||
def run_command(self, *args):
|
||||
sys.argv = ['beet'] # avoid leakage from test suite args
|
||||
if hasattr(self, 'lib'):
|
||||
lib = self.lib
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ from mock import patch
|
|||
|
||||
from test import _common
|
||||
from test._common import unittest
|
||||
from beets.util import displayable_path, bytestring_path
|
||||
from beets.util import displayable_path, bytestring_path, py3_path
|
||||
from test.helper import TestImportSession, TestHelper, has_program, capture_log
|
||||
from beets import importer
|
||||
from beets.importer import albums_in_dir
|
||||
|
|
@ -355,9 +355,9 @@ class NonAutotaggedImportTest(_common.TestCase, ImportHelper):
|
|||
|
||||
|
||||
def create_archive(session):
|
||||
(handle, path) = mkstemp(dir=session.temp_dir)
|
||||
(handle, path) = mkstemp(dir=py3_path(session.temp_dir))
|
||||
os.close(handle)
|
||||
archive = ZipFile(path, mode='w')
|
||||
archive = ZipFile(py3_path(path), mode='w')
|
||||
archive.write(os.path.join(_common.RSRC, b'full.mp3'),
|
||||
'full.mp3')
|
||||
archive.close()
|
||||
|
|
@ -414,7 +414,7 @@ class ImportTarTest(ImportZipTest):
|
|||
def create_archive(self):
|
||||
(handle, path) = mkstemp(dir=self.temp_dir)
|
||||
os.close(handle)
|
||||
archive = TarFile(path, mode='w')
|
||||
archive = TarFile(py3_path(path), mode='w')
|
||||
archive.add(os.path.join(_common.RSRC, b'full.mp3'),
|
||||
'full.mp3')
|
||||
archive.close()
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class KeyFinderTest(unittest.TestCase, TestHelper):
|
|||
item.load()
|
||||
self.assertEqual(item['initial_key'], 'C#m')
|
||||
self.command_output.assert_called_with(
|
||||
['KeyFinder', '-f', util.syspath(item.path)])
|
||||
[b'KeyFinder', b'-f', util.syspath(item.path)])
|
||||
|
||||
def test_add_key_on_import(self):
|
||||
self.command_output.return_value = 'dbm'
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class TypesPluginTest(unittest.TestCase, TestHelper):
|
|||
|
||||
# Match in range
|
||||
out = self.list(u'myint:1..3')
|
||||
self.assertIn(b'aaa', out)
|
||||
self.assertIn('aaa', out)
|
||||
|
||||
def test_album_integer_modify_and_query(self):
|
||||
self.config['types'] = {'myint': u'int'}
|
||||
|
|
@ -64,7 +64,7 @@ class TypesPluginTest(unittest.TestCase, TestHelper):
|
|||
|
||||
# Match in range
|
||||
out = self.list_album(u'myint:1..3')
|
||||
self.assertIn(b'aaa', out)
|
||||
self.assertIn('aaa', out)
|
||||
|
||||
def test_float_modify_and_query(self):
|
||||
self.config['types'] = {'myfloat': u'float'}
|
||||
|
|
@ -76,7 +76,7 @@ class TypesPluginTest(unittest.TestCase, TestHelper):
|
|||
|
||||
# Match in range
|
||||
out = self.list(u'myfloat:-10..0')
|
||||
self.assertIn(b'aaa', out)
|
||||
self.assertIn('aaa', out)
|
||||
|
||||
def test_bool_modify_and_query(self):
|
||||
self.config['types'] = {'mybool': u'bool'}
|
||||
|
|
|
|||
|
|
@ -774,7 +774,7 @@ class ConfigTest(unittest.TestCase, TestHelper, _common.Assertions):
|
|||
])
|
||||
|
||||
def test_cli_config_option(self):
|
||||
config_path = os.path.join(self.temp_dir, 'config.yaml')
|
||||
config_path = os.path.join(self.temp_dir, b'config.yaml')
|
||||
with open(config_path, 'w') as file:
|
||||
file.write('anoption: value')
|
||||
|
||||
|
|
@ -785,7 +785,7 @@ class ConfigTest(unittest.TestCase, TestHelper, _common.Assertions):
|
|||
with open(self.user_config_path, 'w') as file:
|
||||
file.write('anoption: value')
|
||||
|
||||
cli_config_path = os.path.join(self.temp_dir, 'config.yaml')
|
||||
cli_config_path = os.path.join(self.temp_dir, b'config.yaml')
|
||||
with open(cli_config_path, 'w') as file:
|
||||
file.write('anoption: cli overwrite')
|
||||
|
||||
|
|
@ -798,7 +798,7 @@ class ConfigTest(unittest.TestCase, TestHelper, _common.Assertions):
|
|||
with open(env_config_path, 'w') as file:
|
||||
file.write('anoption: value')
|
||||
|
||||
cli_config_path = os.path.join(self.temp_dir, 'config.yaml')
|
||||
cli_config_path = os.path.join(self.temp_dir, b'config.yaml')
|
||||
with open(cli_config_path, 'w') as file:
|
||||
file.write('anoption: cli overwrite')
|
||||
|
||||
|
|
@ -807,8 +807,8 @@ class ConfigTest(unittest.TestCase, TestHelper, _common.Assertions):
|
|||
|
||||
# @unittest.skip('Difficult to implement with optparse')
|
||||
# def test_multiple_cli_config_files(self):
|
||||
# cli_config_path_1 = os.path.join(self.temp_dir, 'config.yaml')
|
||||
# cli_config_path_2 = os.path.join(self.temp_dir, 'config_2.yaml')
|
||||
# cli_config_path_1 = os.path.join(self.temp_dir, b'config.yaml')
|
||||
# cli_config_path_2 = os.path.join(self.temp_dir, b'config_2.yaml')
|
||||
#
|
||||
# with open(cli_config_path_1, 'w') as file:
|
||||
# file.write('first: value')
|
||||
|
|
@ -823,9 +823,9 @@ class ConfigTest(unittest.TestCase, TestHelper, _common.Assertions):
|
|||
#
|
||||
# @unittest.skip('Difficult to implement with optparse')
|
||||
# def test_multiple_cli_config_overwrite(self):
|
||||
# cli_config_path = os.path.join(self.temp_dir, 'config.yaml')
|
||||
# cli_config_path = os.path.join(self.temp_dir, b'config.yaml')
|
||||
# cli_overwrite_config_path = os.path.join(self.temp_dir,
|
||||
# 'overwrite_config.yaml')
|
||||
# b'overwrite_config.yaml')
|
||||
#
|
||||
# with open(cli_config_path, 'w') as file:
|
||||
# file.write('anoption: value')
|
||||
|
|
@ -838,7 +838,7 @@ class ConfigTest(unittest.TestCase, TestHelper, _common.Assertions):
|
|||
# self.assertEqual(config['anoption'].get(), 'cli overwrite')
|
||||
|
||||
def test_cli_config_paths_resolve_relative_to_user_dir(self):
|
||||
cli_config_path = os.path.join(self.temp_dir, 'config.yaml')
|
||||
cli_config_path = os.path.join(self.temp_dir, b'config.yaml')
|
||||
with open(cli_config_path, 'w') as file:
|
||||
file.write('library: beets.db\n')
|
||||
file.write('statefile: state')
|
||||
|
|
@ -856,7 +856,7 @@ class ConfigTest(unittest.TestCase, TestHelper, _common.Assertions):
|
|||
def test_cli_config_paths_resolve_relative_to_beetsdir(self):
|
||||
os.environ['BEETSDIR'] = util.py3_path(self.beetsdir)
|
||||
|
||||
cli_config_path = os.path.join(self.temp_dir, 'config.yaml')
|
||||
cli_config_path = os.path.join(self.temp_dir, b'config.yaml')
|
||||
with open(cli_config_path, 'w') as file:
|
||||
file.write('library: beets.db\n')
|
||||
file.write('statefile: state')
|
||||
|
|
@ -878,7 +878,7 @@ class ConfigTest(unittest.TestCase, TestHelper, _common.Assertions):
|
|||
os.path.join(os.getcwd(), 'foo.db'))
|
||||
|
||||
def test_cli_config_file_loads_plugin_commands(self):
|
||||
cli_config_path = os.path.join(self.temp_dir, 'config.yaml')
|
||||
cli_config_path = os.path.join(self.temp_dir, b'config.yaml')
|
||||
with open(cli_config_path, 'w') as file:
|
||||
file.write('pluginpath: %s\n' % _common.PLUGINPATH)
|
||||
file.write('plugins: test')
|
||||
|
|
|
|||
Loading…
Reference in a new issue