python3Packages.beets-filetote: 1.3.6 -> 1.3.7

Diff: https://github.com/gtronset/beets-filetote/compare/v1.3.6...v1.3.7

Changelog: https://github.com/gtronset/beets-filetote/blob/v1.3.7/CHANGELOG.md
This commit is contained in:
Doron Behar
2026-08-04 23:41:28 +03:00
parent d977555e47
commit e16530252f
2 changed files with 2 additions and 317 deletions

View File

@@ -1,288 +0,0 @@
From 834f253240fa334f8a73b717c86231a9684ddd07 Mon Sep 17 00:00:00 2001
From: Andrew Rogl <arogl1@gmail.com>
Date: Fri, 17 Jul 2026 18:43:23 +1000
Subject: [PATCH 1/3] Fix for issue #350
---
beetsplug/path_utils.py | 7 ++++++-
tests/unit/test_path_utils.py | 31 +++++++++++++++++++++++++++++++
2 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/beetsplug/path_utils.py b/beetsplug/path_utils.py
index 6fadc55f..bb16dd7e 100644
--- a/beetsplug/path_utils.py
+++ b/beetsplug/path_utils.py
@@ -51,7 +51,12 @@ def discover_artifacts(
"""Walks a directory and returns a list of all non-beets-handled files."""
artifacts: list[Path] = []
- for root, _dirs, files in util.sorted_walk(source_path, ignore=ignore):
+ source_path_for_walk = util.bytestring_path(source_path)
+ ignore_for_walk = [util.bytestring_path(pattern) for pattern in ignore]
+
+ for root, _dirs, files in util.sorted_walk(
+ source_path_for_walk, ignore=ignore_for_walk
+ ):
for filename in files:
full_path_bytes = root + PATH_SEP_BYTES + filename
diff --git a/tests/unit/test_path_utils.py b/tests/unit/test_path_utils.py
index 579794f9..46940044 100644
--- a/tests/unit/test_path_utils.py
+++ b/tests/unit/test_path_utils.py
@@ -39,6 +39,37 @@ def test_is_beets_file_type(self, extension: str, expected: bool) -> None:
types = {"mp3": "MPEG", "flac": "FLAC"}
assert path_utils.is_beets_file_type(extension, types) is expected
+ def test_discover_artifacts_converts_pathlib_source_to_beets_path(
+ self, monkeypatch: pytest.MonkeyPatch
+ ) -> None:
+ """Test that artifact discovery converts pathlib input before calling
+ beets' walk helper, which expects bytes/str paths.
+ """
+ seen_paths: list[object] = []
+
+ def mock_sorted_walk(
+ path: object,
+ ignore: list[object] | None = None,
+ *_args: object,
+ **_kwargs: object,
+ ) -> Iterator[tuple[bytes, list[bytes], list[bytes]]]:
+ seen_paths.append((path, ignore))
+ assert not isinstance(path, Path)
+ assert ignore is not None
+ assert all(not isinstance(item, Path) for item in ignore)
+ yield (b"/music/album", [], [b"cover.jpg"])
+
+ monkeypatch.setattr(path_utils.util, "sorted_walk", mock_sorted_walk)
+
+ path_utils.discover_artifacts(
+ Path("/music/album"),
+ ignore=[],
+ beets_file_types={},
+ )
+
+ assert seen_paths
+ assert not isinstance(seen_paths[0][0], Path)
+
def test_discover_artifacts(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""Test artifact discovery and ignoring of beets-handled files. Mock
`sorted_walk` to control the filesystem structure.
From 3ec73e639b8cd03fcaf55d194e09dfcd30d77de3 Mon Sep 17 00:00:00 2001
From: Andrew Rogl <arogl1@gmail.com>
Date: Fri, 17 Jul 2026 20:52:18 +1000
Subject: [PATCH 2/3] More updates to work with latest beets
---
beetsplug/filetote.py | 25 +++++++++++++++++++++----
beetsplug/filetote_dataclasses.py | 3 ++-
2 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/beetsplug/filetote.py b/beetsplug/filetote.py
index d70fe7c7..5c6dc607 100644
--- a/beetsplug/filetote.py
+++ b/beetsplug/filetote.py
@@ -485,6 +485,7 @@ def _get_artifact_destination(
mapping: FiletoteMappingModel,
paired: bool = False,
pattern_category: str | None = None,
+ beets_item: Item | None = None,
) -> Path:
"""Returns a destination path an artifact/file should be moved to. The
artifact filename is unique to ensure files aren't overwritten. This also
@@ -513,7 +514,14 @@ def _get_artifact_destination(
)
# Get template functions and evaluate against mapping
- template_functions = DefaultTemplateFunctions().functions()
+ # DefaultTemplateFunctions requires both item and lib
+ if beets_item is None:
+ raise ValueError(
+ "beets_item is required to initialize DefaultTemplateFunctions"
+ )
+ template_functions = DefaultTemplateFunctions(
+ beets_item, self.filetote_config.session.beets_lib
+ ).functions()
artifact_path = Path(
selected_path_template.substitute(mapping_formatted, template_functions)
+ artifact_ext
@@ -614,6 +622,7 @@ def _collect_paired_artifacts(
mapping=self._generate_mapping(beets_item, item_destination_path),
source_path=source_path,
item_dest=item_destination_path,
+ beets_item=beets_item,
)
)
@@ -637,6 +646,7 @@ def _update_multimove_artifacts(
mapping=self._generate_mapping(beets_item, destination),
source_path=artifact_collection.source_path,
item_dest=destination,
+ beets_item=beets_item,
)
break
@@ -794,6 +804,7 @@ def _queue_artifacts(
mapping=self._generate_mapping(beets_item, item_destination_path),
source_path=source_path,
item_dest=item_destination_path,
+ beets_item=beets_item,
)
)
@@ -841,6 +852,7 @@ def process_events(self, lib: Library) -> None:
source_path=artifact_collection.source_path,
source_artifacts=artifact_collection.artifacts,
mapping=artifact_collection.mapping,
+ beets_item=artifact_collection.beets_item,
)
# Handle all shared artifacts for each source directory
@@ -852,10 +864,12 @@ def process_events(self, lib: Library) -> None:
if not shared_artifacts.artifacts:
continue
- # Mapping derives from the first Item that found this source path
- album_level_mapping = self._run_state.process_queue[
+ # Get mapping and item from the first collection
+ album_level_collection = self._run_state.process_queue[
shared_artifacts.mapping_index
- ].mapping
+ ]
+ album_level_mapping = album_level_collection.mapping
+ album_level_item = album_level_collection.beets_item
artifacts_to_process = [
FiletoteArtifact(path=shared_artifact, paired=False)
@@ -866,6 +880,7 @@ def process_events(self, lib: Library) -> None:
source_path=source_path,
source_artifacts=artifacts_to_process,
mapping=album_level_mapping,
+ beets_item=album_level_item,
)
def _should_process_artifact(
@@ -932,6 +947,7 @@ def process_artifacts(
source_path: Path,
source_artifacts: list[FiletoteArtifact],
mapping: FiletoteMappingModel,
+ beets_item: Item | None = None,
) -> None:
"""Processes and prepares extra files and artifacts for subsequent
manipulation.
@@ -974,6 +990,7 @@ def process_artifacts(
mapping,
artifact.paired,
pattern_category,
+ beets_item,
)
if artifact_source == artifact_dest:
diff --git a/beetsplug/filetote_dataclasses.py b/beetsplug/filetote_dataclasses.py
index 80648f95..c7d36b30 100644
--- a/beetsplug/filetote_dataclasses.py
+++ b/beetsplug/filetote_dataclasses.py
@@ -17,7 +17,7 @@
from collections.abc import Callable
from pathlib import Path
- from beets.library import Library
+ from beets.library import Item, Library
from beets.util import MoveOperation
from .mapping_model import FiletoteMappingModel
@@ -46,6 +46,7 @@ class FiletoteArtifactCollection:
mapping: FiletoteMappingModel
source_path: Path
item_dest: Path
+ beets_item: Item | None = None
@dataclass
From c9617c91c56bb5e3640c3a08ac11fdd79b0a0f7c Mon Sep 17 00:00:00 2001
From: Andrew Rogl <arogl1@gmail.com>
Date: Sat, 18 Jul 2026 09:45:51 +1000
Subject: [PATCH 3/3] All local tests passing, but not elegant solution
---
beetsplug/path_utils.py | 2 +-
tests/pytest_beets_plugin/plugin_fixture.py | 22 +++++++++++++++++----
tests/pytest_beets_plugin/utils.py | 7 ++++++-
3 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/beetsplug/path_utils.py b/beetsplug/path_utils.py
index bb16dd7e..88efe35b 100644
--- a/beetsplug/path_utils.py
+++ b/beetsplug/path_utils.py
@@ -155,7 +155,7 @@ def is_multidisc(path_name: Path) -> bool:
"""Checks if a directory name matches the multi-disc pattern by replicating the
beets importer's pattern matching for disc folders.
"""
- path_name_bytes = util.bytestring_path(path_name.name)
+ path_name_bytes = util.displayable_path(path_name.name)
return any(pat.match(path_name_bytes) for pat in MULTIDISC_PATTERNS)
diff --git a/tests/pytest_beets_plugin/plugin_fixture.py b/tests/pytest_beets_plugin/plugin_fixture.py
index 04d541b9..44665f7a 100644
--- a/tests/pytest_beets_plugin/plugin_fixture.py
+++ b/tests/pytest_beets_plugin/plugin_fixture.py
@@ -10,6 +10,7 @@
from beets import config, library, plugins, util
from beets.importer import ImportSession
+from beets.ui.commands.modify import ModifyOperation
from mediafile import MediaFile
from ._item_model import MediaMeta
@@ -450,13 +451,26 @@ def _run_cli_modify( # noqa: PLR0913
album: str | None = None,
) -> None:
"""Run the `modify` CLI command."""
- mods = mods or {}
- dels = dels or {}
+ if mods:
+ wrapped_mods = {
+ field: ModifyOperation(operator=None, value=val)
+ for field, val in mods.items()
+ }
+ else:
+ wrapped_mods = {}
+
+ if dels:
+ wrapped_dels = {
+ field: ModifyOperation(operator=None, value=val)
+ for field, val in dels.items()
+ }
+ else:
+ wrapped_dels = {}
modify_items(
lib=self.lib,
- mods=mods,
- dels=dels,
+ mods=wrapped_mods,
+ dels=wrapped_dels,
query=query,
write=write,
move=move,
diff --git a/tests/pytest_beets_plugin/utils.py b/tests/pytest_beets_plugin/utils.py
index 476ca033..5182d7e4 100644
--- a/tests/pytest_beets_plugin/utils.py
+++ b/tests/pytest_beets_plugin/utils.py
@@ -53,7 +53,12 @@ def list_files(self, startpath: Path) -> None:
log.debug("{} does not exist", startpath)
return
- for root, _dirs, files in util.sorted_walk(startpath):
+ source_path_for_walk = util.bytestring_path(startpath)
+ ignore_for_walk = [util.bytestring_path("")]
+
+ for root, _dirs, files in util.sorted_walk(
+ source_path_for_walk, ignore_for_walk
+ ):
root_path = Path(util.displayable_path(root))
try:

View File

@@ -1,7 +1,6 @@
{
lib,
fetchFromGitHub,
fetchpatch,
buildPythonPackage,
# build-system
@@ -22,42 +21,16 @@
buildPythonPackage (finalAttrs: {
pname = "beets-filetote";
version = "1.3.6";
version = "1.3.7";
pyproject = true;
src = fetchFromGitHub {
owner = "gtronset";
repo = "beets-filetote";
tag = "v${finalAttrs.version}";
hash = "sha256-ZrF9Z3Eaem8ZzNJgQoW45MvsNOCoLsd7l/yLQ2pldR0=";
hash = "sha256-W5ZZ30LzZLXSMxBBIEQB03Fh04ovETfacZE5gA4oqVM=";
};
patches = [
# Fixes a few test failures needed since beets 2.12, see:
# https://github.com/gtronset/beets-filetote/issues/328
# https://github.com/gtronset/beets-filetote/pull/336
(fetchpatch {
url = "https://github.com/gtronset/beets-filetote/commit/2684482ebe0cd486512b07621e3904de7faf7dc8.patch";
# Cause merge conflicts
excludes = [
# The changes here mainly include ci related changes, and hence can be
# disabled.
"pyproject.toml"
"CHANGELOG.md"
];
hash = "sha256-zVVJY4+f8A+GBxiHZL8OzLWUUmX9uY25tUoLCkzEHh8=";
})
# Fixes test errors with beets 2.13. Upstream PR is
# https://github.com/gtronset/beets-filetote/pull/351 . It is not merged and not even
# commented by upstream, so we vendor it instead.
./beets2.13.patch
];
# https://github.com/gtronset/beets-filetote/issues/328
postPatch = ''
substituteInPlace pyproject.toml --replace-fail "uv_build>=0.11.21,<0.12" "uv-build"
'';
build-system = [
uv-build
];