diff --git a/.gitignore b/.gitignore
index 09a5540..bf360fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,4 @@ db/
AI/models/
main_legacy.py
+docker-compose.override.yaml
diff --git a/games/uwu.py b/games/uwu.py
index 6a81614..73a85ec 100644
--- a/games/uwu.py
+++ b/games/uwu.py
@@ -95,10 +95,15 @@ async def fetch_uwu_post(tags: str) -> dict:
}
async def fetch_furtok_feed(safe: bool = True, page: int = 1, extra_tags: str = "") -> list[dict]:
+ is_custom = bool(extra_tags and extra_tags.strip())
rating = "rating:safe" if safe else "-rating:safe"
- tags = f"{rating} animated order:random score:>200"
- if extra_tags and extra_tags.strip():
- tags = f"{extra_tags.strip()} animated order:random score:>200"
+
+ if is_custom:
+ # Кастомный запрос: без принудительного animated, поддержка картинок
+ tags = f"{extra_tags.strip()} order:random score:>200"
+ else:
+ # Стандартная лента: только видео
+ tags = f"{rating} animated order:random score:>200"
url = "https://e621.net/posts.json"
params = {
@@ -122,18 +127,24 @@ async def fetch_furtok_feed(safe: bool = True, page: int = 1, extra_tags: str =
raise Exception("Не удалось загрузить ленту :(")
data = await resp.json()
+ VIDEO_EXTS = ("gif", "webm", "mp4")
+ IMAGE_EXTS = ("png", "jpg", "jpeg", "webp")
+ allowed_exts = VIDEO_EXTS + IMAGE_EXTS if is_custom else VIDEO_EXTS
+
posts = data.get("posts", [])
feed = []
for post in posts:
file_url = post.get("file", {}).get("url")
ext = post.get("file", {}).get("ext", "")
- if not file_url or ext not in ("gif", "webm", "mp4"):
+ if not file_url or ext not in allowed_exts:
continue
sample_url = post.get("sample", {}).get("url") or file_url
+ media_type = "image" if ext in IMAGE_EXTS else "video"
feed.append({
"url": file_url,
"sample": sample_url,
"ext": ext,
+ "type": media_type,
"score": post.get("score", {}).get("total", 0),
"fav_count": post.get("fav_count", 0),
})
diff --git a/webapp/api.py b/webapp/api.py
index 1cb4133..83ca6b2 100644
--- a/webapp/api.py
+++ b/webapp/api.py
@@ -8,12 +8,16 @@ REST API для Telegram Mini App. Использует те же SQLite баз
import asyncio
import logging
import os
+import hashlib
+from urllib.parse import quote
import sys
from contextlib import asynccontextmanager
from typing import Annotated
-from fastapi import FastAPI, Depends, Header, HTTPException
+import aiohttp
+from fastapi import FastAPI, Depends, Header, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
+from fastapi.responses import Response
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
@@ -401,6 +405,34 @@ async def get_furtok_feed_api(
raise HTTPException(status_code=500, detail=str(e))
+@app.get("/api/proxy-image")
+async def proxy_image(url: str):
+ """Проксирует картинку с e621 чтобы обойти hotlink protection."""
+ allowed = ("static1.e621.net", "static2.e621.net", "static1.e926.net")
+ from urllib.parse import urlparse
+ parsed = urlparse(url)
+ if parsed.hostname not in allowed:
+ raise HTTPException(status_code=403, detail="Forbidden host")
+
+ headers = {
+ "User-Agent": f"FenyaBot/1.0 (by {config.E621_LOGIN or 'unknown'} on e621)",
+ }
+ try:
+ async with aiohttp.ClientSession() as session:
+ async with session.get(url, headers=headers, timeout=aiohttp.ClientTimeout(total=15)) as resp:
+ if resp.status != 200:
+ raise HTTPException(status_code=resp.status, detail="Upstream error")
+ content_type = resp.content_type or "image/png"
+ body = await resp.read()
+ return Response(
+ content=body,
+ media_type=content_type,
+ headers={"Cache-Control": "public, max-age=86400"},
+ )
+ except aiohttp.ClientError:
+ raise HTTPException(status_code=502, detail="Failed to fetch image")
+
+
# --- ИИ Чат ---
@app.post("/api/talk")
diff --git a/webapp/frontend/css/style.css b/webapp/frontend/css/style.css
index d218b2a..47d4970 100644
--- a/webapp/frontend/css/style.css
+++ b/webapp/frontend/css/style.css
@@ -913,9 +913,13 @@ body::before {
.furtok-card video,
.furtok-card img {
+ position: absolute;
+ top: 0;
+ left: 0;
width: 100%;
height: 100%;
object-fit: contain;
+ display: block;
}
.furtok-overlay {
diff --git a/webapp/frontend/js/app.js b/webapp/frontend/js/app.js
index 58b2ce6..7bc14be 100644
--- a/webapp/frontend/js/app.js
+++ b/webapp/frontend/js/app.js
@@ -743,7 +743,7 @@ async function loadFurtokPage(feedEl) {
card.className = 'furtok-card';
let mediaHtml = '';
- if (post.ext === 'gif') {
+ if (post.type === 'image' || post.ext === 'gif') {
mediaHtml = `
`;
} else {
mediaHtml = ``;