mirror of
https://github.com/fergalmoran/picard.git
synced 2026-01-04 15:43:58 +00:00
Merge branch 'metabrainz:master' into single-instance-gsoc
This commit is contained in:
6
.github/workflows/codacy-analysis.yml
vendored
6
.github/workflows/codacy-analysis.yml
vendored
@@ -24,11 +24,11 @@ jobs:
|
||||
steps:
|
||||
# Checkout the repository to the GitHub Actions runner
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
|
||||
- name: Run Codacy Analysis CLI
|
||||
uses: codacy/codacy-analysis-cli-action@1.1.0
|
||||
uses: codacy/codacy-analysis-cli-action@v4
|
||||
with:
|
||||
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
|
||||
# You can also omit the token and run the tools that support default configurations
|
||||
@@ -44,6 +44,6 @@ jobs:
|
||||
|
||||
# Upload the SARIF file generated in the previous step
|
||||
- name: Upload SARIF results file
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -21,11 +21,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: python
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -50,4 +50,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
28
.github/workflows/package.yml
vendored
28
.github/workflows/package.yml
vendored
@@ -36,12 +36,12 @@ jobs:
|
||||
PYTHON_SHA256SUM: 7888174c6fe441b00448c7ab3e9cbf0e6c3c7dea0750577baf09e1383fc44656
|
||||
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macos-deployment-version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Fetch entire history, needed for setting the build number
|
||||
- run: git fetch --depth=1 origin +refs/tags/release-*:refs/tags/release-*
|
||||
- name: Cache libdiscid
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/libdiscid
|
||||
key: ${{ runner.os }}-libdiscid-${{ env.DISCID_VERSION }}-${{ env.MACOSX_DEPLOYMENT_TARGET }}
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
CODESIGN_MACOS_P12_PASSWORD: ${{ secrets.CODESIGN_MACOS_P12_PASSWORD }}
|
||||
- name: Archive production artifacts
|
||||
uses: actions/upload-artifact@v1
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: macos-app-${{ matrix.macos-deployment-version }}
|
||||
path: artifacts/
|
||||
@@ -120,12 +120,12 @@ jobs:
|
||||
- portable
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Fetch entire history, needed for setting the build number
|
||||
- run: git fetch --depth=1 origin +refs/tags/release-*:refs/tags/release-*
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v1
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Setup Windows build environment
|
||||
@@ -216,7 +216,7 @@ jobs:
|
||||
if: env.CODESIGN == '1'
|
||||
run: Remove-Item .\codesign.pfx
|
||||
- name: Archive production artifacts
|
||||
uses: actions/upload-artifact@v1
|
||||
uses: actions/upload-artifact@v3
|
||||
if: matrix.type != 'signed-app' || env.CODESIGN == '1'
|
||||
with:
|
||||
name: windows-${{ matrix.type }}
|
||||
@@ -229,31 +229,31 @@ jobs:
|
||||
- package-macos
|
||||
- package-windows
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: 3.9
|
||||
- uses: actions/download-artifact@v1
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: macos-app-10.12
|
||||
path: artifacts/
|
||||
- uses: actions/download-artifact@v1
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: macos-app-10.14
|
||||
path: artifacts/
|
||||
- uses: actions/download-artifact@v1
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-signed-app
|
||||
path: artifacts/
|
||||
- uses: actions/download-artifact@v1
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-store-app
|
||||
path: artifacts/
|
||||
- uses: actions/download-artifact@v1
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-installer
|
||||
path: artifacts/
|
||||
- uses: actions/download-artifact@v1
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: windows-portable
|
||||
path: artifacts/
|
||||
|
||||
12
.github/workflows/pypi-release.yml
vendored
12
.github/workflows/pypi-release.yml
vendored
@@ -11,8 +11,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Install dependencies
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
run: |
|
||||
python setup.py clean sdist
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: picard-sdist
|
||||
path: dist/*
|
||||
@@ -79,9 +79,9 @@ jobs:
|
||||
- os: macos-10.15
|
||||
python-version: '3.10'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install gettext and openssl (macOS)
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
run: |
|
||||
python setup.py clean bdist_wheel
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: picard-bdist-${{ runner.os }}
|
||||
path: dist/*.whl
|
||||
|
||||
17
.github/workflows/run-tests.yml
vendored
17
.github/workflows/run-tests.yml
vendored
@@ -12,9 +12,6 @@ jobs:
|
||||
exclude:
|
||||
- os: macos-latest
|
||||
python-version: '3.6'
|
||||
# pyobjc is not yet compatible with Python 3.10
|
||||
- os: macos-latest
|
||||
python-version: '3.10'
|
||||
include:
|
||||
- os: macos-10.15
|
||||
python-version: '3.6'
|
||||
@@ -22,9 +19,9 @@ jobs:
|
||||
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
@@ -38,6 +35,7 @@ jobs:
|
||||
isort --check-only --diff --recursive picard test
|
||||
- name: Test with pytest
|
||||
if: always()
|
||||
timeout-minutes: 30
|
||||
run: |
|
||||
pip install pytest pytest-randomly pytest-cov
|
||||
pytest --verbose --cov=picard --cov-report xml:coverage.xml test
|
||||
@@ -71,9 +69,9 @@ jobs:
|
||||
]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
@@ -84,6 +82,7 @@ jobs:
|
||||
env:
|
||||
DEPENDENCIES: ${{ matrix.dependencies }}
|
||||
- name: Test with pytest
|
||||
timeout-minutes: 30
|
||||
run: |
|
||||
pip install pytest pytest-randomly pytest-cov
|
||||
pytest --verbose test
|
||||
@@ -96,9 +95,9 @@ jobs:
|
||||
python-version: ['3.8']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install gettext (Linux)
|
||||
|
||||
10
NEWS.md
10
NEWS.md
@@ -1,3 +1,13 @@
|
||||
# Version 2.8.1 - 2022-06-07
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- [PICARD-2489](https://tickets.metabrainz.org/browse/PICARD-2489) - Preferred Releases UI not loading in Options menu on FreeBSD
|
||||
- [PICARD-2491](https://tickets.metabrainz.org/browse/PICARD-2491) - Crash upon loading album information for releases with MBID redirects
|
||||
- [PICARD-2493](https://tickets.metabrainz.org/browse/PICARD-2493) - If locales for picard-countries or picard-attributes are missing UI translation is completely skipped
|
||||
- [PICARD-2494](https://tickets.metabrainz.org/browse/PICARD-2494) - Remove empty info dialog for "[standalone-recordings]" special album entry
|
||||
|
||||
|
||||
# Version 2.8 - 2022-05-24
|
||||
|
||||
## Tasks
|
||||
|
||||
@@ -42,7 +42,7 @@ PICARD_APP_NAME = "Picard"
|
||||
PICARD_DISPLAY_NAME = "MusicBrainz Picard"
|
||||
PICARD_APP_ID = "org.musicbrainz.Picard"
|
||||
PICARD_DESKTOP_NAME = PICARD_APP_ID + ".desktop"
|
||||
PICARD_VERSION = Version(2, 8, 0, 'final', 0)
|
||||
PICARD_VERSION = Version(2, 8, 1, 'final', 0)
|
||||
|
||||
|
||||
# optional build version
|
||||
|
||||
@@ -35,10 +35,8 @@ import re
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from picard import log
|
||||
from picard.const import (
|
||||
PICARD_URLS,
|
||||
QUERY_LIMIT,
|
||||
)
|
||||
from picard.config import get_config
|
||||
from picard.const import PICARD_URLS
|
||||
from picard.disc import Disc
|
||||
from picard.util import (
|
||||
build_qurl,
|
||||
@@ -172,8 +170,9 @@ class FileLookup(object):
|
||||
def search_entity(self, type_, query, adv=False, mbid_matched_callback=None, force_browser=False):
|
||||
if not force_browser and self.mbid_lookup(query, type_, mbid_matched_callback=mbid_matched_callback):
|
||||
return True
|
||||
config = get_config()
|
||||
params = {
|
||||
'limit': QUERY_LIMIT,
|
||||
'limit': config.setting['query_limit'],
|
||||
'type': type_,
|
||||
'query': query,
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ import re
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from picard.config import get_config
|
||||
from picard.const import QUERY_LIMIT
|
||||
from picard.file import File
|
||||
from picard.metadata import (
|
||||
Metadata,
|
||||
@@ -282,11 +281,12 @@ class Cluster(FileList):
|
||||
N_("Looking up the metadata for cluster %(album)s..."),
|
||||
{'album': self.metadata['album']}
|
||||
)
|
||||
config = get_config()
|
||||
self.lookup_task = self.tagger.mb_api.find_releases(self._lookup_finished,
|
||||
artist=self.metadata['albumartist'],
|
||||
release=self.metadata['album'],
|
||||
tracks=str(len(self.files)),
|
||||
limit=QUERY_LIMIT)
|
||||
limit=config.setting['query_limit'])
|
||||
|
||||
def clear_lookup_task(self):
|
||||
if self.lookup_task:
|
||||
|
||||
@@ -142,7 +142,7 @@ PLUGINS_API = {
|
||||
}
|
||||
|
||||
# Default query limit
|
||||
QUERY_LIMIT = 25
|
||||
QUERY_LIMIT = 50
|
||||
|
||||
# Maximum number of covers to draw in a stack in CoverArtThumbnail
|
||||
MAX_COVERS_TO_STACK = 4
|
||||
|
||||
@@ -43,6 +43,7 @@ from PyQt5.QtCore import (
|
||||
from picard import log
|
||||
from picard.config import get_config
|
||||
from picard.const import DEFAULT_COVER_IMAGE_FILENAME
|
||||
from picard.const.sys import IS_WIN
|
||||
from picard.coverart.utils import (
|
||||
Id3ImageType,
|
||||
image_type_as_id3_num,
|
||||
@@ -55,6 +56,7 @@ from picard.util import (
|
||||
imageinfo,
|
||||
is_absolute_path,
|
||||
periodictouch,
|
||||
sanitize_filename,
|
||||
)
|
||||
from picard.util.scripttofilename import script_to_filename
|
||||
|
||||
@@ -331,7 +333,8 @@ class CoverArtImage:
|
||||
return
|
||||
config = get_config()
|
||||
if config.setting["image_type_as_filename"] and not self.is_front_image():
|
||||
filename = self.maintype
|
||||
win_compat = IS_WIN or config.setting["windows_compatibility"]
|
||||
filename = sanitize_filename(self.maintype, win_compat=win_compat)
|
||||
log.debug("Make cover filename from types: %r -> %r",
|
||||
self.types, filename)
|
||||
else:
|
||||
|
||||
@@ -25,7 +25,10 @@
|
||||
|
||||
import re
|
||||
|
||||
from picard.disc.utils import calculate_mb_toc_numbers
|
||||
from picard.disc.utils import (
|
||||
TocEntry,
|
||||
calculate_mb_toc_numbers,
|
||||
)
|
||||
|
||||
|
||||
RE_TOC_TABLE_HEADER = re.compile(r""" \s*
|
||||
@@ -50,9 +53,6 @@ RE_TOC_TABLE_LINE = re.compile(r"""
|
||||
\s*$""", re.VERBOSE)
|
||||
|
||||
|
||||
PREGAP_LENGTH = 150
|
||||
|
||||
|
||||
def filter_toc_entries(lines):
|
||||
"""
|
||||
Take iterator of lines, return iterator of toc entries
|
||||
@@ -71,7 +71,7 @@ def filter_toc_entries(lines):
|
||||
m = RE_TOC_TABLE_LINE.match(line)
|
||||
if not m:
|
||||
break
|
||||
yield m.groupdict()
|
||||
yield TocEntry(int(m['num']), int(m['start_sector']), int(m['end_sector']))
|
||||
|
||||
|
||||
ENCODING_BOMS = {
|
||||
@@ -92,7 +92,7 @@ def _detect_encoding(path):
|
||||
|
||||
|
||||
def toc_from_file(path):
|
||||
"""Reads EAC / XLD log files, generates musicbrainz disc TOC listing for use as discid.
|
||||
"""Reads EAC / XLD log files, generates MusicBrainz disc TOC listing for use as discid.
|
||||
|
||||
Warning: may work wrong for discs having data tracks. May generate wrong
|
||||
results on other non-standard cases."""
|
||||
|
||||
@@ -23,32 +23,48 @@
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
PREGAP_LENGTH = 150
|
||||
DATA_TRACK_GAP = 11400
|
||||
|
||||
|
||||
TocEntry = namedtuple('TocEntry', 'number start_sector end_sector')
|
||||
|
||||
|
||||
class NotSupportedTOCError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def calculate_mb_toc_numbers(toc_entries):
|
||||
def calculate_mb_toc_numbers(toc):
|
||||
"""
|
||||
Take iterator of toc entries, return a tuple of numbers for musicbrainz disc id
|
||||
Take iterator of TOC entries, return a tuple of numbers for MusicBrainz disc id
|
||||
|
||||
Each toc entry is a dict with the following keys:
|
||||
- num: track number
|
||||
Each entry is a TocEntry namedtuple with the following fields:
|
||||
- number: track number
|
||||
- start_sector: start sector of the track
|
||||
- end_sector: end sector of the track
|
||||
"""
|
||||
eac = tuple(toc_entries)
|
||||
num_tracks = len(eac)
|
||||
toc = tuple(toc)
|
||||
toc = _remove_data_track(toc)
|
||||
num_tracks = len(toc)
|
||||
if not num_tracks:
|
||||
raise NotSupportedTOCError("Empty track list: %s", eac)
|
||||
raise NotSupportedTOCError("Empty track list")
|
||||
|
||||
expected_tracknums = tuple(range(1, num_tracks+1))
|
||||
tracknums = tuple(int(e['num']) for e in eac)
|
||||
tracknums = tuple(e.number for e in toc)
|
||||
if expected_tracknums != tracknums:
|
||||
raise NotSupportedTOCError("Non-standard track number sequence: %s", tracknums)
|
||||
raise NotSupportedTOCError(f"Non-standard track number sequence: {tracknums}")
|
||||
|
||||
leadout_offset = int(eac[-1]['end_sector']) + PREGAP_LENGTH + 1
|
||||
offsets = tuple((int(x['start_sector']) + PREGAP_LENGTH) for x in eac)
|
||||
leadout_offset = toc[-1].end_sector + PREGAP_LENGTH + 1
|
||||
offsets = tuple(e.start_sector + PREGAP_LENGTH for e in toc)
|
||||
return (1, num_tracks, leadout_offset) + offsets
|
||||
|
||||
|
||||
def _remove_data_track(toc):
|
||||
if len(toc) > 1:
|
||||
last_track_gap = toc[-1].start_sector - toc[-2].end_sector
|
||||
if last_track_gap == DATA_TRACK_GAP + 1:
|
||||
toc = toc[:-1]
|
||||
return toc
|
||||
|
||||
@@ -18,10 +18,12 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
import yaml
|
||||
|
||||
from picard.disc.utils import calculate_mb_toc_numbers
|
||||
from picard.disc.utils import (
|
||||
TocEntry,
|
||||
calculate_mb_toc_numbers,
|
||||
)
|
||||
|
||||
|
||||
def toc_from_file(path):
|
||||
@@ -32,11 +34,7 @@ def toc_from_file(path):
|
||||
with open(path, encoding='utf-8') as f:
|
||||
data = yaml.safe_load(f)
|
||||
toc_entries = (
|
||||
{
|
||||
'num': num,
|
||||
'start_sector': t['Start sector'],
|
||||
'end_sector': t['End sector'],
|
||||
}
|
||||
TocEntry(num, t['Start sector'], t['End sector'])
|
||||
for num, t in data['TOC'].items()
|
||||
)
|
||||
return calculate_mb_toc_numbers(toc_entries)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Copyright (C) 2004 Robert Kaye
|
||||
# Copyright (C) 2006-2009, 2011-2013, 2017 Lukáš Lalinský
|
||||
# Copyright (C) 2007-2011, 2015, 2018-2021 Philipp Wolfer
|
||||
# Copyright (C) 2007-2011, 2015, 2018-2022 Philipp Wolfer
|
||||
# Copyright (C) 2008 Gary van der Merwe
|
||||
# Copyright (C) 2008-2009 Nikolai Prokoschenko
|
||||
# Copyright (C) 2009 Carlin Mangar
|
||||
@@ -62,7 +62,6 @@ from picard import (
|
||||
log,
|
||||
)
|
||||
from picard.config import get_config
|
||||
from picard.const import QUERY_LIMIT
|
||||
from picard.const.sys import (
|
||||
IS_MACOS,
|
||||
IS_WIN,
|
||||
@@ -876,6 +875,7 @@ class File(QtCore.QObject, Item):
|
||||
self.clear_lookup_task()
|
||||
metadata = self.metadata
|
||||
self.set_pending()
|
||||
config = get_config()
|
||||
self.lookup_task = self.tagger.mb_api.find_tracks(
|
||||
partial(self._lookup_finished, File.LOOKUP_METADATA),
|
||||
track=metadata['title'],
|
||||
@@ -885,7 +885,7 @@ class File(QtCore.QObject, Item):
|
||||
tracks=metadata['totaltracks'],
|
||||
qdur=str(metadata.length // 2000),
|
||||
isrc=metadata['isrc'],
|
||||
limit=QUERY_LIMIT)
|
||||
limit=config.setting['query_limit'])
|
||||
|
||||
def clear_lookup_task(self):
|
||||
if self.lookup_task:
|
||||
|
||||
@@ -158,6 +158,11 @@ class UserProfileGroups():
|
||||
N_("Ignore track duration difference under x seconds"),
|
||||
["ignore_track_duration_difference_under", "label_track_duration_diff"]
|
||||
),
|
||||
SettingDesc(
|
||||
"query_limit",
|
||||
N_("Maximum number of entities to return per MusicBrainz query"),
|
||||
["query_limit", "label_query_limit"]
|
||||
),
|
||||
SettingDesc("completeness_ignore_videos", N_("Completeness check ignore: Video tracks"), ["completeness_ignore_videos"]),
|
||||
SettingDesc("completeness_ignore_pregap", N_("Completeness check ignore: Pregap tracks"), ["completeness_ignore_pregap"]),
|
||||
SettingDesc("completeness_ignore_data", N_("Completeness check ignore: Data tracks"), ["completeness_ignore_data"]),
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Copyright (C) 2006-2007 Lukáš Lalinský
|
||||
# Copyright (C) 2013-2015, 2018, 2020-2021 Laurent Monin
|
||||
# Copyright (C) 2014, 2019-2021 Philipp Wolfer
|
||||
# Copyright (C) 2014, 2019-2022 Philipp Wolfer
|
||||
# Copyright (C) 2016-2017 Sambhav Kothari
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
@@ -29,6 +29,7 @@ from picard.config import (
|
||||
TextOption,
|
||||
get_config,
|
||||
)
|
||||
from picard.const import QUERY_LIMIT
|
||||
|
||||
from picard.ui.options import (
|
||||
OptionsPage,
|
||||
@@ -51,6 +52,7 @@ class AdvancedOptionsPage(OptionsPage):
|
||||
BoolOption("setting", "ignore_hidden_files", False),
|
||||
BoolOption("setting", "recursively_add_files", True),
|
||||
IntOption("setting", "ignore_track_duration_difference_under", 2),
|
||||
IntOption("setting", "query_limit", QUERY_LIMIT),
|
||||
BoolOption("setting", "completeness_ignore_videos", False),
|
||||
BoolOption("setting", "completeness_ignore_pregap", False),
|
||||
BoolOption("setting", "completeness_ignore_data", False),
|
||||
@@ -70,6 +72,7 @@ class AdvancedOptionsPage(OptionsPage):
|
||||
self.ui.ignore_hidden_files.setChecked(config.setting["ignore_hidden_files"])
|
||||
self.ui.recursively_add_files.setChecked(config.setting["recursively_add_files"])
|
||||
self.ui.ignore_track_duration_difference_under.setValue(config.setting["ignore_track_duration_difference_under"])
|
||||
self.ui.query_limit.setCurrentText(str(config.setting["query_limit"]))
|
||||
self.ui.completeness_ignore_videos.setChecked(config.setting["completeness_ignore_videos"])
|
||||
self.ui.completeness_ignore_pregap.setChecked(config.setting["completeness_ignore_pregap"])
|
||||
self.ui.completeness_ignore_data.setChecked(config.setting["completeness_ignore_data"])
|
||||
@@ -83,6 +86,7 @@ class AdvancedOptionsPage(OptionsPage):
|
||||
config.setting["ignore_hidden_files"] = self.ui.ignore_hidden_files.isChecked()
|
||||
config.setting["recursively_add_files"] = self.ui.recursively_add_files.isChecked()
|
||||
config.setting["ignore_track_duration_difference_under"] = self.ui.ignore_track_duration_difference_under.value()
|
||||
config.setting["query_limit"] = self.ui.query_limit.currentText()
|
||||
config.setting["completeness_ignore_videos"] = self.ui.completeness_ignore_videos.isChecked()
|
||||
config.setting["completeness_ignore_pregap"] = self.ui.completeness_ignore_pregap.isChecked()
|
||||
config.setting["completeness_ignore_data"] = self.ui.completeness_ignore_data.isChecked()
|
||||
|
||||
@@ -31,8 +31,10 @@
|
||||
|
||||
|
||||
from functools import partial
|
||||
from html import escape
|
||||
from operator import attrgetter
|
||||
import os.path
|
||||
import re
|
||||
|
||||
from PyQt5 import (
|
||||
QtCore,
|
||||
@@ -599,16 +601,33 @@ class PluginsOptionsPage(OptionsPage):
|
||||
if plugin.description:
|
||||
text.append(plugin.description + "<hr width='90%'/>")
|
||||
infos = [
|
||||
(_("Name"), plugin.name),
|
||||
(_("Authors"), plugin.author),
|
||||
(_("Name"), escape(plugin.name)),
|
||||
(_("Authors"), self.link_authors(plugin.author)),
|
||||
(_("License"), plugin.license),
|
||||
(_("Files"), plugin.files_list),
|
||||
(_("Files"), escape(plugin.files_list)),
|
||||
]
|
||||
for label, value in infos:
|
||||
if value:
|
||||
text.append("<b>{0}:</b> {1}".format(label, value))
|
||||
self.ui.details.setText("<p>{0}</p>".format("<br/>\n".join(text)))
|
||||
|
||||
@staticmethod
|
||||
def link_authors(authors):
|
||||
formatted_authors = []
|
||||
re_author = re.compile(r"(?P<author>.*?)\s*<(?P<email>.*?@.*?)>")
|
||||
for author in authors.split(','):
|
||||
author = author.strip()
|
||||
match = re_author.fullmatch(author)
|
||||
if match:
|
||||
author_str = '<a href="mailto:{email}">{author}</a>'.format(
|
||||
email=escape(match['email']),
|
||||
author=escape(match['author']),
|
||||
)
|
||||
formatted_authors.append(author_str)
|
||||
else:
|
||||
formatted_authors.append(escape(author))
|
||||
return ', '.join(formatted_authors)
|
||||
|
||||
def change_details(self):
|
||||
item = self.selected_item()
|
||||
if item:
|
||||
|
||||
@@ -31,11 +31,13 @@ from PyQt5 import (
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
|
||||
from picard import log
|
||||
from picard.config import Option
|
||||
from picard.config import (
|
||||
Option,
|
||||
get_config,
|
||||
)
|
||||
from picard.const import (
|
||||
CAA_HOST,
|
||||
CAA_PORT,
|
||||
QUERY_LIMIT,
|
||||
)
|
||||
from picard.coverart.image import CaaThumbnailCoverArtImage
|
||||
from picard.mbjson import (
|
||||
@@ -168,11 +170,12 @@ class AlbumSearchDialog(SearchDialog):
|
||||
self.retry_params = Retry(self.search, text)
|
||||
self.search_box_text(text)
|
||||
self.show_progress()
|
||||
config = get_config()
|
||||
self.tagger.mb_api.find_releases(self.handle_reply,
|
||||
query=text,
|
||||
search=True,
|
||||
advanced_search=self.use_advanced_search,
|
||||
limit=QUERY_LIMIT)
|
||||
limit=config.setting['query_limit'])
|
||||
|
||||
def show_similar_albums(self, cluster):
|
||||
"""Perform search by using existing metadata information
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Copyright (C) 2016 Rahul Raturi
|
||||
# Copyright (C) 2018, 2020-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
|
||||
@@ -23,8 +23,10 @@
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from picard.config import Option
|
||||
from picard.const import QUERY_LIMIT
|
||||
from picard.config import (
|
||||
Option,
|
||||
get_config,
|
||||
)
|
||||
from picard.mbjson import artist_to_metadata
|
||||
from picard.metadata import Metadata
|
||||
|
||||
@@ -64,11 +66,12 @@ class ArtistSearchDialog(SearchDialog):
|
||||
self.retry_params = Retry(self.search, text)
|
||||
self.search_box_text(text)
|
||||
self.show_progress()
|
||||
config = get_config()
|
||||
self.tagger.mb_api.find_artists(self.handle_reply,
|
||||
query=text,
|
||||
search=True,
|
||||
advanced_search=self.use_advanced_search,
|
||||
limit=QUERY_LIMIT)
|
||||
limit=config.setting['query_limit'])
|
||||
|
||||
def retry(self):
|
||||
self.retry_params.function(self.retry_params.query)
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from picard.config import Option
|
||||
from picard.const import QUERY_LIMIT
|
||||
from picard.config import (
|
||||
Option,
|
||||
get_config,
|
||||
)
|
||||
from picard.file import File
|
||||
from picard.mbjson import (
|
||||
countries_from_node,
|
||||
@@ -76,11 +78,12 @@ class TrackSearchDialog(SearchDialog):
|
||||
self.retry_params = Retry(self.search, text)
|
||||
self.search_box_text(text)
|
||||
self.show_progress()
|
||||
config = get_config()
|
||||
self.tagger.mb_api.find_tracks(self.handle_reply,
|
||||
query=text,
|
||||
search=True,
|
||||
advanced_search=self.use_advanced_search,
|
||||
limit=QUERY_LIMIT)
|
||||
limit=config.setting['query_limit'])
|
||||
|
||||
def show_similar_tracks(self, file_):
|
||||
"""Perform search using existing metadata information
|
||||
|
||||
@@ -10,7 +10,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
class Ui_AdvancedOptionsPage(object):
|
||||
def setupUi(self, AdvancedOptionsPage):
|
||||
AdvancedOptionsPage.setObjectName("AdvancedOptionsPage")
|
||||
AdvancedOptionsPage.resize(570, 435)
|
||||
AdvancedOptionsPage.resize(570, 455)
|
||||
self.vboxlayout = QtWidgets.QVBoxLayout(AdvancedOptionsPage)
|
||||
self.vboxlayout.setObjectName("vboxlayout")
|
||||
self.groupBox = QtWidgets.QGroupBox(AdvancedOptionsPage)
|
||||
@@ -18,9 +18,45 @@ class Ui_AdvancedOptionsPage(object):
|
||||
self.gridlayout = QtWidgets.QGridLayout(self.groupBox)
|
||||
self.gridlayout.setSpacing(2)
|
||||
self.gridlayout.setObjectName("gridlayout")
|
||||
self.recursively_add_files = QtWidgets.QCheckBox(self.groupBox)
|
||||
self.recursively_add_files.setObjectName("recursively_add_files")
|
||||
self.gridlayout.addWidget(self.recursively_add_files, 5, 0, 1, 1)
|
||||
self.label_ignore_regex = QtWidgets.QLabel(self.groupBox)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.label_ignore_regex.sizePolicy().hasHeightForWidth())
|
||||
self.label_ignore_regex.setSizePolicy(sizePolicy)
|
||||
self.label_ignore_regex.setWordWrap(True)
|
||||
self.label_ignore_regex.setObjectName("label_ignore_regex")
|
||||
self.gridlayout.addWidget(self.label_ignore_regex, 1, 0, 1, 1)
|
||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||
self.label_query_limit = QtWidgets.QLabel(self.groupBox)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.label_query_limit.sizePolicy().hasHeightForWidth())
|
||||
self.label_query_limit.setSizePolicy(sizePolicy)
|
||||
self.label_query_limit.setObjectName("label_query_limit")
|
||||
self.horizontalLayout_2.addWidget(self.label_query_limit)
|
||||
self.query_limit = QtWidgets.QComboBox(self.groupBox)
|
||||
self.query_limit.setCurrentText("50")
|
||||
self.query_limit.setObjectName("query_limit")
|
||||
self.query_limit.addItem("")
|
||||
self.query_limit.setItemText(0, "25")
|
||||
self.query_limit.addItem("")
|
||||
self.query_limit.setItemText(1, "50")
|
||||
self.query_limit.addItem("")
|
||||
self.query_limit.setItemText(2, "75")
|
||||
self.query_limit.addItem("")
|
||||
self.query_limit.setItemText(3, "100")
|
||||
self.horizontalLayout_2.addWidget(self.query_limit)
|
||||
self.gridlayout.addLayout(self.horizontalLayout_2, 8, 0, 1, 1)
|
||||
self.regex_error = QtWidgets.QLabel(self.groupBox)
|
||||
self.regex_error.setText("")
|
||||
self.regex_error.setObjectName("regex_error")
|
||||
self.gridlayout.addWidget(self.regex_error, 3, 0, 1, 1)
|
||||
self.ignore_regex = QtWidgets.QLineEdit(self.groupBox)
|
||||
self.ignore_regex.setObjectName("ignore_regex")
|
||||
self.gridlayout.addWidget(self.ignore_regex, 2, 0, 1, 1)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
@@ -48,25 +84,12 @@ class Ui_AdvancedOptionsPage(object):
|
||||
self.ignore_track_duration_difference_under.setObjectName("ignore_track_duration_difference_under")
|
||||
self.horizontalLayout.addWidget(self.ignore_track_duration_difference_under)
|
||||
self.gridlayout.addLayout(self.horizontalLayout, 6, 0, 2, 1)
|
||||
self.label_ignore_regex = QtWidgets.QLabel(self.groupBox)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.label_ignore_regex.sizePolicy().hasHeightForWidth())
|
||||
self.label_ignore_regex.setSizePolicy(sizePolicy)
|
||||
self.label_ignore_regex.setWordWrap(True)
|
||||
self.label_ignore_regex.setObjectName("label_ignore_regex")
|
||||
self.gridlayout.addWidget(self.label_ignore_regex, 1, 0, 1, 1)
|
||||
self.regex_error = QtWidgets.QLabel(self.groupBox)
|
||||
self.regex_error.setText("")
|
||||
self.regex_error.setObjectName("regex_error")
|
||||
self.gridlayout.addWidget(self.regex_error, 3, 0, 1, 1)
|
||||
self.recursively_add_files = QtWidgets.QCheckBox(self.groupBox)
|
||||
self.recursively_add_files.setObjectName("recursively_add_files")
|
||||
self.gridlayout.addWidget(self.recursively_add_files, 5, 0, 1, 1)
|
||||
self.ignore_hidden_files = QtWidgets.QCheckBox(self.groupBox)
|
||||
self.ignore_hidden_files.setObjectName("ignore_hidden_files")
|
||||
self.gridlayout.addWidget(self.ignore_hidden_files, 4, 0, 1, 1)
|
||||
self.ignore_regex = QtWidgets.QLineEdit(self.groupBox)
|
||||
self.ignore_regex.setObjectName("ignore_regex")
|
||||
self.gridlayout.addWidget(self.ignore_regex, 2, 0, 1, 1)
|
||||
self.vboxlayout.addWidget(self.groupBox)
|
||||
self.groupBox_completeness = QtWidgets.QGroupBox(AdvancedOptionsPage)
|
||||
self.groupBox_completeness.setObjectName("groupBox_completeness")
|
||||
@@ -101,11 +124,13 @@ class Ui_AdvancedOptionsPage(object):
|
||||
self.vboxlayout.addWidget(self.groupBox_2)
|
||||
|
||||
self.retranslateUi(AdvancedOptionsPage)
|
||||
self.query_limit.setCurrentIndex(1)
|
||||
QtCore.QMetaObject.connectSlotsByName(AdvancedOptionsPage)
|
||||
AdvancedOptionsPage.setTabOrder(self.ignore_regex, self.ignore_hidden_files)
|
||||
AdvancedOptionsPage.setTabOrder(self.ignore_hidden_files, self.recursively_add_files)
|
||||
AdvancedOptionsPage.setTabOrder(self.recursively_add_files, self.ignore_track_duration_difference_under)
|
||||
AdvancedOptionsPage.setTabOrder(self.ignore_track_duration_difference_under, self.completeness_ignore_videos)
|
||||
AdvancedOptionsPage.setTabOrder(self.ignore_track_duration_difference_under, self.query_limit)
|
||||
AdvancedOptionsPage.setTabOrder(self.query_limit, self.completeness_ignore_videos)
|
||||
AdvancedOptionsPage.setTabOrder(self.completeness_ignore_videos, self.completeness_ignore_pregap)
|
||||
AdvancedOptionsPage.setTabOrder(self.completeness_ignore_pregap, self.completeness_ignore_data)
|
||||
AdvancedOptionsPage.setTabOrder(self.completeness_ignore_data, self.completeness_ignore_silence)
|
||||
@@ -113,9 +138,10 @@ class Ui_AdvancedOptionsPage(object):
|
||||
def retranslateUi(self, AdvancedOptionsPage):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
self.groupBox.setTitle(_("Advanced options"))
|
||||
self.recursively_add_files.setText(_("Include sub-folders when adding files from folder"))
|
||||
self.label_track_duration_diff.setText(_("Ignore track duration difference under this number of seconds"))
|
||||
self.label_ignore_regex.setText(_("Ignore file paths matching the following regular expression:"))
|
||||
self.label_query_limit.setText(_("Maximum number of entities to return per MusicBrainz query"))
|
||||
self.label_track_duration_diff.setText(_("Ignore track duration difference under this number of seconds"))
|
||||
self.recursively_add_files.setText(_("Include sub-folders when adding files from folder"))
|
||||
self.ignore_hidden_files.setText(_("Ignore hidden files"))
|
||||
self.groupBox_completeness.setTitle(_("Ignore the following tracks when determining whether a release is complete"))
|
||||
self.completeness_ignore_videos.setText(_("Video tracks"))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Translators:
|
||||
# Lukáš Lalinský <lalinsky@gmail.com>, 2020
|
||||
# Maurits Meulenbelt <mfmeulenbelt@gmail.com>, 2021
|
||||
# Maurits Meulenbelt, 2021
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
@@ -14,7 +14,7 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-01-14 23:03+0100\n"
|
||||
"PO-Revision-Date: 2018-11-25 07:37+0000\n"
|
||||
"Last-Translator: Maurits Meulenbelt <mfmeulenbelt@gmail.com>, 2021\n"
|
||||
"Last-Translator: Maurits Meulenbelt, 2021\n"
|
||||
"Language-Team: Dutch (https://www.transifex.com/musicbrainz/teams/13846/nl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
# Translators:
|
||||
# reneweesp <bmom43@hotmail.com>, 2017
|
||||
# Maurits Meulenbelt <email address hidden>, 2012
|
||||
# Maurits Meulenbelt <mfmeulenbelt@gmail.com>, 2012
|
||||
# Maurits Meulenbelt <mfmeulenbelt@gmail.com>, 2013-2022
|
||||
# Maurits Meulenbelt, 2012
|
||||
# Maurits Meulenbelt, 2013-2022
|
||||
# Nikolai Prokoschenko <email address hidden>, 2011
|
||||
# Nikolai Prokoschenko <nikolai@prokoschenko.de>, 2011
|
||||
# Niko Strijbol <strijbol.niko@gmail.com>, 2018
|
||||
@@ -12,7 +12,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: MusicBrainz\n"
|
||||
"PO-Revision-Date: 2012-05-24 18:52+0000\n"
|
||||
"Last-Translator: Maurits Meulenbelt <mfmeulenbelt@gmail.com>, 2013-2022\n"
|
||||
"Last-Translator: Maurits Meulenbelt, 2013-2022\n"
|
||||
"Language-Team: Dutch (http://www.transifex.com/musicbrainz/musicbrainz/language/nl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -186,7 +186,7 @@ msgstr "Een ballet is muziek die is gecomponeerd om samen met choreografie te wo
|
||||
#: DB:release_packaging/description:19
|
||||
msgctxt "release_packaging"
|
||||
msgid "A box usually containing multiple discs as part of a boxed set."
|
||||
msgstr ""
|
||||
msgstr "Een doos met meestal meerdere cd’s als onderdeel van een boxset."
|
||||
|
||||
#: DB:work_type/description:3
|
||||
msgctxt "work_type"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#
|
||||
# Translators:
|
||||
# Maurits Meulenbelt <mfmeulenbelt@gmail.com>, 2012
|
||||
# Maurits Meulenbelt <mfmeulenbelt@gmail.com>, 2014-2015
|
||||
# Maurits Meulenbelt, 2012
|
||||
# Maurits Meulenbelt, 2014-2015
|
||||
# Nikolai Prokoschenko <nikolai@prokoschenko.de>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: MusicBrainz\n"
|
||||
"PO-Revision-Date: 2012-05-24 19:20+0000\n"
|
||||
"Last-Translator: Maurits Meulenbelt <mfmeulenbelt@gmail.com>, 2014-2015\n"
|
||||
"Last-Translator: Maurits Meulenbelt, 2014-2015\n"
|
||||
"Language-Team: Dutch (http://www.transifex.com/musicbrainz/musicbrainz/language/nl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
16
po/es.po
16
po/es.po
@@ -84,7 +84,7 @@ msgstr "Archivos desagrupados"
|
||||
msgid "Added %(count)i release to collection \"%(name)s\""
|
||||
msgid_plural "Added %(count)i releases to collection \"%(name)s\""
|
||||
msgstr[0] "Añadida %(count)i publicación a la colección \"%(name)s\""
|
||||
msgstr[1] ""
|
||||
msgstr[1] "Añadidas %(count)i publicaciones a la colección \"%(name)s\""
|
||||
msgstr[2] "Añadidas %(count)i publicaciones a la colección \"%(name)s\""
|
||||
|
||||
#: picard/collection.py:86
|
||||
@@ -92,7 +92,7 @@ msgstr[2] "Añadidas %(count)i publicaciones a la colección \"%(name)s\""
|
||||
msgid "Removed %(count)i release from collection \"%(name)s\""
|
||||
msgid_plural "Removed %(count)i releases from collection \"%(name)s\""
|
||||
msgstr[0] "Eliminada %(count)i publicación de la colección \"%(name)s\""
|
||||
msgstr[1] ""
|
||||
msgstr[1] "Eliminadas %(count)i publicaciones de la colección \"%(name)s\""
|
||||
msgstr[2] "Eliminadas %(count)i publicaciones de la colección \"%(name)s\""
|
||||
|
||||
#: picard/collection.py:97
|
||||
@@ -3675,7 +3675,7 @@ msgstr "Actualizar la lista"
|
||||
msgid "%s (%i release)"
|
||||
msgid_plural "%s (%i releases)"
|
||||
msgstr[0] "%s (%i publicación)"
|
||||
msgstr[1] ""
|
||||
msgstr[1] "%s (%i publicaciones)"
|
||||
msgstr[2] "%s (%i publicaciones)"
|
||||
|
||||
#: picard/ui/colors.py:37
|
||||
@@ -3904,7 +3904,7 @@ msgstr "&Información"
|
||||
msgid "%i file in this track"
|
||||
msgid_plural "%i files in this track"
|
||||
msgstr[0] "%i fichero en esta pista"
|
||||
msgstr[1] ""
|
||||
msgstr[1] "%i ficheros en esta pista"
|
||||
msgstr[2] "%i ficheros en esta pista"
|
||||
|
||||
#: picard/ui/infodialog.py:409
|
||||
@@ -4215,7 +4215,7 @@ msgid ""
|
||||
msgid_plural ""
|
||||
"There are %d unsaved files. Closing Picard will lose all unsaved changes."
|
||||
msgstr[0] "Hay %d archivo no guardado. Si cierra Picard perderá todos los cambios sin guardar."
|
||||
msgstr[1] ""
|
||||
msgstr[1] "Hay %d archivos no guardados. Si cierras Picard perderás todos los cambios sin guardar."
|
||||
msgstr[2] "Hay %d archivos no guardados. Si cierras Picard perderás todos los cambios sin guardar."
|
||||
|
||||
#: picard/ui/mainwindow.py:347
|
||||
@@ -4740,7 +4740,7 @@ msgstr ""
|
||||
msgid "(different across %d item)"
|
||||
msgid_plural "(different across %d items)"
|
||||
msgstr[0] "(varía en %d elemento)"
|
||||
msgstr[1] ""
|
||||
msgstr[1] "(varía en %d elementos)"
|
||||
msgstr[2] "(varía en %d elementos)"
|
||||
|
||||
#: picard/ui/metadatabox.py:120
|
||||
@@ -4748,7 +4748,7 @@ msgstr[2] "(varía en %d elementos)"
|
||||
msgid "(missing from %d item)"
|
||||
msgid_plural "(missing from %d items)"
|
||||
msgstr[0] "(falta en %d elemento)"
|
||||
msgstr[1] ""
|
||||
msgstr[1] "(falta en %d elementos)"
|
||||
msgstr[2] "(falta en %d elementos)"
|
||||
|
||||
#: picard/ui/metadatabox.py:218
|
||||
@@ -4807,7 +4807,7 @@ msgstr "Remover de la lista 'Etiquetas Preservadas'"
|
||||
msgid "Use Original Value"
|
||||
msgid_plural "Use Original Values"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[1] "Usar valor original"
|
||||
msgstr[2] "Usar valor original"
|
||||
|
||||
#: picard/ui/metadatabox.py:439
|
||||
|
||||
6
po/nl.po
6
po/nl.po
@@ -6,8 +6,8 @@
|
||||
# reneweesp <bmom43@hotmail.com>, 2017-2019
|
||||
# Dogmatica <jeroendoggen@hotmail.com>, 2006
|
||||
# Jan van Thiel <zout@gewis.nl>, 2006
|
||||
# Maurits Meulenbelt <mfmeulenbelt@gmail.com>, 2012
|
||||
# Maurits Meulenbelt <mfmeulenbelt@gmail.com>, 2012-2022
|
||||
# Maurits Meulenbelt, 2012
|
||||
# Maurits Meulenbelt, 2012-2022
|
||||
# Philipp Wolfer <ph.wolfer@gmail.com>, 2019-2021
|
||||
msgid ""
|
||||
msgstr ""
|
||||
@@ -15,7 +15,7 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2022-05-01 15:38+0200\n"
|
||||
"PO-Revision-Date: 2012-05-29 16:17+0000\n"
|
||||
"Last-Translator: Maurits Meulenbelt <mfmeulenbelt@gmail.com>, 2012-2022\n"
|
||||
"Last-Translator: Maurits Meulenbelt, 2012-2022\n"
|
||||
"Language-Team: Dutch (http://www.transifex.com/musicbrainz/musicbrainz/language/nl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# Philipp Wolfer <ph.wolfer@gmail.com>, 2019-2022
|
||||
# S B <koalaleaves@protonmail.com>, 2022
|
||||
# Shen-Ta Hsieh(BestSteve) <ibmibmibm.tw@gmail.com>, 2016
|
||||
# Shen-Ta Hsieh(BestSteve) <ibmibmibm.tw@gmail.com>, 2016-2020
|
||||
# Shen-Ta Hsieh(BestSteve) <ibmibmibm.tw@gmail.com>, 2016-2020,2022
|
||||
# riotism, 2017-2018,2020
|
||||
msgid ""
|
||||
msgstr ""
|
||||
@@ -15,7 +15,7 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2022-05-01 15:38+0200\n"
|
||||
"PO-Revision-Date: 2012-05-29 16:17+0000\n"
|
||||
"Last-Translator: Philipp Wolfer <ph.wolfer@gmail.com>, 2019-2022\n"
|
||||
"Last-Translator: Shen-Ta Hsieh(BestSteve) <ibmibmibm.tw@gmail.com>, 2016-2020,2022\n"
|
||||
"Language-Team: Chinese (Taiwan) (http://www.transifex.com/musicbrainz/musicbrainz/language/zh_TW/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -6933,7 +6933,7 @@ msgstr "指揮"
|
||||
|
||||
#: picard/util/tags.py:53
|
||||
msgid "Copyright"
|
||||
msgstr "著作權"
|
||||
msgstr "版權"
|
||||
|
||||
#: picard/util/tags.py:55
|
||||
msgid "Video Director"
|
||||
|
||||
BIN
resources/img-src/picard-logo-haiku.iom
Normal file
BIN
resources/img-src/picard-logo-haiku.iom
Normal file
Binary file not shown.
BIN
test/data/eac-datatrack.log
Normal file
BIN
test/data/eac-datatrack.log
Normal file
Binary file not shown.
@@ -196,6 +196,7 @@ class BrowserLookupTest(PicardTestCase):
|
||||
|
||||
@patch.object(webbrowser2, 'open')
|
||||
def test_search_entity(self, mock_open):
|
||||
self.set_config_values({'query_limit': 25})
|
||||
result = self.lookup.search_entity('foo', 'search:123')
|
||||
self.assertTrue(result)
|
||||
url = mock_open.call_args[0][0]
|
||||
@@ -209,6 +210,7 @@ class BrowserLookupTest(PicardTestCase):
|
||||
|
||||
@patch.object(webbrowser2, 'open')
|
||||
def test_search_entity_advanced(self, mock_open):
|
||||
self.set_config_values({'query_limit': 25})
|
||||
result = self.lookup.search_entity('foo', 'search:123', adv=True)
|
||||
self.assertTrue(result)
|
||||
url = mock_open.call_args[0][0]
|
||||
@@ -232,6 +234,7 @@ class BrowserLookupTest(PicardTestCase):
|
||||
|
||||
@patch.object(webbrowser2, 'open')
|
||||
def test_search_entity_mbid_lookup_force_browser(self, mock_open):
|
||||
self.set_config_values({'query_limit': 25})
|
||||
with patch.object(self.lookup, 'mbid_lookup') as mock_lookup:
|
||||
entity = 'artist'
|
||||
mbid = '4836aa50-a9ae-490a-983b-cfc8efca92de'
|
||||
|
||||
@@ -31,7 +31,10 @@ from picard.disc.eaclog import (
|
||||
filter_toc_entries,
|
||||
toc_from_file,
|
||||
)
|
||||
from picard.disc.utils import NotSupportedTOCError
|
||||
from picard.disc.utils import (
|
||||
NotSupportedTOCError,
|
||||
TocEntry,
|
||||
)
|
||||
|
||||
|
||||
test_log = (
|
||||
@@ -46,25 +49,9 @@ test_log = (
|
||||
)
|
||||
|
||||
test_entries = [
|
||||
{
|
||||
'num': '1',
|
||||
'start_time': '0:00.00',
|
||||
'length_time': '5:32.14',
|
||||
'start_sector': '0',
|
||||
'end_sector': '24913'
|
||||
}, {
|
||||
'num': '2',
|
||||
'start_time': '5:32.14',
|
||||
'length_time': '4:07.22',
|
||||
'start_sector': '24914',
|
||||
'end_sector': '43460'
|
||||
}, {
|
||||
'num': '3',
|
||||
'start_time': '9:39.36',
|
||||
'length_time': '3:50.29',
|
||||
'start_sector': '43461',
|
||||
'end_sector': '60739'
|
||||
}
|
||||
TocEntry(1, 0, 24913),
|
||||
TocEntry(2, 24914, 43460),
|
||||
TocEntry(3, 43461, 60739),
|
||||
]
|
||||
|
||||
|
||||
@@ -93,6 +80,11 @@ class TestTocFromFile(PicardTestCase):
|
||||
def test_toc_from_file_xld(self):
|
||||
self._test_toc_from_file('xld.log')
|
||||
|
||||
def test_toc_from_file_with_datatrack(self):
|
||||
test_log = get_test_data_path('eac-datatrack.log')
|
||||
toc = toc_from_file(test_log)
|
||||
self.assertEqual((1, 8, 178288, 150, 20575, 42320, 62106, 78432, 94973, 109750, 130111), toc)
|
||||
|
||||
def test_toc_from_empty_file(self):
|
||||
test_log = get_test_data_path('eac-empty.log')
|
||||
with self.assertRaises(NotSupportedTOCError):
|
||||
|
||||
@@ -18,35 +18,19 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
from test.picardtestcase import PicardTestCase
|
||||
|
||||
from picard.disc.utils import (
|
||||
NotSupportedTOCError,
|
||||
TocEntry,
|
||||
calculate_mb_toc_numbers,
|
||||
)
|
||||
|
||||
|
||||
test_entries = [
|
||||
{
|
||||
'num': '1',
|
||||
'start_time': '0:00.00',
|
||||
'length_time': '5:32.14',
|
||||
'start_sector': '0',
|
||||
'end_sector': '24913'
|
||||
}, {
|
||||
'num': '2',
|
||||
'start_time': '5:32.14',
|
||||
'length_time': '4:07.22',
|
||||
'start_sector': '24914',
|
||||
'end_sector': '43460'
|
||||
}, {
|
||||
'num': '3',
|
||||
'start_time': '9:39.36',
|
||||
'length_time': '3:50.29',
|
||||
'start_sector': '43461',
|
||||
'end_sector': '60739'
|
||||
}
|
||||
TocEntry(1, 0, 24913),
|
||||
TocEntry(2, 24914, 43460),
|
||||
TocEntry(3, 43461, 60739),
|
||||
]
|
||||
|
||||
|
||||
@@ -56,10 +40,14 @@ class TestCalculateMbTocNumbers(PicardTestCase):
|
||||
self.assertEqual((1, 3, 60890, 150, 25064, 43611), calculate_mb_toc_numbers(test_entries))
|
||||
|
||||
def test_calculate_mb_toc_numbers_invalid_track_numbers(self):
|
||||
entries = [{'num': '1'}, {'num': '3'}, {'num': '4'}]
|
||||
with self.assertRaises(NotSupportedTOCError):
|
||||
entries = [TocEntry(1, 0, 100), TocEntry(3, 101, 200), TocEntry(4, 201, 300)]
|
||||
with self.assertRaisesRegex(NotSupportedTOCError, r"^Non-standard track number sequence: \(1, 3, 4\)$"):
|
||||
calculate_mb_toc_numbers(entries)
|
||||
|
||||
def test_calculate_mb_toc_numbers_empty_entries(self):
|
||||
with self.assertRaises(NotSupportedTOCError):
|
||||
with self.assertRaisesRegex(NotSupportedTOCError, r"^Empty track list$"):
|
||||
calculate_mb_toc_numbers([])
|
||||
|
||||
def test_calculate_mb_toc_numbers_ignore_datatrack(self):
|
||||
entries = [*test_entries, TocEntry(4, 72140, 80000)]
|
||||
self.assertEqual((1, 3, 60890, 150, 25064, 43611), calculate_mb_toc_numbers(entries))
|
||||
|
||||
32
test/test_ui_options_plugins.py
Normal file
32
test/test_ui_options_plugins.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Picard, the next-generation MusicBrainz tagger
|
||||
#
|
||||
# Copyright (C) 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
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
from test.picardtestcase import PicardTestCase
|
||||
|
||||
from picard.ui.options.plugins import PluginsOptionsPage
|
||||
|
||||
|
||||
class PluginsOptionsPageTest(PicardTestCase):
|
||||
|
||||
def test_link_authors(self):
|
||||
self.assertEqual(
|
||||
'<a href="mailto:coyote@acme.com">Wile E. Coyote</a>, Road <Runner>',
|
||||
PluginsOptionsPage.link_authors('Wile E. Coyote <coyote@acme.com>, Road <Runner>'),
|
||||
)
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>570</width>
|
||||
<height>435</height>
|
||||
<height>455</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
@@ -20,13 +20,79 @@
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="recursively_add_files">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_ignore_regex">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Include sub-folders when adding files from folder</string>
|
||||
<string>Ignore file paths matching the following regular expression:</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_query_limit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Maximum number of entities to return per MusicBrainz query</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="query_limit">
|
||||
<property name="currentText">
|
||||
<string notr="true">50</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">25</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">50</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">75</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">100</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="regex_error">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLineEdit" name="ignore_regex"/>
|
||||
</item>
|
||||
<item row="6" column="0" rowspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="sizeConstraint">
|
||||
@@ -78,26 +144,10 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_ignore_regex">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="recursively_add_files">
|
||||
<property name="text">
|
||||
<string>Ignore file paths matching the following regular expression:</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="regex_error">
|
||||
<property name="text">
|
||||
<string/>
|
||||
<string>Include sub-folders when adding files from folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -108,9 +158,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLineEdit" name="ignore_regex"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -188,6 +235,7 @@
|
||||
<tabstop>ignore_hidden_files</tabstop>
|
||||
<tabstop>recursively_add_files</tabstop>
|
||||
<tabstop>ignore_track_duration_difference_under</tabstop>
|
||||
<tabstop>query_limit</tabstop>
|
||||
<tabstop>completeness_ignore_videos</tabstop>
|
||||
<tabstop>completeness_ignore_pregap</tabstop>
|
||||
<tabstop>completeness_ignore_data</tabstop>
|
||||
|
||||
Reference in New Issue
Block a user