1use super::{PostMethod, PostQuery, Query};
2use crate::Result;
3use crate::auth::AuthToken;
4use crate::common::{LyricsID, SongTrackingUrl, VideoID, YoutubeID};
5use crate::parse::Lyrics;
6use serde_json::json;
7use std::borrow::Cow;
8use std::time::SystemTime;
9
10pub struct GetLyricsIDQuery<'a> {
11 video_id: VideoID<'a>,
12}
13
14pub struct GetLyricsQuery<'a> {
15 id: LyricsID<'a>,
16}
17
18pub struct GetSongTrackingUrlQuery<'a> {
19 video_id: VideoID<'a>,
20 signature_timestamp: u64,
21}
22
23impl<'a> GetLyricsIDQuery<'a> {
24 pub fn new(video_id: VideoID<'a>) -> GetLyricsIDQuery<'a> {
25 GetLyricsIDQuery { video_id }
26 }
27}
28
29impl<'a> GetLyricsQuery<'a> {
30 pub fn new(id: LyricsID<'a>) -> GetLyricsQuery<'a> {
31 GetLyricsQuery { id }
32 }
33}
34
35impl GetSongTrackingUrlQuery<'_> {
36 pub fn new(video_id: VideoID<'_>) -> Result<GetSongTrackingUrlQuery<'_>> {
41 let signature_timestamp = get_signature_timestamp()?;
42 Ok(GetSongTrackingUrlQuery {
43 video_id,
44 signature_timestamp,
45 })
46 }
47}
48
49impl<A: AuthToken> Query<A> for GetLyricsIDQuery<'_> {
50 type Output = LyricsID<'static>;
51 type Method = PostMethod;
52}
53impl PostQuery for GetLyricsIDQuery<'_> {
54 fn header(&self) -> serde_json::Map<String, serde_json::Value> {
55 let serde_json::Value::Object(map) = json!({
56 "enablePersistentPlaylistPanel": true,
57 "isAudioOnly": true,
58 "tunerSettingValue": "AUTOMIX_SETTING_NORMAL",
59 "playlistId" : format!("RDAMVM{}", self.video_id.get_raw()),
60 "videoId" : self.video_id.get_raw(),
61 }) else {
62 unreachable!()
63 };
64 map
65 }
66 fn path(&self) -> &str {
67 "next"
68 }
69 fn params(&self) -> Vec<(&str, Cow<'_, str>)> {
70 vec![]
71 }
72}
73
74impl<A: AuthToken> Query<A> for GetLyricsQuery<'_> {
75 type Output = Lyrics;
76 type Method = PostMethod;
77}
78impl PostQuery for GetLyricsQuery<'_> {
79 fn header(&self) -> serde_json::Map<String, serde_json::Value> {
80 let serde_json::Value::Object(map) = json!({
81 "browseId": self.id.get_raw(),
82 }) else {
83 unreachable!()
84 };
85 map
86 }
87 fn path(&self) -> &str {
88 "browse"
89 }
90 fn params(&self) -> std::vec::Vec<(&str, std::borrow::Cow<'_, str>)> {
91 vec![]
92 }
93}
94
95impl<A: AuthToken> Query<A> for GetSongTrackingUrlQuery<'_> {
96 type Output = SongTrackingUrl<'static>;
97 type Method = PostMethod;
98}
99impl PostQuery for GetSongTrackingUrlQuery<'_> {
100 fn header(&self) -> serde_json::Map<String, serde_json::Value> {
101 serde_json::Map::from_iter([
102 (
103 "playbackContext".to_string(),
104 json!(
105 {
106 "contentPlaybackContext": {
107 "signatureTimestamp": self.signature_timestamp
108 }
109 }
110 ),
111 ),
112 ("video_id".to_string(), json!(self.video_id)),
113 ])
114 }
115 fn params(&self) -> Vec<(&str, Cow<'_, str>)> {
116 vec![]
117 }
118 fn path(&self) -> &str {
119 "player"
120 }
121}
122
123fn get_signature_timestamp() -> Result<u64> {
127 const SECONDS_IN_DAY: u64 = 60 * 60 * 24;
128 Ok(SystemTime::now()
129 .duration_since(SystemTime::UNIX_EPOCH)?
130 .as_secs()
131 .saturating_div(SECONDS_IN_DAY))
133}