99
1010from aiohttp import ClientSession
1111from aiohttp .hdrs import METH_DELETE , METH_GET , METH_POST , METH_PUT
12+ from mashumaro .codecs .json import JSONDecoder
1213import orjson
1314from yarl import URL
1415
3839 EpisodesResponse ,
3940 FeaturedPlaylistResponse ,
4041 FollowedArtistResponse ,
42+ Image ,
43+ ModifyPlaylistResponse ,
4144 NewReleasesResponse ,
4245 NewReleasesResponseInner ,
4346 PlaybackState ,
4447 PlayedTrack ,
4548 PlayedTrackResponse ,
4649 Playlist ,
4750 PlaylistResponse ,
51+ PlaylistTrack ,
52+ PlaylistTrackResponse ,
4853 RepeatMode ,
4954 SavedAlbum ,
5055 SavedAlbumResponse ,
@@ -442,10 +447,6 @@ async def are_episodes_saved(self, episode_ids: list[str]) -> dict[str, bool]:
442447 body : list [bool ] = orjson .loads (response ) # pylint: disable=no-member
443448 return dict (zip (identifiers , body ))
444449
445- # Get genre seeds
446-
447- # Get available markets
448-
449450 async def get_playback (self ) -> PlaybackState | None :
450451 """Get playback state."""
451452 response = await self ._get (
@@ -570,23 +571,126 @@ async def get_playlist(self, playlist_id: str) -> Playlist:
570571 )
571572 return Playlist .from_json (response )
572573
573- # Update playlist details
574-
575- # Get a playlist items
576-
577- # Update a playlist items
574+ async def update_playlist_details (
575+ self ,
576+ playlist_id : str ,
577+ * ,
578+ name : str | None = None ,
579+ description : str | None = None ,
580+ public : bool | None = None ,
581+ collaborative : bool | None = None ,
582+ ) -> None :
583+ """Update playlist details."""
584+ identifier = get_identifier (playlist_id )
585+ data : dict [str , Any ] = {}
586+ if name :
587+ data ["name" ] = name
588+ if description :
589+ data ["description" ] = description
590+ if public is not None :
591+ data ["public" ] = public
592+ if collaborative is not None :
593+ data ["collaborative" ] = collaborative
594+ await self ._put (f"v1/playlists/{ identifier } " , data = data )
595+
596+ async def get_playlist_items (self , playlist_id : str ) -> list [PlaylistTrack ]:
597+ """Get playlist tracks."""
598+ identifier = get_identifier (playlist_id )
599+ params : dict [str , Any ] = {"limit" : 48 }
600+ response = await self ._get (f"v1/playlists/{ identifier } /tracks" , params = params )
601+ return PlaylistTrackResponse .from_json (response ).items
578602
579- # Remove a playlist items
603+ async def update_playlist_items (
604+ self ,
605+ playlist_id : str ,
606+ uris : list [str ],
607+ * ,
608+ range_start : int | None = None ,
609+ insert_before : int | None = None ,
610+ range_length : int | None = None ,
611+ snapshot_id : str | None = None ,
612+ ) -> str :
613+ """Update playlist tracks."""
614+ identifier = get_identifier (playlist_id )
615+ data : dict [str , Any ] = {"uris" : uris }
616+ if range_start is not None :
617+ data ["range_start" ] = range_start
618+ if insert_before is not None :
619+ data ["insert_before" ] = insert_before
620+ if range_length is not None :
621+ data ["range_length" ] = range_length
622+ if snapshot_id :
623+ data ["snapshot_id" ] = snapshot_id
624+ response = await self ._put (f"v1/playlists/{ identifier } /tracks" , data = data )
625+ return ModifyPlaylistResponse .from_json (response ).snapshot_id
626+
627+ async def add_playlist_items (
628+ self ,
629+ playlist_id : str ,
630+ uris : list [str ],
631+ position : int | None = None ,
632+ ) -> str :
633+ """Add playlist tracks."""
634+ if len (uris ) > 100 :
635+ msg = "Maximum of 100 tracks can be added at once"
636+ raise ValueError (msg )
637+ identifier = get_identifier (playlist_id )
638+ data : dict [str , Any ] = {"uris" : uris }
639+ if position is not None :
640+ data ["position" ] = position
641+ response = await self ._post (f"v1/playlists/{ identifier } /tracks" , data = data )
642+ return ModifyPlaylistResponse .from_json (response ).snapshot_id
643+
644+ async def remove_playlist_items (
645+ self ,
646+ playlist_id : str ,
647+ uris : list [str ],
648+ snapshot_id : str | None = None ,
649+ ) -> str :
650+ """Remove playlist tracks."""
651+ if len (uris ) > 100 :
652+ msg = "Maximum of 100 tracks can be removed at once"
653+ raise ValueError (msg )
654+ identifier = get_identifier (playlist_id )
655+ data : dict [str , Any ] = {"tracks" : [{"uri" : uri } for uri in uris ]}
656+ if snapshot_id :
657+ data ["snapshot_id" ] = snapshot_id
658+ response = await self ._delete (f"v1/playlists/{ identifier } /tracks" , data = data )
659+ return ModifyPlaylistResponse .from_json (response ).snapshot_id
580660
581661 async def get_playlists_for_current_user (self ) -> list [BasePlaylist ]:
582662 """Get playlists."""
583663 params : dict [str , Any ] = {"limit" : 48 }
584664 response = await self ._get ("v1/me/playlists" , params = params )
585665 return PlaylistResponse .from_json (response ).items
586666
587- # Get users playlists
667+ async def get_playlists_for_user (self , user_id : str ) -> list [BasePlaylist ]:
668+ """Get playlists."""
669+ identifier = get_identifier (user_id )
670+ params : dict [str , Any ] = {"limit" : 48 }
671+ response = await self ._get (f"v1/user/{ identifier } /playlists" , params = params )
672+ return PlaylistResponse .from_json (response ).items
588673
589- # Create a playlist
674+ async def create_playlist (
675+ self ,
676+ user_id : str ,
677+ name : str ,
678+ * ,
679+ description : str | None = None ,
680+ public : bool | None = None ,
681+ collaborative : bool | None = None ,
682+ ) -> Playlist :
683+ """Create playlist."""
684+ identifier = get_identifier (user_id )
685+ data : dict [str , Any ] = {"name" : name }
686+ if description :
687+ data ["description" ] = description
688+ if public is not None :
689+ data ["public" ] = public
690+ if collaborative is not None :
691+ data ["collaborative" ] = collaborative
692+ response = await self ._post (f"v1/users/{ identifier } /playlists" , data = data )
693+ return Playlist .from_json (response )
590694
591695 async def get_featured_playlists (self ) -> list [BasePlaylist ]:
592696 """Get featured playlists."""
@@ -603,7 +707,11 @@ async def get_category_playlists(self, category_id: str) -> list[BasePlaylist]:
603707 )
604708 return CategoryPlaylistResponse .from_json (response ).playlists .items
605709
606- # Get playlist cover image
710+ async def get_playlist_cover_image (self , playlist_id : str ) -> list [Image ]:
711+ """Get playlist cover image."""
712+ identifier = get_identifier (playlist_id )
713+ response = await self ._get (f"v1/playlists/{ identifier } /images" )
714+ return JSONDecoder (list [Image ]).decode (response )
607715
608716 # Upload a custom playlist cover image
609717
0 commit comments