From 3d6f8548c3909bfd236b70a8c671c27030f44f59 Mon Sep 17 00:00:00 2001 From: YoursFunny Date: Fri, 7 Aug 2026 16:12:01 +0800 Subject: [PATCH] style: cargo fmt across the workspace Apply rustfmt to the 11 files that had drifted (86 hunks): x-media site modules (bsky/pixiv/twitter) and xmedia-bot (config/main/ photo/send). Formatting only - no semantic changes; full test suite still green. --- crates/x-media/src/site/bsky/interface.rs | 36 ++- crates/x-media/src/site/mod.rs | 1 - crates/x-media/src/site/pixiv/api.rs | 171 +++++------ crates/x-media/src/site/pixiv/interface.rs | 90 ++++-- crates/x-media/src/site/pixiv/mod.rs | 2 +- crates/x-media/src/site/twitter/auth.rs | 26 +- crates/x-media/src/site/twitter/interface.rs | 37 ++- crates/xmedia-bot/src/config.rs | 58 ++-- crates/xmedia-bot/src/main.rs | 19 +- crates/xmedia-bot/src/photo.rs | 26 +- crates/xmedia-bot/src/send.rs | 293 ++++++++++++------- 11 files changed, 457 insertions(+), 302 deletions(-) diff --git a/crates/x-media/src/site/bsky/interface.rs b/crates/x-media/src/site/bsky/interface.rs index f6d4567..8c6fe23 100644 --- a/crates/x-media/src/site/bsky/interface.rs +++ b/crates/x-media/src/site/bsky/interface.rs @@ -5,9 +5,8 @@ use html_escape::encode_text; use regex::Regex; use std::sync::LazyLock; -pub static PATTERN: LazyLock = LazyLock::new(|| { - Regex::new(r"bsky\.app/profile/([\w.\-:]+)/post/([\w.\-~]+)").unwrap() -}); +pub static PATTERN: LazyLock = + LazyLock::new(|| Regex::new(r"bsky\.app/profile/([\w.\-:]+)/post/([\w.\-~]+)").unwrap()); pub fn enabled() -> bool { true @@ -15,8 +14,14 @@ pub fn enabled() -> bool { pub async fn fetch_from_url(url: &str) -> Result { let caps = PATTERN.captures(url).ok_or(FetchError::NotFound)?; - let handle = caps.get(1).map(|m| m.as_str()).ok_or(FetchError::NotFound)?; - let rkey = caps.get(2).map(|m| m.as_str()).ok_or(FetchError::NotFound)?; + let handle = caps + .get(1) + .map(|m| m.as_str()) + .ok_or(FetchError::NotFound)?; + let rkey = caps + .get(2) + .map(|m| m.as_str()) + .ok_or(FetchError::NotFound)?; Ok(fetch(handle, rkey).await?.into()) } @@ -197,7 +202,10 @@ mod tests { })); let post = Post::from_json(&raw.to_string(), "3xxxx".into()).unwrap(); let fetched: Fetched = post.into(); - assert_eq!(fetched.source_url, "https://bsky.app/profile/user.bsky.social/post/3xxxx"); + assert_eq!( + fetched.source_url, + "https://bsky.app/profile/user.bsky.social/post/3xxxx" + ); assert_eq!(fetched.title, "hello "); assert_eq!(fetched.media.len(), 1); assert!(!fetched.sensitive); @@ -246,11 +254,10 @@ mod tests { #[tokio::test] async fn live_fetch_with_photos() { - let fetched = fetch_from_url( - "https://bsky.app/profile/asagi0398.bsky.social/post/3mqkhrq5w6k2m", - ) - .await - .unwrap(); + let fetched = + fetch_from_url("https://bsky.app/profile/asagi0398.bsky.social/post/3mqkhrq5w6k2m") + .await + .unwrap(); assert_eq!( fetched.source_url, "https://bsky.app/profile/asagi0398.bsky.social/post/3mqkhrq5w6k2m" @@ -260,9 +267,10 @@ mod tests { #[tokio::test] async fn live_fetch_smoke() { - let fetched = fetch_from_url("https://bsky.app/profile/fu-futa.bsky.social/post/3laoveufjv224") - .await - .unwrap(); + let fetched = + fetch_from_url("https://bsky.app/profile/fu-futa.bsky.social/post/3laoveufjv224") + .await + .unwrap(); assert_eq!( fetched.source_url, "https://bsky.app/profile/fu-futa.bsky.social/post/3laoveufjv224" diff --git a/crates/x-media/src/site/mod.rs b/crates/x-media/src/site/mod.rs index 50bae74..79570ba 100644 --- a/crates/x-media/src/site/mod.rs +++ b/crates/x-media/src/site/mod.rs @@ -179,7 +179,6 @@ impl From for FetchError { } } - impl From for FetchError { fn from(e: serde_json::Error) -> Self { FetchError::Json(e) diff --git a/crates/x-media/src/site/pixiv/api.rs b/crates/x-media/src/site/pixiv/api.rs index d98ea0d..5be2e39 100644 --- a/crates/x-media/src/site/pixiv/api.rs +++ b/crates/x-media/src/site/pixiv/api.rs @@ -10,8 +10,8 @@ use crate::site::FetchError; use std::env; use std::fmt; use std::io::{Cursor, Read}; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::LazyLock; +use std::sync::atomic::{AtomicBool, Ordering}; use std::time::{Duration, SystemTime}; const AUTH_TOKEN_URL: &str = "https://oauth.secure.pixiv.net/auth/token"; @@ -127,7 +127,9 @@ impl PixivAPI { pub async fn illust_detail(&self, illust_id: u64) -> Result { let access_token = self.get_access_token().await?; let response = crate::site::CLIENT - .get(format!("{APP_API_URL}/v1/illust/detail?illust_id={illust_id}")) + .get(format!( + "{APP_API_URL}/v1/illust/detail?illust_id={illust_id}" + )) .header("app-os", "ios") .header("app-os-version", "14.6") .header("User-Agent", APP_USER_AGENT) @@ -175,7 +177,9 @@ impl PixivAPI { pub async fn ugoira_metadata(&self, illust_id: u64) -> Result { let access_token = self.get_access_token().await?; let response = crate::site::CLIENT - .get(format!("{APP_API_URL}/v1/ugoira/metadata?illust_id={illust_id}")) + .get(format!( + "{APP_API_URL}/v1/ugoira/metadata?illust_id={illust_id}" + )) .header("app-os", "ios") .header("app-os-version", "14.6") .header("User-Agent", APP_USER_AGENT) @@ -218,87 +222,89 @@ impl PixivAPI { let Some(zip_url) = zip_url else { return Ok(None); }; - let zip_bytes = crate::site::download_media(&zip_url).await.map_err(|e| match e { - FetchError::Http(e) => PixivError::Http(e), - other => PixivError::Api(format!("frame zip download failed: {other}")), - })?; + let zip_bytes = crate::site::download_media(&zip_url) + .await + .map_err(|e| match e { + FetchError::Http(e) => PixivError::Http(e), + other => PixivError::Api(format!("frame zip download failed: {other}")), + })?; let frame_delays = metadata.frames.iter().map(|f| f.delay).collect::>(); - let result = tokio::task::spawn_blocking( - move || -> Result<(String, tempfile::TempDir), String> { - let frames_dir = tempfile::tempdir().map_err(|e| e.to_string())?; - let out_dir = tempfile::tempdir().map_err(|e| e.to_string())?; + let result = + tokio::task::spawn_blocking(move || -> Result<(String, tempfile::TempDir), String> { + let frames_dir = tempfile::tempdir().map_err(|e| e.to_string())?; + let out_dir = tempfile::tempdir().map_err(|e| e.to_string())?; - // Extract frames to canonical zero-padded names; pixiv ugoira - // frames are uniformly jpg or png per artwork. - let mut archive = zip::ZipArchive::new(Cursor::new(zip_bytes)) - .map_err(|e| format!("unzip: {e}"))?; - // pixiv ugoira frames are uniformly jpg or png per artwork; take - // the extension from the first entry. - let extension = if archive.len() > 0 { - let first_name = archive - .by_index(0) - .map_err(|e| e.to_string())? - .name() - .to_string(); - first_name - .rsplit('.') - .next() - .unwrap_or("jpg") - .to_string() - } else { - "jpg".to_string() - }; - let mut count = 0usize; - for i in 0..archive.len() { - let mut entry = archive.by_index(i).map_err(|e| e.to_string())?; - let mut bytes = Vec::new(); - entry.read_to_end(&mut bytes).map_err(|e| e.to_string())?; - let path = frames_dir.path().join(format!("img_{count:05}.{extension}")); - std::fs::write(&path, bytes).map_err(|e| e.to_string())?; - count += 1; - } - if count == 0 { - return Err("empty frame zip".to_string()); - } + // Extract frames to canonical zero-padded names; pixiv ugoira + // frames are uniformly jpg or png per artwork. + let mut archive = zip::ZipArchive::new(Cursor::new(zip_bytes)) + .map_err(|e| format!("unzip: {e}"))?; + // pixiv ugoira frames are uniformly jpg or png per artwork; take + // the extension from the first entry. + let extension = if archive.len() > 0 { + let first_name = archive + .by_index(0) + .map_err(|e| e.to_string())? + .name() + .to_string(); + first_name.rsplit('.').next().unwrap_or("jpg").to_string() + } else { + "jpg".to_string() + }; + let mut count = 0usize; + for i in 0..archive.len() { + let mut entry = archive.by_index(i).map_err(|e| e.to_string())?; + let mut bytes = Vec::new(); + entry.read_to_end(&mut bytes).map_err(|e| e.to_string())?; + let path = frames_dir + .path() + .join(format!("img_{count:05}.{extension}")); + std::fs::write(&path, bytes).map_err(|e| e.to_string())?; + count += 1; + } + if count == 0 { + return Err("empty frame zip".to_string()); + } - // Constant rate from the median frame delay (ms). - let mut delays = frame_delays; - delays.sort_unstable(); - let median = delays[delays.len() / 2].max(1); - let framerate = 1000.0 / median as f64; + // Constant rate from the median frame delay (ms). + let mut delays = frame_delays; + delays.sort_unstable(); + let median = delays[delays.len() / 2].max(1); + let framerate = 1000.0 / median as f64; - let output = out_dir.path().join("ugoira.mp4"); - let status = std::process::Command::new("ffmpeg") - .args([ - "-y", - "-framerate", - &framerate.to_string(), - "-i", - &frames_dir.path().join(format!("img_%05d.{extension}")).to_string_lossy(), - // libx264 needs even dimensions; pixiv ugoira frames can - // be odd-sized (e.g. 277x405). - "-vf", - "scale=trunc(iw/2)*2:trunc(ih/2)*2", - "-c:v", - "libx264", - "-pix_fmt", - "yuv420p", - "-movflags", - "+faststart", - &output.to_string_lossy(), - ]) - .stdout(std::process::Stdio::null()) - .stderr(std::process::Stdio::null()) - .status() - .map_err(|e| format!("ffmpeg spawn failed: {e}"))?; - if !status.success() { - return Err(format!("ffmpeg exited with {status}")); - } - Ok((output.to_string_lossy().into_owned(), out_dir)) - }, - ) - .await - .expect("ugoira encode worker panicked"); + let output = out_dir.path().join("ugoira.mp4"); + let status = std::process::Command::new("ffmpeg") + .args([ + "-y", + "-framerate", + &framerate.to_string(), + "-i", + &frames_dir + .path() + .join(format!("img_%05d.{extension}")) + .to_string_lossy(), + // libx264 needs even dimensions; pixiv ugoira frames can + // be odd-sized (e.g. 277x405). + "-vf", + "scale=trunc(iw/2)*2:trunc(ih/2)*2", + "-c:v", + "libx264", + "-pix_fmt", + "yuv420p", + "-movflags", + "+faststart", + &output.to_string_lossy(), + ]) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .status() + .map_err(|e| format!("ffmpeg spawn failed: {e}"))?; + if !status.success() { + return Err(format!("ffmpeg exited with {status}")); + } + Ok((output.to_string_lossy().into_owned(), out_dir)) + }) + .await + .expect("ugoira encode worker panicked"); match result { Ok(pair) => Ok(Some(pair)), Err(message) => { @@ -332,9 +338,8 @@ fn log_once_ffmpeg_missing() { } /// pixiv3-rs replacement: `None` when `PIXIV_REFRESH_TOKEN` is unset. -static PIXIV_CLIENT: LazyLock> = LazyLock::new(|| { - env::var("PIXIV_REFRESH_TOKEN").ok().map(PixivAPI::new) -}); +static PIXIV_CLIENT: LazyLock> = + LazyLock::new(|| env::var("PIXIV_REFRESH_TOKEN").ok().map(PixivAPI::new)); /// Set at startup when the login validation fails; pixiv stays disabled until /// the next process start. diff --git a/crates/x-media/src/site/pixiv/interface.rs b/crates/x-media/src/site/pixiv/interface.rs index aadc1c5..92c1d01 100644 --- a/crates/x-media/src/site/pixiv/interface.rs +++ b/crates/x-media/src/site/pixiv/interface.rs @@ -82,12 +82,15 @@ impl Illustration { // keeps media empty when encoding fails or ffmpeg is missing. } else if model.page_count > 1 { media.extend(model.meta_pages.iter().filter_map(|page| { - page.image_urls.original.clone().map(|original| Media::Illustration { - title: None, - url: original, - thumbnail_url: Some(page.image_urls.medium.clone()), - fallback_url: Some(page.image_urls.large.clone()), - }) + page.image_urls + .original + .clone() + .map(|original| Media::Illustration { + title: None, + url: original, + thumbnail_url: Some(page.image_urls.medium.clone()), + fallback_url: Some(page.image_urls.large.clone()), + }) })); } else if let Some(original) = model .meta_single_page @@ -147,8 +150,8 @@ impl From for Fetched { #[cfg(test)] mod tests { - use super::*; use super::super::model::IllustrationModel; + use super::*; fn illust_json( type_: &str, @@ -203,8 +206,14 @@ mod tests { ("https://pixiv.net/artworks/123456", "123456"), ("https://www.pixiv.net/en/artworks/123456", "123456"), ("https://www.pixiv.net/i/123456", "123456"), - ("https://www.pixiv.net/member_illust.php?mode=medium&illust_id=123456", "123456"), - ("https://www.pixiv.net/en/member_illust.php?illust_id=123456", "123456"), + ( + "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=123456", + "123456", + ), + ( + "https://www.pixiv.net/en/member_illust.php?illust_id=123456", + "123456", + ), ]; for (url, id) in cases { let caps = PATTERN.captures(url).unwrap_or_else(|| panic!("{url}")); @@ -225,7 +234,14 @@ mod tests { #[test] fn ugoira_yields_empty_media() { - let v = illust_json("ugoira", 1, Some("https://i.pximg.net/orig.jpg"), None, vec![], 0); + let v = illust_json( + "ugoira", + 1, + Some("https://i.pximg.net/orig.jpg"), + None, + vec![], + 0, + ); let illustration = parse(v); let fetched: Fetched = illustration.into(); assert!(fetched.media.is_empty()); @@ -296,7 +312,12 @@ mod tests { let fetched: Fetched = parse(v).into(); assert_eq!(fetched.media.len(), 1); match &fetched.media[0] { - Media::Illustration { url, thumbnail_url, fallback_url, .. } => { + Media::Illustration { + url, + thumbnail_url, + fallback_url, + .. + } => { assert_eq!(url, "https://i.pximg.net/p2.jpg"); assert_eq!(thumbnail_url.as_deref(), Some("m2.jpg")); assert_eq!(fallback_url.as_deref(), Some("l2.jpg")); @@ -307,7 +328,14 @@ mod tests { #[test] fn caption_with_escapes_format_and_substitutes() { - let v = illust_json("illust", 1, Some("https://i.pximg.net/o.jpg"), None, vec![], 0); + let v = illust_json( + "illust", + 1, + Some("https://i.pximg.net/o.jpg"), + None, + vec![], + 0, + ); let fetched: Fetched = parse(v).into(); // Format string is escaped in full, then placeholders substituted. let out = fetched.caption_with("{title} by {author}