ytmapi_rs/simplified_queries.rs
1//! This module contains the implementation for more convenient ways to call the
2//! API, in many cases without the need of building Query structs.
3//! This module contains purely additional implementations for YtMusic. To see
4//! the documentation, refer to the [`YtMusic`] documentation itself.
5//! # Optional
6//! To enable this module, feature `simplified-queries` must be enabled (enabled
7//! by default)
8use crate::auth::{AuthToken, LoggedIn};
9use crate::common::{
10 AlbumID, ApiOutcome, ArtistChannelID, BrowseParams, EpisodeID, FeedbackTokenRemoveFromHistory,
11 LikeStatus, LyricsID, MoodCategoryParams, PlaylistID, PodcastChannelID, PodcastChannelParams,
12 PodcastID, SearchSuggestion, SetVideoID, SongTrackingUrl, TasteToken, UploadAlbumID,
13 UploadArtistID, UploadEntityID, UserChannelID, UserPlaylistsParams, UserVideosParams, VideoID,
14};
15use crate::parse::{
16 AddPlaylistItem, GetAlbum, GetArtist, GetArtistAlbumsAlbum, GetPlaylistDetails, GetUser,
17 HistoryPeriod, LibraryArtist, LibraryArtistSubscription, LibraryPlaylist, Lyrics, PlaylistItem,
18 SearchResultAlbum, SearchResultArtist, SearchResultEpisode, SearchResultFeaturedPlaylist,
19 SearchResultPlaylist, SearchResultPodcast, SearchResultProfile, SearchResultSong,
20 SearchResultVideo, SearchResults, UserPlaylist, UserVideo, WatchPlaylistTrack,
21};
22use crate::query::playlist::{CreatePlaylistType, DuplicateHandlingMode, GetPlaylistDetailsQuery};
23use crate::query::rate::{RatePlaylistQuery, RateSongQuery};
24use crate::query::search::BasicSearch;
25use crate::query::search::filteredsearch::{
26 AlbumsFilter, ArtistsFilter, CommunityPlaylistsFilter, EpisodesFilter, FeaturedPlaylistsFilter,
27 FilteredSearch, PlaylistsFilter, PodcastsFilter, ProfilesFilter, SongsFilter, VideosFilter,
28};
29use crate::query::song::{GetLyricsQuery, GetSongTrackingUrlQuery};
30use crate::query::{
31 AddHistoryItemQuery, AddPlaylistItemsQuery, CreatePlaylistQuery, DeletePlaylistQuery,
32 DeleteUploadEntityQuery, EditPlaylistQuery, EditSongLibraryStatusQuery, GetAlbumQuery,
33 GetArtistAlbumsQuery, GetArtistQuery, GetChannelEpisodesQuery, GetChannelQuery,
34 GetEpisodeQuery, GetHistoryQuery, GetLibraryAlbumsQuery, GetLibraryArtistSubscriptionsQuery,
35 GetLibraryArtistsQuery, GetLibraryChannelsQuery, GetLibraryPlaylistsQuery,
36 GetLibraryPodcastsQuery, GetLibrarySongsQuery, GetLibraryUploadAlbumQuery,
37 GetLibraryUploadAlbumsQuery, GetLibraryUploadArtistQuery, GetLibraryUploadArtistsQuery,
38 GetLibraryUploadSongsQuery, GetLyricsIDQuery, GetMoodCategoriesQuery, GetMoodPlaylistsQuery,
39 GetNewEpisodesQuery, GetPlaylistTracksQuery, GetPodcastQuery, GetSearchSuggestionsQuery,
40 GetTasteProfileQuery, GetUserPlaylistsQuery, GetUserQuery, GetUserVideosQuery,
41 GetWatchPlaylistQuery, Query, RemoveHistoryItemsQuery, RemovePlaylistItemsQuery, SearchQuery,
42 SetTasteProfileQuery, SubscribeArtistQuery, UnsubscribeArtistsQuery,
43};
44use crate::{Result, YtMusic};
45
46impl<A: AuthToken> YtMusic<A> {
47 /// API Search Query that returns results for each category if available.
48 /// # Usage
49 /// ```no_run
50 /// # async {
51 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
52 /// .await
53 /// .unwrap();
54 /// yt.search("Beatles").await
55 /// # };
56 /// ```
57 #[deprecated = "To be removed in future release - see issue #353"]
58 pub async fn search<'a, Q: Into<SearchQuery<'a, BasicSearch>>>(
59 &self,
60 query: Q,
61 ) -> Result<SearchResults> {
62 let query = query.into();
63 self.query(query).await
64 }
65 /// API Search Query for Artists only.
66 /// ```no_run
67 /// # async {
68 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
69 /// .await
70 /// .unwrap();
71 /// yt.search_artists("Beatles").await
72 /// # };
73 /// ```
74 pub async fn search_artists<'a, Q: Into<SearchQuery<'a, FilteredSearch<ArtistsFilter>>>>(
75 &self,
76 query: Q,
77 ) -> Result<Vec<SearchResultArtist>> {
78 let query = query.into();
79 self.query(query).await
80 }
81 /// API Search Query for Albums only.
82 /// ```no_run
83 /// # async {
84 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE").await.unwrap();
85 /// yt.search_albums("Beatles").await
86 /// # };
87 pub async fn search_albums<'a, Q: Into<SearchQuery<'a, FilteredSearch<AlbumsFilter>>>>(
88 &self,
89 query: Q,
90 ) -> Result<Vec<SearchResultAlbum>> {
91 let query = query.into();
92 self.query(query).await
93 }
94 /// API Search Query for Songs only.
95 /// ```no_run
96 /// # async {
97 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
98 /// .await
99 /// .unwrap();
100 /// yt.search_songs("Beatles").await
101 /// # };
102 /// ```
103 pub async fn search_songs<'a, Q: Into<SearchQuery<'a, FilteredSearch<SongsFilter>>>>(
104 &self,
105 query: Q,
106 ) -> Result<Vec<SearchResultSong>> {
107 let query = query.into();
108 self.query(query).await
109 }
110 /// API Search Query for Playlists only.
111 /// ```no_run
112 /// # async {
113 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
114 /// .await
115 /// .unwrap();
116 /// yt.search_playlists("Beatles").await
117 /// # };
118 /// ```
119 pub async fn search_playlists<'a, Q: Into<SearchQuery<'a, FilteredSearch<PlaylistsFilter>>>>(
120 &self,
121 query: Q,
122 ) -> Result<Vec<SearchResultPlaylist>> {
123 let query = query.into();
124 self.query(query).await
125 }
126 /// API Search Query for Community Playlists only.
127 /// ```no_run
128 /// # async {
129 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
130 /// .await
131 /// .unwrap();
132 /// yt.search_community_playlists("Beatles").await
133 /// # };
134 /// ```
135 pub async fn search_community_playlists<
136 'a,
137 Q: Into<SearchQuery<'a, FilteredSearch<CommunityPlaylistsFilter>>>,
138 >(
139 &self,
140 query: Q,
141 ) -> Result<Vec<SearchResultPlaylist>> {
142 let query = query.into();
143 self.query(query).await
144 }
145 /// API Search Query for Featured Playlists only.
146 /// ```no_run
147 /// # async {
148 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
149 /// .await
150 /// .unwrap();
151 /// yt.search_featured_playlists("Beatles").await
152 /// # };
153 /// ```
154 pub async fn search_featured_playlists<
155 'a,
156 Q: Into<SearchQuery<'a, FilteredSearch<FeaturedPlaylistsFilter>>>,
157 >(
158 &self,
159 query: Q,
160 ) -> Result<Vec<SearchResultFeaturedPlaylist>> {
161 let query = query.into();
162 self.query(query).await
163 }
164 /// API Search Query for Episodes only.
165 /// ```no_run
166 /// # async {
167 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
168 /// .await
169 /// .unwrap();
170 /// yt.search_episodes("Beatles").await
171 /// # };
172 /// ```
173 pub async fn search_episodes<'a, Q: Into<SearchQuery<'a, FilteredSearch<EpisodesFilter>>>>(
174 &self,
175 query: Q,
176 ) -> Result<Vec<SearchResultEpisode>> {
177 let query = query.into();
178 self.query(query).await
179 }
180 /// API Search Query for Podcasts only.
181 /// ```no_run
182 /// # async {
183 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
184 /// .await
185 /// .unwrap();
186 /// yt.search_podcasts("Beatles").await
187 /// # };
188 /// ```
189 pub async fn search_podcasts<'a, Q: Into<SearchQuery<'a, FilteredSearch<PodcastsFilter>>>>(
190 &self,
191 query: Q,
192 ) -> Result<Vec<SearchResultPodcast>> {
193 let query = query.into();
194 self.query(query).await
195 }
196 /// API Search Query for Videos only.
197 /// ```no_run
198 /// # async {
199 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
200 /// .await
201 /// .unwrap();
202 /// yt.search_videos("Beatles").await
203 /// # };
204 /// ```
205 pub async fn search_videos<'a, Q: Into<SearchQuery<'a, FilteredSearch<VideosFilter>>>>(
206 &self,
207 query: Q,
208 ) -> Result<Vec<SearchResultVideo>> {
209 let query = query.into();
210 self.query(query).await
211 }
212 /// API Search Query for Profiles only.
213 /// ```no_run
214 /// # async {
215 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
216 /// .await
217 /// .unwrap();
218 /// yt.search_profiles("Beatles").await
219 /// # };
220 /// ```
221 pub async fn search_profiles<'a, Q: Into<SearchQuery<'a, FilteredSearch<ProfilesFilter>>>>(
222 &self,
223 query: Q,
224 ) -> Result<Vec<SearchResultProfile>> {
225 let query = query.into();
226 self.query(query).await
227 }
228 /// Gets information about an artist and their top releases.
229 /// ```no_run
230 /// # async {
231 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
232 /// .await
233 /// .unwrap();
234 /// let results = yt.search_artists("Beatles").await.unwrap();
235 /// yt.get_artist(&results[0].browse_id).await
236 /// # };
237 /// ```
238 pub async fn get_artist<'a>(&self, query: impl Into<GetArtistQuery<'a>>) -> Result<GetArtist> {
239 self.query(query.into()).await
240 }
241 /// Gets a full list albums for an artist.
242 /// ```no_run
243 /// # async {
244 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
245 /// .await
246 /// .unwrap();
247 /// let results = yt.search_artists("Beatles").await.unwrap();
248 /// let artist_top_albums = yt
249 /// .get_artist(&results[0].browse_id)
250 /// .await
251 /// .unwrap()
252 /// .top_releases
253 /// .albums
254 /// .unwrap();
255 /// yt.get_artist_albums(
256 /// artist_top_albums.browse_id.unwrap(),
257 /// artist_top_albums.params.unwrap(),
258 /// )
259 /// .await
260 /// # };
261 /// ```
262 pub async fn get_artist_albums<'a, T: Into<ArtistChannelID<'a>>, U: Into<BrowseParams<'a>>>(
263 &self,
264 channel_id: T,
265 browse_params: U,
266 ) -> Result<Vec<GetArtistAlbumsAlbum>> {
267 let query = GetArtistAlbumsQuery::new(channel_id.into(), browse_params.into());
268 self.query(query).await
269 }
270 /// Gets information about an album and its tracks.
271 /// ```no_run
272 /// # async {
273 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
274 /// .await
275 /// .unwrap();
276 /// let results = yt.search_albums("Dark Side Of The Moon").await.unwrap();
277 /// yt.get_album(&results[0].album_id).await
278 /// # };
279 /// ```
280 pub async fn get_album<'a, T: Into<AlbumID<'a>>>(&self, album_id: T) -> Result<GetAlbum> {
281 let query = GetAlbumQuery::new(album_id);
282 self.query(query).await
283 }
284 /// Gets the information that's available when playing a song or playlist;
285 /// upcoming tracks and lyrics.
286 /// # Partially implemented
287 /// Tracks are not implemented - empty vector always returned.
288 /// See [`GetWatchPlaylistQuery`] and [`YtMusic.query()`]
289 /// for more ways to construct and run
290 /// a GetWatchPlaylistQuery.
291 ///
292 /// [`YtMusic.query()`]: crate::YtMusic::query
293 /// [GetWatchPlaylistQuery]: crate::query::watch::GetWatchPlaylistQuery
294 ///
295 /// ```no_run
296 /// # async {
297 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
298 /// .await
299 /// .unwrap();
300 /// let results = yt
301 /// .search_songs("While My Guitar Gently Weeps")
302 /// .await
303 /// .unwrap();
304 /// yt.get_watch_playlist_from_video_id(&results[0].video_id)
305 /// .await
306 /// # };
307 /// ```
308 // NOTE: Could be generic across PlaylistID or VideoID using
309 // Into<GetWatchPlaylistQuery>
310 pub async fn get_watch_playlist_from_video_id<'a, S: Into<VideoID<'a>>>(
311 &self,
312 video_id: S,
313 ) -> Result<Vec<WatchPlaylistTrack>> {
314 let query = GetWatchPlaylistQuery::new_from_video_id(video_id.into());
315 self.query(query).await
316 }
317 /// Gets the `LyricsID` required to get lyrics.
318 /// ```no_run
319 /// # async {
320 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
321 /// .await
322 /// .unwrap();
323 /// let results = yt
324 /// .search_songs("While My Guitar Gently Weeps")
325 /// .await
326 /// .unwrap();
327 /// yt.get_lyrics_id(&results[0].video_id).await
328 /// # };
329 /// ```
330 pub async fn get_lyrics_id<'a, T: Into<VideoID<'a>>>(
331 &self,
332 video_id: T,
333 ) -> Result<LyricsID<'static>> {
334 let query = GetLyricsIDQuery::new(video_id.into());
335 self.query(query).await
336 }
337 /// Gets song lyrics and the source.
338 /// ```no_run
339 /// # async {
340 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
341 /// .await
342 /// .unwrap();
343 /// let results = yt
344 /// .search_songs("While My Guitar Gently Weeps")
345 /// .await
346 /// .unwrap();
347 /// let lyrics_id = yt.get_lyrics_id(&results[0].video_id).await.unwrap();
348 /// yt.get_lyrics(lyrics_id).await
349 /// # };
350 /// ```
351 pub async fn get_lyrics<'a, T: Into<LyricsID<'a>>>(&self, lyrics_id: T) -> Result<Lyrics> {
352 let query = GetLyricsQuery::new(lyrics_id.into());
353 self.query(query).await
354 }
355 /// Gets a playlists tracks.
356 /// ```no_run
357 /// # async {
358 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
359 /// .await
360 /// .unwrap();
361 /// let results = yt.search_featured_playlists("Heavy metal").await.unwrap();
362 /// yt.get_playlist_tracks(&results[0].playlist_id).await
363 /// # };
364 /// ```
365 pub async fn get_playlist_tracks<'a, T: Into<PlaylistID<'a>>>(
366 &self,
367 playlist_id: T,
368 ) -> Result<Vec<PlaylistItem>> {
369 let query = GetPlaylistTracksQuery::new(playlist_id.into());
370 self.query(query).await
371 }
372 /// Gets information about a playlist.
373 /// ```no_run
374 /// # async {
375 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
376 /// .await
377 /// .unwrap();
378 /// let results = yt.search_featured_playlists("Heavy metal").await.unwrap();
379 /// yt.get_playlist_details(&results[0].playlist_id).await
380 /// # };
381 /// ```
382 pub async fn get_playlist_details<'a, T: Into<PlaylistID<'a>>>(
383 &self,
384 playlist_id: T,
385 ) -> Result<GetPlaylistDetails> {
386 let query = GetPlaylistDetailsQuery::new(playlist_id.into());
387 self.query(query).await
388 }
389 /// Gets search suggestions
390 /// ```no_run
391 /// # async {
392 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
393 /// .await
394 /// .unwrap();
395 /// yt.get_search_suggestions("The Beat").await;
396 /// # };
397 /// ```
398 pub async fn get_search_suggestions<'a, S: Into<GetSearchSuggestionsQuery<'a>>>(
399 &self,
400 query: S,
401 ) -> Result<Vec<SearchSuggestion>> {
402 let query = query.into();
403 self.query(query).await
404 }
405 /// Fetches suggested artists from taste profile
406 /// <https://music.youtube.com/tasteprofile>.
407 /// Tasteprofile allows users to pick artists to update their
408 /// recommendations.
409 /// ```no_run
410 /// # async {
411 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
412 /// .await
413 /// .unwrap();
414 /// yt.get_taste_profile().await
415 /// # };
416 /// ```
417 pub async fn get_taste_profile(&self) -> Result<<GetTasteProfileQuery as Query<A>>::Output> {
418 self.query(GetTasteProfileQuery).await
419 }
420 /// Sets artists as favourites to influence your recommendations.
421 /// ```no_run
422 /// # async {
423 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
424 /// .await
425 /// .unwrap();
426 /// let results = yt.get_taste_profile().await.unwrap();
427 /// yt.set_taste_profile(results.into_iter().take(5).map(|r| r.taste_tokens))
428 /// .await
429 /// # };
430 /// ```
431 pub async fn set_taste_profile<'a>(
432 &self,
433 taste_tokens: impl IntoIterator<Item = TasteToken<'a>>,
434 ) -> Result<<SetTasteProfileQuery<'a> as Query<A>>::Output> {
435 self.query(SetTasteProfileQuery::new(taste_tokens)).await
436 }
437 /// Fetches 'Moods & Genres' categories.
438 /// ```no_run
439 /// # async {
440 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
441 /// .await
442 /// .unwrap();
443 /// yt.get_mood_categories().await
444 /// # };
445 /// ```
446 pub async fn get_mood_categories(
447 &self,
448 ) -> Result<<GetMoodCategoriesQuery as Query<A>>::Output> {
449 self.query(GetMoodCategoriesQuery).await
450 }
451 /// Returns a list of playlists for a given mood category.
452 /// ```no_run
453 /// # async {
454 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
455 /// .await
456 /// .unwrap();
457 /// let results = yt.get_mood_categories().await.unwrap();
458 /// yt.get_mood_playlists(&results[0].mood_categories[0].params)
459 /// .await
460 /// # };
461 /// ```
462 pub async fn get_mood_playlists<'a, T: Into<MoodCategoryParams<'a>>>(
463 &self,
464 mood_params: T,
465 ) -> Result<<GetMoodPlaylistsQuery<'_> as Query<A>>::Output> {
466 self.query(GetMoodPlaylistsQuery::new(mood_params.into()))
467 .await
468 }
469 /// Get the 'SongTrackingUrl' for a song. This is used to add items to
470 /// history using `add_history_item()`.
471 /// ```no_run
472 /// # async {
473 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
474 /// .await
475 /// .unwrap();
476 /// let song = yt
477 /// .search_songs("While My Guitar Gently Weeps")
478 /// .await
479 /// .unwrap()
480 /// .into_iter()
481 /// .next()
482 /// .unwrap();
483 /// yt.get_song_tracking_url(song.video_id).await
484 /// # };
485 /// ```
486 pub async fn get_song_tracking_url<'a, T: Into<VideoID<'a>>>(
487 &self,
488 video_id: T,
489 ) -> Result<SongTrackingUrl<'static>> {
490 let query = GetSongTrackingUrlQuery::new(video_id.into())?;
491 self.query(query).await
492 }
493 /// Gets information about a Channel of Podcasts.
494 /// ```no_run
495 /// # async {
496 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
497 /// .await
498 /// .unwrap();
499 /// let podcasts = yt.search_podcasts("Rustacean").await.unwrap();
500 /// let podcast = yt.get_podcast(&podcasts[0].podcast_id).await.unwrap();
501 /// yt.get_channel(podcast.channels[0].id.as_ref().unwrap())
502 /// .await
503 /// # };
504 /// ```
505 pub async fn get_channel(
506 &self,
507 channel_id: impl Into<PodcastChannelID<'_>>,
508 ) -> Result<<GetChannelQuery<'_> as Query<A>>::Output> {
509 self.query(GetChannelQuery::new(channel_id)).await
510 }
511 /// Gets a list of all Episodes for a Channel. Note, if GetPodcastChannel
512 /// doesn't contain `episode_params`, you can be sure that all episodes are
513 /// already included at `episodes` key.
514 /// ```no_run
515 /// # async {
516 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
517 /// .await
518 /// .unwrap();
519 /// let podcasts = yt.search_podcasts("Rustacean").await.unwrap();
520 /// let podcast = yt.get_podcast(&podcasts[0].podcast_id).await.unwrap();
521 /// let channel_id = podcast.channels[0].id.as_ref().unwrap();
522 /// let channel = yt.get_channel(channel_id).await.unwrap();
523 /// match channel.episode_params {
524 /// Some(p) => yt.get_channel_episodes(channel_id, p).await,
525 /// None => Ok(channel.episodes),
526 /// }
527 /// # };
528 /// ```
529 pub async fn get_channel_episodes<'a>(
530 &self,
531 channel_id: impl Into<PodcastChannelID<'a>>,
532 podcast_channel_params: impl Into<PodcastChannelParams<'a>>,
533 ) -> Result<<GetChannelEpisodesQuery<'_> as Query<A>>::Output> {
534 self.query(GetChannelEpisodesQuery::new(
535 channel_id,
536 podcast_channel_params,
537 ))
538 .await
539 }
540 /// Gets information about a Podcast, including Episodes.
541 /// ```no_run
542 /// # async {
543 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
544 /// .await
545 /// .unwrap();
546 /// let podcasts = yt.search_podcasts("Rustacean").await.unwrap();
547 /// yt.get_podcast(&podcasts[0].podcast_id).await
548 /// # };
549 /// ```
550 pub async fn get_podcast(
551 &self,
552 podcast_id: impl Into<PodcastID<'_>>,
553 ) -> Result<<GetPodcastQuery<'_> as Query<A>>::Output> {
554 self.query(GetPodcastQuery::new(podcast_id)).await
555 }
556 /// ```no_run
557 /// # async {
558 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
559 /// .await
560 /// .unwrap();
561 /// let episodes = yt.search_episodes("Ratatui").await.unwrap();
562 /// yt.get_episode(&episodes[0].episode_id).await
563 /// # };
564 /// ```
565 pub async fn get_episode(
566 &self,
567 episode_id: impl Into<EpisodeID<'_>>,
568 ) -> Result<<GetEpisodeQuery<'_> as Query<A>>::Output> {
569 self.query(GetEpisodeQuery::new(episode_id)).await
570 }
571 /// Gets the special 'New Episodes' playlist.
572 /// ```no_run
573 /// # async {
574 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
575 /// .await
576 /// .unwrap();
577 /// yt.get_new_episodes().await
578 /// # };
579 /// ```
580 pub async fn get_new_episodes(&self) -> Result<<GetNewEpisodesQuery as Query<A>>::Output> {
581 self.query(GetNewEpisodesQuery).await
582 }
583 /// Gets information about an user and their videos and playlists.
584 /// ```no_run
585 /// # async {
586 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
587 /// .await
588 /// .unwrap();
589 /// let results = yt.search_profiles("PewDiePie").await.unwrap();
590 /// yt.get_user(&results[0].profile_id).await
591 /// # };
592 /// ```
593 pub async fn get_user<'a>(&self, id: impl Into<UserChannelID<'a>>) -> Result<GetUser> {
594 self.query(GetUserQuery::new(id.into())).await
595 }
596 /// Gets a full list of videos for a user.
597 /// ```no_run
598 /// # async {
599 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
600 /// .await
601 /// .unwrap();
602 /// let user_id = &yt.search_profiles("PewDiePie").await.unwrap()[0].profile_id;
603 /// let user = yt.get_user(&*user_id).await.unwrap();
604 /// yt.get_user_videos(&*user_id, user.all_videos_params.unwrap())
605 /// .await
606 /// # };
607 /// ```
608 pub async fn get_user_videos<'a, T: Into<UserChannelID<'a>>, U: Into<UserVideosParams<'a>>>(
609 &self,
610 channel_id: T,
611 browse_params: U,
612 ) -> Result<Vec<UserVideo>> {
613 let query = GetUserVideosQuery::new(channel_id.into(), browse_params.into());
614 self.query(query).await
615 }
616 /// Gets a full list of playlists for a user.
617 /// ```no_run
618 /// # async {
619 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
620 /// .await
621 /// .unwrap();
622 /// let user_id = &yt.search_profiles("PewDiePie").await.unwrap()[0].profile_id;
623 /// let user = yt.get_user(&*user_id).await.unwrap();
624 /// yt.get_user_playlists(&*user_id, user.all_playlists_params.unwrap())
625 /// .await
626 /// # };
627 /// ```
628 pub async fn get_user_playlists<
629 'a,
630 T: Into<UserChannelID<'a>>,
631 U: Into<UserPlaylistsParams<'a>>,
632 >(
633 &self,
634 channel_id: T,
635 browse_params: U,
636 ) -> Result<Vec<UserPlaylist>> {
637 let query = GetUserPlaylistsQuery::new(channel_id.into(), browse_params.into());
638 self.query(query).await
639 }
640}
641
642impl<A: LoggedIn> YtMusic<A> {
643 /// Removes items from a playlist you own.
644 /// ```no_run
645 /// # async {
646 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE").await.unwrap();
647 /// let ytmapi_rs::parse::LibraryPlaylist { playlist_id, .. } =
648 /// yt.get_library_playlists().await.unwrap().pop().unwrap();
649 /// let source_playlist = yt.search_featured_playlists("Heavy metal")
650 /// .await
651 /// .unwrap();
652 /// let outcome = yt.add_playlist_to_playlist(
653 /// &playlist_id,
654 /// &source_playlist[0].playlist_id
655 /// ).await.unwrap();
656 /// yt.remove_playlist_items(
657 /// playlist_id,
658 /// outcome.iter().map(|o| (&o.set_video_id).into()),
659 /// ).await
660 /// # };
661 /// ```
662 pub async fn remove_playlist_items<'a, T: Into<PlaylistID<'a>>>(
663 &self,
664 playlist_id: T,
665 video_items: impl IntoIterator<Item = SetVideoID<'a>>,
666 ) -> Result<()> {
667 let query = RemovePlaylistItemsQuery::new(playlist_id.into(), video_items);
668 self.query(query).await
669 }
670 /// Makes changes to a playlist.
671 /// ```no_run
672 /// # async {
673 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE").await.unwrap();
674 /// let playlists = yt.get_library_playlists()
675 /// .await
676 /// .unwrap();
677 /// let query = ytmapi_rs::query::EditPlaylistQuery::new_title(
678 /// &playlists[0].playlist_id,
679 /// "Better playlist title",
680 /// )
681 /// .with_new_description("Edited description");
682 /// yt.edit_playlist(query).await
683 /// # };
684 /// ```
685 pub async fn edit_playlist(&self, query: EditPlaylistQuery<'_>) -> Result<ApiOutcome> {
686 self.query(query).await
687 }
688 /// Gets a list of all uploaded songs in your Library.
689 /// # Additional functionality
690 /// See [`GetLibraryUploadSongsQuery`] and [`YtMusic.query()`]
691 /// for more ways to construct and run.
692 ///
693 /// [`YtMusic.query()`]: crate::YtMusic::query
694 /// [GetLibraryUploadSongsQuery]: crate::query::GetLibraryUploadSongsQuery
695 ///
696 /// ```no_run
697 /// # async {
698 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
699 /// .await
700 /// .unwrap();
701 /// yt.get_library_upload_songs().await
702 /// # };
703 /// ```
704 pub async fn get_library_upload_songs(
705 &self,
706 ) -> Result<<GetLibraryUploadSongsQuery as Query<A>>::Output> {
707 let query = GetLibraryUploadSongsQuery::default();
708 self.query(query).await
709 }
710 /// Gets a list of all uploaded artists in your Library.
711 /// # Additional functionality
712 /// See [`GetLibraryUploadArtistsQuery`] and [`YtMusic.query()`]
713 /// for more ways to construct and run.
714 ///
715 /// [`YtMusic.query()`]: crate::YtMusic::query
716 /// [GetLibraryUploadArtistsQuery]: crate::query::GetLibraryUploadArtistsQuery
717 ///
718 /// ```no_run
719 /// # async {
720 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
721 /// .await
722 /// .unwrap();
723 /// yt.get_library_upload_artists().await
724 /// # };
725 /// ```
726 pub async fn get_library_upload_artists(
727 &self,
728 ) -> Result<<GetLibraryUploadArtistsQuery as Query<A>>::Output> {
729 let query = GetLibraryUploadArtistsQuery::default();
730 self.query(query).await
731 }
732 /// Gets a list of all uploaded albums in your Library.
733 /// # Additional functionality
734 /// See [`GetLibraryUploadAlbumsQuery`] and [`YtMusic.query()`]
735 /// for more ways to construct and run.
736 ///
737 /// [`YtMusic.query()`]: crate::YtMusic::query
738 /// [GetLibraryUploadAlbumsQuery]: crate::query::GetLibraryUploadAlbumsQuery
739 ///
740 /// ```no_run
741 /// # async {
742 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
743 /// .await
744 /// .unwrap();
745 /// yt.get_library_upload_albums().await
746 /// # };
747 /// ```
748 pub async fn get_library_upload_albums(
749 &self,
750 ) -> Result<<GetLibraryUploadAlbumsQuery as Query<A>>::Output> {
751 let query = GetLibraryUploadAlbumsQuery::default();
752 self.query(query).await
753 }
754 /// Gets information and tracks for an uploaded album in your Library.
755 /// ```no_run
756 /// # async {
757 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
758 /// .await
759 /// .unwrap();
760 /// let albums = yt.get_library_upload_albums().await.unwrap();
761 /// yt.get_library_upload_album(&albums[0].album_id).await
762 /// # };
763 /// ```
764 pub async fn get_library_upload_album<'a, T: Into<UploadAlbumID<'a>>>(
765 &self,
766 upload_album_id: T,
767 ) -> Result<<GetLibraryUploadAlbumQuery<'_> as Query<A>>::Output> {
768 let query = GetLibraryUploadAlbumQuery::new(upload_album_id.into());
769 self.query(query).await
770 }
771 /// Gets all tracks for an uploaded artist in your Library.
772 /// ```no_run
773 /// # async {
774 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
775 /// .await
776 /// .unwrap();
777 /// let artists = yt.get_library_upload_artists().await.unwrap();
778 /// yt.get_library_upload_artist(&artists[0].artist_id).await
779 /// # };
780 /// ```
781 pub async fn get_library_upload_artist<'a, T: Into<UploadArtistID<'a>>>(
782 &self,
783 upload_artist_id: T,
784 ) -> Result<<GetLibraryUploadArtistQuery<'_> as Query<A>>::Output> {
785 let query = GetLibraryUploadArtistQuery::new(upload_artist_id.into());
786 self.query(query).await
787 }
788 /// Deletes an upload entity from your library - this is either a song or an
789 /// album.
790 /// ```no_run
791 /// # async {
792 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
793 /// .await
794 /// .unwrap();
795 /// let albums = yt.get_library_upload_albums().await.unwrap();
796 /// yt.delete_upload_entity(&albums[0].entity_id).await
797 /// # };
798 /// ```
799 pub async fn delete_upload_entity<'a, T: Into<UploadEntityID<'a>>>(
800 &self,
801 upload_entity_id: T,
802 ) -> Result<<DeleteUploadEntityQuery<'_> as Query<A>>::Output> {
803 let query = DeleteUploadEntityQuery::new(upload_entity_id.into());
804 self.query(query).await
805 }
806 /// Removes a list of items from your recently played history.
807 /// ```no_run
808 /// # async {
809 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
810 /// .await
811 /// .unwrap();
812 /// let history = yt.get_history().await.unwrap();
813 /// let first_history_token = match history.first().unwrap().items.first().unwrap() {
814 /// ytmapi_rs::parse::HistoryItem::Song(i) => &i.feedback_token_remove,
815 /// ytmapi_rs::parse::HistoryItem::Video(i) => &i.feedback_token_remove,
816 /// ytmapi_rs::parse::HistoryItem::Episode(i) => &i.feedback_token_remove,
817 /// ytmapi_rs::parse::HistoryItem::UploadSong(i) => &i.feedback_token_remove,
818 /// }
819 /// .into();
820 /// yt.remove_history_items(vec![first_history_token]).await
821 /// # };
822 /// ```
823 pub async fn remove_history_items(
824 &self,
825 feedback_tokens: impl IntoIterator<Item = FeedbackTokenRemoveFromHistory<'_>>,
826 ) -> Result<Vec<ApiOutcome>> {
827 let query = RemoveHistoryItemsQuery::new(feedback_tokens);
828 self.query(query).await
829 }
830 // TODO: Docs / alternative constructors.
831 pub async fn edit_song_library_status(
832 &self,
833 query: EditSongLibraryStatusQuery<'_>,
834 ) -> Result<Vec<ApiOutcome>> {
835 self.query(query).await
836 }
837 /// Sets the like status for a song.
838 /// ```no_run
839 /// # async {
840 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
841 /// .await
842 /// .unwrap();
843 /// let results = yt
844 /// .search_songs("While My Guitar Gently Weeps")
845 /// .await
846 /// .unwrap();
847 /// yt.rate_song(&results[0].video_id, ytmapi_rs::common::LikeStatus::Liked)
848 /// .await
849 /// # };
850 /// ```
851 pub async fn rate_song<'a, T: Into<VideoID<'a>>>(
852 &self,
853 video_id: T,
854 rating: LikeStatus,
855 ) -> Result<()> {
856 let query = RateSongQuery::new(video_id.into(), rating);
857 self.query(query).await
858 }
859 /// Sets the like status for a playlist.
860 /// A 'Liked' playlist will be added to your library, an 'Indifferent' will
861 /// be removed, and a 'Disliked' will reduce the chance of it appearing in
862 /// your recommendations.
863 /// ```no_run
864 /// # async {
865 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE").await.unwrap();
866 /// let results = yt.search_featured_playlists("Heavy metal")
867 /// .await
868 /// .unwrap();
869 /// yt.rate_playlist(
870 /// &results[0].playlist_id,
871 /// ytmapi_rs::common::LikeStatus::Liked,
872 /// ).await
873 /// # };
874 /// ```
875 pub async fn rate_playlist<'a, T: Into<PlaylistID<'a>>>(
876 &self,
877 playlist_id: T,
878 rating: LikeStatus,
879 ) -> Result<()> {
880 let query = RatePlaylistQuery::new(playlist_id.into(), rating);
881 self.query(query).await
882 }
883 /// Deletes a playlist you own.
884 /// ```no_run
885 /// # async {
886 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE").await.unwrap();
887 /// let results = yt.get_library_playlists().await.unwrap();
888 /// yt.delete_playlist(&results[0].playlist_id).await
889 /// # };
890 /// ```
891 pub async fn delete_playlist<'a, T: Into<PlaylistID<'a>>>(&self, playlist_id: T) -> Result<()> {
892 let query = DeletePlaylistQuery::new(playlist_id.into());
893 self.query(query).await
894 }
895 /// Creates a new playlist.
896 /// ```no_run
897 /// # async {
898 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE").await.unwrap();
899 /// let playlists = yt.search_featured_playlists("Heavy metal")
900 /// .await
901 /// .unwrap();
902 /// let query = ytmapi_rs::query::CreatePlaylistQuery::new(
903 /// "My heavy metal playlist",
904 /// None,
905 /// ytmapi_rs::query::playlist::PrivacyStatus::Public,
906 /// )
907 /// .with_source(&playlists[0].playlist_id);
908 /// yt.create_playlist(query).await
909 /// # };
910 /// ```
911 pub async fn create_playlist<T: CreatePlaylistType>(
912 &self,
913 query: CreatePlaylistQuery<'_, T>,
914 ) -> Result<PlaylistID<'static>> {
915 self.query(query).await
916 }
917 /// Adds video items to a playlist you own.
918 /// ```no_run
919 /// # async {
920 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE").await.unwrap();
921 /// let ytmapi_rs::parse::LibraryPlaylist { playlist_id, .. } =
922 /// yt.get_library_playlists().await.unwrap().pop().unwrap();
923 /// let songs = yt.search_songs("Master of puppets").await.unwrap();
924 /// yt.add_video_items_to_playlist(
925 /// playlist_id,
926 /// songs.iter().map(|s| (&s.video_id).into())
927 /// ).await
928 /// # };
929 /// ```
930 pub async fn add_video_items_to_playlist<'a, T: Into<PlaylistID<'a>>>(
931 &self,
932 playlist_id: T,
933 video_ids: impl IntoIterator<Item = VideoID<'a>>,
934 ) -> Result<Vec<AddPlaylistItem>> {
935 let query = AddPlaylistItemsQuery::new_from_videos(
936 playlist_id.into(),
937 video_ids,
938 DuplicateHandlingMode::default(),
939 );
940 self.query(query).await
941 }
942 /// Appends another playlist to a playlist you own.
943 /// ```no_run
944 /// # async {
945 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE").await.unwrap();
946 /// let ytmapi_rs::parse::LibraryPlaylist { playlist_id, .. } =
947 /// yt.get_library_playlists().await.unwrap().pop().unwrap();
948 /// let source_playlist = yt.search_featured_playlists("Heavy metal")
949 /// .await
950 /// .unwrap();
951 /// yt.add_playlist_to_playlist(
952 /// playlist_id,
953 /// &source_playlist[0].playlist_id
954 /// ).await
955 /// # };
956 /// ```
957 pub async fn add_playlist_to_playlist<'a, T: Into<PlaylistID<'a>>, U: Into<PlaylistID<'a>>>(
958 &self,
959 destination_playlist: T,
960 source_playlist: U,
961 ) -> Result<Vec<AddPlaylistItem>> {
962 let query = AddPlaylistItemsQuery::new_from_playlist(
963 destination_playlist.into(),
964 source_playlist.into(),
965 );
966 self.query(query).await
967 }
968 /// Gets a list of all playlists in your Library.
969 /// ```no_run
970 /// # async {
971 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
972 /// .await
973 /// .unwrap();
974 /// yt.get_library_playlists().await;
975 /// # };
976 /// ```
977 pub async fn get_library_playlists(&self) -> Result<Vec<LibraryPlaylist>> {
978 let query = GetLibraryPlaylistsQuery;
979 self.query(query).await
980 }
981 /// Gets a list of all artists in your Library.
982 /// # Additional functionality
983 /// See [`GetLibraryArtistsQuery`] and [`YtMusic.query()`]
984 /// for more ways to construct and run.
985 ///
986 /// [`YtMusic.query()`]: crate::YtMusic::query
987 /// [GetLibraryArtistsQuery]: crate::query::GetLibraryArtistsQuery
988 ///
989 /// ```no_run
990 /// # async {
991 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
992 /// .await
993 /// .unwrap();
994 /// let results = yt.get_library_artists().await;
995 /// # };
996 /// ```
997 pub async fn get_library_artists(&self) -> Result<Vec<LibraryArtist>> {
998 let query = GetLibraryArtistsQuery::default();
999 self.query(query).await
1000 }
1001 /// Gets a list of all songs in your Library.
1002 /// # Additional functionality
1003 /// See [`GetLibrarySongsQuery`] and [`YtMusic.query()`]
1004 /// for more ways to construct and run.
1005 ///
1006 /// [`YtMusic.query()`]: crate::YtMusic::query
1007 /// [GetLibrarySongsQuery]: crate::query::GetLibrarySongsQuery
1008 ///
1009 /// ```no_run
1010 /// # async {
1011 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
1012 /// .await
1013 /// .unwrap();
1014 /// let results = yt.get_library_songs().await;
1015 /// # };
1016 /// ```
1017 pub async fn get_library_songs(&self) -> Result<<GetLibrarySongsQuery as Query<A>>::Output> {
1018 let query = GetLibrarySongsQuery::default();
1019 self.query(query).await
1020 }
1021 /// Gets a list of all albums in your Library.
1022 /// # Additional functionality
1023 /// See [`GetLibraryAlbumsQuery`] and [`YtMusic.query()`]
1024 /// for more ways to construct and run.
1025 ///
1026 /// [`YtMusic.query()`]: crate::YtMusic::query
1027 /// [GetLibraryAlbumsQuery]: crate::query::GetLibraryAlbumsQuery
1028 ///
1029 /// ```no_run
1030 /// # async {
1031 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
1032 /// .await
1033 /// .unwrap();
1034 /// let results = yt.get_library_albums().await;
1035 /// # };
1036 /// ```
1037 pub async fn get_library_albums(&self) -> Result<Vec<SearchResultAlbum>> {
1038 let query = GetLibraryAlbumsQuery::default();
1039 self.query(query).await
1040 }
1041 /// Gets a list of all artist subscriptions in your Library.
1042 /// # Additional functionality
1043 /// See [`GetLibraryArtistSubscriptionsQuery`] and [`YtMusic.query()`]
1044 /// for more ways to construct and run.
1045 ///
1046 /// [`YtMusic.query()`]: crate::YtMusic::query
1047 /// [GetLibraryArtistSubscriptionsQuery]: crate::query::GetLibraryArtistSubscriptionsQuery
1048 ///
1049 /// ```no_run
1050 /// # async {
1051 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
1052 /// .await
1053 /// .unwrap();
1054 /// let results = yt.get_library_artist_subscriptions().await;
1055 /// # };
1056 /// ```
1057 pub async fn get_library_artist_subscriptions(&self) -> Result<Vec<LibraryArtistSubscription>> {
1058 let query = GetLibraryArtistSubscriptionsQuery::default();
1059 self.query(query).await
1060 }
1061 /// Gets a list of all podcasts in your Library.
1062 /// # Additional functionality
1063 /// See [`GetLibraryPodcastsQuery`] and [`YtMusic.query()`]
1064 /// for more ways to construct and run.
1065 ///
1066 /// [`YtMusic.query()`]: crate::YtMusic::query
1067 /// [GetLibraryPodcastsQuery]: crate::query::GetLibraryPodcastsQuery
1068 ///
1069 /// ```no_run
1070 /// # async {
1071 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
1072 /// .await
1073 /// .unwrap();
1074 /// let results = yt.get_library_podcasts().await;
1075 /// # };
1076 /// ```
1077 pub async fn get_library_podcasts(
1078 &self,
1079 ) -> Result<<GetLibraryPodcastsQuery as Query<A>>::Output> {
1080 let query = GetLibraryPodcastsQuery::default();
1081 self.query(query).await
1082 }
1083 /// Gets a list of all channels in your Library.
1084 /// # Additional functionality
1085 /// See [`GetLibraryChannelsQuery`] and [`YtMusic.query()`]
1086 /// for more ways to construct and run.
1087 ///
1088 /// [`YtMusic.query()`]: crate::YtMusic::query
1089 /// [GetLibraryChannelsQuery]: crate::query::GetLibraryChannelsQuery
1090 ///
1091 /// ```no_run
1092 /// # async {
1093 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
1094 /// .await
1095 /// .unwrap();
1096 /// let results = yt.get_library_channels().await;
1097 /// # };
1098 /// ```
1099 pub async fn get_library_channels(
1100 &self,
1101 ) -> Result<<GetLibraryChannelsQuery as Query<A>>::Output> {
1102 let query = GetLibraryChannelsQuery::default();
1103 self.query(query).await
1104 }
1105 /// Gets your recently played history.
1106 /// ```no_run
1107 /// # async {
1108 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
1109 /// .await
1110 /// .unwrap();
1111 /// let results = yt.get_history().await;
1112 /// # };
1113 /// ```
1114 pub async fn get_history(&self) -> Result<Vec<HistoryPeriod>> {
1115 let query = GetHistoryQuery;
1116 self.query(query).await
1117 }
1118 /// Adds an item to the accounts history.
1119 /// ```no_run
1120 /// # async {
1121 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
1122 /// .await
1123 /// .unwrap();
1124 /// let song = yt
1125 /// .search_songs("While My Guitar Gently Weeps")
1126 /// .await
1127 /// .unwrap()
1128 /// .into_iter()
1129 /// .next()
1130 /// .unwrap();
1131 /// let url = yt.get_song_tracking_url(song.video_id).await.unwrap();
1132 /// yt.add_history_item(url).await
1133 /// # };
1134 /// ```
1135 pub async fn add_history_item<'a, T: Into<SongTrackingUrl<'a>>>(
1136 &self,
1137 song_url: T,
1138 ) -> Result<<AddHistoryItemQuery<'a> as Query<A>>::Output> {
1139 self.query(AddHistoryItemQuery::new(song_url.into())).await
1140 }
1141 /// Subscribe to an artist.
1142 /// This does not error if the artist was already subscribed to.
1143 /// ```no_run
1144 /// # async {
1145 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
1146 /// .await
1147 /// .unwrap();
1148 /// let the_beatles = &yt.search_artists("The Beatles").await.unwrap()[0].browse_id;
1149 /// yt.subscribe_artist(the_beatles).await
1150 /// # };
1151 /// ```
1152 pub async fn subscribe_artist(&self, channel_id: impl Into<ArtistChannelID<'_>>) -> Result<()> {
1153 self.query(SubscribeArtistQuery::new(channel_id.into()))
1154 .await
1155 }
1156 /// Unsubscribe to one or more artists.
1157 /// This does not error if the artists were not subscribed.
1158 /// ```no_run
1159 /// # async {
1160 /// let yt = ytmapi_rs::YtMusic::from_cookie("FAKE COOKIE")
1161 /// .await
1162 /// .unwrap();
1163 /// let some_beatles = yt
1164 /// .search_artists("The Beatles")
1165 /// .await
1166 /// .unwrap()
1167 /// .into_iter()
1168 /// .map(|artist| artist.browse_id)
1169 /// .take(2);
1170 /// yt.unsubscribe_artists(some_beatles).await
1171 /// # };
1172 /// ```
1173 pub async fn unsubscribe_artists<'a>(
1174 &self,
1175 channels: impl IntoIterator<Item = impl Into<ArtistChannelID<'a>>>,
1176 ) -> Result<()> {
1177 self.query(UnsubscribeArtistsQuery::new(
1178 channels.into_iter().map(Into::into),
1179 ))
1180 .await
1181 }
1182}