about summary refs log blame commit diff stats
path: root/src/storage/video_database/mod.rs
blob: 28263ca132bbe42ede8b2193d192a47f944d2516 (plain) (tree)








































































































































































                                                                                                     
// 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",
        }
    }
}