diff options
author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-02-17 19:28:55 +0100 |
---|---|---|
committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-02-17 19:29:38 +0100 |
commit | 74346a5e43235be37bffca6dd0cb0ead66a529b5 (patch) | |
tree | 81acd823ef58b906734efb94d11998c51384ecfa | |
parent | feat(yt/storage/migrate): Add db version One (diff) | |
download | yt-74346a5e43235be37bffca6dd0cb0ead66a529b5.zip |
fix(yt): Remove most of the references to the zero version `Video` struct
-rw-r--r-- | yt/src/cache/mod.rs | 10 | ||||
-rw-r--r-- | yt/src/status/mod.rs | 29 | ||||
-rw-r--r-- | yt/src/storage/video_database/downloader.rs | 38 | ||||
-rw-r--r-- | yt/src/storage/video_database/getters.rs | 99 | ||||
-rw-r--r-- | yt/src/storage/video_database/setters.rs | 108 | ||||
-rw-r--r-- | yt/src/update/mod.rs | 5 |
6 files changed, 134 insertions, 155 deletions
diff --git a/yt/src/cache/mod.rs b/yt/src/cache/mod.rs index e3dfda2..6cd240c 100644 --- a/yt/src/cache/mod.rs +++ b/yt/src/cache/mod.rs @@ -16,7 +16,6 @@ use crate::{ app::App, storage::video_database::{ Video, VideoStatus, downloader::set_video_cache_path, getters::get_videos, - setters::set_state_change, }, }; @@ -90,13 +89,8 @@ pub async fn maintain(app: &App, all: bool) -> Result<()> { invalidate_video(app, &vid, false).await?; } } - if vid.status_change { - info!( - "Video '{}' has it's changing bit set. This is probably the result of an unexpectet exit. Clearing it", - vid.title - ); - set_state_change(app, &vid.extractor_hash, false).await?; - } + + // TODO(@bpeetz): Check if only one video is set `is_focused`. <2025-02-17> } Ok(()) diff --git a/yt/src/status/mod.rs b/yt/src/status/mod.rs index 56d29e6..345ae3c 100644 --- a/yt/src/status/mod.rs +++ b/yt/src/status/mod.rs @@ -38,13 +38,6 @@ macro_rules! get { .filter(|vid| vid.status == VideoStatus::$status) .collect() }; - - (@changing $videos:expr, $status:ident) => { - $videos - .iter() - .filter(|vid| vid.status == VideoStatus::$status && vid.status_change) - .count() - }; } pub async fn show(app: &App) -> Result<()> { @@ -75,16 +68,6 @@ pub async fn show(app: &App) -> Result<()> { let drop_videos_len = get!(all_videos, Drop); let dropped_videos_len = get!(all_videos, Dropped); - // changing - let picked_videos_changing = get!(@changing all_videos, Pick); - - let watch_videos_changing = get!(@changing all_videos, Watch); - let cached_videos_changing = get!(@changing all_videos, Cached); - let watched_videos_changing = get!(@changing all_videos, Watched); - - let drop_videos_changing = get!(@changing all_videos, Drop); - let dropped_videos_changing = get!(@changing all_videos, Dropped); - let subscriptions = get(app).await?; let subscriptions_len = subscriptions.0.len(); @@ -127,14 +110,14 @@ pub async fn show(app: &App) -> Result<()> { let cache_usage: Bytes = cache_usage_raw; println!( "\ -Picked Videos: {picked_videos_len} ({picked_videos_changing} changing) +Picked Videos: {picked_videos_len} -Watch Videos: {watch_videos_len} ({watch_videos_changing} changing) -Cached Videos: {cached_videos_len} ({cached_videos_changing} changing) -Watched Videos: {watched_videos_len} ({watched_videos_changing} changing) +Watch Videos: {watch_videos_len} +Cached Videos: {cached_videos_len} +Watched Videos: {watched_videos_len} -Drop Videos: {drop_videos_len} ({drop_videos_changing} changing) -Dropped Videos: {dropped_videos_len} ({dropped_videos_changing} changing) +Drop Videos: {drop_videos_len} +Dropped Videos: {dropped_videos_len} {watchtime_status} diff --git a/yt/src/storage/video_database/downloader.rs b/yt/src/storage/video_database/downloader.rs index bfb7aa3..d8b2041 100644 --- a/yt/src/storage/video_database/downloader.rs +++ b/yt/src/storage/video_database/downloader.rs @@ -15,7 +15,9 @@ use log::debug; use sqlx::query; use url::Url; -use crate::{app::App, storage::video_database::VideoStatus, unreachable::Unreachable}; +use crate::{ + app::App, storage::video_database::VideoStatus, unreachable::Unreachable, video_from_record, +}; use super::{ExtractorHash, Video}; @@ -46,39 +48,7 @@ pub async fn get_next_uncached_video(app: &App) -> Result<Option<Video>> { } else { let base = result?; - let thumbnail_url = base - .thumbnail_url - .as_ref() - .map(|url| Url::parse(url).unreachable("Parsing this as url should always work")); - - let status_change = if base.status_change == 1 { - true - } else { - assert_eq!(base.status_change, 0, "Can only be 1 or 0"); - false - }; - - let video = Video { - cache_path: base.cache_path.as_ref().map(PathBuf::from), - description: base.description.clone(), - duration: base.duration, - extractor_hash: ExtractorHash::from_hash( - base.extractor_hash - .parse() - .expect("The hash in the db should be valid"), - ), - last_status_change: base.last_status_change, - parent_subscription_name: base.parent_subscription_name.clone(), - priority: base.priority, - publish_date: base.publish_date, - status: VideoStatus::from_db_integer(base.status), - status_change, - thumbnail_url, - title: base.title.clone(), - url: Url::parse(&base.url).expect("Parsing this as url should always work"), - }; - - Ok(Some(video)) + Ok(Some(video_from_record! {base})) } } diff --git a/yt/src/storage/video_database/getters.rs b/yt/src/storage/video_database/getters.rs index 3470442..09cc9ee 100644 --- a/yt/src/storage/video_database/getters.rs +++ b/yt/src/storage/video_database/getters.rs @@ -8,7 +8,7 @@ // You should have received a copy of the License along with this program. // If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>. -//! These functions interact with the storage db in a read-only way. They are added on-demaned (as +//! These functions interact with the storage db in a read-only way. They are added on-demand (as //! you could theoretically just could do everything with the `get_videos` function), as //! performance or convince requires. use std::{fs::File, path::PathBuf}; @@ -24,22 +24,17 @@ use crate::{ app::App, storage::{ subscriptions::Subscription, - video_database::{Video, extractor_hash::ExtractorHash}, + video_database::{InPlaylist, Video, extractor_hash::ExtractorHash}, }, unreachable::Unreachable, }; use super::{MpvOptions, VideoOptions, VideoStatus, YtDlpOptions}; +#[macro_export] macro_rules! video_from_record { ($record:expr) => { - let thumbnail_url = if let Some(url) = &$record.thumbnail_url { - Some(Url::parse(&url).expect("Parsing this as url should always work")) - } else { - None - }; - - Ok(Video { + Video { cache_path: $record.cache_path.as_ref().map(|val| PathBuf::from(val)), description: $record.description.clone(), duration: $record.duration, @@ -53,17 +48,29 @@ macro_rules! video_from_record { parent_subscription_name: $record.parent_subscription_name.clone(), publish_date: $record.publish_date, status: VideoStatus::from_db_integer($record.status), - thumbnail_url, + thumbnail_url: if let Some(url) = &$record.thumbnail_url { + Some(Url::parse(&url).expect("Parsing this as url should always work")) + } else { + None + }, title: $record.title.clone(), url: Url::parse(&$record.url).expect("Parsing this as url should always work"), priority: $record.priority, - status_change: if $record.status_change == 1 { - true - } else { - assert_eq!($record.status_change, 0); - false + + in_playlist: { + if $record.in_playlist == 1 && $record.is_focused == 1 { + super::InPlaylist::Focused + } else if $record.in_playlist == 1 && $record.is_focused == 0 { + super::InPlaylist::Hidden + } else if $record.in_playlist == 0 && $record.is_focused == 0 { + super::InPlaylist::Excluded + } else { + unreachable!("Other combinations should not exist") + } }, - }) + + watch_progress: $record.watch_progress as u32, + } }; } @@ -159,15 +166,22 @@ pub async fn get_videos( As it was an URL when we put it in.", ), priority: base.get("priority"), - status_change: { - let val = base.get::<i64, &str>("status_change"); - if val == 1 { - true + + in_playlist: { + let in_playlist = base.get::<u8, &str>("in_playlist"); + let is_focused = base.get::<u8, &str>("is_focused"); + + if in_playlist == 1 && is_focused == 1 { + InPlaylist::Focused + } else if in_playlist == 1 && is_focused == 0 { + InPlaylist::Hidden + } else if in_playlist == 0 && is_focused == 0 { + InPlaylist::Excluded } else { - assert_eq!(val, 0, "Can only be 1 or 0"); - false + unreachable!("Other combinations should not be possible") } }, + watch_progress: base.get::<i64, &str>("watch_progress") as u32, }) }) .collect::<Result<Vec<Video>>>()?; @@ -204,51 +218,24 @@ pub async fn get_video_by_hash(app: &App, hash: &ExtractorHash) -> Result<Video> .fetch_one(&app.database) .await?; - video_from_record! {raw_video} + Ok(video_from_record! {raw_video}) } /// # Panics /// Only if assertions fail. pub async fn get_currently_playing_video(app: &App) -> Result<Option<Video>> { - let mut videos: Vec<Video> = get_changing_videos(app, VideoStatus::Cached).await?; + let record = query!("SELECT * FROM videos WHERE is_focused = 1 AND in_playlist = 1") + .fetch_one(&app.database) + .await; - if videos.is_empty() { + if let Err(sqlx::Error::RowNotFound) = record { Ok(None) } else { - assert_eq!( - videos.len(), - 1, - "Only one video can change from cached to watched at once!" - ); - - Ok(Some(videos.remove(0))) + let base = record?; + Ok(Some(video_from_record! {base})) } } -pub async fn get_changing_videos(app: &App, old_state: VideoStatus) -> Result<Vec<Video>> { - let status = old_state.as_db_integer(); - - let matching = query!( - r#" - SELECT * - FROM videos - WHERE status_change = 1 AND status = ?; - "#, - status - ) - .fetch_all(&app.database) - .await?; - - let real_videos: Vec<Video> = matching - .iter() - .map(|base| -> Result<Video> { - video_from_record! {base} - }) - .collect::<Result<Vec<Video>>>()?; - - Ok(real_videos) -} - pub async fn get_all_hashes(app: &App) -> Result<Vec<Hash>> { let hashes_hex = query!( r#" diff --git a/yt/src/storage/video_database/setters.rs b/yt/src/storage/video_database/setters.rs index 4531fd1..32a745b 100644 --- a/yt/src/storage/video_database/setters.rs +++ b/yt/src/storage/video_database/setters.rs @@ -16,7 +16,12 @@ use log::{debug, info}; use sqlx::query; use tokio::fs; -use crate::{app::App, storage::video_database::extractor_hash::ExtractorHash}; +use crate::{ + app::App, + storage::video_database::{ + extractor_hash::ExtractorHash, getters::get_currently_playing_video, + }, +}; use super::{Video, VideoOptions, VideoStatus}; @@ -166,26 +171,46 @@ pub async fn set_video_watched(app: &App, video: &Video) -> Result<()> { Ok(()) } -pub async fn set_state_change( +/// Set a video to be focused. +/// This optionally takes the `old_video_hash` to disable. +pub async fn set_focused( app: &App, - video_extractor_hash: &ExtractorHash, - changing: bool, + new_video_hash: &ExtractorHash, + old_video_hash: Option<&ExtractorHash>, ) -> Result<()> { - let state_change = u32::from(changing); - let video_extractor_hash = video_extractor_hash.hash().to_string(); + if let Some(old) = old_video_hash { + let hash = old.hash().to_string(); + query!( + r#" + UPDATE videos + SET is_focused = 0 + WHERE extractor_hash = ?; + "#, + hash + ) + .execute(&app.database) + .await?; + } + let new_hash = new_video_hash.hash().to_string(); query!( r#" UPDATE videos - SET status_change = ? + SET is_focused = 1 WHERE extractor_hash = ?; "#, - state_change, - video_extractor_hash, + new_hash, ) .execute(&app.database) .await?; + assert_eq!( + *new_video_hash, + get_currently_playing_video(app) + .await? + .expect("This is some at this point") + .extractor_hash + ); Ok(()) } @@ -220,54 +245,73 @@ pub async fn add_video(app: &App, video: Video) -> Result<()> { let thumbnail_url = video.thumbnail_url.map(|val| val.to_string()); let status = video.status.as_db_integer(); - let status_change = u32::from(video.status_change); let url = video.url.to_string(); let extractor_hash = video.extractor_hash.hash().to_string(); let default_subtitle_langs = &app.config.select.subtitle_langs; let default_mpv_playback_speed = app.config.select.playback_speed; + let (in_playlist, is_focused) = { + match video.in_playlist { + super::InPlaylist::Excluded => (false, false), + super::InPlaylist::Hidden => (true, false), + super::InPlaylist::Focused => (true, true), + } + }; + + let mut tx = app.database.begin().await?; query!( r#" - BEGIN; INSERT INTO videos ( - parent_subscription_name, - status, - status_change, - last_status_change, - title, - url, description, duration, + extractor_hash, + in_playlist, + is_focused, + last_status_change, + parent_subscription_name, publish_date, + status, thumbnail_url, - extractor_hash) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + title, + url, + watch_progress + ) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + "#, + video.description, + video.duration, + extractor_hash, + in_playlist, + is_focused, + video.last_status_change, + parent_subscription_name, + video.publish_date, + status, + thumbnail_url, + video.title, + url, + video.watch_progress + ) + .execute(&mut *tx) + .await?; + query!( + r#" INSERT INTO video_options ( extractor_hash, subtitle_langs, playback_speed) VALUES (?, ?, ?); - COMMIT; "#, - parent_subscription_name, - status, - status_change, - video.last_status_change, - video.title, - url, - video.description, - video.duration, - video.publish_date, - thumbnail_url, - extractor_hash, extractor_hash, default_subtitle_langs, default_mpv_playback_speed ) - .execute(&app.database) + .execute(&mut *tx) .await?; + tx.commit().await?; + Ok(()) } diff --git a/yt/src/update/mod.rs b/yt/src/update/mod.rs index 9d34498..7bd37b6 100644 --- a/yt/src/update/mod.rs +++ b/yt/src/update/mod.rs @@ -21,7 +21,7 @@ use crate::{ storage::{ subscriptions::{self, Subscription}, video_database::{ - Video, VideoStatus, extractor_hash::ExtractorHash, getters::get_all_hashes, + InPlaylist, Video, VideoStatus, extractor_hash::ExtractorHash, getters::get_all_hashes, setters::add_video, }, }, @@ -170,10 +170,11 @@ pub fn video_entry_to_video(entry: InfoJson, sub: Option<&Subscription>) -> Resu priority: 0, publish_date, status: VideoStatus::Pick, - status_change: false, thumbnail_url, title: unwrap_option!(entry.title.clone()), url, + in_playlist: InPlaylist::Excluded, + watch_progress: 0, }; Ok(video) } |