ytmapi_rs/query/
history.rs

1use super::{GetMethod, GetQuery, PostMethod, PostQuery, Query};
2use crate::{
3    auth::AuthToken,
4    common::{ApiOutcome, FeedbackTokenRemoveFromHistory, SongTrackingUrl, YoutubeID},
5    parse::HistoryPeriod,
6};
7use rand::Rng;
8use serde_json::json;
9use std::borrow::Cow;
10
11pub struct GetHistoryQuery;
12pub struct RemoveHistoryItemsQuery<'a> {
13    feedback_tokens: Vec<FeedbackTokenRemoveFromHistory<'a>>,
14}
15pub struct AddHistoryItemQuery<'a> {
16    song_url: SongTrackingUrl<'a>,
17}
18
19impl<'a> RemoveHistoryItemsQuery<'a> {
20    pub fn new(feedback_tokens: Vec<FeedbackTokenRemoveFromHistory<'a>>) -> Self {
21        Self { feedback_tokens }
22    }
23}
24
25impl<'a> AddHistoryItemQuery<'a> {
26    pub fn new(song_url: SongTrackingUrl<'a>) -> Self {
27        Self { song_url }
28    }
29}
30
31// NOTE: Requires auth
32// TODO: Return played and feedback_token component.
33impl<A: AuthToken> Query<A> for GetHistoryQuery {
34    type Output = Vec<HistoryPeriod>;
35    type Method = PostMethod;
36}
37impl PostQuery for GetHistoryQuery {
38    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
39        serde_json::Map::from_iter([("browseId".to_string(), json!("FEmusic_history"))])
40    }
41    fn params(&self) -> std::vec::Vec<(&str, std::borrow::Cow<'_, str>)> {
42        vec![]
43    }
44    fn path(&self) -> &str {
45        "browse"
46    }
47}
48
49// NOTE: Does not work on brand accounts
50impl<A: AuthToken> Query<A> for RemoveHistoryItemsQuery<'_> {
51    type Output = Vec<ApiOutcome>;
52    type Method = PostMethod;
53}
54impl PostQuery for RemoveHistoryItemsQuery<'_> {
55    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
56        serde_json::Map::from_iter([("feedbackTokens".to_string(), json!(self.feedback_tokens))])
57    }
58    fn params(&self) -> std::vec::Vec<(&str, std::borrow::Cow<'_, str>)> {
59        vec![]
60    }
61    fn path(&self) -> &str {
62        "feedback"
63    }
64}
65
66impl<A: AuthToken> Query<A> for AddHistoryItemQuery<'_> {
67    type Output = ();
68    type Method = GetMethod;
69}
70
71impl GetQuery for AddHistoryItemQuery<'_> {
72    fn url(&self) -> &str {
73        self.song_url.get_raw()
74    }
75    fn params(&self) -> Vec<(&str, Cow<str>)> {
76        // Original implementation by sigma67
77        // https://github.com/sigma67/ytmusicapi/blob/a15d90c4f356a530c6b2596277a9d70c0b117a0c/ytmusicapi/mixins/library.py#L310
78        let possible_chars: Vec<char> =
79            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
80                .chars()
81                .collect();
82        let random_cpn: String = rand::rng()
83            .sample_iter(
84                rand::distr::slice::Choose::new(&possible_chars)
85                    .expect("Provided a hard-coded non-empty slice"),
86            )
87            .take(16)
88            .collect();
89        vec![
90            ("ver", "2".into()),
91            ("c", "WEB_REMIX".into()),
92            ("cpn", random_cpn.into()),
93        ]
94    }
95}