feat: show Miruro series artwork in descriptions

This commit is contained in:
2026-10-17 17:01:33 +02:00
parent ba04cbe158
commit 9743d1993d
8 changed files with 421 additions and 147 deletions
+35 -1
View File
@@ -679,6 +679,25 @@ impl ThumbnailJob {
"Invalid YouTube video ID",
));
}
Self::from_url(&format!("https://i.ytimg.com/vi/{id}/hqdefault.jpg"))
}
pub fn from_url(url: &str) -> io::Result<Self> {
let uri: ureq::http::Uri = url
.parse()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "Invalid artwork URL"))?;
if uri.scheme_str() != Some("https")
|| uri.host().is_none_or(str::is_empty)
|| uri
.authority()
.is_some_and(|authority| authority.as_str().contains('@'))
|| url.chars().any(char::is_control)
{
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Artwork URLs must be absolute HTTPS without credentials",
));
}
let mut child = isolated_command(OsStr::new("curl"))
.args([
"--disable",
@@ -686,6 +705,7 @@ impl ThumbnailJob {
"--show-error",
"--fail",
"--location",
"--globoff",
"--proto",
"=https",
"--proto-redir",
@@ -697,7 +717,7 @@ impl ThumbnailJob {
"--max-filesize",
"2097152", // 2 MiB; read_thumbnail also enforces the limit.
])
.arg(format!("https://i.ytimg.com/vi/{id}/hqdefault.jpg"))
.arg(url)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
@@ -1570,6 +1590,20 @@ mod tests {
Err(error) if error.kind() == io::ErrorKind::InvalidInput
));
}
for url in [
"http://example.test/cover.jpg",
"file:///tmp/cover.jpg",
"ftp://example.test/cover.jpg",
"//example.test/cover.jpg",
"https://",
"https://user:secret@example.test/cover.jpg",
"https://example.test/\ncover.jpg",
] {
assert!(matches!(
ThumbnailJob::from_url(url),
Err(error) if error.kind() == io::ErrorKind::InvalidInput
));
}
}
#[test]