Search avatars: proxy with a UA + graceful initials fallback
Screenshot showed channel results with empty circles, not the initials chip — i.e. an avatar URL was present but the image was failing to load. Two causes addressed: - The image proxy fetched googleusercontent/yt3 with NO User-Agent, which the CDN 403s for some avatars → blank. Now sends a browser UA + Accept header. - A failed avatar now swaps to the initials chip (onerror → outerHTML) instead of hiding the img and leaving an empty circle. Also proxy protocol-relative + deeper-subdomain YouTube image hosts. img-proxy tests green.
This commit is contained in:
parent
0ff0d07c22
commit
3d67f51c29
2 changed files with 14 additions and 5 deletions
|
|
@ -133,7 +133,13 @@ def register_routes(bp):
|
||||||
abort(404)
|
abort(404)
|
||||||
try:
|
try:
|
||||||
import requests
|
import requests
|
||||||
upstream = requests.get(url, timeout=15, stream=True)
|
# A browser UA — Google's image CDN (yt3/googleusercontent) 403s some
|
||||||
|
# avatars when fetched with no User-Agent, which blanked search results.
|
||||||
|
upstream = requests.get(url, timeout=15, stream=True, headers={
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
||||||
|
"(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
||||||
|
"Accept": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8",
|
||||||
|
})
|
||||||
if upstream.status_code != 200:
|
if upstream.status_code != 200:
|
||||||
abort(404)
|
abort(404)
|
||||||
resp = Response(upstream.iter_content(8192),
|
resp = Response(upstream.iter_content(8192),
|
||||||
|
|
|
||||||
|
|
@ -37,18 +37,21 @@
|
||||||
// policy can't blank them out. Non-YouTube / already-proxied urls pass through.
|
// policy can't blank them out. Non-YouTube / already-proxied urls pass through.
|
||||||
function img(url) {
|
function img(url) {
|
||||||
if (!url) return url;
|
if (!url) return url;
|
||||||
if (/^https:\/\/([\w-]+\.)?(ytimg\.com|ggpht\.com|googleusercontent\.com)\//i.test(url))
|
if (url.indexOf('//') === 0) url = 'https:' + url; // protocol-relative
|
||||||
|
if (/^https:\/\/([\w-]+\.)*(ytimg\.com|ggpht\.com|googleusercontent\.com)\//i.test(url))
|
||||||
return '/api/video/img?u=' + encodeURIComponent(url);
|
return '/api/video/img?u=' + encodeURIComponent(url);
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
function avatar(ch, cls) {
|
function avatar(ch, cls) {
|
||||||
var url = ch && (ch.poster_url || ch.avatar_url);
|
var url = ch && (ch.poster_url || ch.avatar_url);
|
||||||
|
var ini = esc(initials(ch && ch.title));
|
||||||
if (url) {
|
if (url) {
|
||||||
return '<img class="' + cls + '" src="' + esc(img(url)) +
|
// If the image can't load, swap to the initials chip (never an empty circle).
|
||||||
'" alt="" loading="lazy" onerror="this.style.display=\'none\'">';
|
return '<img class="' + cls + '" src="' + esc(img(url)) + '" alt="" loading="lazy" ' +
|
||||||
|
'onerror="this.outerHTML=\'<span class="' + cls + ' vyt-avatar--ph">' + ini + '</span>\'">';
|
||||||
}
|
}
|
||||||
return '<span class="' + cls + ' vyt-avatar--ph">' + esc(initials(ch && ch.title)) + '</span>';
|
return '<span class="' + cls + ' vyt-avatar--ph">' + ini + '</span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// A single wished video tile (thumbnail, title, date, remove).
|
// A single wished video tile (thumbnail, title, date, remove).
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue