233 lines
9.8 KiB
Python
233 lines
9.8 KiB
Python
import traceback
|
|
|
|
try:
|
|
from .config import Config
|
|
from .pixiv_client import PixivClient
|
|
|
|
_config = Config()
|
|
_client_instance = PixivClient(_config)
|
|
|
|
from server import PromptServer
|
|
from aiohttp import web
|
|
import aiohttp
|
|
|
|
routes = PromptServer.instance.routes
|
|
|
|
# ── Auth ──────────────────────────────────────────────────────────────────
|
|
|
|
@routes.get("/pixiv/status")
|
|
async def pixiv_status(request):
|
|
# Consider logged-in if we have a saved token — actual auth happens lazily on first API call
|
|
logged_in = _client_instance._logged_in or bool(_config.get_refresh_token())
|
|
username = ""
|
|
if _client_instance._logged_in:
|
|
try:
|
|
username = str(_client_instance.api.user_id)
|
|
except Exception:
|
|
pass
|
|
return web.json_response({"logged_in": logged_in, "username": username})
|
|
|
|
# Temporary store for PKCE verifier during auth flow
|
|
_pending_verifier = {}
|
|
|
|
@routes.post("/pixiv/auth/login")
|
|
async def pixiv_auth_login(request):
|
|
verifier, challenge = _client_instance.generate_pkce()
|
|
auth_url = _client_instance.get_login_url(challenge)
|
|
_pending_verifier["current"] = verifier
|
|
return web.json_response({"auth_url": auth_url})
|
|
|
|
@routes.post("/pixiv/auth/callback")
|
|
async def pixiv_auth_callback(request):
|
|
data = await request.json()
|
|
redirect_url = data.get("redirect_url", "")
|
|
verifier = _pending_verifier.pop("current", None)
|
|
if not verifier:
|
|
return web.json_response({"error": "No pending auth"}, status=400)
|
|
try:
|
|
code = _client_instance.extract_code(redirect_url)
|
|
_client_instance.login_with_code(code, verifier)
|
|
return web.json_response({"ok": True})
|
|
except Exception as e:
|
|
return web.json_response({"error": str(e)}, status=400)
|
|
|
|
@routes.post("/pixiv/auth/set_token")
|
|
async def pixiv_set_token(request):
|
|
data = await request.json()
|
|
token = data.get("refresh_token", "").strip()
|
|
if not token:
|
|
return web.json_response({"error": "refresh_token 不能为空"}, status=400)
|
|
try:
|
|
import time
|
|
_client_instance.api.auth(refresh_token=token)
|
|
_client_instance._logged_in = True
|
|
_client_instance._auth_time = time.time()
|
|
_config.save_refresh_token(token)
|
|
return web.json_response({"ok": True})
|
|
except Exception as e:
|
|
return web.json_response({"error": str(e)}, status=400)
|
|
|
|
# ── Content ───────────────────────────────────────────────────────────────
|
|
|
|
@routes.get("/pixiv/recommended")
|
|
async def pixiv_recommended(request):
|
|
next_url = request.query.get("next_url")
|
|
try:
|
|
return web.json_response(_client_instance.get_recommended(next_url=next_url))
|
|
except RuntimeError as e:
|
|
return web.json_response({"error": str(e)}, status=401)
|
|
except Exception as e:
|
|
return web.json_response({"error": str(e)}, status=500)
|
|
|
|
@routes.get("/pixiv/ranking")
|
|
async def pixiv_ranking(request):
|
|
mode = request.query.get("mode", "day")
|
|
next_url = request.query.get("next_url")
|
|
try:
|
|
return web.json_response(_client_instance.get_ranking(mode=mode, next_url=next_url))
|
|
except RuntimeError as e:
|
|
return web.json_response({"error": str(e)}, status=401)
|
|
except Exception as e:
|
|
return web.json_response({"error": str(e)}, status=500)
|
|
|
|
@routes.get("/pixiv/bookmarks")
|
|
async def pixiv_bookmarks(request):
|
|
next_url = request.query.get("next_url")
|
|
try:
|
|
return web.json_response(_client_instance.get_bookmarks(next_url=next_url))
|
|
except RuntimeError as e:
|
|
return web.json_response({"error": str(e)}, status=401)
|
|
except Exception as e:
|
|
return web.json_response({"error": str(e)}, status=500)
|
|
|
|
@routes.get("/pixiv/bookmarked_artists")
|
|
async def pixiv_bookmarked_artists(request):
|
|
next_url = request.query.get("next_url")
|
|
try:
|
|
return web.json_response(_client_instance.get_bookmarked_artists(next_url=next_url))
|
|
except RuntimeError as e:
|
|
return web.json_response({"error": str(e)}, status=401)
|
|
except Exception as e:
|
|
return web.json_response({"error": str(e)}, status=500)
|
|
|
|
@routes.get("/pixiv/artist/{artist_id}/works")
|
|
async def pixiv_artist_works(request):
|
|
artist_id = int(request.match_info["artist_id"])
|
|
next_url = request.query.get("next_url")
|
|
try:
|
|
return web.json_response(
|
|
_client_instance.get_artist_works(artist_id=artist_id, next_url=next_url)
|
|
)
|
|
except RuntimeError as e:
|
|
return web.json_response({"error": str(e)}, status=401)
|
|
except Exception as e:
|
|
return web.json_response({"error": str(e)}, status=500)
|
|
|
|
@routes.post("/pixiv/bookmark")
|
|
async def pixiv_add_bookmark(request):
|
|
data = await request.json()
|
|
illust_id = data.get("illust_id")
|
|
if not illust_id:
|
|
return web.json_response({"error": "illust_id required"}, status=400)
|
|
try:
|
|
_client_instance.ensure_logged_in()
|
|
_client_instance.api.illust_bookmark_add(int(illust_id))
|
|
return web.json_response({"ok": True})
|
|
except Exception as e:
|
|
return web.json_response({"error": str(e)}, status=500)
|
|
|
|
@routes.post("/pixiv/bookmark_delete")
|
|
async def pixiv_del_bookmark(request):
|
|
data = await request.json()
|
|
illust_id = data.get("illust_id")
|
|
if not illust_id:
|
|
return web.json_response({"error": "illust_id required"}, status=400)
|
|
try:
|
|
_client_instance.ensure_logged_in()
|
|
_client_instance.api.illust_bookmark_delete(int(illust_id))
|
|
return web.json_response({"ok": True})
|
|
except Exception as e:
|
|
return web.json_response({"error": str(e)}, status=500)
|
|
|
|
@routes.get("/pixiv/search/illusts")
|
|
async def pixiv_search_illusts(request):
|
|
word = request.query.get("word", "")
|
|
next_url = request.query.get("next_url")
|
|
if not word and not next_url:
|
|
return web.json_response({"error": "word required"}, status=400)
|
|
try:
|
|
return web.json_response(_client_instance.search_illusts(word, next_url=next_url))
|
|
except RuntimeError as e:
|
|
return web.json_response({"error": str(e)}, status=401)
|
|
except Exception as e:
|
|
return web.json_response({"error": str(e)}, status=500)
|
|
|
|
@routes.get("/pixiv/search/users")
|
|
async def pixiv_search_users(request):
|
|
word = request.query.get("word", "")
|
|
next_url = request.query.get("next_url")
|
|
if not word and not next_url:
|
|
return web.json_response({"error": "word required"}, status=400)
|
|
try:
|
|
return web.json_response(_client_instance.search_users(word, next_url=next_url))
|
|
except RuntimeError as e:
|
|
return web.json_response({"error": str(e)}, status=401)
|
|
except Exception as e:
|
|
return web.json_response({"error": str(e)}, status=500)
|
|
|
|
@routes.post("/pixiv/follow")
|
|
async def pixiv_add_follow(request):
|
|
data = await request.json()
|
|
user_id = data.get("user_id")
|
|
if not user_id:
|
|
return web.json_response({"error": "user_id required"}, status=400)
|
|
try:
|
|
_client_instance.ensure_logged_in()
|
|
_client_instance.api.user_follow_add(int(user_id))
|
|
return web.json_response({"ok": True})
|
|
except Exception as e:
|
|
return web.json_response({"error": str(e)}, status=500)
|
|
|
|
# ── Image Proxy ───────────────────────────────────────────────────────────
|
|
|
|
@routes.get("/pixiv/image_proxy")
|
|
async def pixiv_image_proxy(request):
|
|
url = request.query.get("url", "")
|
|
if "pximg.net" not in url:
|
|
return web.Response(status=403, text="Only pximg.net URLs allowed")
|
|
try:
|
|
async with aiohttp.ClientSession() as session:
|
|
async with session.get(
|
|
url,
|
|
headers={"Referer": "https://www.pixiv.net/"},
|
|
timeout=aiohttp.ClientTimeout(total=30),
|
|
) as resp:
|
|
data = await resp.read()
|
|
ct = resp.headers.get("Content-Type", "image/jpeg")
|
|
return web.Response(body=data, content_type=ct)
|
|
except Exception as e:
|
|
return web.Response(status=502, text=str(e))
|
|
|
|
# ── Node registration ─────────────────────────────────────────────────────
|
|
|
|
from .pixiv_node import PixivBrowser
|
|
from . import pixiv_node as _pn_mod
|
|
_pn_mod._client = _client_instance # inject client — avoids circular import
|
|
|
|
NODE_CLASS_MAPPINGS = {"PixivBrowser": PixivBrowser}
|
|
NODE_DISPLAY_NAME_MAPPINGS = {"PixivBrowser": "Pixiv Browser"}
|
|
WEB_DIRECTORY = "./web"
|
|
|
|
print("[PixivBrowser] Loaded successfully")
|
|
|
|
except ImportError as e:
|
|
print(f"[PixivBrowser] ERROR: Missing dependency — {e}")
|
|
print("[PixivBrowser] Run: pip install pixivpy3 Pillow")
|
|
NODE_CLASS_MAPPINGS = {}
|
|
NODE_DISPLAY_NAME_MAPPINGS = {}
|
|
except Exception as e:
|
|
print(f"[PixivBrowser] ERROR during load: {e}")
|
|
traceback.print_exc()
|
|
NODE_CLASS_MAPPINGS = {}
|
|
NODE_DISPLAY_NAME_MAPPINGS = {}
|