fix: ffmpeg version detection and player letterboxing
ffmpeg only accepts -version (single dash); it exits 8 on --version and writes to stderr, so the status panel reported it missing even when installed. Version flags are now per-tool and both streams are read. The player used max-h-full inside a grid, which a 1080x1920 Short overflowed. Switched to absolute fill with object-contain so portrait and landscape both letterbox correctly. Also sized the window to 1180x780 centered, which fits a laptop display.
This commit is contained in:
+19
-16
@@ -80,29 +80,32 @@ fn bin(name: &str) -> String {
|
||||
name.to_string()
|
||||
}
|
||||
|
||||
fn version_of(name: &str) -> Option<String> {
|
||||
std::process::Command::new(bin(name))
|
||||
.arg("--version")
|
||||
/// `flag` differs per tool: yt-dlp takes `--version`, ffmpeg only accepts
|
||||
/// `-version` (it exits non-zero on `--version` and writes to stderr), so the
|
||||
/// flag is passed in and both streams are consulted.
|
||||
fn version_of(name: &str, flag: &str) -> Option<String> {
|
||||
let out = std::process::Command::new(bin(name))
|
||||
.arg(flag)
|
||||
.output()
|
||||
.ok()
|
||||
.filter(|o| o.status.success())
|
||||
.map(|o| {
|
||||
String::from_utf8_lossy(&o.stdout)
|
||||
.lines()
|
||||
.next()
|
||||
.unwrap_or("")
|
||||
.trim()
|
||||
.to_string()
|
||||
})
|
||||
.filter(|s| !s.is_empty())
|
||||
.ok()?;
|
||||
if !out.status.success() {
|
||||
return None;
|
||||
}
|
||||
let text = if out.stdout.is_empty() {
|
||||
String::from_utf8_lossy(&out.stderr).to_string()
|
||||
} else {
|
||||
String::from_utf8_lossy(&out.stdout).to_string()
|
||||
};
|
||||
let first = text.lines().next().unwrap_or("").trim().to_string();
|
||||
(!first.is_empty()).then_some(first)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn check_prereqs(state: State<'_, AppState>) -> Result<Prereqs, String> {
|
||||
let library = state.library.lock().await.clone();
|
||||
Ok(Prereqs {
|
||||
yt_dlp: version_of("yt-dlp"),
|
||||
ffmpeg: version_of("ffmpeg").map(|v| {
|
||||
yt_dlp: version_of("yt-dlp", "--version"),
|
||||
ffmpeg: version_of("ffmpeg", "-version").map(|v| {
|
||||
// ffmpeg's first line is long; keep the useful head of it.
|
||||
v.split_whitespace().take(3).collect::<Vec<_>>().join(" ")
|
||||
}),
|
||||
|
||||
@@ -13,10 +13,11 @@
|
||||
"windows": [
|
||||
{
|
||||
"title": "FlightTube",
|
||||
"width": 1280,
|
||||
"height": 840,
|
||||
"minWidth": 940,
|
||||
"minHeight": 600
|
||||
"width": 1180,
|
||||
"height": 780,
|
||||
"minWidth": 900,
|
||||
"minHeight": 560,
|
||||
"center": true
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
//! Populates the installed app's database using the app's own code paths, so
|
||||
//! the UI can be verified against real data without driving the file dialog.
|
||||
//! Run with: cargo test --test seed_real_db -- --ignored --nocapture
|
||||
|
||||
use flighttube_lib::{db::Db, feed, models::FeedFilter, thumbs};
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn app_data() -> PathBuf {
|
||||
PathBuf::from(std::env::var("HOME").unwrap())
|
||||
.join("Library/Application Support/com.vincent.flighttube")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn seed() {
|
||||
let dir = app_data();
|
||||
let mut db = Db::open(&dir.join("flighttube.db")).expect("open real db");
|
||||
|
||||
let channels = db.channel_ids().unwrap();
|
||||
println!("channels in db: {}", channels.len());
|
||||
assert!(!channels.is_empty(), "seed channels first");
|
||||
|
||||
let http = reqwest::Client::builder()
|
||||
.user_agent("FlightTube/0.1 (+desktop)")
|
||||
.timeout(std::time::Duration::from_secs(20))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let mut total = 0;
|
||||
for cid in &channels {
|
||||
if let Ok(v) = feed::fetch_channel(&http, cid).await {
|
||||
total += v.len();
|
||||
db.upsert_videos(&v).unwrap();
|
||||
}
|
||||
}
|
||||
println!("videos upserted: {total}");
|
||||
|
||||
// Cache thumbnails so the feed renders with no network.
|
||||
let pending = db.videos_missing_thumbs(400).unwrap();
|
||||
println!("thumbnails to cache: {}", pending.len());
|
||||
let tdir = thumbs::cache_dir(&dir);
|
||||
let mut ok = 0;
|
||||
for (id, url) in pending {
|
||||
if let Ok(p) = thumbs::cache_one(&http, &id, &url, &tdir).await {
|
||||
db.set_thumb_path(&id, &p.to_string_lossy()).unwrap();
|
||||
ok += 1;
|
||||
}
|
||||
}
|
||||
println!("thumbnails cached: {ok}");
|
||||
|
||||
let feed_rows = db.list_feed(&FeedFilter::default()).unwrap();
|
||||
println!("feed rows now: {}", feed_rows.len());
|
||||
for i in feed_rows.iter().take(3) {
|
||||
println!(" [{}] {}", i.channel_title, i.title);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user