feat: Takeout CSV, Atom feed, yt-dlp progress, and SQLite layers
37 unit tests covering the three parsers and the database, including that a feed refresh preserves download state and that a truncated feed response fails rather than storing partial results.
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A YouTube channel the user is subscribed to, as imported from Takeout.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Channel {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
/// A video as described by a channel's public Atom feed.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Video {
|
||||
pub id: String,
|
||||
pub channel_id: String,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
/// Unix seconds, so feed ordering is a plain indexed sort.
|
||||
pub published: i64,
|
||||
pub thumb_url: String,
|
||||
pub views: i64,
|
||||
pub is_short: bool,
|
||||
}
|
||||
|
||||
/// A channel plus how many of its videos we currently know about.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ChannelWithCount {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub url: String,
|
||||
pub video_count: i64,
|
||||
pub downloaded_count: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum DownloadState {
|
||||
Queued,
|
||||
Running,
|
||||
Done,
|
||||
Failed,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
impl DownloadState {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
DownloadState::Queued => "queued",
|
||||
DownloadState::Running => "running",
|
||||
DownloadState::Done => "done",
|
||||
DownloadState::Failed => "failed",
|
||||
DownloadState::Cancelled => "cancelled",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(s: &str) -> Option<Self> {
|
||||
match s {
|
||||
"queued" => Some(DownloadState::Queued),
|
||||
"running" => Some(DownloadState::Running),
|
||||
"done" => Some(DownloadState::Done),
|
||||
"failed" => Some(DownloadState::Failed),
|
||||
"cancelled" => Some(DownloadState::Cancelled),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A feed row: video metadata joined with whatever we know about its download.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FeedItem {
|
||||
pub id: String,
|
||||
pub channel_id: String,
|
||||
pub channel_title: String,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub published: i64,
|
||||
pub thumb_url: String,
|
||||
pub thumb_path: Option<String>,
|
||||
pub views: i64,
|
||||
pub is_short: bool,
|
||||
pub state: Option<DownloadState>,
|
||||
pub path: Option<String>,
|
||||
pub pct: Option<f64>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct FeedFilter {
|
||||
pub channel_id: Option<String>,
|
||||
pub search: Option<String>,
|
||||
#[serde(default)]
|
||||
pub downloaded_only: bool,
|
||||
#[serde(default)]
|
||||
pub hide_shorts: bool,
|
||||
pub limit: Option<i64>,
|
||||
}
|
||||
|
||||
/// Result of checking that the external binaries we shell out to exist.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Prereqs {
|
||||
pub yt_dlp: Option<String>,
|
||||
pub ffmpeg: Option<String>,
|
||||
pub library_path: String,
|
||||
}
|
||||
Reference in New Issue
Block a user