/
/
/
1"""Mylist adapter for nicovideo."""
2
3from __future__ import annotations
4
5from typing import TYPE_CHECKING
6
7from music_assistant.providers.nicovideo.helpers import PlaylistWithTracks
8from music_assistant.providers.nicovideo.services.base import NicovideoBaseService
9
10if TYPE_CHECKING:
11 from music_assistant_models.media_items import Playlist
12 from niconico.objects.nvapi import CreateMylistData
13
14 from music_assistant.providers.nicovideo.services.manager import NicovideoServiceManager
15
16
17class NicovideoMylistService(NicovideoBaseService):
18 """Handles mylist related operations for nicovideo."""
19
20 def __init__(self, adapter: NicovideoServiceManager) -> None:
21 """Initialize NicovideoMylistService with reference to parent adapter."""
22 super().__init__(adapter)
23
24 async def get_own_mylists(self) -> list[Playlist]:
25 """Get own mylists and convert them."""
26 results = await self.service_manager._call_with_throttler(
27 self.niconico_py_client.user.get_own_mylists
28 )
29 if results is None:
30 return []
31 return [self.converter_manager.playlist.convert_by_mylist(entry) for entry in results]
32
33 async def get_mylist_or_own_mylist(
34 self, mylist_id: str, page_size: int = 500, page: int = 1
35 ) -> PlaylistWithTracks | None:
36 """Get mylist with fallback to own_mylist for private mylists."""
37 # Try public mylist first
38 playlist_with_tracks = await self._get_mylist(mylist_id, page_size=page_size, page=page)
39 if not playlist_with_tracks:
40 # Fallback to own mylist (for private mylists)
41 playlist_with_tracks = await self.get_own_mylist(
42 mylist_id, page_size=page_size, page=page
43 )
44 return playlist_with_tracks
45
46 async def get_own_mylist(
47 self, mylist_id: str, page_size: int = 500, page: int = 1
48 ) -> PlaylistWithTracks | None:
49 """Get own mylist details and convert as Playlist."""
50 mylist = await self.service_manager._call_with_throttler(
51 self.niconico_py_client.user.get_own_mylist,
52 mylist_id,
53 page_size=page_size,
54 page=page,
55 )
56 if not mylist:
57 return None
58 playlist_with_tracks = self.converter_manager.playlist.convert_with_tracks_by_mylist(mylist)
59 self._update_positions_in_playlist(playlist_with_tracks)
60 return playlist_with_tracks
61
62 async def add_mylist_item(self, mylist_id: str, video_id: str) -> bool:
63 """Add a video to mylist."""
64 result = await self.service_manager._call_with_throttler(
65 self.niconico_py_client.user.add_mylist_item,
66 mylist_id,
67 video_id,
68 )
69 return bool(result)
70
71 async def remove_mylist_items(self, mylist_id: str, video_ids: list[str]) -> bool:
72 """Remove videos from mylist."""
73 result = await self.service_manager._call_with_throttler(
74 self.niconico_py_client.user.remove_mylist_items,
75 mylist_id,
76 video_ids,
77 )
78 return bool(result)
79
80 async def create_mylist(
81 self, name: str, description: str = "", is_public: bool = False
82 ) -> CreateMylistData | None:
83 """Create a new mylist."""
84 return await self.service_manager._call_with_throttler(
85 self.niconico_py_client.user.create_mylist,
86 name,
87 description=description,
88 is_public=is_public,
89 )
90
91 async def _get_mylist(
92 self, mylist_id: str, page_size: int = 500, page: int = 1
93 ) -> PlaylistWithTracks | None:
94 """Get mylist details and convert as Playlist."""
95 mylist = await self.service_manager._call_with_throttler(
96 self.niconico_py_client.video.get_mylist,
97 mylist_id,
98 page_size=page_size,
99 page=page,
100 )
101 if not mylist:
102 return None
103 playlist_with_tracks = self.converter_manager.playlist.convert_with_tracks_by_mylist(mylist)
104 self._update_positions_in_playlist(playlist_with_tracks)
105 return playlist_with_tracks
106
107 def _update_positions_in_playlist(self, playlist: PlaylistWithTracks) -> None:
108 """Update positions in playlist tracks."""
109 # Ensure tracks have position set (1-based)
110 for index, track in enumerate(playlist.tracks, start=1):
111 track.position = index
112