ytmapi_rs/query/playlist/
edit.rs

1use super::PrivacyStatus;
2use crate::{
3    auth::AuthToken,
4    common::{ApiOutcome, PlaylistID, SetVideoID, YoutubeID},
5    query::{PostMethod, PostQuery, Query},
6};
7use serde_json::json;
8use std::borrow::Cow;
9
10// TODO: Confirm if all options can be passed - or mutually exclusive.
11pub struct EditPlaylistQuery<'a> {
12    id: PlaylistID<'a>,
13    new_title: Option<Cow<'a, str>>,
14    new_description: Option<Cow<'a, str>>,
15    new_privacy_status: Option<PrivacyStatus>,
16    swap_videos_order: Option<(SetVideoID<'a>, SetVideoID<'a>)>,
17    change_add_order: Option<AddOrder>,
18    add_playlist: Option<PlaylistID<'a>>,
19}
20
21#[derive(Default)]
22pub enum AddOrder {
23    AddToTop,
24    #[default]
25    AddToBottom,
26}
27
28impl<'a> EditPlaylistQuery<'a> {
29    pub fn new_title<T: Into<PlaylistID<'a>>, S: Into<Cow<'a, str>>>(id: T, new_title: S) -> Self {
30        let id = id.into();
31        Self {
32            id,
33            new_title: Some(new_title.into()),
34            new_description: None,
35            new_privacy_status: None,
36            swap_videos_order: None,
37            change_add_order: None,
38            add_playlist: None,
39        }
40    }
41    pub fn new_description<T: Into<PlaylistID<'a>>, S: Into<Cow<'a, str>>>(
42        id: T,
43        new_description: S,
44    ) -> Self {
45        let id = id.into();
46        Self {
47            id,
48            new_title: None,
49            new_description: Some(new_description.into()),
50            new_privacy_status: None,
51            swap_videos_order: None,
52            change_add_order: None,
53            add_playlist: None,
54        }
55    }
56    pub fn new_privacy_status<T: Into<PlaylistID<'a>>>(
57        id: T,
58        new_privacy_status: PrivacyStatus,
59    ) -> Self {
60        let id = id.into();
61        Self {
62            id,
63            new_title: None,
64            new_privacy_status: Some(new_privacy_status),
65            new_description: None,
66            swap_videos_order: None,
67            change_add_order: None,
68            add_playlist: None,
69        }
70    }
71    pub fn swap_videos_order<T: Into<PlaylistID<'a>>>(
72        id: T,
73        video_1: SetVideoID<'a>,
74        video_2: SetVideoID<'a>,
75    ) -> Self {
76        let id = id.into();
77        Self {
78            id,
79            new_title: None,
80            swap_videos_order: Some((video_1, video_2)),
81            new_privacy_status: None,
82            new_description: None,
83            change_add_order: None,
84            add_playlist: None,
85        }
86    }
87    pub fn change_add_order<T: Into<PlaylistID<'a>>>(id: T, change_add_order: AddOrder) -> Self {
88        let id = id.into();
89        Self {
90            id,
91            new_title: None,
92            change_add_order: Some(change_add_order),
93            new_privacy_status: None,
94            swap_videos_order: None,
95            new_description: None,
96            add_playlist: None,
97        }
98    }
99    pub fn add_playlist<T: Into<PlaylistID<'a>>>(id: T, add_playlist: PlaylistID<'a>) -> Self {
100        let id = id.into();
101        Self {
102            id,
103            new_title: None,
104            add_playlist: Some(add_playlist),
105            new_privacy_status: None,
106            swap_videos_order: None,
107            change_add_order: None,
108            new_description: None,
109        }
110    }
111    pub fn with_new_description<S: Into<Cow<'a, str>>>(mut self, new_description: S) -> Self {
112        self.new_description = Some(new_description.into());
113        self
114    }
115    pub fn with_new_privacy_status(mut self, new_privacy_status: PrivacyStatus) -> Self {
116        self.new_privacy_status = Some(new_privacy_status);
117        self
118    }
119    pub fn with_change_add_order(mut self, change_add_order: AddOrder) -> Self {
120        self.change_add_order = Some(change_add_order);
121        self
122    }
123    pub fn with_add_playlist(mut self, add_playlist: PlaylistID<'a>) -> Self {
124        self.add_playlist = Some(add_playlist);
125        self
126    }
127    pub fn with_swap_videos_order(
128        mut self,
129        first_video: SetVideoID<'a>,
130        second_video: SetVideoID<'a>,
131    ) -> Self {
132        self.swap_videos_order = Some((first_video, second_video));
133        self
134    }
135}
136
137impl<A: AuthToken> Query<A> for EditPlaylistQuery<'_> {
138    type Output = ApiOutcome;
139    type Method = PostMethod;
140}
141impl PostQuery for EditPlaylistQuery<'_> {
142    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
143        let mut actions = Vec::new();
144        if let Some(new_title) = &self.new_title {
145            actions.push(json!({
146                "action" : "ACTION_SET_PLAYLIST_NAME",
147                "playlistName" : new_title
148            }))
149        };
150        if let Some(new_description) = &self.new_description {
151            actions.push(json!({
152                "action" : "ACTION_SET_PLAYLIST_DESCRIPTION",
153                "playlistDescription" : new_description
154            }))
155        };
156        if let Some(new_privacy_status) = &self.new_privacy_status {
157            actions.push(json!({
158                "action" : "ACTION_SET_PLAYLIST_PRIVACY",
159                "playlistPrivacy" : new_privacy_status
160            }))
161        };
162        if let Some((video_1, video_2)) = &self.swap_videos_order {
163            actions.push(json!({
164                "action" : "ACTION_MOVE_VIDEO_BEFORE",
165                "setVideoId" : video_1,
166                "movedSetVideoIdSuccessor" : video_2
167            }))
168        };
169        if let Some(add_playlist) = &self.add_playlist {
170            actions.push(json!({
171                "action" : "ACTION_ADD_PLAYLIST",
172                "addedFullListId" : add_playlist
173            }))
174        };
175        if let Some(change_add_order) = &self.change_add_order {
176            let add_to_top = match change_add_order {
177                AddOrder::AddToTop => true,
178                AddOrder::AddToBottom => false,
179            };
180            actions.push(json!({
181                "action" : "ACTION_SET_ADD_TO_TOP",
182                "addToTop" : add_to_top
183            }))
184        };
185        if let Some(new_privacy_status) = &self.new_privacy_status {
186            actions.push(json!({
187                "action" : "ACTION_SET_PLAYLIST_PRIVACY",
188                "playlistPrivacy" : new_privacy_status
189            }))
190        };
191        // TODO: Confirm if VL needs to be stripped / added from playlistId
192        // Confirmed!
193        let serde_json::Value::Object(map) = json!({
194            "playlistId" : self.id.get_raw(),
195            "actions" : actions,
196        }) else {
197            unreachable!()
198        };
199        map
200    }
201    fn path(&self) -> &str {
202        "browse/edit_playlist"
203    }
204    fn params(&self) -> Vec<(&str, Cow<str>)> {
205        vec![]
206    }
207}