ytmapi_rs/query/playlist/
additems.rs

1use serde_json::json;
2
3use crate::{
4    auth::AuthToken,
5    common::{PlaylistID, VideoID},
6    parse::AddPlaylistItem,
7    query::{PostMethod, PostQuery, Query},
8};
9use std::borrow::Cow;
10
11use super::SpecialisedQuery;
12
13#[derive(Default, Debug, Clone, PartialEq)]
14pub enum DuplicateHandlingMode {
15    #[default]
16    ReturnError,
17    Unhandled,
18}
19
20// XXX: Query type potentially does not need to be mutually exclusive.
21pub struct AddPlaylistItemsQuery<'a, T: SpecialisedQuery> {
22    id: PlaylistID<'a>,
23    query_type: T,
24}
25/// Helper struct for AddPlaylistItemsQuery
26#[derive(Default, Debug, Clone, PartialEq)]
27pub struct AddVideosToPlaylist<'a> {
28    video_ids: Vec<VideoID<'a>>,
29    duplicate_handling_mode: DuplicateHandlingMode,
30}
31/// Helper struct for AddPlaylistItemsQuery
32#[derive(Debug, Clone, PartialEq)]
33pub struct AddPlaylistToPlaylist<'a> {
34    source_playlist: PlaylistID<'a>,
35}
36impl SpecialisedQuery for AddVideosToPlaylist<'_> {
37    fn additional_header(&self) -> Option<(String, serde_json::Value)> {
38        let actions = self
39            .video_ids
40            .iter()
41            .map(|v| match self.duplicate_handling_mode {
42                DuplicateHandlingMode::ReturnError => json!({
43                    "action" : "ACTION_ADD_VIDEO",
44                    "addedVideoId" : v,
45                }),
46                DuplicateHandlingMode::Unhandled => json!({
47                    "action" : "ACTION_ADD_VIDEO",
48                    "addedVideoId" : v,
49                    "dedupeOption" : "DEDUPE_OPTION_SKIP",
50                }),
51            });
52        Some(("actions".to_string(), actions.collect()))
53    }
54}
55impl SpecialisedQuery for AddPlaylistToPlaylist<'_> {
56    fn additional_header(&self) -> Option<(String, serde_json::Value)> {
57        Some((
58            "actions".to_string(),
59            json!([{
60                "action" : "ACTION_ADD_PLAYLIST",
61                "addedFullListId" : self.source_playlist,
62            },
63            {
64                "action" : "ACTION_ADD_VIDEO",
65                "addedVideoId" : null,
66            }]),
67        ))
68    }
69}
70impl<'a> AddPlaylistItemsQuery<'a, AddPlaylistToPlaylist<'a>> {
71    pub fn new_from_playlist(id: PlaylistID<'a>, source_playlist: PlaylistID<'a>) -> Self {
72        Self {
73            id,
74            query_type: AddPlaylistToPlaylist { source_playlist },
75        }
76    }
77}
78impl<'a> AddPlaylistItemsQuery<'a, AddVideosToPlaylist<'a>> {
79    pub fn new_from_videos(
80        id: PlaylistID<'a>,
81        video_ids: Vec<VideoID<'a>>,
82        duplicate_handling_mode: DuplicateHandlingMode,
83    ) -> Self {
84        Self {
85            id,
86            query_type: AddVideosToPlaylist {
87                video_ids,
88                duplicate_handling_mode,
89            },
90        }
91    }
92}
93
94impl<A: AuthToken, T: SpecialisedQuery> Query<A> for AddPlaylistItemsQuery<'_, T> {
95    type Output = Vec<AddPlaylistItem>;
96    type Method = PostMethod;
97}
98impl<T: SpecialisedQuery> PostQuery for AddPlaylistItemsQuery<'_, T> {
99    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
100        let serde_json::Value::Object(mut map) = json!({
101            "playlistId" : self.id,
102        }) else {
103            unreachable!()
104        };
105        if let Some(additional_header) = self.query_type.additional_header() {
106            map.insert(additional_header.0, additional_header.1);
107        }
108        map
109    }
110    fn path(&self) -> &str {
111        "browse/edit_playlist"
112    }
113    fn params(&self) -> Vec<(&str, Cow<str>)> {
114        vec![]
115    }
116}