load image fallback
This commit is contained in:
@@ -103,6 +103,51 @@ def videos_proxy():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({"error": str(e)}), 500
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/api/image', methods=['GET', 'HEAD'])
|
||||||
|
def image_proxy():
|
||||||
|
image_url = request.args.get('url')
|
||||||
|
if not image_url:
|
||||||
|
return jsonify({"error": "No URL provided"}), 400
|
||||||
|
|
||||||
|
parsed = urllib.parse.urlparse(image_url)
|
||||||
|
if parsed.scheme not in ('http', 'https') or not parsed.netloc:
|
||||||
|
return jsonify({"error": "Invalid target URL"}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
safe_request_headers = {}
|
||||||
|
for k in ('User-Agent', 'Accept', 'Accept-Encoding', 'Accept-Language'):
|
||||||
|
if k in request.headers:
|
||||||
|
safe_request_headers[k] = request.headers[k]
|
||||||
|
|
||||||
|
resp = session.get(image_url, headers=safe_request_headers, stream=True, timeout=15, allow_redirects=True)
|
||||||
|
|
||||||
|
hop_by_hop = {
|
||||||
|
'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',
|
||||||
|
'te', 'trailers', 'transfer-encoding', 'upgrade'
|
||||||
|
}
|
||||||
|
|
||||||
|
forwarded_headers = []
|
||||||
|
for name, value in resp.headers.items():
|
||||||
|
if name.lower() in hop_by_hop:
|
||||||
|
continue
|
||||||
|
forwarded_headers.append((name, value))
|
||||||
|
|
||||||
|
if request.method == 'HEAD':
|
||||||
|
resp.close()
|
||||||
|
return Response("", status=resp.status_code, headers=forwarded_headers)
|
||||||
|
|
||||||
|
def generate():
|
||||||
|
try:
|
||||||
|
for chunk in resp.iter_content(1024 * 16):
|
||||||
|
if chunk:
|
||||||
|
yield chunk
|
||||||
|
finally:
|
||||||
|
resp.close()
|
||||||
|
|
||||||
|
return Response(generate(), status=resp.status_code, headers=forwarded_headers)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def index():
|
def index():
|
||||||
return send_from_directory(app.static_folder, 'index.html')
|
return send_from_directory(app.static_folder, 'index.html')
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ App.favorites = App.favorites || {};
|
|||||||
${uploaderText ? `<p><button class="uploader-link" type="button" data-uploader="${uploaderText}">${uploaderText}</button></p>` : ''}
|
${uploaderText ? `<p><button class="uploader-link" type="button" data-uploader="${uploaderText}">${uploaderText}</button></p>` : ''}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
const thumb = card.querySelector('img');
|
||||||
|
if (App.videos && typeof App.videos.attachNoReferrerRetry === 'function') {
|
||||||
|
App.videos.attachNoReferrerRetry(thumb);
|
||||||
|
}
|
||||||
card.onclick = () => App.player.open(item.url);
|
card.onclick = () => App.player.open(item.url);
|
||||||
const favoriteBtn = card.querySelector('.favorite-btn');
|
const favoriteBtn = card.querySelector('.favorite-btn');
|
||||||
if (favoriteBtn) {
|
if (favoriteBtn) {
|
||||||
|
|||||||
@@ -28,6 +28,36 @@ App.videos = App.videos || {};
|
|||||||
return `${minutes}m`;
|
return `${minutes}m`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
App.videos.buildImageProxyUrl = function(imageUrl) {
|
||||||
|
if (!imageUrl) return '';
|
||||||
|
try {
|
||||||
|
return `/api/image?url=${encodeURIComponent(imageUrl)}&ts=${Date.now()}`;
|
||||||
|
} catch (err) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
App.videos.attachNoReferrerRetry = function(img) {
|
||||||
|
if (!img) return;
|
||||||
|
if (!img.dataset.originalSrc) {
|
||||||
|
img.dataset.originalSrc = img.currentSrc || img.src || '';
|
||||||
|
}
|
||||||
|
img.dataset.noReferrerRetry = '0';
|
||||||
|
img.addEventListener('error', () => {
|
||||||
|
if (img.dataset.noReferrerRetry === '1') return;
|
||||||
|
img.dataset.noReferrerRetry = '1';
|
||||||
|
img.referrerPolicy = 'no-referrer';
|
||||||
|
img.removeAttribute('crossorigin');
|
||||||
|
const original = img.dataset.originalSrc || img.currentSrc || img.src || '';
|
||||||
|
const proxyUrl = App.videos.buildImageProxyUrl(original);
|
||||||
|
if (proxyUrl) {
|
||||||
|
img.src = proxyUrl;
|
||||||
|
} else if (original) {
|
||||||
|
img.src = original;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Fetches the next page of videos and renders them into the grid.
|
// Fetches the next page of videos and renders them into the grid.
|
||||||
App.videos.loadVideos = async function() {
|
App.videos.loadVideos = async function() {
|
||||||
const session = App.storage.getSession();
|
const session = App.storage.getSession();
|
||||||
@@ -108,6 +138,8 @@ App.videos = App.videos || {};
|
|||||||
${uploaderText ? `<p class="video-meta"><button class="uploader-link" type="button" data-uploader="${uploaderText}">${uploaderText}</button></p>` : ''}
|
${uploaderText ? `<p class="video-meta"><button class="uploader-link" type="button" data-uploader="${uploaderText}">${uploaderText}</button></p>` : ''}
|
||||||
${durationText ? `<p class="video-duration">${durationText}</p>` : ''}
|
${durationText ? `<p class="video-duration">${durationText}</p>` : ''}
|
||||||
`;
|
`;
|
||||||
|
const thumb = card.querySelector('img');
|
||||||
|
App.videos.attachNoReferrerRetry(thumb);
|
||||||
const favoriteBtn = card.querySelector('.favorite-btn');
|
const favoriteBtn = card.querySelector('.favorite-btn');
|
||||||
if (favoriteBtn && favoriteKey) {
|
if (favoriteBtn && favoriteKey) {
|
||||||
App.favorites.setButtonState(favoriteBtn, favoritesSet.has(favoriteKey));
|
App.favorites.setButtonState(favoriteBtn, favoritesSet.has(favoriteKey));
|
||||||
|
|||||||
Reference in New Issue
Block a user