Trim wishlist runtime plumbing
- remove the redundant wishlist-service injection from the runtime wrappers - keep the package owning its own singleton service access - simplify the route runtime API and update the wishlist tests to match
This commit is contained in:
parent
7f3272f3ba
commit
0125f478fc
6 changed files with 28 additions and 46 deletions
|
|
@ -11,6 +11,7 @@ from typing import Any, Callable, Dict
|
|||
|
||||
from core.wishlist.payloads import build_failed_track_wishlist_context
|
||||
from core.wishlist.selection import filter_wishlist_tracks_by_category, sanitize_and_dedupe_wishlist_tracks
|
||||
from core.wishlist.service import get_wishlist_service
|
||||
from core.wishlist.state import get_wishlist_cycle, set_wishlist_cycle
|
||||
from utils.logging_config import get_logger
|
||||
|
||||
|
|
@ -26,7 +27,6 @@ class WishlistAutoProcessingRuntime:
|
|||
processing_guard: Callable[[], AbstractContextManager[bool]]
|
||||
is_actually_processing: Callable[[], bool]
|
||||
app_context_factory: Callable[[], AbstractContextManager[Any]]
|
||||
get_wishlist_service: Callable[[], Any]
|
||||
get_profiles_database: Callable[[], Any]
|
||||
get_music_database: Callable[[], Any]
|
||||
download_batches: Dict[str, Dict[str, Any]]
|
||||
|
|
@ -292,7 +292,6 @@ def remove_tracks_already_in_library(
|
|||
class WishlistManualDownloadRuntime:
|
||||
"""Dependencies needed to start a manual wishlist download batch outside the controller."""
|
||||
|
||||
get_wishlist_service: Callable[[], Any]
|
||||
get_music_database: Callable[[], Any]
|
||||
download_batches: Dict[str, Dict[str, Any]]
|
||||
tasks_lock: Any
|
||||
|
|
@ -316,7 +315,7 @@ def start_manual_wishlist_download_batch(
|
|||
logger = runtime.logger
|
||||
|
||||
try:
|
||||
wishlist_service = runtime.get_wishlist_service()
|
||||
wishlist_service = get_wishlist_service()
|
||||
db = runtime.get_music_database()
|
||||
manual_profile_id = runtime.profile_id
|
||||
|
||||
|
|
@ -471,7 +470,7 @@ def process_wishlist_automatically(runtime: WishlistAutoProcessingRuntime, autom
|
|||
return
|
||||
|
||||
with runtime.app_context_factory():
|
||||
wishlist_service = runtime.get_wishlist_service()
|
||||
wishlist_service = get_wishlist_service()
|
||||
|
||||
# Check if wishlist has tracks across all profiles
|
||||
database = runtime.get_profiles_database()
|
||||
|
|
@ -627,7 +626,6 @@ def automatic_wishlist_cleanup_after_db_update(
|
|||
"""Remove wishlist entries that already exist in the library after a DB update."""
|
||||
try:
|
||||
from config.settings import config_manager
|
||||
from core.wishlist.service import get_wishlist_service
|
||||
from database.music_database import MusicDatabase, get_database
|
||||
|
||||
wishlist_service = wishlist_service or get_wishlist_service()
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from typing import Any, Callable, Dict
|
|||
|
||||
from core.wishlist.reporting import build_wishlist_stats_payload
|
||||
from core.wishlist.selection import prepare_wishlist_tracks_for_display
|
||||
from core.wishlist.service import get_wishlist_service
|
||||
from core.wishlist.state import get_wishlist_cycle as _get_wishlist_cycle
|
||||
from core.wishlist.state import set_wishlist_cycle as _set_wishlist_cycle
|
||||
from utils.logging_config import get_logger
|
||||
|
|
@ -23,13 +24,11 @@ logger = module_logger
|
|||
class WishlistRouteRuntime:
|
||||
"""Dependencies needed to service wishlist HTTP endpoints outside the controller."""
|
||||
|
||||
get_wishlist_service: Callable[[], Any]
|
||||
get_music_database: Callable[[], Any]
|
||||
get_current_profile_id: Callable[[], int]
|
||||
profile_id: int
|
||||
download_batches: Dict[str, Dict[str, Any]]
|
||||
download_tasks: Dict[str, Dict[str, Any]]
|
||||
tasks_lock: Any
|
||||
is_wishlist_auto_processing_flag: Callable[[], bool]
|
||||
is_wishlist_actually_processing: Callable[[], bool]
|
||||
reset_wishlist_processing_state: Callable[[], None]
|
||||
add_activity_item: Callable[[Any, Any, Any, Any], Any]
|
||||
|
|
@ -113,7 +112,7 @@ def process_wishlist_api(
|
|||
) -> tuple[Dict[str, Any], int]:
|
||||
"""Trigger wishlist processing in the background."""
|
||||
try:
|
||||
if runtime.is_wishlist_auto_processing_flag():
|
||||
if runtime.is_wishlist_actually_processing():
|
||||
return {"success": False, "error": "Wishlist processing already in progress"}, 409
|
||||
|
||||
thread = runtime.thread_factory(target=start_processing, daemon=True)
|
||||
|
|
@ -127,8 +126,7 @@ def process_wishlist_api(
|
|||
def get_wishlist_count(runtime: WishlistRouteRuntime) -> tuple[Dict[str, Any], int]:
|
||||
"""Return the current wishlist count for the active profile."""
|
||||
try:
|
||||
wishlist_service = runtime.get_wishlist_service()
|
||||
count = wishlist_service.get_wishlist_count(profile_id=runtime.get_current_profile_id())
|
||||
count = get_wishlist_service().get_wishlist_count(profile_id=runtime.profile_id)
|
||||
return {"count": count}, 200
|
||||
except Exception as exc:
|
||||
runtime.logger.error("Error getting wishlist count: %s", exc)
|
||||
|
|
@ -138,8 +136,7 @@ def get_wishlist_count(runtime: WishlistRouteRuntime) -> tuple[Dict[str, Any], i
|
|||
def get_wishlist_stats(runtime: WishlistRouteRuntime) -> tuple[Dict[str, Any], int]:
|
||||
"""Return wishlist statistics for the UI."""
|
||||
try:
|
||||
wishlist_service = runtime.get_wishlist_service()
|
||||
raw_tracks = wishlist_service.get_wishlist_tracks_for_download(profile_id=runtime.get_current_profile_id())
|
||||
raw_tracks = get_wishlist_service().get_wishlist_tracks_for_download(profile_id=runtime.profile_id)
|
||||
next_run_in_seconds = runtime.get_next_run_seconds("process_wishlist") if runtime.get_next_run_seconds else 0
|
||||
is_processing = runtime.is_wishlist_actually_processing()
|
||||
current_cycle = _get_wishlist_cycle(runtime.get_music_database)
|
||||
|
|
@ -188,7 +185,6 @@ def get_wishlist_tracks(
|
|||
) -> tuple[Dict[str, Any], int]:
|
||||
"""Return wishlist tracks for the modal UI."""
|
||||
try:
|
||||
wishlist_service = runtime.get_wishlist_service()
|
||||
db = runtime.get_music_database()
|
||||
|
||||
with runtime.tasks_lock:
|
||||
|
|
@ -198,13 +194,13 @@ def get_wishlist_tracks(
|
|||
)
|
||||
|
||||
if not wishlist_batch_active:
|
||||
duplicates_removed = db.remove_wishlist_duplicates(profile_id=runtime.get_current_profile_id())
|
||||
duplicates_removed = db.remove_wishlist_duplicates(profile_id=runtime.profile_id)
|
||||
if duplicates_removed > 0:
|
||||
runtime.logger.warning("Cleaned %s duplicate tracks from wishlist", duplicates_removed)
|
||||
else:
|
||||
runtime.logger.warning("Skipping wishlist duplicate cleanup - download in progress")
|
||||
|
||||
raw_tracks = wishlist_service.get_wishlist_tracks_for_download(profile_id=runtime.get_current_profile_id())
|
||||
raw_tracks = get_wishlist_service().get_wishlist_tracks_for_download(profile_id=runtime.profile_id)
|
||||
prepared = prepare_wishlist_tracks_for_display(raw_tracks, category=category, limit=limit)
|
||||
|
||||
if prepared["duplicates_found"] > 0:
|
||||
|
|
@ -232,8 +228,7 @@ def get_wishlist_tracks(
|
|||
def clear_wishlist(runtime: WishlistRouteRuntime) -> tuple[Dict[str, Any], int]:
|
||||
"""Clear the wishlist and cancel active wishlist batches."""
|
||||
try:
|
||||
wishlist_service = runtime.get_wishlist_service()
|
||||
success = wishlist_service.clear_wishlist(profile_id=runtime.get_current_profile_id())
|
||||
success = get_wishlist_service().clear_wishlist(profile_id=runtime.profile_id)
|
||||
|
||||
if success:
|
||||
cancelled_count = 0
|
||||
|
|
@ -282,10 +277,9 @@ def remove_track_from_wishlist(
|
|||
if not spotify_track_id:
|
||||
return {"success": False, "error": "No spotify_track_id provided"}, 400
|
||||
|
||||
wishlist_service = runtime.get_wishlist_service()
|
||||
success = wishlist_service.remove_track_from_wishlist(
|
||||
success = get_wishlist_service().remove_track_from_wishlist(
|
||||
spotify_track_id,
|
||||
profile_id=runtime.get_current_profile_id(),
|
||||
profile_id=runtime.profile_id,
|
||||
)
|
||||
|
||||
if success:
|
||||
|
|
@ -310,8 +304,8 @@ def remove_album_from_wishlist(
|
|||
if not album_id and not album_name_filter:
|
||||
return {"success": False, "error": "No album_id or album_name provided"}, 400
|
||||
|
||||
wishlist_service = runtime.get_wishlist_service()
|
||||
all_tracks = wishlist_service.get_wishlist_tracks_for_download(profile_id=runtime.get_current_profile_id())
|
||||
wishlist_service = get_wishlist_service()
|
||||
all_tracks = wishlist_service.get_wishlist_tracks_for_download(profile_id=runtime.profile_id)
|
||||
|
||||
tracks_to_remove = []
|
||||
for track in all_tracks:
|
||||
|
|
@ -334,7 +328,7 @@ def remove_album_from_wishlist(
|
|||
tracks_to_remove.append(spotify_track_id)
|
||||
|
||||
removed_count = 0
|
||||
album_remove_pid = runtime.get_current_profile_id()
|
||||
album_remove_pid = runtime.profile_id
|
||||
for spotify_track_id in tracks_to_remove:
|
||||
if wishlist_service.remove_track_from_wishlist(spotify_track_id, profile_id=album_remove_pid):
|
||||
removed_count += 1
|
||||
|
|
@ -363,11 +357,10 @@ def remove_batch_from_wishlist(
|
|||
if not spotify_track_ids or not isinstance(spotify_track_ids, list):
|
||||
return {"success": False, "error": "Missing or invalid spotify_track_ids"}, 400
|
||||
|
||||
wishlist_service = runtime.get_wishlist_service()
|
||||
removed = 0
|
||||
pid = runtime.get_current_profile_id()
|
||||
pid = runtime.profile_id
|
||||
for track_id in spotify_track_ids:
|
||||
if wishlist_service.remove_track_from_wishlist(track_id, profile_id=pid):
|
||||
if get_wishlist_service().remove_track_from_wishlist(track_id, profile_id=pid):
|
||||
removed += 1
|
||||
|
||||
runtime.logger.info("Batch removed %s track(s) from wishlist", removed)
|
||||
|
|
@ -406,13 +399,12 @@ def add_album_track_to_wishlist(
|
|||
"added_via": "library_wishlist_modal",
|
||||
}
|
||||
|
||||
wishlist_service = runtime.get_wishlist_service()
|
||||
success = wishlist_service.add_spotify_track_to_wishlist(
|
||||
success = get_wishlist_service().add_spotify_track_to_wishlist(
|
||||
spotify_track_data=spotify_track_data,
|
||||
failure_reason="Added from library (incomplete album)",
|
||||
source_type=source_type,
|
||||
source_context=enhanced_source_context,
|
||||
profile_id=runtime.get_current_profile_id(),
|
||||
profile_id=runtime.profile_id,
|
||||
)
|
||||
|
||||
if success:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from contextlib import contextmanager
|
||||
from types import SimpleNamespace
|
||||
|
||||
from core.wishlist import processing
|
||||
from core.wishlist.processing import WishlistAutoProcessingRuntime, process_wishlist_automatically
|
||||
|
||||
|
||||
|
|
@ -139,6 +140,7 @@ def _build_runtime(
|
|||
batch_map = {}
|
||||
|
||||
wishlist_service = _FakeWishlistService(tracks, count=count)
|
||||
processing.get_wishlist_service = lambda: wishlist_service
|
||||
profiles_db = _FakeProfilesDatabase(profiles or [{"id": 1}])
|
||||
music_db = _FakeMusicDatabase(cycle_value=cycle_value)
|
||||
executor = _FakeExecutor()
|
||||
|
|
@ -162,7 +164,6 @@ def _build_runtime(
|
|||
runtime = WishlistAutoProcessingRuntime(
|
||||
processing_guard=guard,
|
||||
app_context_factory=app_context,
|
||||
get_wishlist_service=lambda: wishlist_service,
|
||||
get_profiles_database=lambda: profiles_db,
|
||||
get_music_database=lambda: music_db,
|
||||
download_batches=batch_map,
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ class _FakeLock:
|
|||
|
||||
def _build_runtime(tracks, owned_matches=None, batch_map=None):
|
||||
wishlist_service = _FakeWishlistService(tracks)
|
||||
processing.get_wishlist_service = lambda: wishlist_service
|
||||
music_db = _FakeMusicDatabase(owned_matches=owned_matches)
|
||||
executor = _FakeExecutor()
|
||||
logger = _FakeLogger()
|
||||
|
|
@ -87,7 +88,6 @@ def _build_runtime(tracks, owned_matches=None, batch_map=None):
|
|||
batch_map = batch_map or {}
|
||||
|
||||
runtime = WishlistManualDownloadRuntime(
|
||||
get_wishlist_service=lambda: wishlist_service,
|
||||
get_music_database=lambda: music_db,
|
||||
download_batches=batch_map,
|
||||
tasks_lock=_FakeLock(),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import json
|
||||
|
||||
import core.wishlist.routes as routes_module
|
||||
from core.wishlist.routes import (
|
||||
WishlistRouteRuntime,
|
||||
add_album_track_to_wishlist,
|
||||
|
|
@ -148,7 +149,6 @@ def _build_runtime(
|
|||
cycle_value="albums",
|
||||
duplicate_removals=0,
|
||||
clear_result=True,
|
||||
auto_processing_flag=False,
|
||||
actually_processing=False,
|
||||
next_run_seconds=0,
|
||||
download_batches=None,
|
||||
|
|
@ -157,17 +157,16 @@ def _build_runtime(
|
|||
reset_callback=None,
|
||||
):
|
||||
service = _FakeWishlistService(tracks=tracks, count=count, clear_result=clear_result)
|
||||
routes_module.get_wishlist_service = lambda: service
|
||||
db = _FakeMusicDatabase(cycle_value=cycle_value, duplicate_removals=duplicate_removals)
|
||||
logger = _FakeLogger()
|
||||
activity_calls = []
|
||||
runtime = WishlistRouteRuntime(
|
||||
get_wishlist_service=lambda: service,
|
||||
get_music_database=lambda: db,
|
||||
get_current_profile_id=lambda: 1,
|
||||
profile_id=1,
|
||||
download_batches=download_batches if download_batches is not None else {},
|
||||
download_tasks=download_tasks if download_tasks is not None else {},
|
||||
tasks_lock=_FakeLock(),
|
||||
is_wishlist_auto_processing_flag=lambda: auto_processing_flag,
|
||||
is_wishlist_actually_processing=lambda: actually_processing,
|
||||
reset_wishlist_processing_state=reset_callback or (lambda: None),
|
||||
add_activity_item=lambda *args: activity_calls.append(args),
|
||||
|
|
@ -200,7 +199,7 @@ def test_process_wishlist_api_starts_background_thread_when_idle():
|
|||
def test_process_wishlist_api_rejects_when_flag_is_set():
|
||||
thread_factory = _FakeThreadFactory()
|
||||
runtime, _service, _db, logger, _activity_calls = _build_runtime(
|
||||
auto_processing_flag=True,
|
||||
actually_processing=True,
|
||||
thread_factory=thread_factory,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -17529,7 +17529,6 @@ def _process_wishlist_automatically(automation_id=None):
|
|||
processing_guard=_processing_guard,
|
||||
is_actually_processing=is_wishlist_actually_processing,
|
||||
app_context_factory=lambda: app.app_context(),
|
||||
get_wishlist_service=get_wishlist_service,
|
||||
get_profiles_database=get_database,
|
||||
get_music_database=MusicDatabase,
|
||||
download_batches=download_batches,
|
||||
|
|
@ -18344,22 +18343,18 @@ def get_wishlist_tracks():
|
|||
|
||||
def _build_wishlist_route_runtime(
|
||||
*,
|
||||
is_auto_processing_flag=None,
|
||||
is_actually_processing_fn=None,
|
||||
reset_wishlist_processing_state=None,
|
||||
get_next_run_seconds=None,
|
||||
):
|
||||
from core.wishlist_service import get_wishlist_service
|
||||
from database.music_database import MusicDatabase
|
||||
|
||||
return _WishlistRouteRuntime(
|
||||
get_wishlist_service=get_wishlist_service,
|
||||
get_music_database=MusicDatabase,
|
||||
get_current_profile_id=get_current_profile_id,
|
||||
profile_id=get_current_profile_id(),
|
||||
download_batches=download_batches,
|
||||
download_tasks=download_tasks,
|
||||
tasks_lock=tasks_lock,
|
||||
is_wishlist_auto_processing_flag=is_auto_processing_flag or (lambda: wishlist_auto_processing),
|
||||
is_wishlist_actually_processing=is_actually_processing_fn or is_wishlist_actually_processing,
|
||||
reset_wishlist_processing_state=reset_wishlist_processing_state or (lambda: None),
|
||||
add_activity_item=add_activity_item,
|
||||
|
|
@ -18385,14 +18380,11 @@ def start_wishlist_missing_downloads():
|
|||
}), 409
|
||||
|
||||
data = request.get_json() or {}
|
||||
from core.wishlist_service import get_wishlist_service
|
||||
from database.music_database import MusicDatabase
|
||||
|
||||
wishlist_service = get_wishlist_service()
|
||||
db = MusicDatabase()
|
||||
manual_profile_id = get_current_profile_id()
|
||||
manual_runtime = _WishlistManualDownloadRuntime(
|
||||
get_wishlist_service=lambda: wishlist_service,
|
||||
get_music_database=lambda: db,
|
||||
download_batches=download_batches,
|
||||
tasks_lock=tasks_lock,
|
||||
|
|
|
|||
Loading…
Reference in a new issue