Skip to main content

ytmapi_rs/
common.rs

1//! Re-usable core structures.
2// Intended to be for structures that are also suitable to be reused by other
3// libraries. As opposed to simply part of the interface.
4use serde::{Deserialize, Serialize};
5use std::borrow::Cow;
6
7/// A search suggestion containing a list of TextRuns.
8/// May be a history suggestion.
9#[derive(PartialEq, Debug, Clone, Deserialize)]
10#[non_exhaustive]
11pub struct SearchSuggestion {
12    pub runs: Vec<TextRun>,
13    pub suggestion_type: SuggestionType,
14}
15
16#[derive(PartialEq, Debug, Clone, Deserialize, Copy)]
17pub enum SuggestionType {
18    History,
19    Prediction,
20}
21
22/// A block of text that may be boldened.
23#[derive(PartialEq, Debug, Clone, Deserialize)]
24pub enum TextRun {
25    Bold(String),
26    Normal(String),
27}
28
29impl TextRun {
30    /// Take the text from the run, ignoring format.
31    pub fn take_text(self) -> String {
32        match self {
33            TextRun::Bold(s) => s,
34            TextRun::Normal(s) => s,
35        }
36    }
37    /// Get a reference to the text from the run, ignoring format.
38    pub fn get_text(&self) -> &str {
39        match self {
40            TextRun::Bold(s) => s,
41            TextRun::Normal(s) => s,
42        }
43    }
44}
45
46impl SearchSuggestion {
47    /// Gets the text of the runs concaternated into a String.
48    /// Note - allocates a new String.
49    pub fn get_text(&self) -> String {
50        self.runs
51            .iter()
52            .fold(String::new(), |acc, r| acc + r.get_text())
53    }
54    pub(crate) fn new(suggestion_type: SuggestionType, runs: Vec<TextRun>) -> Self {
55        Self {
56            runs,
57            suggestion_type,
58        }
59    }
60}
61
62#[derive(PartialEq, Debug, Clone, Deserialize, Serialize)]
63#[must_use]
64/// Indicates a result from an API action such as a 'delete playlist'
65pub enum ApiOutcome {
66    #[serde(alias = "STATUS_SUCCEEDED")]
67    Success,
68    #[serde(alias = "STATUS_FAILED")]
69    Failure,
70}
71
72#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
73// Intentionally not marked non_exhaustive - not expecting this to change.
74pub struct Thumbnail {
75    pub height: u64,
76    pub width: u64,
77    pub url: String,
78}
79
80#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
81/// Set of both taste tokens.
82// Intentionally not marked non_exhaustive - not expecting this to change.
83// TODO: constructor
84pub struct TasteToken<'a> {
85    pub impression_value: TasteTokenImpression<'a>,
86    pub selection_value: TasteTokenSelection<'a>,
87}
88
89/// Collection of required fields to identify and change library status.
90// Intentionally not marked non_exhaustive - not expecting this to change.
91#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
92pub struct LibraryManager {
93    pub status: LibraryStatus,
94    pub add_to_library_token: FeedbackTokenAddToLibrary<'static>,
95    pub remove_from_library_token: FeedbackTokenRemoveFromLibrary<'static>,
96}
97
98#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
99/// LIBRARY_SAVED and LIBRARY_ADD icons are being phased out so this is an
100/// intermediate step to check if a/b test is still running.
101///
102/// See https://github.com/nick42d/youtui/issues/271
103enum LibraryStatusIcon {
104    #[serde(rename = "LIBRARY_SAVED")]
105    #[deprecated = "Future deprecation see https://github.com/nick42d/youtui/issues/271"]
106    LibrarySaved,
107    #[serde(rename = "LIBRARY_ADD")]
108    #[deprecated = "Future deprecation see https://github.com/nick42d/youtui/issues/271"]
109    LibraryAdd,
110    #[serde(rename = "BOOKMARK_BORDER")]
111    BookmarkBorder,
112    #[serde(rename = "BOOKMARK")]
113    Bookmark,
114}
115
116impl From<LibraryStatusIcon> for LibraryStatus {
117    fn from(value: LibraryStatusIcon) -> Self {
118        match value {
119            LibraryStatusIcon::LibrarySaved => {
120                ab_warn!();
121                LibraryStatus::InLibrary
122            }
123            LibraryStatusIcon::LibraryAdd => {
124                ab_warn!();
125                LibraryStatus::NotInLibrary
126            }
127            LibraryStatusIcon::BookmarkBorder => LibraryStatus::NotInLibrary,
128            LibraryStatusIcon::Bookmark => LibraryStatus::InLibrary,
129        }
130    }
131}
132
133#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
134#[serde(from = "LibraryStatusIcon")]
135pub enum LibraryStatus {
136    InLibrary,
137    NotInLibrary,
138}
139
140#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
141pub enum LikeStatus {
142    #[serde(rename = "LIKE")]
143    Liked,
144    #[serde(rename = "DISLIKE")]
145    Disliked,
146    #[serde(rename = "INDIFFERENT")]
147    /// Indifferent means that the song has not been liked or disliked.
148    Indifferent,
149}
150
151#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
152pub enum Explicit {
153    IsExplicit,
154    NotExplicit,
155}
156
157#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
158pub enum AlbumType {
159    Single,
160    Album,
161    EP,
162}
163
164/// Type safe version of API ID used as part of YTM's interface.
165pub trait YoutubeID<'a> {
166    fn get_raw(&self) -> &str;
167    // TODO: Create fallible version for when parsing is required. This could
168    // possiby be a seperate trait YoutubeIDFallible
169    fn from_raw<S: Into<Cow<'a, str>>>(raw_str: S) -> Self;
170}
171
172#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
173pub struct FeedbackTokenRemoveFromHistory<'a>(Cow<'a, str>);
174#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
175pub struct FeedbackTokenAddToLibrary<'a>(Cow<'a, str>);
176#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
177pub struct FeedbackTokenRemoveFromLibrary<'a>(Cow<'a, str>);
178#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
179pub struct BrowseParams<'a>(Cow<'a, str>);
180#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
181pub struct UserVideosParams<'a>(Cow<'a, str>);
182#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
183pub struct UserPlaylistsParams<'a>(Cow<'a, str>);
184#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
185pub struct PodcastChannelParams<'a>(Cow<'a, str>);
186// TODO: Add parsing - PlaylistID begining with VL should fail.
187#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
188pub struct PlaylistID<'a>(Cow<'a, str>);
189#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
190pub struct AlbumID<'a>(Cow<'a, str>);
191#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
192pub struct ArtistChannelID<'a>(Cow<'a, str>);
193#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
194pub struct PodcastChannelID<'a>(Cow<'a, str>);
195#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
196pub struct ContinuationParams<'a>(Cow<'a, str>);
197#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
198pub struct UserChannelID<'a>(Cow<'a, str>);
199#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
200pub struct PodcastID<'a>(Cow<'a, str>);
201#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
202pub struct EpisodeID<'a>(Cow<'a, str>);
203#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
204pub struct VideoID<'a>(Cow<'a, str>);
205#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
206pub struct UploadEntityID<'a>(Cow<'a, str>);
207#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
208pub struct LyricsID<'a>(Cow<'a, str>);
209#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
210pub struct SetVideoID<'a>(Cow<'a, str>);
211#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
212pub struct UploadAlbumID<'a>(Cow<'a, str>);
213#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
214pub struct UploadArtistID<'a>(Cow<'a, str>);
215#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
216pub struct TasteTokenSelection<'a>(Cow<'a, str>);
217#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
218pub struct TasteTokenImpression<'a>(Cow<'a, str>);
219#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
220pub struct MoodCategoryParams<'a>(Cow<'a, str>);
221#[derive(PartialEq, Eq, Hash, Debug, Clone, Serialize, Deserialize)]
222pub struct SongTrackingUrl<'a>(Cow<'a, str>);
223
224impl_youtube_id!(UploadEntityID<'a>);
225impl_youtube_id!(SetVideoID<'a>);
226impl_youtube_id!(AlbumID<'a>);
227impl_youtube_id!(UploadAlbumID<'a>);
228impl_youtube_id!(UploadArtistID<'a>);
229impl_youtube_id!(UserChannelID<'a>);
230impl_youtube_id!(PodcastID<'a>);
231impl_youtube_id!(EpisodeID<'a>);
232impl_youtube_id!(VideoID<'a>);
233impl_youtube_id!(PlaylistID<'a>);
234impl_youtube_id!(ArtistChannelID<'a>);
235impl_youtube_id!(PodcastChannelID<'a>);
236impl_youtube_id!(LyricsID<'a>);
237impl_youtube_id!(BrowseParams<'a>);
238impl_youtube_id!(PodcastChannelParams<'a>);
239impl_youtube_id!(ContinuationParams<'a>);
240impl_youtube_id!(FeedbackTokenRemoveFromHistory<'a>);
241impl_youtube_id!(FeedbackTokenRemoveFromLibrary<'a>);
242impl_youtube_id!(FeedbackTokenAddToLibrary<'a>);
243impl_youtube_id!(TasteTokenImpression<'a>);
244impl_youtube_id!(TasteTokenSelection<'a>);
245impl_youtube_id!(MoodCategoryParams<'a>);
246impl_youtube_id!(SongTrackingUrl<'a>);
247impl_youtube_id!(UserVideosParams<'a>);
248impl_youtube_id!(UserPlaylistsParams<'a>);