Failing channels are visible instead of buried in a toast count. Each refresh records its outcome per channel, the sidebar marks the failures and counts them in its heading, and selecting one explains why above its videos. Yours turn out to be three channels returning HTTP 404 — removed or renamed on YouTube. Video lengths now fill for what is on screen. Filling the newest across all subscriptions meant a channel's videos stayed blank forever, since the global newest always won the queue. yt-dlp can be updated from Settings, which also says whether it is current. It breaks whenever YouTube changes something, so it lands in app data and takes precedence over the bundled copy; ffmpeg is stable and ships with each release, so it is shown but not updated. Also: 'Downloaded only' is now 'Local'; Hide Shorts moved to Settings; the seven-step Takeout guide moved behind a button, since it dominated the panel; the player names the height it is actually streaming, which changes as an adaptive stream switches rendition; the player's delete is an icon; and the window minimum drops to 1080 now that the control row has one fewer button, while still never wrapping.
119 lines
3.3 KiB
Rust
119 lines
3.3 KiB
Rust
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,
|
|
/// Why the last refresh of this channel failed, if it did.
|
|
pub last_error: Option<String>,
|
|
}
|
|
|
|
#[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>,
|
|
/// Seconds watched, and the video's length — drives the feed progress bar.
|
|
pub position: Option<f64>,
|
|
pub duration: Option<f64>,
|
|
}
|
|
|
|
#[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,
|
|
}
|
|
|
|
/// What a replacing Takeout import would add and destroy.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ImportPreview {
|
|
pub incoming: i64,
|
|
pub removed_channels: i64,
|
|
pub removed_videos: i64,
|
|
pub removed_downloads: i64,
|
|
}
|