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 = ``;