diff options
author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-10-14 14:56:29 +0200 |
---|---|---|
committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-10-14 14:56:29 +0200 |
commit | 6c9286857ef8b314962b67f4a16a66e8c35531bc (patch) | |
tree | 9ced4485ec38b39f82cba258c06321a21c40000a /src/storage/video_database/getters.rs | |
parent | build(Cargo.toml): Add further lints (diff) | |
download | yt-6c9286857ef8b314962b67f4a16a66e8c35531bc.tar.gz yt-6c9286857ef8b314962b67f4a16a66e8c35531bc.zip |
refactor(treewide): Combine the separate crates in one workspace
Diffstat (limited to 'src/storage/video_database/getters.rs')
-rw-r--r-- | src/storage/video_database/getters.rs | 345 |
1 files changed, 0 insertions, 345 deletions
diff --git a/src/storage/video_database/getters.rs b/src/storage/video_database/getters.rs deleted file mode 100644 index 29dd014..0000000 --- a/src/storage/video_database/getters.rs +++ /dev/null @@ -1,345 +0,0 @@ -// yt - A fully featured command line YouTube client -// -// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> -// SPDX-License-Identifier: GPL-3.0-or-later -// -// This file is part of Yt. -// -// 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 -//! you could theoretically just could do everything with the `get_videos` function), as -//! performance or convince requires. -use std::{fs::File, path::PathBuf}; - -use anyhow::{bail, Context, Result}; -use blake3::Hash; -use log::debug; -use sqlx::{query, QueryBuilder, Row, Sqlite}; -use url::Url; -use yt_dlp::wrapper::info_json::InfoJson; - -use crate::{ - app::App, - storage::{ - subscriptions::Subscription, - video_database::{extractor_hash::ExtractorHash, Video}, - }, -}; - -use super::{MpvOptions, VideoOptions, VideoStatus, YtDlpOptions}; - -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 { - cache_path: $record.cache_path.as_ref().map(|val| PathBuf::from(val)), - description: $record.description.clone(), - duration: $record.duration, - extractor_hash: ExtractorHash::from_hash( - $record - .extractor_hash - .parse() - .expect("The db hash should be a valid blake3 hash"), - ), - last_status_change: $record.last_status_change, - parent_subscription_name: $record.parent_subscription_name.clone(), - publish_date: $record.publish_date, - status: VideoStatus::from_db_integer($record.status), - thumbnail_url, - 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 - }, - }) - }; -} - -/// Get the lines to display at the selection file -/// [`changing` = true]: Means that we include *only* videos, that have the `status_changing` flag set -/// [`changing` = None]: Means that we include *both* videos, that have the `status_changing` flag set and not set -pub async fn get_videos( - app: &App, - allowed_states: &[VideoStatus], - changing: Option<bool>, -) -> Result<Vec<Video>> { - let mut qb: QueryBuilder<Sqlite> = QueryBuilder::new( - "\ - SELECT * - FROM videos - WHERE status IN ", - ); - - qb.push("("); - allowed_states - .iter() - .enumerate() - .for_each(|(index, state)| { - qb.push("'"); - qb.push(state.as_db_integer()); - qb.push("'"); - - if index != allowed_states.len() - 1 { - qb.push(","); - } - }); - qb.push(")"); - - if let Some(val) = changing { - if val { - qb.push(" AND status_change = 1"); - } else { - qb.push(" AND status_change = 0"); - } - } - - qb.push("\n ORDER BY priority DESC, publish_date DESC;"); - - debug!("Will run: \"{}\"", qb.sql()); - - let videos = qb.build().fetch_all(&app.database).await.with_context(|| { - format!( - "Failed to query videos with states: '{}'", - allowed_states.iter().fold(String::new(), |mut acc, state| { - acc.push(' '); - acc.push_str(state.as_str()); - acc - }), - ) - })?; - - let real_videos: Vec<Video> = videos - .iter() - .map(|base| -> Result<Video> { - Ok(Video { - cache_path: base - .get::<Option<String>, &str>("cache_path") - .as_ref() - .map(PathBuf::from), - description: base.get::<Option<String>, &str>("description").clone(), - duration: base.get("duration"), - extractor_hash: ExtractorHash::from_hash( - base.get::<String, &str>("extractor_hash") - .parse() - .expect("The db hash should be a valid blake3 hash"), - ), - last_status_change: base.get("last_status_change"), - parent_subscription_name: base - .get::<Option<String>, &str>("parent_subscription_name") - .clone(), - publish_date: base.get("publish_date"), - status: VideoStatus::from_db_integer(base.get("status")), - thumbnail_url: base - .get::<Option<String>, &str>("thumbnail_url") - .as_ref() - .map(|url| Url::parse(url).expect("Parsing this as url should always work")), - title: base.get::<String, &str>("title").to_owned(), - url: Url::parse(base.get("url")).expect("Parsing this as url should always work"), - priority: base.get("priority"), - status_change: { - let val = base.get::<i64, &str>("status_change"); - if val == 1 { - true - } else { - assert_eq!(val, 0, "Can only be 1 or 0"); - false - } - }, - }) - }) - .collect::<Result<Vec<Video>>>()?; - - Ok(real_videos) -} - -pub async fn get_video_info_json(video: &Video) -> Result<Option<InfoJson>> { - if let Some(mut path) = video.cache_path.clone() { - if !path.set_extension("info.json") { - bail!( - "Failed to change path extension to 'info.json': {}", - path.display() - ); - } - let info_json_string = File::open(path)?; - let info_json: InfoJson = serde_json::from_reader(&info_json_string)?; - - Ok(Some(info_json)) - } else { - Ok(None) - } -} - -pub async fn get_video_by_hash(app: &App, hash: &ExtractorHash) -> Result<Video> { - let ehash = hash.hash().to_string(); - - let raw_video = query!( - " - SELECT * FROM videos WHERE extractor_hash = ?; - ", - ehash - ) - .fetch_one(&app.database) - .await?; - - video_from_record! {raw_video} -} - -pub async fn get_currently_playing_video(app: &App) -> Result<Option<Video>> { - let mut videos: Vec<Video> = get_changing_videos(app, VideoStatus::Cached).await?; - - if videos.is_empty() { - Ok(None) - } else { - assert_eq!( - videos.len(), - 1, - "Only one video can change from cached to watched at once!" - ); - - Ok(Some(videos.remove(0))) - } -} - -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#" - SELECT extractor_hash - FROM videos; - "# - ) - .fetch_all(&app.database) - .await?; - - Ok(hashes_hex - .iter() - .map(|hash| { - Hash::from_hex(&hash.extractor_hash) - .expect("These values started as blake3 hashes, they should stay blake3 hashes") - }) - .collect()) -} - -pub async fn get_video_hashes(app: &App, subs: &Subscription) -> Result<Vec<Hash>> { - let hashes_hex = query!( - r#" - SELECT extractor_hash - FROM videos - WHERE parent_subscription_name = ?; - "#, - subs.name - ) - .fetch_all(&app.database) - .await?; - - Ok(hashes_hex - .iter() - .map(|hash| { - Hash::from_hex(&hash.extractor_hash) - .expect("These values started as blake3 hashes, they should stay blake3 hashes") - }) - .collect()) -} - -pub async fn get_video_yt_dlp_opts(app: &App, hash: &ExtractorHash) -> Result<YtDlpOptions> { - let ehash = hash.hash().to_string(); - - let yt_dlp_options = query!( - r#" - SELECT subtitle_langs - FROM video_options - WHERE extractor_hash = ?; - "#, - ehash - ) - .fetch_one(&app.database) - .await - .with_context(|| { - format!( - "Failed to fetch the `yt_dlp_video_opts` for video: {}", - hash - ) - })?; - - Ok(YtDlpOptions { - subtitle_langs: yt_dlp_options.subtitle_langs, - }) -} -pub async fn get_video_mpv_opts(app: &App, hash: &ExtractorHash) -> Result<MpvOptions> { - let ehash = hash.hash().to_string(); - - let mpv_options = query!( - r#" - SELECT playback_speed - FROM video_options - WHERE extractor_hash = ?; - "#, - ehash - ) - .fetch_one(&app.database) - .await - .with_context(|| format!("Failed to fetch the `mpv_video_opts` for video: {}", hash))?; - - Ok(MpvOptions { - playback_speed: mpv_options.playback_speed, - }) -} - -pub async fn get_video_opts(app: &App, hash: &ExtractorHash) -> Result<VideoOptions> { - let ehash = hash.hash().to_string(); - - let opts = query!( - r#" - SELECT playback_speed, subtitle_langs - FROM video_options - WHERE extractor_hash = ?; - "#, - ehash - ) - .fetch_one(&app.database) - .await - .with_context(|| format!("Failed to fetch the `video_opts` for video: {}", hash))?; - - let mpv = MpvOptions { - playback_speed: opts.playback_speed, - }; - let yt_dlp = YtDlpOptions { - subtitle_langs: opts.subtitle_langs, - }; - - Ok(VideoOptions { mpv, yt_dlp }) -} |