feat(gifs): serve every medium, tiny and nano variant klipy offers (#1829)

This commit is contained in:
Hampus
2026-08-24 12:26:44 +02:00
committed by GitHub
parent d339b82f8e
commit 56f6009390
6 changed files with 440 additions and 130 deletions
@@ -2,6 +2,7 @@
import {Logger} from '@fluxer/logger/src/Logger';
import type {ResolvedGifEntrySchema} from '@fluxer/schema/src/domains/gif/FavoriteGifSchemas';
import {inferFormatContentType, PREVIEW_FORMAT_PRIORITY} from '@fluxer/schema/src/domains/gif/GifMediaFormatKeys';
import type {GifMediaFormat, GifResponse} from '@fluxer/schema/src/domains/gif/GifSchemas';
import type {EmbedMediaResponse} from '@fluxer/schema/src/domains/message/EmbedSchemas';
import {tryExtractGifProviderSlug} from '../gif/GifProviderUtils';
@@ -10,17 +11,6 @@ import type {IGifProvider} from '../gif/IGifProvider';
import type {IMediaService, MediaProxyMetadataResponse} from '../infrastructure/IMediaService';
import type {IUnfurlerService} from '../infrastructure/IUnfurlerService';
const PREVIEW_FORMAT_PRIORITY = ['webm', 'mp4', 'tinywebm', 'tinymp4', 'webp', 'gif', 'tinygif', 'nanogif'] as const;
const FORMAT_CONTENT_TYPES: Record<string, string> = {
webm: 'video/webm',
tinywebm: 'video/webm',
mp4: 'video/mp4',
tinymp4: 'video/mp4',
webp: 'image/webp',
gif: 'image/gif',
tinygif: 'image/gif',
nanogif: 'image/gif',
};
const logger = new Logger('FavoriteGifResolver');
function pickFavoriteGifPreviewFormat(
@@ -221,10 +211,6 @@ function isUsableGifMediaFormat(format: GifMediaFormat | undefined): format is G
return Boolean(format?.src && format.proxy_src && format.width > 0 && format.height > 0);
}
function inferFormatContentType(formatKey: string): string {
return FORMAT_CONTENT_TYPES[formatKey] ?? '';
}
function isRenderableMediaType(contentType: string | null | undefined): boolean {
if (!contentType) return false;
return contentType.startsWith('image/') || contentType.startsWith('video/');
+352 -106
View File
@@ -7,6 +7,7 @@ use reqwest::Url;
use serde::Deserialize;
use serde_json::Value;
use std::collections::{BTreeMap, HashSet};
use std::sync::LazyLock;
use std::time::Duration;
use tokio::time::sleep;
@@ -20,10 +21,13 @@ const KLIPY_RESPONSE_LIMIT_BYTES: usize = 512 * 1024;
const FLUXER_USER_AGENT: &str = "Fluxerbot/1.0 (+https://fluxer.app)";
const KLIPY_PROVIDER_NAME: &str = "klipy";
const KLIPY_FEATURED_CATEGORY_REFRESH_COUNTRY: &str = "US";
const UNRESOLVABLE_CACHE_KEY: &str = "unresolvable";
const SIZE_PREFERENCE: [&str; 4] = ["hd", "md", "sm", "xs"];
const FILE_FORMAT_PREFERENCE: [&str; 4] = ["webm", "mp4", "webp", "gif"];
const MEDIA_FORMAT_PREFERENCE: [&str; 11] = [
const SIZE_KEY_PREFIXES: [(&str, &str); 4] =
[("hd", ""), ("md", "medium"), ("sm", "tiny"), ("xs", "nano")];
const FILE_FORMATS: [&str; 4] = ["webm", "mp4", "webp", "gif"];
const UNSIZED_MEDIA_FORMAT_KEYS: [&str; 1] = ["loopedmp4"];
const MEDIA_FORMAT_PREFERENCE: [&str; 17] = [
"webm",
"mp4",
"webp",
@@ -35,9 +39,14 @@ const MEDIA_FORMAT_PREFERENCE: [&str; 11] = [
"nanowebm",
"nanomp4",
"nanogif",
"loopedmp4",
"mediummp4",
"mediumwebm",
"mediumwebp",
"nanowebp",
"tinywebp",
];
const MEDIA_FILTER: &str =
"webm,mp4,webp,gif,mediumgif,tinywebm,tinymp4,tinygif,nanowebm,nanomp4,nanogif";
static MEDIA_FILTER: LazyLock<String> = LazyLock::new(|| MEDIA_FORMAT_PREFERENCE.join(","));
#[derive(Clone)]
pub struct KlipyClient {
@@ -168,7 +177,7 @@ impl KlipyClient {
("country", country),
("locale", &locale),
("limit", &limit),
("media_filter", MEDIA_FILTER),
("media_filter", MEDIA_FILTER.as_str()),
],
)
.await
@@ -188,7 +197,7 @@ impl KlipyClient {
("country", country),
("locale", &locale),
("limit", "1"),
("media_filter", MEDIA_FILTER),
("media_filter", MEDIA_FILTER.as_str()),
],
)
.await
@@ -208,7 +217,7 @@ impl KlipyClient {
("country", country),
("locale", &locale),
("limit", "50"),
("media_filter", MEDIA_FILTER),
("media_filter", MEDIA_FILTER.as_str()),
],
)
.await
@@ -253,7 +262,13 @@ impl KlipyClient {
("q", q),
],
)?;
let response = self.http_client.get(url).send().await?;
let response = self
.http_client
.get(url)
.send()
.await
.map_err(reqwest::Error::without_url)
.context("KLIPY registershare request failed")?;
if !response.status().is_success() {
anyhow::bail!(
"KLIPY registershare failed with status {}",
@@ -372,7 +387,10 @@ impl KlipyClient {
url: Url,
path: &KlipyPath,
) -> anyhow::Result<Option<GifItem>> {
match self.fetch_json_response::<DirectGifResponse>(url).await? {
match self
.fetch_json_response::<DirectGifResponse>(url, klipy_resource(path.path_type))
.await?
{
KlipyJsonFetch::NotFound => Ok(None),
KlipyJsonFetch::Found(response) => Ok(response
.data
@@ -388,7 +406,7 @@ impl KlipyClient {
let url = self.create_url(endpoint, params)?;
let mut last_error = None;
for attempt in 0..MAX_RETRIES {
match self.fetch_json_once(url.clone()).await {
match self.fetch_json_once(url.clone(), endpoint).await {
Ok(value) => return Ok(value),
Err(error) if attempt + 1 < MAX_RETRIES => {
last_error = Some(error);
@@ -400,26 +418,41 @@ impl KlipyClient {
Err(last_error.unwrap_or_else(|| anyhow::anyhow!("exceeded KLIPY retry limit")))
}
async fn fetch_json_once<T>(&self, url: Url) -> anyhow::Result<T>
async fn fetch_json_once<T>(&self, url: Url, label: &str) -> anyhow::Result<T>
where
T: serde::de::DeserializeOwned,
{
match self.fetch_json_response(url).await? {
match self.fetch_json_response(url, label).await? {
KlipyJsonFetch::Found(value) => Ok(value),
KlipyJsonFetch::NotFound => anyhow::bail!("KLIPY request returned not found"),
KlipyJsonFetch::NotFound => {
anyhow::bail!("KLIPY {label} request returned not found")
}
}
}
async fn fetch_json_response<T>(&self, url: Url) -> anyhow::Result<KlipyJsonFetch<T>>
async fn fetch_json_response<T>(
&self,
url: Url,
label: &str,
) -> anyhow::Result<KlipyJsonFetch<T>>
where
T: serde::de::DeserializeOwned,
{
let response = self.http_client.get(url.clone()).send().await?;
let response = self
.http_client
.get(url)
.send()
.await
.map_err(reqwest::Error::without_url)
.with_context(|| format!("KLIPY {label} request failed"))?;
if response.status() == reqwest::StatusCode::NOT_FOUND {
return Ok(KlipyJsonFetch::NotFound);
}
if !response.status().is_success() {
anyhow::bail!("KLIPY request failed with status {}", response.status());
anyhow::bail!(
"KLIPY {label} request failed with status {}",
response.status()
);
}
if response
.content_length()
@@ -427,12 +460,16 @@ impl KlipyClient {
{
anyhow::bail!("KLIPY response declared more than {KLIPY_RESPONSE_LIMIT_BYTES} bytes");
}
let bytes = response.bytes().await?;
let bytes = response
.bytes()
.await
.map_err(reqwest::Error::without_url)
.with_context(|| format!("KLIPY {label} response body failed"))?;
if bytes.len() > KLIPY_RESPONSE_LIMIT_BYTES {
anyhow::bail!("KLIPY response exceeded {KLIPY_RESPONSE_LIMIT_BYTES} bytes");
anyhow::bail!("KLIPY {label} response exceeded {KLIPY_RESPONSE_LIMIT_BYTES} bytes");
}
serde_json::from_slice(&bytes)
.with_context(|| format!("failed to parse KLIPY response from {url}"))
.with_context(|| format!("failed to parse KLIPY {label} response"))
.map(KlipyJsonFetch::Found)
}
@@ -519,52 +556,39 @@ impl KlipyClient {
) -> (BTreeMap<String, GifMediaFormat>, Option<GifMediaFormat>) {
let mut media = BTreeMap::new();
let mut preferred = None;
for size in SIZE_PREFERENCE {
let Some(KlipyFileGroup::Sized(bucket)) =
input.file.as_ref().and_then(|files| files.get(size))
else {
continue;
};
for format in FILE_FORMAT_PREFERENCE {
let Some(entry) = bucket.get(format) else {
if let Some(files) = input.file.as_ref() {
report_unmapped_file_keys(files);
for (size, _) in SIZE_KEY_PREFIXES {
let Some(KlipyFileGroup::Sized(bucket)) = files.get(size) else {
continue;
};
let Some(media_format) = self.to_media_format(entry, None) else {
continue;
};
let public_key = public_format_key(size, format);
media.insert(public_key, media_format.clone());
if preferred.is_none() {
preferred = Some(media_format);
for format in FILE_FORMATS {
let Some(entry) = bucket.get(format) else {
continue;
};
let Some(media_format) = self.to_media_format(entry, None) else {
continue;
};
let Some(public_key) = public_format_key(size, format) else {
continue;
};
media
.entry(public_key)
.or_insert_with(|| media_format.clone());
if preferred.is_none() {
preferred = Some(media_format);
}
}
}
}
for format in FILE_FORMAT_PREFERENCE {
let Some(KlipyFileGroup::Flat(entry)) =
input.file.as_ref().and_then(|files| files.get(format))
else {
continue;
};
let meta = input
.file_meta
.as_ref()
.and_then(|file_meta| file_meta.get(format));
let Some(media_format) = self.to_media_format(entry, meta) else {
continue;
};
media
.entry(format.to_owned())
.or_insert_with(|| media_format.clone());
if preferred.is_none() {
preferred = Some(media_format);
}
}
if let Some(formats) = input.media_formats.as_ref() {
for format in MEDIA_FORMAT_PREFERENCE {
let Some(entry) = formats.get(format) else {
for format in FILE_FORMATS {
let Some(KlipyFileGroup::Flat(entry)) = files.get(format) else {
continue;
};
let Some(media_format) = self.to_media_format(entry, None) else {
let meta = input
.file_meta
.as_ref()
.and_then(|file_meta| file_meta.get(format));
let Some(media_format) = self.to_media_format(entry, meta) else {
continue;
};
media
@@ -574,18 +598,18 @@ impl KlipyClient {
preferred = Some(media_format);
}
}
for (format, entry) in formats {
if MEDIA_FORMAT_PREFERENCE.contains(&format.as_str())
|| !is_supported_media_format_key(format)
{
}
if let Some(formats) = input.media_formats.as_ref() {
report_unmapped_media_format_keys(formats);
for format in MEDIA_FORMAT_PREFERENCE {
let Some(entry) = formats.get(format) else {
continue;
}
};
let Some(media_format) = self.to_media_format(entry, None) else {
continue;
};
media
.entry(format.clone())
.entry(format.to_owned())
.or_insert_with(|| media_format.clone());
if preferred.is_none() {
preferred = Some(media_format);
@@ -665,6 +689,13 @@ pub fn extract_slug_from_url(url: &str) -> Option<String> {
parse_klipy_path(url).map(|path| path.slug)
}
pub fn resolve_cache_key(url: &str) -> String {
match parse_klipy_path(url) {
Some(path) => format!("{}:{}", klipy_resource(path.path_type), path.slug),
None => UNRESOLVABLE_CACHE_KEY.to_owned(),
}
}
fn klipy_id_as_string(value: &Value) -> Option<String> {
match value {
Value::String(value) => {
@@ -712,27 +743,49 @@ fn klipy_resource(path_type: KlipyPathType) -> &'static str {
}
}
fn public_format_key(size: &str, format: &str) -> String {
match (size, format) {
("hd", "webm") => "webm",
("hd", "mp4") => "mp4",
("hd", "webp") => "webp",
("hd", "gif") => "gif",
("md", "webm") => "mediumwebm",
("md", "mp4") => "mediummp4",
("md", "webp") => "mediumwebp",
("md", "gif") => "mediumgif",
("sm", "webm") => "tinywebm",
("sm", "mp4") => "tinymp4",
("sm", "webp") => "tinywebp",
("sm", "gif") => "tinygif",
("xs", "webm") => "nanowebm",
("xs", "mp4") => "nanomp4",
("xs", "webp") => "nanowebp",
("xs", "gif") => "nanogif",
_ => format,
fn size_key_prefix(size: &str) -> Option<&'static str> {
SIZE_KEY_PREFIXES
.iter()
.find(|(known, _)| *known == size)
.map(|(_, prefix)| *prefix)
}
fn public_format_key(size: &str, format: &str) -> Option<String> {
if !FILE_FORMATS.contains(&format) {
return None;
}
Some(format!("{}{format}", size_key_prefix(size)?))
}
fn report_unmapped_file_keys(files: &BTreeMap<String, KlipyFileGroup>) {
for (key, group) in files {
match group {
KlipyFileGroup::Sized(bucket) => {
if size_key_prefix(key).is_none() {
tracing::warn!(size = %key, "skipping gif file bucket with an unmapped size");
continue;
}
for format in bucket.keys() {
if !FILE_FORMATS.contains(&format.as_str()) {
tracing::warn!(size = %key, format = %format, "skipping gif file entry with an unmapped format");
}
}
}
KlipyFileGroup::Flat(_) => {
if !FILE_FORMATS.contains(&key.as_str()) {
tracing::warn!(format = %key, "skipping flat gif file entry with an unmapped format");
}
}
}
}
}
fn report_unmapped_media_format_keys(formats: &BTreeMap<String, KlipyMediaEntry>) {
for key in formats.keys() {
if !is_supported_media_format_key(key) {
tracing::debug!(format = %key, "skipping unsupported gif media format");
}
}
.to_owned()
}
fn valid_dimensions(width: i32, height: i32) -> Option<(i32, i32)> {
@@ -740,21 +793,12 @@ fn valid_dimensions(width: i32, height: i32) -> Option<(i32, i32)> {
}
fn is_supported_media_format_key(format: &str) -> bool {
matches!(
format,
"webm"
| "tinywebm"
| "nanowebm"
| "mp4"
| "loopedmp4"
| "tinymp4"
| "nanomp4"
| "webp"
| "gif"
| "mediumgif"
| "tinygif"
| "nanogif"
)
UNSIZED_MEDIA_FORMAT_KEYS.contains(&format)
|| SIZE_KEY_PREFIXES.iter().any(|(_, prefix)| {
format
.strip_prefix(prefix)
.is_some_and(|codec| FILE_FORMATS.contains(&codec))
})
}
fn category_response(name: String, gif: Option<GifItem>) -> GifCategoryTag {
@@ -992,10 +1036,212 @@ mod tests {
}
#[test]
fn maps_provider_format_keys() {
assert_eq!(public_format_key("hd", "webm"), "webm");
assert_eq!(public_format_key("sm", "gif"), "tinygif");
assert_eq!(public_format_key("xs", "webp"), "nanowebp");
fn resolve_cache_key_ignores_everything_but_the_klipy_path() {
assert_eq!(
resolve_cache_key("https://klipy.com/gifs/funny-123"),
"gifs:funny-123"
);
assert_eq!(
resolve_cache_key("https://www.klipy.com/gif/funny-123?utm_source=x"),
"gifs:funny-123"
);
assert_eq!(
resolve_cache_key("https://klipy.com/clips/kittens"),
"clips:kittens"
);
assert_ne!(
resolve_cache_key("https://klipy.com/gifs/kittens"),
resolve_cache_key("https://klipy.com/clips/kittens")
);
assert_eq!(
resolve_cache_key("https://notklipy.com/gifs/funny"),
"unresolvable"
);
}
const FROZEN_PUBLIC_FORMAT_KEYS: [(&str, &str, &str); 16] = [
("hd", "webm", "webm"),
("hd", "mp4", "mp4"),
("hd", "webp", "webp"),
("hd", "gif", "gif"),
("md", "webm", "mediumwebm"),
("md", "mp4", "mediummp4"),
("md", "webp", "mediumwebp"),
("md", "gif", "mediumgif"),
("sm", "webm", "tinywebm"),
("sm", "mp4", "tinymp4"),
("sm", "webp", "tinywebp"),
("sm", "gif", "tinygif"),
("xs", "webm", "nanowebm"),
("xs", "mp4", "nanomp4"),
("xs", "webp", "nanowebp"),
("xs", "gif", "nanogif"),
];
#[test]
fn every_size_and_format_pair_maps_to_its_frozen_key() {
assert_eq!(
FROZEN_PUBLIC_FORMAT_KEYS.len(),
SIZE_KEY_PREFIXES.len() * FILE_FORMATS.len()
);
for (size, format, expected) in FROZEN_PUBLIC_FORMAT_KEYS {
assert_eq!(public_format_key(size, format).as_deref(), Some(expected));
}
}
#[test]
fn unmapped_sizes_and_formats_produce_no_key() {
assert_eq!(public_format_key("xxl", "webm"), None);
assert_eq!(public_format_key("orig", "gif"), None);
assert_eq!(public_format_key("hd", "avif"), None);
assert_eq!(public_format_key("", "webm"), None);
}
#[test]
fn the_accepted_vocabulary_is_frozen() {
let mut expected: Vec<&str> = FROZEN_PUBLIC_FORMAT_KEYS
.iter()
.map(|(_, _, key)| *key)
.collect();
expected.extend(UNSIZED_MEDIA_FORMAT_KEYS);
expected.sort_unstable();
let mut ordered = MEDIA_FORMAT_PREFERENCE.to_vec();
ordered.sort_unstable();
assert_eq!(
ordered, expected,
"preference order must cover exactly the accepted keys"
);
for key in expected {
assert!(is_supported_media_format_key(key), "dropped {key}");
}
for key in [
"preview", "avif", "medium", "hdwebm", "webmm", "tinyavif", "",
] {
assert!(!is_supported_media_format_key(key), "admitted {key}");
}
}
#[test]
fn media_filter_requests_every_accepted_key() {
assert_eq!(
MEDIA_FILTER.as_str(),
"webm,mp4,webp,gif,mediumgif,tinywebm,tinymp4,tinygif,nanowebm,nanomp4,nanogif,loopedmp4,mediummp4,mediumwebm,mediumwebp,nanowebp,tinywebp"
);
}
#[test]
fn sized_payloads_emit_exactly_the_frozen_key_set() {
let client = KlipyClient::new(MediaProxyUrlBuilder::for_test(
"https://media.example.test",
"secret",
))
.expect("client");
let input = serde_json::from_value::<KlipyGif>(serde_json::json!({
"id": "frozen-keys",
"title": "Frozen Keys",
"itemurl": "https://klipy.com/gifs/frozen-keys",
"file": {
"hd": {
"webm": {"url": "https://static.klipy.com/hd.webm", "width": 640, "height": 420},
"mp4": {"url": "https://static.klipy.com/hd.mp4", "width": 640, "height": 420},
"webp": {"url": "https://static.klipy.com/hd.webp", "width": 640, "height": 420},
"gif": {"url": "https://static.klipy.com/hd.gif", "width": 640, "height": 420},
"avif": {"url": "https://static.klipy.com/hd.avif", "width": 640, "height": 420}
},
"md": {
"webm": {"url": "https://static.klipy.com/md.webm", "width": 498, "height": 327},
"mp4": {"url": "https://static.klipy.com/md.mp4", "width": 498, "height": 327},
"webp": {"url": "https://static.klipy.com/md.webp", "width": 498, "height": 327},
"gif": {"url": "https://static.klipy.com/md.gif", "width": 498, "height": 327}
},
"sm": {
"webm": {"url": "https://static.klipy.com/sm.webm", "width": 220, "height": 144},
"mp4": {"url": "https://static.klipy.com/sm.mp4", "width": 220, "height": 144},
"webp": {"url": "https://static.klipy.com/sm.webp", "width": 220, "height": 144},
"gif": {"url": "https://static.klipy.com/sm.gif", "width": 220, "height": 144}
},
"xs": {
"webm": {"url": "https://static.klipy.com/xs.webm", "width": 137, "height": 90},
"mp4": {"url": "https://static.klipy.com/xs.mp4", "width": 137, "height": 90},
"webp": {"url": "https://static.klipy.com/xs.webp", "width": 137, "height": 90},
"gif": {"url": "https://static.klipy.com/xs.gif", "width": 137, "height": 90}
},
"xxl": {
"webm": {"url": "https://static.klipy.com/xxl.webm", "width": 1280, "height": 840}
}
}
}))
.expect("fixture");
let gif = client.transform_gif(input).expect("transformed gif");
let keys: Vec<&str> = gif.media.keys().map(String::as_str).collect();
assert_eq!(
keys,
[
"gif",
"mediumgif",
"mediummp4",
"mediumwebm",
"mediumwebp",
"mp4",
"nanogif",
"nanomp4",
"nanowebm",
"nanowebp",
"tinygif",
"tinymp4",
"tinywebm",
"tinywebp",
"webm",
"webp"
]
);
assert_eq!(gif.media["webm"].src, "https://static.klipy.com/hd.webm");
assert_eq!(gif.src, gif.media["webm"].src);
}
#[test]
fn keeps_medium_variants_from_the_v2_media_formats_payload() {
let client = KlipyClient::new(MediaProxyUrlBuilder::for_test(
"https://media.example.test",
"secret",
))
.expect("client");
let input = serde_json::from_value::<KlipyGif>(serde_json::json!({
"id": "medium-variants",
"title": "Medium Variants",
"itemurl": "https://klipy.com/gifs/medium-variants",
"media_formats": {
"webp": {"url": "https://static.klipy.com/m.webp", "dims": [498, 327]},
"mediumwebm": {"url": "https://static.klipy.com/m-medium.webm", "dims": [640, 420]},
"mediummp4": {"url": "https://static.klipy.com/m-medium.mp4", "dims": [640, 420]},
"mediumwebp": {"url": "https://static.klipy.com/m-medium.webp", "dims": [640, 420]},
"tinywebp": {"url": "https://static.klipy.com/m-tiny.webp", "dims": [220, 144]},
"nanowebp": {"url": "https://static.klipy.com/m-nano.webp", "dims": [137, 90]}
}
}))
.expect("fixture");
let gif = client.transform_gif(input).expect("transformed gif");
for format in [
"mediumwebm",
"mediummp4",
"mediumwebp",
"tinywebp",
"nanowebp",
] {
assert!(gif.media.contains_key(format), "missing {format}");
}
assert_eq!(
gif.media.get("mediumwebm").map(|format| (
format.src.as_str(),
format.width,
format.height
)),
Some(("https://static.klipy.com/m-medium.webm", 640, 420))
);
}
#[test]
+39 -6
View File
@@ -80,12 +80,9 @@ fn request_key(req: &GifRequest) -> Option<String> {
} => Some(format!("trending:{locale}:{country}")),
GifRequest::Suggest { q, locale, .. } => Some(format!("suggest:{locale}:{q}")),
GifRequest::RegisterShare { .. } => None,
GifRequest::ResolveByUrl {
url,
locale,
country,
..
} => Some(format!("resolve:{locale}:{country}:{url}")),
GifRequest::ResolveByUrl { url, .. } => {
Some(format!("resolve:{}", crate::klipy::resolve_cache_key(url)))
}
GifRequest::BuildShareUrl { slug } => Some(format!("share-url:{slug}")),
GifRequest::ExtractSlugFromUrl { url } => Some(format!("extract-slug:{url}")),
}
@@ -167,6 +164,42 @@ mod tests {
assert_eq!(request_l1_key(&base), Some("trending:en_US:US".to_owned()));
}
#[test]
fn resolve_key_collapses_locale_country_and_url_spelling() {
let make = |url: &str, locale: &str, country: &str| GifRequest::ResolveByUrl {
api_key: "key".to_owned(),
url: url.to_owned(),
locale: locale.to_owned(),
country: country.to_owned(),
};
let base = make("https://klipy.com/gifs/kittens", "en_US", "US");
assert_eq!(
GifsRouter::coalesce_key(&base),
Some("resolve:gifs:kittens".to_owned())
);
assert_eq!(
GifsRouter::coalesce_key(&base),
GifsRouter::coalesce_key(&make("https://klipy.com/gifs/kittens", "sv_SE", "SE"))
);
assert_eq!(
GifsRouter::coalesce_key(&base),
GifsRouter::coalesce_key(&make(
"https://www.klipy.com/gif/kittens?x=1",
"en_US",
"US"
))
);
assert_ne!(
GifsRouter::coalesce_key(&base),
GifsRouter::coalesce_key(&make("https://klipy.com/clips/kittens", "en_US", "US"))
);
assert_eq!(
request_l1_key(&base),
Some("resolve:gifs:kittens".to_owned())
);
}
#[test]
fn register_share_is_not_cached_or_coalesced() {
let request = GifRequest::RegisterShare {
+2 -2
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
use crate::klipy::{KlipyClient, build_share_url, extract_slug_from_url};
use crate::klipy::{KlipyClient, build_share_url, extract_slug_from_url, resolve_cache_key};
use crate::media_proxy::MediaProxyUrlBuilder;
use crate::types::{GifCategoryTag, GifItem, GifRequest, GifServiceResponse};
use fluxer_svc::config::ServiceConfig;
@@ -310,7 +310,7 @@ impl GifsShard {
locale: String,
country: String,
) -> anyhow::Result<GifServiceResponse> {
let key = format!("resolve:{locale}:{country}:{url}");
let key = format!("resolve:{}", resolve_cache_key(&url));
let this = self.clone();
let gif = self
.get_cached(
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
export const FORMAT_CONTENT_TYPES: Record<string, string> = {
gif: 'image/gif',
loopedmp4: 'video/mp4',
mediumgif: 'image/gif',
mediummp4: 'video/mp4',
mediumwebm: 'video/webm',
mediumwebp: 'image/webp',
mp4: 'video/mp4',
nanogif: 'image/gif',
nanomp4: 'video/mp4',
nanowebm: 'video/webm',
nanowebp: 'image/webp',
tinygif: 'image/gif',
tinymp4: 'video/mp4',
tinywebm: 'video/webm',
tinywebp: 'image/webp',
webm: 'video/webm',
webp: 'image/webp',
};
export const PREVIEW_FORMAT_PRIORITY = [
'tinywebm',
'tinymp4',
'mediumwebm',
'mediummp4',
'webm',
'mp4',
'loopedmp4',
'nanowebm',
'nanomp4',
'tinywebp',
'tinygif',
'mediumwebp',
'webp',
'mediumgif',
'gif',
'nanowebp',
'nanogif',
] as const;
export function inferFormatContentType(formatKey: string): string {
return FORMAT_CONTENT_TYPES[formatKey] ?? '';
}
@@ -55,7 +55,7 @@ export const GifResponse = z.object({
media: z
.record(z.string(), GifMediaFormat)
.describe(
'Map of format-name → media descriptor. Keys are provider-issued format names (e.g. "webm", "mp4", "webp", "gif", "tinygif", "nanogif"). Clients without webm support should pick "webp" / "gif" / "tinygif" / "nanogif" in that order.',
'Map of format-name → media descriptor. Keys are a size prefix (none for full size, "medium", "tiny", "nano") joined to a codec name ("webm", "mp4", "webp", "gif"), plus "loopedmp4". Video keys are "webm" / "mp4" / "loopedmp4" / "mediumwebm" / "mediummp4" / "tinywebm" / "tinymp4" / "nanowebm" / "nanomp4"; image keys are "webp" / "gif" / "mediumwebp" / "mediumgif" / "tinywebp" / "tinygif" / "nanowebp" / "nanogif". Every key is optional, so clients must walk a priority list rather than index a single key. Clients that cannot decode the video keys should prefer "tinywebp" / "tinygif" / "mediumwebp" / "mediumgif" / "webp" / "gif" / "nanowebp" / "nanogif" in that order.',
),
placeholder: z
.string()