Skip to content

Commit

Permalink
feat!: Implement History queries and refactor 'playlist' result types (
Browse files Browse the repository at this point in the history
…#59) - Resolves #58

* Skeletons for Query and Parse
* Refactor of SongResult in progress - AlbumParams parse tests now passing
* Fix error return from get songs in youtui
* Complete refactor of SongResult
* Remove a case of cookie inclusion from gitignore
* Remove deprecated ResultCore struct
* Cargo fixes
* Clippy fixes, and corrected part of implementation of GetWatchPlaylistQuery
* Add library functions
* Add rate functions
* Complete tests for rate functions
* Clippy linting

BREAKING CHANGE: AlbumParams other versions removed, AlbumParams like_status removed, replaced with new field library_status, AlbumLikeStatus renamed to InLikedSongs, ParseTarget for errors modified - only types now Array or Other(String), module YoutubeResult and usage of ResultCore and YoutubeResult trait removed.
  • Loading branch information
nick42d authored Jul 10, 2024
1 parent c695a3d commit 62a6f88
Show file tree
Hide file tree
Showing 53 changed files with 153,313 additions and 8,602 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*cookie.txt
*cookie.txt*
*.data
*expired-headers.json
*expired-oauth.json
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Chrome example (Select manually and paste):
|Endpoint | Implemented: Query | Implemented: Continuations |
|--- | --- | --- |
|GetArtist | [x] ||
|GetAlbum | [x] ||
|GetAlbum | [ ]* ||
|GetArtistAlbums | [x] ||
|Search | [x] |[ ]|
|GetSearchSuggestions|[x]||
Expand Down Expand Up @@ -114,6 +114,9 @@ Chrome example (Select manually and paste):
\* get watch playlist is partially implemented only
- only returns playlist and lyrics ids

\* get artist is partially implemented only
- only returns albums and songs

# Additional information
See the wiki for additional information
https://github.com/nick42d/youtui/wiki
35 changes: 12 additions & 23 deletions youtui/src/app/server/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ use crate::Result;
use tokio::sync::mpsc;
use tracing::{error, info};
use ytmapi_rs::auth::BrowserToken;
use ytmapi_rs::common::youtuberesult::YoutubeResult;
use ytmapi_rs::common::AlbumID;
use ytmapi_rs::common::SearchSuggestion;
use ytmapi_rs::common::YoutubeID;
use ytmapi_rs::parse::AlbumSong;
use ytmapi_rs::parse::GetArtistAlbums;
use ytmapi_rs::parse::SongResult;
use ytmapi_rs::ChannelID;

pub enum Request {
Expand All @@ -30,7 +29,7 @@ pub enum Response {
NoSongsFound(TaskID),
SongsFound(TaskID),
AppendSongList {
song_list: Vec<SongResult>,
song_list: Vec<AlbumSong>,
album: String,
year: String,
artist: String,
Expand Down Expand Up @@ -242,6 +241,11 @@ impl Api {
Ok(a) => a,
Err(e) => {
let Some((json, key)) = e.get_json_and_key() else {
error!("API error received <{e}>");
info!("Telling caller no songs found (error)");
let _ = tx
.send(super::Response::Api(Response::NoSongsFound(id)))
.await;
return;
};
// TODO: Bring loggable json errors into their own function.
Expand Down Expand Up @@ -270,30 +274,15 @@ impl Api {
params: artist_albums_params,
results: artist_albums_results,
} = albums;
let browse_id_list = if artist_albums_browse_id.is_none()
let browse_id_list: Vec<AlbumID> = if artist_albums_browse_id.is_none()
&& artist_albums_params.is_none()
&& !artist_albums_results.is_empty()
{
// Assume we already got all the albums from the search.
let browse_id_list: Option<Vec<_>> = artist_albums_results
.iter()
.map(|r| {
r.get_channel_id()
.as_ref()
.map(|c_id| AlbumID::from_raw(c_id.get_raw()))
})
.collect();
if let Some(browse_id_list) = browse_id_list {
browse_id_list
} else {
tracing::info!(
"Telling caller no songs found (some albums missing browse id)"
);
let _ = tx
.send(super::Response::Api(Response::NoSongsFound(id)))
.await;
return;
}
artist_albums_results
.into_iter()
.map(|r| r.album_id)
.collect()
} else if artist_albums_params.is_none() || artist_albums_browse_id.is_none() {
tracing::info!("Telling caller no songs found (no params or browse_id)");
let _ = tx
Expand Down
25 changes: 7 additions & 18 deletions youtui/src/app/structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use super::view::{SortDirection, TableItem};
use std::borrow::Cow;
use std::rc::Rc;
use std::sync::Arc;
use ytmapi_rs::common::youtuberesult::{ResultCore, YoutubeResult};
use ytmapi_rs::parse::SongResult;
use ytmapi_rs::parse::{AlbumSong};

pub trait SongListComponent {
fn get_song_from_idx(&self, idx: usize) -> Option<&ListSong>;
Expand All @@ -26,7 +25,7 @@ pub struct Percentage(pub u8);

#[derive(Clone, Debug)]
pub struct ListSong {
pub raw: SongResult,
pub raw: AlbumSong,
pub download_status: DownloadStatus,
pub id: ListSongID,
year: Rc<String>,
Expand Down Expand Up @@ -105,7 +104,7 @@ impl ListSong {
&self.album
}
pub fn get_track_no(&self) -> usize {
self.raw.get_track_no()
self.raw.track_no
}
pub fn get_fields_iter(&self) -> TableItem {
Box::new(
Expand All @@ -125,26 +124,16 @@ impl ListSong {
.unwrap_or_default()
.into(),
self.get_album().into(),
self.get_title().into(),
(&self.raw.title).into(),
// TODO: Remove allocation
self.get_duration()
.as_ref()
.map(|d| d.as_str())
.unwrap_or("")
.into(),
(&self.raw.duration).into(),
self.get_year().into(),
]
.into_iter(),
)
}
}

impl YoutubeResult for ListSong {
fn get_core(&self) -> &ResultCore {
self.raw.get_core()
}
}

impl Default for AlbumSongsList {
fn default() -> Self {
AlbumSongsList {
Expand Down Expand Up @@ -184,7 +173,7 @@ impl AlbumSongsList {
// Naive implementation
pub fn append_raw_songs(
&mut self,
raw_list: Vec<SongResult>,
raw_list: Vec<AlbumSong>,
album: String,
year: String,
artist: String,
Expand All @@ -201,7 +190,7 @@ impl AlbumSongsList {
}
pub fn add_raw_song(
&mut self,
song: SongResult,
song: AlbumSong,
album: Rc<String>,
year: Rc<String>,
artist: Rc<String>,
Expand Down
4 changes: 2 additions & 2 deletions youtui/src/app/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::error::Error;
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
use tokio::sync::mpsc;
use ytmapi_rs::common::SearchSuggestion;
use ytmapi_rs::parse::{SearchResultArtist, SongResult};
use ytmapi_rs::parse::{AlbumSong, SearchResultArtist};

mod browser;
pub mod draw;
Expand Down Expand Up @@ -377,7 +377,7 @@ impl YoutuiWindow {
}
pub fn handle_append_song_list(
&mut self,
song_list: Vec<SongResult>,
song_list: Vec<AlbumSong>,
album: String,
year: String,
artist: String,
Expand Down
4 changes: 2 additions & 2 deletions youtui/src/app/ui/browser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use tokio::sync::mpsc;
use tracing::error;
use ytmapi_rs::{
common::SearchSuggestion,
parse::{SearchResultArtist, SongResult},
parse::{AlbumSong, SearchResultArtist},
};

const PAGE_KEY_LINES: isize = 10;
Expand Down Expand Up @@ -459,7 +459,7 @@ impl Browser {
}
pub fn handle_append_song_list(
&mut self,
song_list: Vec<SongResult>,
song_list: Vec<AlbumSong>,
album: String,
year: String,
artist: String,
Expand Down
5 changes: 2 additions & 3 deletions youtui/src/app/ui/footer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use ratatui::{
text::{Line, Span},
widgets::{block::Title, Block, Borders, Gauge, Paragraph},
};
use ytmapi_rs::common::youtuberesult::YoutubeResult;

pub fn parse_simple_time_to_secs<S: AsRef<str>>(time_string: S) -> usize {
time_string
Expand Down Expand Up @@ -42,7 +41,7 @@ pub fn draw_footer(f: &mut Frame, w: &super::YoutuiWindow, chunk: Rect) {
duration = w
.playlist
.get_song_from_id(*id)
.and_then(|s| s.raw.get_duration().as_deref())
.map(|s| &s.raw.duration)
.map(parse_simple_time_to_secs)
.unwrap_or(0);
progress = w.playlist.cur_played_secs.unwrap_or(0.0);
Expand All @@ -57,7 +56,7 @@ pub fn draw_footer(f: &mut Frame, w: &super::YoutuiWindow, chunk: Rect) {
PlayState::Playing(id) | PlayState::Paused(id) | PlayState::Buffering(id) => w
.playlist
.get_song_from_id(id)
.map(|s| s.raw.get_title().to_owned())
.map(|s| s.raw.title.to_owned())
.unwrap_or("No title".to_string()),
PlayState::NotPlaying => "Not playing".to_string(),
PlayState::Stopped => "Not playing".to_string(),
Expand Down
2 changes: 1 addition & 1 deletion youtui/src/app/ui/playlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ impl Playlist {
};
send_or_error(
&self.ui_tx,
AppCallback::DownloadSong(song.raw.get_video_id().clone(), id),
AppCallback::DownloadSong(song.raw.video_id.clone(), id),
)
.await;
song.download_status = DownloadStatus::Queued;
Expand Down
71 changes: 62 additions & 9 deletions youtui/src/cli/querybuilder.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use ytmapi_rs::{
auth::AuthToken,
common::{AlbumID, BrowseParams, PlaylistID, SetVideoID, YoutubeID},
common::{
AlbumID, BrowseParams, FeedbackTokenRemoveFromHistory, PlaylistID, SetVideoID, YoutubeID,
},
parse::LikeStatus,
query::{
rate::{RatePlaylistQuery, RateSongQuery},
AddPlaylistItemsQuery, AlbumsFilter, ArtistsFilter, CommunityPlaylistsFilter,
CreatePlaylistQuery, DeletePlaylistQuery, EditPlaylistQuery, EpisodesFilter,
FeaturedPlaylistsFilter, GetAlbumQuery, GetArtistAlbumsQuery, GetArtistQuery,
GetLibraryAlbumsQuery, GetLibraryArtistSubscriptionsQuery, GetLibraryArtistsQuery,
GetLibraryPlaylistsQuery, GetLibrarySongsQuery, GetPlaylistQuery,
GetHistoryQuery, GetLibraryAlbumsQuery, GetLibraryArtistSubscriptionsQuery,
GetLibraryArtistsQuery, GetLibraryPlaylistsQuery, GetLibrarySongsQuery, GetPlaylistQuery,
GetSearchSuggestionsQuery, PlaylistsFilter, PodcastsFilter, ProfilesFilter, Query,
RemovePlaylistItemsQuery, SearchQuery, SongsFilter, VideosFilter,
RemoveHistoryItemsQuery, RemovePlaylistItemsQuery, SearchQuery, SongsFilter, VideosFilter,
},
ChannelID, VideoID, YtMusic,
};
Expand Down Expand Up @@ -189,10 +193,7 @@ pub async fn command_to_query<A: AuthToken>(
yt,
RemovePlaylistItemsQuery::new(
PlaylistID::from_raw(playlist_id),
set_video_ids
.iter()
.map(|v| SetVideoID::from_raw(v))
.collect(),
set_video_ids.iter().map(SetVideoID::from_raw).collect(),
),
cli_query,
)
Expand All @@ -206,7 +207,7 @@ pub async fn command_to_query<A: AuthToken>(
yt,
AddPlaylistItemsQuery::new_from_videos(
PlaylistID::from_raw(playlist_id),
video_ids.iter().map(|v| VideoID::from_raw(v)).collect(),
video_ids.iter().map(VideoID::from_raw).collect(),
Default::default(),
),
cli_query,
Expand Down Expand Up @@ -248,6 +249,58 @@ pub async fn command_to_query<A: AuthToken>(
get_string_output_of_query(yt, GetLibraryArtistSubscriptionsQuery::default(), cli_query)
.await
}
Command::GetHistory => get_string_output_of_query(yt, GetHistoryQuery, cli_query).await,
Command::RemoveHistoryItems { feedback_tokens } => {
get_string_output_of_query(
yt,
RemoveHistoryItemsQuery::new(
feedback_tokens
.iter()
.map(FeedbackTokenRemoveFromHistory::from_raw)
.collect(),
),
cli_query,
)
.await
}
Command::RateSong {
video_id,
like_status,
} => {
get_string_output_of_query(
yt,
RateSongQuery::new(
VideoID::from_raw(video_id),
match like_status.as_str() {
"Like" => LikeStatus::Liked,
"Dislike" => LikeStatus::Disliked,
"Indifferent" => LikeStatus::Indifferent,
other => panic!("Unhandled like status <{other}>"),
},
),
cli_query,
)
.await
}
Command::RatePlaylist {
playlist_id,
like_status,
} => {
get_string_output_of_query(
yt,
RatePlaylistQuery::new(
PlaylistID::from_raw(playlist_id),
match like_status.as_str() {
"Like" => LikeStatus::Liked,
"Dislike" => LikeStatus::Disliked,
"Indifferent" => LikeStatus::Indifferent,
other => panic!("Unhandled like status <{other}>"),
},
),
cli_query,
)
.await
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions youtui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,18 @@ enum Command {
playlist_id: String,
new_title: String,
},
GetHistory,
RemoveHistoryItems {
feedback_tokens: Vec<String>,
},
RateSong {
video_id: String,
like_status: String,
},
RatePlaylist {
playlist_id: String,
like_status: String,
},
}

pub struct RuntimeInfo {
Expand Down
1 change: 1 addition & 0 deletions ytmapi-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ tokio-stream = "0.1.15"
async-stream = "0.3.5"
const_format = "0.2.32"
chrono = "0.4.38"
rand = "0.8.5"

[features]
# Provide alternative TLS options, but use reqwest's default by default.
Expand Down
Loading

0 comments on commit 62a6f88

Please sign in to comment.