Skip to main content

ytmapi_rs/query/playlist/
edit.rs

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