mirror of
https://github.com/fergalmoran/picard.git
synced 2026-05-27 13:26:13 +00:00
Merge branch 'master' into single-instance-gsoc
This commit is contained in:
2
.github/workflows/package.yml
vendored
2
.github/workflows/package.yml
vendored
@@ -79,6 +79,7 @@ jobs:
|
||||
pip3 install -r requirements-build.txt
|
||||
pip3 install -r requirements-macos-${MACOSX_DEPLOYMENT_TARGET}.txt
|
||||
- name: Run tests
|
||||
timeout-minutes: 30
|
||||
run: |
|
||||
python3 setup.py test
|
||||
- name: Prepare code signing certificate
|
||||
@@ -153,6 +154,7 @@ jobs:
|
||||
pip install -r requirements-build.txt
|
||||
pip install -r requirements-win.txt
|
||||
- name: Run tests
|
||||
timeout-minutes: 30
|
||||
run: python setup.py test
|
||||
- name: Prepare code signing certificate
|
||||
if: matrix.type != 'store-app'
|
||||
|
||||
2
.github/workflows/pypi-release.yml
vendored
2
.github/workflows/pypi-release.yml
vendored
@@ -20,6 +20,7 @@ jobs:
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade -r requirements.txt
|
||||
- name: Run tests
|
||||
timeout-minutes: 30
|
||||
run: |
|
||||
python setup.py test
|
||||
- name: Build Python source distribution
|
||||
@@ -97,6 +98,7 @@ jobs:
|
||||
python -m pip install --upgrade pip wheel
|
||||
pip install --upgrade -r requirements.txt
|
||||
- name: Run tests
|
||||
timeout-minutes: 30
|
||||
run: |
|
||||
python setup.py test
|
||||
- name: Build Python binary distribution
|
||||
|
||||
1
.github/workflows/run-tests.yml
vendored
1
.github/workflows/run-tests.yml
vendored
@@ -120,6 +120,7 @@ jobs:
|
||||
run: picard --long-version --no-crash-dialog
|
||||
- name: Verify sdist package
|
||||
if: runner.os != 'Windows'
|
||||
timeout-minutes: 30
|
||||
run: |
|
||||
pip install pytest
|
||||
scripts/package/run-sdist-test.sh
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# Copyright (C) 2017-2018 Sambhav Kothari
|
||||
# Copyright (C) 2018 Vishal Choudhary
|
||||
# Copyright (C) 2018-2021 Laurent Monin
|
||||
# Copyright (C) 2018-2021 Philipp Wolfer
|
||||
# Copyright (C) 2018-2022 Philipp Wolfer
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -36,8 +36,12 @@ from picard import log
|
||||
from picard.acoustid.json_helpers import parse_recording
|
||||
from picard.config import get_config
|
||||
from picard.const import FPCALC_NAMES
|
||||
from picard.const.sys import IS_WIN
|
||||
from picard.file import File
|
||||
from picard.util import find_executable
|
||||
from picard.util import (
|
||||
find_executable,
|
||||
win_prefix_longpath,
|
||||
)
|
||||
|
||||
|
||||
def get_score(node):
|
||||
@@ -248,7 +252,10 @@ class AcoustIDClient(QtCore.QObject):
|
||||
process.setProperty('picard_finished', False)
|
||||
process.finished.connect(partial(self._on_fpcalc_finished, task))
|
||||
process.error.connect(partial(self._on_fpcalc_error, task))
|
||||
process.start(self._fpcalc, ["-json", "-length", "120", task.file.filename])
|
||||
file_path = task.file.filename
|
||||
if IS_WIN:
|
||||
file_path = win_prefix_longpath(file_path)
|
||||
process.start(self._fpcalc, ["-json", "-length", "120", file_path])
|
||||
log.debug("Starting fingerprint calculator %r %r", self._fpcalc, task.file.filename)
|
||||
|
||||
def analyze(self, file, next_func):
|
||||
|
||||
@@ -75,7 +75,6 @@ _MEDIUM_TO_METADATA = {
|
||||
'format': 'media',
|
||||
'position': 'discnumber',
|
||||
'title': 'discsubtitle',
|
||||
'track-count': 'totaltracks',
|
||||
}
|
||||
|
||||
_RECORDING_TO_METADATA = {
|
||||
@@ -478,6 +477,11 @@ def medium_to_metadata(node, m):
|
||||
for key, value in _node_skip_empty_iter(node):
|
||||
if key in _MEDIUM_TO_METADATA:
|
||||
m[_MEDIUM_TO_METADATA[key]] = value
|
||||
totaltracks = node.get('track-count', 0)
|
||||
if node.get('pregap'):
|
||||
totaltracks += 1
|
||||
if totaltracks:
|
||||
m['totaltracks'] = totaltracks
|
||||
|
||||
|
||||
def artist_to_metadata(node, m):
|
||||
|
||||
@@ -188,7 +188,8 @@ class Tagger(QtWidgets.QApplication):
|
||||
_debug = False
|
||||
_no_restore = False
|
||||
|
||||
def __init__(self, picard_args, unparsed_args, localedir, autoupdate, pipe_handler=None):
|
||||
|
||||
def __init__(self, picard_args, localedir, autoupdate, pipe_handler=None):
|
||||
|
||||
super().__init__(sys.argv)
|
||||
self.__class__.__instance = self
|
||||
@@ -1066,8 +1067,8 @@ def process_picard_args():
|
||||
parser.add_argument("-V", "--long-version", action='store_true',
|
||||
help="display long version information and exit")
|
||||
parser.add_argument('FILE', nargs='*')
|
||||
picard_args, unparsed_args = parser.parse_known_args()
|
||||
return picard_args, unparsed_args
|
||||
|
||||
return parser.parse_known_args()[0]
|
||||
|
||||
|
||||
class OverrideStyle(QtWidgets.QProxyStyle):
|
||||
@@ -1106,7 +1107,7 @@ def main(localedir=None, autoupdate=True):
|
||||
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
picard_args, unparsed_args = process_picard_args()
|
||||
picard_args = process_picard_args()
|
||||
if picard_args.version:
|
||||
return version()
|
||||
if picard_args.long_version:
|
||||
@@ -1142,7 +1143,7 @@ def main(localedir=None, autoupdate=True):
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
tagger = Tagger(picard_args, unparsed_args, localedir, autoupdate, pipe_handler=pipe_handler)
|
||||
tagger = Tagger(picard_args, localedir, autoupdate, pipe_handler=pipe_handler)
|
||||
|
||||
# Initialize Qt default translations
|
||||
translator = QtCore.QTranslator()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Picard, the next-generation MusicBrainz tagger
|
||||
#
|
||||
# Copyright (C) 2021 Bob Swift
|
||||
# Copyright (C) 2021-2022 Bob Swift
|
||||
# Copyright (C) 2021 Laurent Monin
|
||||
# Copyright (C) 2021-2022 Philipp Wolfer
|
||||
#
|
||||
@@ -50,6 +50,7 @@ from picard.const import (
|
||||
PICARD_URLS,
|
||||
)
|
||||
from picard.file import File
|
||||
from picard.metadata import Metadata
|
||||
from picard.script import (
|
||||
ScriptError,
|
||||
ScriptParser,
|
||||
@@ -154,14 +155,18 @@ class ScriptEditorExamples():
|
||||
Returns:
|
||||
tuple: Example before and after names for the specified file
|
||||
"""
|
||||
# Operate on a copy of the file object metadata to avoid multiple changes to file metadata. See PICARD-2508.
|
||||
c_metadata = Metadata()
|
||||
c_metadata.copy(file.metadata)
|
||||
try:
|
||||
if self.settings["enable_tagger_scripts"]:
|
||||
# Only apply scripts if the original file metadata has not been changed.
|
||||
if self.settings["enable_tagger_scripts"] and not c_metadata.diff(file.orig_metadata):
|
||||
for s_pos, s_name, s_enabled, s_text in self.settings["list_of_scripts"]:
|
||||
if s_enabled and s_text:
|
||||
parser = ScriptParser()
|
||||
parser.eval(s_text, file.metadata)
|
||||
parser.eval(s_text, c_metadata)
|
||||
filename_before = file.filename
|
||||
filename_after = file.make_filename(filename_before, file.metadata, self.settings, self.script_text)
|
||||
filename_after = file.make_filename(filename_before, c_metadata, self.settings, self.script_text)
|
||||
if not self.settings["move_files"]:
|
||||
return os.path.basename(filename_before), os.path.basename(filename_after)
|
||||
return filename_before, filename_after
|
||||
|
||||
@@ -228,8 +228,13 @@ def normpath(path):
|
||||
# If the path is longer than 259 characters on Windows, prepend the \\?\
|
||||
# prefix. This enables access to long paths using the Windows API. See
|
||||
# https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
|
||||
if (IS_WIN and len(path) > WIN_MAX_FILEPATH_LEN and not system_supports_long_paths()
|
||||
and not path.startswith(WIN_LONGPATH_PREFIX)):
|
||||
if IS_WIN and not system_supports_long_paths():
|
||||
path = win_prefix_longpath(path)
|
||||
return path
|
||||
|
||||
|
||||
def win_prefix_longpath(path):
|
||||
if len(path) > WIN_MAX_FILEPATH_LEN and not path.startswith(WIN_LONGPATH_PREFIX):
|
||||
path = WIN_LONGPATH_PREFIX + path
|
||||
return path
|
||||
|
||||
|
||||
614
po/picard.pot
614
po/picard.pot
File diff suppressed because it is too large
Load Diff
83
test/data/ws_data/media_pregap.json
Normal file
83
test/data/ws_data/media_pregap.json
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"track-count": 8,
|
||||
"discs": [
|
||||
{
|
||||
"sectors": 134481,
|
||||
"offset-count": 8,
|
||||
"id": "7v3LmtkMIT49mHs7LobaAwBNsck-",
|
||||
"offsets": [
|
||||
6824,
|
||||
18966,
|
||||
37134,
|
||||
52930,
|
||||
69024,
|
||||
80329,
|
||||
98814,
|
||||
117353
|
||||
]
|
||||
}
|
||||
],
|
||||
"format-id": "8a08dc62-1aa2-34de-a904-fa467c53052c",
|
||||
"position": 1,
|
||||
"track-offset": 0,
|
||||
"format": "Enhanced CD",
|
||||
"title": "",
|
||||
"pregap": {
|
||||
"number": "0",
|
||||
"position": 0,
|
||||
"artist-credit": [
|
||||
{
|
||||
"name": "Lemon Demon",
|
||||
"joinphrase": "",
|
||||
"artist": {
|
||||
"type": "Person",
|
||||
"sort-name": "Lemon Demon",
|
||||
"disambiguation": "",
|
||||
"id": "6b014cfd-4927-4187-a741-715998e6d785",
|
||||
"type-id": "b6e035f4-3ce9-331c-97df-83397230b0df",
|
||||
"aliases": [
|
||||
{
|
||||
"name": "Deporitaz",
|
||||
"primary": null,
|
||||
"type-id": null,
|
||||
"locale": null,
|
||||
"begin": null,
|
||||
"ended": false,
|
||||
"end": null,
|
||||
"sort-name": "Deporitaz",
|
||||
"type": null
|
||||
}
|
||||
],
|
||||
"name": "Lemon Demon"
|
||||
}
|
||||
}
|
||||
],
|
||||
"recording": {
|
||||
"video": false,
|
||||
"id": "a6f68447-7810-4d1e-a5a2-d10ff0943aee",
|
||||
"first-release-date": "2019",
|
||||
"aliases": [],
|
||||
"title": "Sexy DVD",
|
||||
"length": 88000,
|
||||
"disambiguation": "",
|
||||
"artist-credit": [
|
||||
{
|
||||
"artist": {
|
||||
"id": "6b014cfd-4927-4187-a741-715998e6d785",
|
||||
"type": "Person",
|
||||
"sort-name": "Lemon Demon",
|
||||
"type-id": "b6e035f4-3ce9-331c-97df-83397230b0df",
|
||||
"disambiguation": "",
|
||||
"name": "Lemon Demon"
|
||||
},
|
||||
"joinphrase": "",
|
||||
"name": "Lemon Demon"
|
||||
}
|
||||
]
|
||||
},
|
||||
"length": 88000,
|
||||
"title": "Sexy DVD",
|
||||
"id": "74fa2a03-325a-4a6b-bcfd-12f95ba30d16"
|
||||
},
|
||||
"tracks": []
|
||||
}
|
||||
@@ -402,6 +402,18 @@ class MediaTest(MBJSONTest):
|
||||
self.assertEqual(m['totaltracks'], '10')
|
||||
|
||||
|
||||
class MediaPregapTest(MBJSONTest):
|
||||
|
||||
filename = 'media_pregap.json'
|
||||
|
||||
def test_track(self):
|
||||
m = Metadata()
|
||||
medium_to_metadata(self.json_doc, m)
|
||||
self.assertEqual(m['discnumber'], '1')
|
||||
self.assertEqual(m['media'], 'Enhanced CD')
|
||||
self.assertEqual(m['totaltracks'], '9')
|
||||
|
||||
|
||||
class NullMediaTest(MBJSONTest):
|
||||
|
||||
filename = 'media_null.json'
|
||||
|
||||
@@ -71,6 +71,7 @@ from picard.util import (
|
||||
tracknum_from_filename,
|
||||
uniqify,
|
||||
wildcards_to_regex_pattern,
|
||||
win_prefix_longpath,
|
||||
)
|
||||
|
||||
|
||||
@@ -738,6 +739,17 @@ class NormpathTest(PicardTestCase):
|
||||
self.assertEqual('\\\\?\\' + path, normpath(path))
|
||||
|
||||
|
||||
class WinPrefixLongpathTest(PicardTestCase):
|
||||
|
||||
def test_win_prefix_longpath_is_long(self):
|
||||
path = 'C:\\foo\\' + (253 * 'a')
|
||||
self.assertEqual('\\\\?\\' + path, win_prefix_longpath(path))
|
||||
|
||||
def test_win_prefix_longpath_is_short(self):
|
||||
path = 'C:\\foo\\' + (252 * 'a')
|
||||
self.assertEqual(path, win_prefix_longpath(path))
|
||||
|
||||
|
||||
class SystemSupportsLongPathsTest(PicardTestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
Reference in New Issue
Block a user