mirror of
https://github.com/beetbox/beets.git
synced 2026-02-26 17:21:24 +01:00
parentwork: simplify work retrieval and tests
This commit is contained in:
parent
36964e433e
commit
741f5c4be1
2 changed files with 83 additions and 132 deletions
|
|
@ -16,56 +16,51 @@
|
|||
and work composition date
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import musicbrainzngs
|
||||
|
||||
from beets import ui
|
||||
from beets import __version__, ui
|
||||
from beets.plugins import BeetsPlugin
|
||||
|
||||
|
||||
def direct_parent_id(mb_workid, work_date=None):
|
||||
"""Given a Musicbrainz work id, find the id one of the works the work is
|
||||
part of and the first composition date it encounters.
|
||||
"""
|
||||
work_info = musicbrainzngs.get_work_by_id(
|
||||
mb_workid, includes=["work-rels", "artist-rels"]
|
||||
)
|
||||
if "artist-relation-list" in work_info["work"] and work_date is None:
|
||||
for artist in work_info["work"]["artist-relation-list"]:
|
||||
if artist["type"] == "composer":
|
||||
if "end" in artist.keys():
|
||||
work_date = artist["end"]
|
||||
|
||||
if "work-relation-list" in work_info["work"]:
|
||||
for direct_parent in work_info["work"]["work-relation-list"]:
|
||||
if (
|
||||
direct_parent["type"] == "parts"
|
||||
and direct_parent.get("direction") == "backward"
|
||||
):
|
||||
direct_id = direct_parent["work"]["id"]
|
||||
return direct_id, work_date
|
||||
return None, work_date
|
||||
musicbrainzngs.set_useragent("beets", __version__, "https://beets.io/")
|
||||
|
||||
|
||||
def work_parent_id(mb_workid):
|
||||
"""Find the parent work id and composition date of a work given its id."""
|
||||
work_date = None
|
||||
while True:
|
||||
new_mb_workid, work_date = direct_parent_id(mb_workid, work_date)
|
||||
if not new_mb_workid:
|
||||
return mb_workid, work_date
|
||||
mb_workid = new_mb_workid
|
||||
return mb_workid, work_date
|
||||
|
||||
|
||||
def find_parentwork_info(mb_workid):
|
||||
def find_parentwork_info(mb_workid: str) -> tuple[dict[str, Any], str | None]:
|
||||
"""Get the MusicBrainz information dict about a parent work, including
|
||||
the artist relations, and the composition date for a work's parent work.
|
||||
"""
|
||||
parent_id, work_date = work_parent_id(mb_workid)
|
||||
work_info = musicbrainzngs.get_work_by_id(
|
||||
parent_id, includes=["artist-rels"]
|
||||
)
|
||||
return work_info, work_date
|
||||
work_date = None
|
||||
|
||||
parent_id: str | None = mb_workid
|
||||
|
||||
while parent_id:
|
||||
current_id = parent_id
|
||||
work_info = musicbrainzngs.get_work_by_id(
|
||||
current_id, includes=["work-rels", "artist-rels"]
|
||||
)["work"]
|
||||
work_date = work_date or next(
|
||||
(
|
||||
end
|
||||
for a in work_info.get("artist-relation-list", [])
|
||||
if a["type"] == "composer" and (end := a.get("end"))
|
||||
),
|
||||
None,
|
||||
)
|
||||
parent_id = next(
|
||||
(
|
||||
w["work"]["id"]
|
||||
for w in work_info.get("work-relation-list", [])
|
||||
if w["type"] == "parts" and w["direction"] == "backward"
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
return musicbrainzngs.get_work_by_id(
|
||||
current_id, includes=["artist-rels"]
|
||||
), work_date
|
||||
|
||||
|
||||
class ParentWorkPlugin(BeetsPlugin):
|
||||
|
|
|
|||
|
|
@ -14,74 +14,13 @@
|
|||
|
||||
"""Tests for the 'parentwork' plugin."""
|
||||
|
||||
from unittest.mock import patch
|
||||
from typing import Any
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from beets.library import Item
|
||||
from beets.test.helper import PluginTestCase
|
||||
from beetsplug import parentwork
|
||||
|
||||
work = {
|
||||
"work": {
|
||||
"id": "1",
|
||||
"title": "work",
|
||||
"work-relation-list": [
|
||||
{"type": "parts", "direction": "backward", "work": {"id": "2"}}
|
||||
],
|
||||
"artist-relation-list": [
|
||||
{
|
||||
"type": "composer",
|
||||
"artist": {
|
||||
"name": "random composer",
|
||||
"sort-name": "composer, random",
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
dp_work = {
|
||||
"work": {
|
||||
"id": "2",
|
||||
"title": "directparentwork",
|
||||
"work-relation-list": [
|
||||
{"type": "parts", "direction": "backward", "work": {"id": "3"}}
|
||||
],
|
||||
"artist-relation-list": [
|
||||
{
|
||||
"type": "composer",
|
||||
"artist": {
|
||||
"name": "random composer",
|
||||
"sort-name": "composer, random",
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
p_work = {
|
||||
"work": {
|
||||
"id": "3",
|
||||
"title": "parentwork",
|
||||
"artist-relation-list": [
|
||||
{
|
||||
"type": "composer",
|
||||
"artist": {
|
||||
"name": "random composer",
|
||||
"sort-name": "composer, random",
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def mock_workid_response(mbid, includes):
|
||||
if mbid == "1":
|
||||
return work
|
||||
elif mbid == "2":
|
||||
return dp_work
|
||||
elif mbid == "3":
|
||||
return p_work
|
||||
|
||||
|
||||
@pytest.mark.integration_test
|
||||
|
|
@ -134,36 +73,57 @@ class ParentWorkIntegrationTest(PluginTestCase):
|
|||
item.load()
|
||||
assert item["mb_parentworkid"] == "XXX"
|
||||
|
||||
# test different cases, still with Matthew Passion Ouverture or Mozart
|
||||
# requiem
|
||||
|
||||
def test_direct_parent_work_real(self):
|
||||
mb_workid = "2e4a3668-458d-3b2a-8be2-0b08e0d8243a"
|
||||
assert (
|
||||
"f04b42df-7251-4d86-a5ee-67cfa49580d1"
|
||||
== parentwork.direct_parent_id(mb_workid)[0]
|
||||
)
|
||||
assert (
|
||||
"45afb3b2-18ac-4187-bc72-beb1b1c194ba"
|
||||
== parentwork.work_parent_id(mb_workid)[0]
|
||||
)
|
||||
def mock_workid_response(mbid, includes):
|
||||
works: list[dict[str, Any]] = [
|
||||
{
|
||||
"id": "1",
|
||||
"title": "work",
|
||||
"work-relation-list": [
|
||||
{
|
||||
"type": "parts",
|
||||
"direction": "backward",
|
||||
"work": {"id": "2"},
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"title": "directparentwork",
|
||||
"work-relation-list": [
|
||||
{
|
||||
"type": "parts",
|
||||
"direction": "backward",
|
||||
"work": {"id": "3"},
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"title": "parentwork",
|
||||
},
|
||||
]
|
||||
|
||||
return {
|
||||
"work": {
|
||||
**next(w for w in works if mbid == w["id"]),
|
||||
"artist-relation-list": [
|
||||
{
|
||||
"type": "composer",
|
||||
"artist": {
|
||||
"name": "random composer",
|
||||
"sort-name": "composer, random",
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@patch("musicbrainzngs.get_work_by_id", Mock(side_effect=mock_workid_response))
|
||||
class ParentWorkTest(PluginTestCase):
|
||||
plugin = "parentwork"
|
||||
|
||||
def setUp(self):
|
||||
"""Set up configuration"""
|
||||
super().setUp()
|
||||
self.patcher = patch(
|
||||
"musicbrainzngs.get_work_by_id", side_effect=mock_workid_response
|
||||
)
|
||||
self.patcher.start()
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
self.patcher.stop()
|
||||
|
||||
def test_normal_case(self):
|
||||
item = Item(path="/file", mb_workid="1", parentwork_workid_current="1")
|
||||
item.add(self.lib)
|
||||
|
|
@ -204,7 +164,3 @@ class ParentWorkTest(PluginTestCase):
|
|||
|
||||
item.load()
|
||||
assert item["mb_parentworkid"] == "XXX"
|
||||
|
||||
def test_direct_parent_work(self):
|
||||
assert "2" == parentwork.direct_parent_id("1")[0]
|
||||
assert "3" == parentwork.work_parent_id("1")[0]
|
||||
|
|
|
|||
Loading…
Reference in a new issue