Merge branch 'master' into overhacked/fix_log_line_numbers

This commit is contained in:
Šarūnas Nejus
2026-07-14 14:45:48 +01:00
committed by GitHub

View File

@@ -1,55 +1,61 @@
import os
from pathlib import Path
from shlex import quote
import beets
from beets.test import _common
from beets.test.helper import PluginTestCase
class PlaylistTestCase(PluginTestCase):
plugin = "playlist"
preload_plugin = False
c_track_path = Path("a") / "b" / "c.mp3"
f_track_path = Path("d") / "e" / "f.mp3"
i_track_path = Path("g") / "h" / "i.mp3"
w_track_path = Path("u") / "v" / "w.mp3"
z_track_path = Path("x") / "y" / "z.mp3"
nonexisting_track_path = Path("nonexisting.mp3")
def setUp(self):
super().setUp()
self.music_dir = os.path.expanduser(os.path.join("~", "Music"))
self.music_dir = (Path("~") / "Music").expanduser()
i1 = _common.item()
i1.path = beets.util.normpath(
os.path.join(self.music_dir, "a", "b", "c.mp3")
)
i1.title = "some item"
i1.album = "some album"
self.lib.add(i1)
self.lib.add_album([i1])
i2 = _common.item()
i2.path = beets.util.normpath(
os.path.join(self.music_dir, "d", "e", "f.mp3")
)
i2.title = "another item"
i2.album = "another album"
self.lib.add(i2)
self.lib.add_album([i2])
i3 = _common.item()
i3.path = beets.util.normpath(
os.path.join(self.music_dir, "x", "y", "z.mp3")
)
i3.title = "yet another item"
i3.album = "yet another album"
self.lib.add(i3)
self.lib.add_album([i3])
for p, title, album in [
(self.c_track_path, "some item", "some album"),
(self.f_track_path, "another item", "another album"),
(self.z_track_path, "yet another item", "yet another album"),
]:
self.add_album(path=self.music_dir / p, title=title, album=album)
self.playlist_dir = self.temp_dir_path / "playlists"
self.playlist_dir.mkdir(parents=True, exist_ok=True)
self.config["directory"] = self.music_dir
self.config["directory"] = str(self.music_dir)
self.config["playlist"]["playlist_dir"] = str(self.playlist_dir)
self.absolute_playlist_path = self.playlist_dir / "absolute.m3u"
self.relative_playlist_path = self.playlist_dir / "relative.m3u"
self.write_absolute_playlist()
self.write_relative_playlist()
self.setup_test()
self.load_plugins()
def write_absolute_playlist(self):
lines = [
self.music_dir / self.c_track_path,
self.music_dir / self.f_track_path,
self.music_dir / self.nonexisting_track_path,
]
self.absolute_playlist_path.write_text("\n".join(map(str, lines)))
def write_relative_playlist(self):
lines = [
self.c_track_path,
self.f_track_path,
self.nonexisting_track_path,
]
self.relative_playlist_path.write_text("\n".join(map(str, lines)))
def setup_test(self):
raise NotImplementedError
@@ -61,7 +67,7 @@ class PlaylistQueryTest:
assert {i.title for i in results} == {"some item", "another item"}
def test_path_query_with_absolute_paths_in_playlist(self):
q = f"playlist:{quote(os.path.join(self.playlist_dir, 'absolute.m3u'))}"
q = f"playlist:{quote(str(self.absolute_playlist_path))}"
results = self.lib.items(q)
assert {i.title for i in results} == {"some item", "another item"}
@@ -71,7 +77,7 @@ class PlaylistQueryTest:
assert {i.title for i in results} == {"some item", "another item"}
def test_path_query_with_relative_paths_in_playlist(self):
q = f"playlist:{quote(os.path.join(self.playlist_dir, 'relative.m3u'))}"
q = f"playlist:{quote(str(self.relative_playlist_path))}"
results = self.lib.items(q)
assert {i.title for i in results} == {"some item", "another item"}
@@ -81,113 +87,43 @@ class PlaylistQueryTest:
assert set(results) == set()
def test_path_query_with_nonexisting_playlist(self):
q = f"playlist:{os.path.join(self.playlist_dir, 'nonexisting.m3u')!r}"
q = f"playlist:{quote(str(self.nonexisting_track_path))}"
results = self.lib.items(q)
assert set(results) == set()
class PlaylistTestRelativeToLib(PlaylistQueryTest, PlaylistTestCase):
def setup_test(self):
with open(os.path.join(self.playlist_dir, "absolute.m3u"), "w") as f:
f.writelines(
[
os.path.join(self.music_dir, "a", "b", "c.mp3") + "\n",
os.path.join(self.music_dir, "d", "e", "f.mp3") + "\n",
os.path.join(self.music_dir, "nonexisting.mp3") + "\n",
]
)
with open(os.path.join(self.playlist_dir, "relative.m3u"), "w") as f:
f.writelines(
[
os.path.join("a", "b", "c.mp3") + "\n",
os.path.join("d", "e", "f.mp3") + "\n",
"nonexisting.mp3\n",
]
)
self.config["playlist"]["relative_to"] = "library"
class PlaylistTestRelativeToDir(PlaylistQueryTest, PlaylistTestCase):
def setup_test(self):
with open(os.path.join(self.playlist_dir, "absolute.m3u"), "w") as f:
f.writelines(
[
os.path.join(self.music_dir, "a", "b", "c.mp3") + "\n",
os.path.join(self.music_dir, "d", "e", "f.mp3") + "\n",
os.path.join(self.music_dir, "nonexisting.mp3") + "\n",
]
)
with open(os.path.join(self.playlist_dir, "relative.m3u"), "w") as f:
f.writelines(
[
os.path.join("a", "b", "c.mp3") + "\n",
os.path.join("d", "e", "f.mp3") + "\n",
"nonexisting.mp3\n",
]
)
self.config["playlist"]["relative_to"] = self.music_dir
self.config["playlist"]["relative_to"] = str(self.music_dir)
class PlaylistTestRelativeToPls(PlaylistQueryTest, PlaylistTestCase):
def write_relative_playlist(self):
lines = [
os.path.relpath(
self.music_dir / self.c_track_path, self.playlist_dir
),
os.path.relpath(
self.music_dir / self.f_track_path, self.playlist_dir
),
os.path.relpath(
self.music_dir / self.nonexisting_track_path, self.playlist_dir
),
]
self.relative_playlist_path.write_text("\n".join(lines) + "\n")
def setup_test(self):
with open(os.path.join(self.playlist_dir, "absolute.m3u"), "w") as f:
f.writelines(
[
os.path.join(self.music_dir, "a", "b", "c.mp3") + "\n",
os.path.join(self.music_dir, "d", "e", "f.mp3") + "\n",
os.path.join(self.music_dir, "nonexisting.mp3") + "\n",
]
)
with open(os.path.join(self.playlist_dir, "relative.m3u"), "w") as f:
f.writelines(
[
os.path.relpath(
os.path.join(self.music_dir, "a", "b", "c.mp3"),
start=self.playlist_dir,
)
+ "\n",
os.path.relpath(
os.path.join(self.music_dir, "d", "e", "f.mp3"),
start=self.playlist_dir,
)
+ "\n",
os.path.relpath(
os.path.join(self.music_dir, "nonexisting.mp3"),
start=self.playlist_dir,
)
+ "\n",
]
)
self.config["playlist"]["relative_to"] = "playlist"
self.config["playlist"]["playlist_dir"] = str(self.playlist_dir)
class PlaylistUpdateTest:
def setup_test(self):
with open(os.path.join(self.playlist_dir, "absolute.m3u"), "w") as f:
f.writelines(
[
os.path.join(self.music_dir, "a", "b", "c.mp3") + "\n",
os.path.join(self.music_dir, "d", "e", "f.mp3") + "\n",
os.path.join(self.music_dir, "nonexisting.mp3") + "\n",
]
)
with open(os.path.join(self.playlist_dir, "relative.m3u"), "w") as f:
f.writelines(
[
os.path.join("a", "b", "c.mp3") + "\n",
os.path.join("d", "e", "f.mp3") + "\n",
"nonexisting.mp3\n",
]
)
self.config["playlist"]["auto"] = True
self.config["playlist"]["relative_to"] = "library"
@@ -195,91 +131,75 @@ class PlaylistUpdateTest:
class PlaylistTestItemMoved(PlaylistUpdateTest, PlaylistTestCase):
def test_item_moved(self):
# Emit item_moved event for an item that is in a playlist
results = self.lib.items(
f"path:{quote(os.path.join(self.music_dir, 'd', 'e', 'f.mp3'))}"
)
q = f"path:{quote(str(self.music_dir / self.f_track_path))}"
results = self.lib.items(q)
item = results[0]
beets.plugins.send(
"item_moved",
item=item,
source=item.path,
destination=beets.util.bytestring_path(
os.path.join(self.music_dir, "g", "h", "i.mp3")
),
destination=os.fsencode(self.music_dir / self.i_track_path),
)
# Emit item_moved event for an item that is not in a playlist
results = self.lib.items(
f"path:{quote(os.path.join(self.music_dir, 'x', 'y', 'z.mp3'))}"
f"path:{quote(str(self.music_dir / self.z_track_path))}"
)
item = results[0]
beets.plugins.send(
"item_moved",
item=item,
source=item.path,
destination=beets.util.bytestring_path(
os.path.join(self.music_dir, "u", "v", "w.mp3")
),
destination=os.fsencode(self.music_dir / self.w_track_path),
)
# Emit cli_exit event
beets.plugins.send("cli_exit", lib=self.lib)
# Check playlist with absolute paths
playlist_path = os.path.join(self.playlist_dir, "absolute.m3u")
with open(playlist_path) as f:
lines = [line.strip() for line in f.readlines()]
assert lines == [
os.path.join(self.music_dir, "a", "b", "c.mp3"),
os.path.join(self.music_dir, "g", "h", "i.mp3"),
os.path.join(self.music_dir, "nonexisting.mp3"),
expected_paths = [
self.c_track_path,
self.i_track_path,
self.nonexisting_track_path,
]
# Check playlist with absolute paths
lines = list(
map(Path, self.absolute_playlist_path.read_text().splitlines())
)
assert lines == [self.music_dir / p for p in expected_paths]
# Check playlist with relative paths
playlist_path = os.path.join(self.playlist_dir, "relative.m3u")
with open(playlist_path) as f:
lines = [line.strip() for line in f.readlines()]
assert lines == [
os.path.join("a", "b", "c.mp3"),
os.path.join("g", "h", "i.mp3"),
"nonexisting.mp3",
]
lines = list(
map(Path, self.relative_playlist_path.read_text().splitlines())
)
assert lines == expected_paths
class PlaylistTestItemRemoved(PlaylistUpdateTest, PlaylistTestCase):
def test_item_removed(self):
# Emit item_removed event for an item that is in a playlist
results = self.lib.items(
f"path:{quote(os.path.join(self.music_dir, 'd', 'e', 'f.mp3'))}"
)
q = f"path:{quote(str(self.music_dir / self.f_track_path))}"
results = self.lib.items(q)
item = results[0]
beets.plugins.send("item_removed", item=item)
# Emit item_removed event for an item that is not in a playlist
results = self.lib.items(
f"path:{quote(os.path.join(self.music_dir, 'x', 'y', 'z.mp3'))}"
)
q = f"path:{quote(str(self.music_dir / self.z_track_path))}"
results = self.lib.items(q)
item = results[0]
beets.plugins.send("item_removed", item=item)
# Emit cli_exit event
beets.plugins.send("cli_exit", lib=self.lib)
expected_paths = [self.c_track_path, self.nonexisting_track_path]
# Check playlist with absolute paths
playlist_path = os.path.join(self.playlist_dir, "absolute.m3u")
with open(playlist_path) as f:
lines = [line.strip() for line in f.readlines()]
assert lines == [
os.path.join(self.music_dir, "a", "b", "c.mp3"),
os.path.join(self.music_dir, "nonexisting.mp3"),
]
lines = list(
map(Path, self.absolute_playlist_path.read_text().splitlines())
)
assert lines == [self.music_dir / p for p in expected_paths]
# Check playlist with relative paths
playlist_path = os.path.join(self.playlist_dir, "relative.m3u")
with open(playlist_path) as f:
lines = [line.strip() for line in f.readlines()]
assert lines == [os.path.join("a", "b", "c.mp3"), "nonexisting.mp3"]
lines = list(
map(Path, self.relative_playlist_path.read_text().splitlines())
)
assert lines == expected_paths