// 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>.
use std::{fmt::Write, path::PathBuf};
use url::Url;
use crate::{
constants::{DEFAULT_MPV_PLAYBACK_SPEED, DEFAULT_SUBTITLE_LANGS},
storage::video_database::extractor_hash::ExtractorHash,
};
pub mod downloader;
pub mod extractor_hash;
pub mod getters;
pub mod setters;
#[derive(Debug)]
pub struct Video {
pub cache_path: Option<PathBuf>,
pub description: Option<String>,
pub duration: Option<f64>,
pub extractor_hash: ExtractorHash,
pub last_status_change: i64,
/// The associated subscription this video was fetched from (null, when the video was `add`ed)
pub parent_subscription_name: Option<String>,
pub priority: i64,
pub publish_date: Option<i64>,
pub status: VideoStatus,
/// The video is currently changing its state (for example from being `SELECT` to being `CACHE`)
pub status_change: bool,
pub thumbnail_url: Option<Url>,
pub title: String,
pub url: Url,
}
#[derive(Debug)]
pub struct VideoOptions {
pub yt_dlp: YtDlpOptions,
pub mpv: MpvOptions,
}
impl VideoOptions {
pub(crate) fn new(subtitle_langs: String, playback_speed: f64) -> Self {
let yt_dlp = YtDlpOptions { subtitle_langs };
let mpv = MpvOptions { playback_speed };
Self { yt_dlp, mpv }
}
/// This will write out the options that are different from the defaults.
/// Beware, that this does not set the priority.
pub fn to_cli_flags(self) -> String {
let mut f = String::new();
if self.mpv.playback_speed != DEFAULT_MPV_PLAYBACK_SPEED {
write!(f, " --speed '{}'", self.mpv.playback_speed).expect("Works");
}
if self.yt_dlp.subtitle_langs != DEFAULT_SUBTITLE_LANGS {
write!(f, " --subtitle-langs '{}'", self.yt_dlp.subtitle_langs).expect("Works");
}
f.trim().to_owned()
}
}
#[derive(Debug)]
/// Additionally settings passed to mpv on watch
pub struct MpvOptions {
/// The playback speed. (1 is 100%, 2.7 is 270%, and so on)
pub playback_speed: f64,
}
#[derive(Debug)]
/// Additionally configuration options, passed to yt-dlp on download
pub struct YtDlpOptions {
/// In the form of `lang1,lang2,lang3` (e.g. `en,de,sv`)
pub subtitle_langs: String,
}
/// # Video Lifetime (words in <brackets> are commands):
/// <Pick>
/// / \
/// <Watch> <Drop> -> Dropped // yt select
/// |
/// Cache // yt cache
/// |
/// Watched // yt watch
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum VideoStatus {
#[default]
Pick,
/// The video has been select to be watched
Watch,
/// The video has been cached and is ready to be watched
Cached,
/// The video has been watched
Watched,
/// The video has been select to be dropped
Drop,
/// The video has been dropped
Dropped,
}
impl VideoStatus {
pub fn as_command(&self) -> &str {
// NOTE: Keep the serialize able variants synced with the main `select` function <2024-06-14>
match self {
VideoStatus::Pick => "pick",
VideoStatus::Watch => "watch",
VideoStatus::Cached => "watch",
VideoStatus::Watched => "watch",
VideoStatus::Drop => "drop",
VideoStatus::Dropped => "drop",
}
}
pub fn as_db_integer(&self) -> i64 {
// These numbers should not change their mapping!
// Oh, and keep them in sync with the SQLite check constraint.
match self {
VideoStatus::Pick => 0,
VideoStatus::Watch => 1,
VideoStatus::Cached => 2,
VideoStatus::Watched => 3,
VideoStatus::Drop => 4,
VideoStatus::Dropped => 5,
}
}
pub fn from_db_integer(num: i64) -> Self {
match num {
0 => Self::Pick,
1 => Self::Watch,
2 => Self::Cached,
3 => Self::Watched,
4 => Self::Drop,
5 => Self::Dropped,
other => unreachable!(
"The database returned a enum discriminator, unknown to us: '{}'",
other
),
}
}
pub fn as_str(&self) -> &'static str {
match self {
VideoStatus::Pick => "Pick",
VideoStatus::Watch => "Watch",
VideoStatus::Cached => "Cache",
VideoStatus::Watched => "Watched",
VideoStatus::Drop => "Drop",
VideoStatus::Dropped => "Dropped",
}
}
}