ytmapi_rs/query/
library.rs

1use super::{PostMethod, PostQuery, Query};
2use crate::{
3    auth::AuthToken,
4    common::{ApiOutcome, FeedbackTokenAddToLibrary, FeedbackTokenRemoveFromLibrary, YoutubeID},
5    parse::{
6        GetLibraryAlbums, GetLibraryArtistSubscriptions, GetLibraryArtists, GetLibraryPlaylists,
7        GetLibrarySongs,
8    },
9};
10use serde_json::json;
11use std::borrow::Cow;
12
13// NOTE: Authentication is required to use the queries in this module.
14// Currently, all queries are implemented with authentication however in future
15// this could be scaled back.
16
17#[derive(Default, Clone)]
18pub enum GetLibrarySortOrder {
19    NameAsc,
20    NameDesc,
21    MostSongs,
22    RecentlySaved,
23    #[default]
24    Default,
25}
26
27pub struct GetLibraryPlaylistsQuery;
28#[derive(Default)]
29// TODO: Method to add sort order
30pub struct GetLibrarySongsQuery {
31    sort_order: GetLibrarySortOrder,
32}
33#[derive(Default)]
34// TODO: Method to add sort order
35pub struct GetLibraryAlbumsQuery {
36    sort_order: GetLibrarySortOrder,
37}
38#[derive(Default)]
39// TODO: Method to add sort order
40pub struct GetLibraryArtistSubscriptionsQuery {
41    sort_order: GetLibrarySortOrder,
42}
43#[derive(Default)]
44// TODO: Method to add sort order
45pub struct GetLibraryArtistsQuery {
46    sort_order: GetLibrarySortOrder,
47}
48pub struct EditSongLibraryStatusQuery<'a> {
49    add_to_library_feedback_tokens: Vec<FeedbackTokenAddToLibrary<'a>>,
50    remove_from_library_feedback_tokens: Vec<FeedbackTokenRemoveFromLibrary<'a>>,
51}
52
53impl GetLibrarySongsQuery {
54    pub fn new(sort_order: GetLibrarySortOrder) -> Self {
55        Self { sort_order }
56    }
57}
58impl GetLibraryAlbumsQuery {
59    pub fn new(sort_order: GetLibrarySortOrder) -> Self {
60        Self { sort_order }
61    }
62}
63impl GetLibraryArtistSubscriptionsQuery {
64    pub fn new(sort_order: GetLibrarySortOrder) -> Self {
65        Self { sort_order }
66    }
67}
68impl GetLibraryArtistsQuery {
69    pub fn new(sort_order: GetLibrarySortOrder) -> Self {
70        Self { sort_order }
71    }
72}
73impl<'a> EditSongLibraryStatusQuery<'a> {
74    pub fn new_from_add_to_library_feedback_tokens(
75        add_to_library_feedback_tokens: Vec<FeedbackTokenAddToLibrary<'a>>,
76    ) -> Self {
77        Self {
78            add_to_library_feedback_tokens,
79            remove_from_library_feedback_tokens: vec![],
80        }
81    }
82    pub fn new_from_remove_from_library_feedback_tokens(
83        remove_from_library_feedback_tokens: Vec<FeedbackTokenRemoveFromLibrary<'a>>,
84    ) -> Self {
85        Self {
86            add_to_library_feedback_tokens: vec![],
87            remove_from_library_feedback_tokens,
88        }
89    }
90    pub fn with_add_to_library_feedback_tokens(
91        mut self,
92        add_to_library_feedback_tokens: Vec<FeedbackTokenAddToLibrary<'a>>,
93    ) -> Self {
94        self.add_to_library_feedback_tokens = add_to_library_feedback_tokens;
95        self
96    }
97    pub fn with_remove_from_library_feedback_tokens(
98        mut self,
99        remove_from_library_feedback_tokens: Vec<FeedbackTokenRemoveFromLibrary<'a>>,
100    ) -> Self {
101        self.remove_from_library_feedback_tokens = remove_from_library_feedback_tokens;
102        self
103    }
104}
105
106impl<A: AuthToken> Query<A> for GetLibraryPlaylistsQuery {
107    type Output = GetLibraryPlaylists;
108    type Method = PostMethod;
109}
110impl PostQuery for GetLibraryPlaylistsQuery {
111    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
112        FromIterator::from_iter([("browseId".to_string(), json!("FEmusic_liked_playlists"))])
113    }
114    fn path(&self) -> &str {
115        "browse"
116    }
117    fn params(&self) -> Vec<(&str, Cow<str>)> {
118        vec![]
119    }
120}
121impl<A: AuthToken> Query<A> for GetLibraryArtistsQuery {
122    type Output = GetLibraryArtists;
123    type Method = PostMethod;
124}
125impl PostQuery for GetLibraryArtistsQuery {
126    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
127        if let Some(params) = get_sort_order_params(&self.sort_order) {
128            FromIterator::from_iter([
129                (
130                    "browseId".to_string(),
131                    json!("FEmusic_library_corpus_track_artists"),
132                ),
133                ("params".to_string(), json!(params)),
134            ])
135        } else {
136            FromIterator::from_iter([(
137                "browseId".to_string(),
138                json!("FEmusic_library_corpus_track_artists"),
139            )])
140        }
141    }
142    fn path(&self) -> &str {
143        "browse"
144    }
145    fn params(&self) -> Vec<(&str, Cow<str>)> {
146        vec![]
147    }
148}
149
150impl<A: AuthToken> Query<A> for GetLibrarySongsQuery {
151    type Output = GetLibrarySongs;
152    type Method = PostMethod;
153}
154impl PostQuery for GetLibrarySongsQuery {
155    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
156        if let Some(params) = get_sort_order_params(&self.sort_order) {
157            serde_json::Map::from_iter([
158                ("browseId".to_string(), json!("FEmusic_liked_videos")),
159                ("params".to_string(), json!(params)),
160            ])
161        } else {
162            serde_json::Map::from_iter([("browseId".to_string(), json!("FEmusic_liked_videos"))])
163        }
164    }
165    fn params(&self) -> Vec<(&str, Cow<str>)> {
166        vec![]
167    }
168    fn path(&self) -> &str {
169        "browse"
170    }
171}
172impl<A: AuthToken> Query<A> for GetLibraryAlbumsQuery {
173    type Output = GetLibraryAlbums;
174    type Method = PostMethod;
175}
176impl PostQuery for GetLibraryAlbumsQuery {
177    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
178        if let Some(params) = get_sort_order_params(&self.sort_order) {
179            serde_json::Map::from_iter([
180                ("browseId".to_string(), json!("FEmusic_liked_albums")),
181                ("params".to_string(), json!(params)),
182            ])
183        } else {
184            serde_json::Map::from_iter([("browseId".to_string(), json!("FEmusic_liked_albums"))])
185        }
186    }
187    fn params(&self) -> Vec<(&str, Cow<str>)> {
188        vec![]
189    }
190    fn path(&self) -> &str {
191        "browse"
192    }
193}
194impl<A: AuthToken> Query<A> for GetLibraryArtistSubscriptionsQuery {
195    type Output = GetLibraryArtistSubscriptions;
196    type Method = PostMethod;
197}
198impl PostQuery for GetLibraryArtistSubscriptionsQuery {
199    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
200        if let Some(params) = get_sort_order_params(&self.sort_order) {
201            serde_json::Map::from_iter([
202                (
203                    "browseId".to_string(),
204                    json!("FEmusic_library_corpus_artists"),
205                ),
206                ("params".to_string(), json!(params)),
207            ])
208        } else {
209            serde_json::Map::from_iter([(
210                "browseId".to_string(),
211                json!("FEmusic_library_corpus_artists"),
212            )])
213        }
214    }
215    fn params(&self) -> Vec<(&str, Cow<str>)> {
216        vec![]
217    }
218    fn path(&self) -> &str {
219        "browse"
220    }
221}
222// NOTE: Does not work on brand accounts
223// NOTE: Auth required
224impl<A: AuthToken> Query<A> for EditSongLibraryStatusQuery<'_> {
225    type Output = Vec<ApiOutcome>;
226    type Method = PostMethod;
227}
228impl PostQuery for EditSongLibraryStatusQuery<'_> {
229    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
230        let add_feedback_tokens_raw = self
231            .add_to_library_feedback_tokens
232            .iter()
233            .map(|t| t.get_raw());
234        let feedback_tokens = self
235            .remove_from_library_feedback_tokens
236            .iter()
237            .map(|t| t.get_raw())
238            .chain(add_feedback_tokens_raw)
239            .collect::<Vec<_>>();
240        serde_json::Map::from_iter([("feedbackTokens".to_string(), json!(feedback_tokens))])
241    }
242    fn params(&self) -> Vec<(&str, Cow<str>)> {
243        vec![]
244    }
245    fn path(&self) -> &str {
246        "feedback"
247    }
248}
249
250pub(crate) fn get_sort_order_params(o: &GetLibrarySortOrder) -> Option<&'static str> {
251    match o {
252        GetLibrarySortOrder::NameAsc => Some("ggMGKgQIARAA"),
253        GetLibrarySortOrder::NameDesc => Some("ggMGKgQIARAB"),
254        // This option is available in the UI - but unsure where to get the params from.
255        GetLibrarySortOrder::MostSongs => todo!(),
256        GetLibrarySortOrder::RecentlySaved => Some("ggMGKgQIABAB"),
257        GetLibrarySortOrder::Default => None,
258    }
259}