Compare commits

...

1 Commits

Author SHA1 Message Date
tkrmagid
e1205c294b v0.4.37: percent-encode preload download URL so non-ASCII paths cache
All checks were successful
build / build (push) Successful in 8m8s
VideoCache.download() opened the connection with URI.create(url).toURL()
on the raw URL. URLs whose path has non-ASCII segments (e.g. the Korean
".../음악퀴즈/...") were sent unencoded, so the server answered HTTP 400 and
every preload aborted — the disk cache stayed empty and clients fell back
to live streaming for every video (instant on a fast host, 10s+/no-show on
a slower remote client). FFmpeg tolerates the raw URL, masking the bug at
playback time. Encode only the bytes put on the wire; the READY key, sha256
filename, and lookup() all keep the original url string so cache hits still
match the anchor's raw URL. Verified: raw URL -> HTTP 400, encoded -> 200.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-14 02:17:07 +09:00
2 changed files with 27 additions and 2 deletions

View File

@@ -5,7 +5,7 @@ org.gradle.configuration-cache=false
# Mod # Mod
mod_id=video_player mod_id=video_player
mod_version=0.4.36 mod_version=0.4.37
maven_group=com.ejclaw.videoplayer maven_group=com.ejclaw.videoplayer
archives_base_name=video_player archives_base_name=video_player

View File

@@ -381,7 +381,7 @@ public final class VideoCache {
return; return;
} }
URLConnection raw = URI.create(url).toURL().openConnection(); URLConnection raw = URI.create(encodeForRequest(url)).toURL().openConnection();
raw.setConnectTimeout(10_000); raw.setConnectTimeout(10_000);
raw.setReadTimeout(30_000); raw.setReadTimeout(30_000);
raw.setRequestProperty("User-Agent", "video_player/" + VideoPlayerMod.MOD_ID); raw.setRequestProperty("User-Agent", "video_player/" + VideoPlayerMod.MOD_ID);
@@ -522,6 +522,31 @@ public final class VideoCache {
} }
} }
/**
* Percent-encode any non-ASCII characters in the URL so Java's {@link HttpURLConnection}
* puts a valid request line on the wire. The stored cache key, the {@link #sha256(String)}
* filename, and the {@link #READY} map all keep the ORIGINAL {@code url} string — only the
* bytes actually sent in the HTTP request are encoded — so {@link #lookup(String)} still
* matches the anchor's raw URL.
*
* <p>Without this, a URL with a non-ASCII path segment (e.g. {@code .../음악퀴즈/...}) is sent
* verbatim and the server answers HTTP 400, so every preload fails silently and the disk
* cache stays empty — clients then fall back to live streaming for every video. FFmpeg
* (playback) tolerates the raw URL, which is why playback "works" while the cache never
* fills. The multi-arg {@link URI} constructor encodes each component; an already-encoded
* URL round-trips unchanged (decode-then-encode is idempotent for these paths). On any
* parse failure we fall back to the raw string rather than dropping the download.
*/
private static String encodeForRequest(String url) {
try {
URI u = URI.create(url);
URI enc = new URI(u.getScheme(), u.getAuthority(), u.getPath(), u.getQuery(), u.getFragment());
return enc.toASCIIString();
} catch (Throwable t) {
return url;
}
}
/** /**
* Best-effort filename extension from the URL path so FFmpeg's container probe gets a hint * Best-effort filename extension from the URL path so FFmpeg's container probe gets a hint
* (e.g. {@code .webm} for a webm stream). Falls back to {@code .bin}. * (e.g. {@code .webm} for a webm stream). Falls back to {@code .bin}.