mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-07-17 04:01:01 +08:00
chore: move Auto_Bangumi/src -> Auto_Bangumi/backend/src, prepare for merge WebUI repo
This commit is contained in:
1
backend/src/module/api/__init__.py
Normal file
1
backend/src/module/api/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .web import router
|
||||
63
backend/src/module/api/auth.py
Normal file
63
backend/src/module/api/auth.py
Normal file
@@ -0,0 +1,63 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
|
||||
from module.models.user import User
|
||||
from module.security import (
|
||||
auth_user,
|
||||
create_access_token,
|
||||
get_current_user,
|
||||
update_user_info,
|
||||
)
|
||||
|
||||
from .program import router
|
||||
|
||||
|
||||
@router.post("/api/v1/auth/login", response_model=dict, tags=["auth"])
|
||||
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
username = form_data.username
|
||||
password = form_data.password
|
||||
auth_user(username, password)
|
||||
token = create_access_token(
|
||||
data={"sub": username}, expires_delta=timedelta(days=1)
|
||||
)
|
||||
|
||||
return {"access_token": token, "token_type": "bearer", "expire": 86400}
|
||||
|
||||
|
||||
@router.get("/api/v1/auth/refresh_token", response_model=dict, tags=["auth"])
|
||||
async def refresh(current_user: User = Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
token = create_access_token(
|
||||
data = {"sub": current_user.username}
|
||||
|
||||
)
|
||||
return {"access_token": token, "token_type": "bearer", "expire": 86400}
|
||||
|
||||
|
||||
@router.get("/api/v1/auth/logout", response_model=dict, tags=["auth"])
|
||||
async def logout(current_user: User = Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
return {"message": "logout success"}
|
||||
|
||||
|
||||
@router.post("/api/v1/auth/update", response_model=dict, tags=["auth"])
|
||||
async def update_user(user_data: User, current_user: User = Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
if update_user_info(user_data, current_user):
|
||||
return {
|
||||
"message": "update success",
|
||||
"access_token": create_access_token({"sub": user_data.username}),
|
||||
"token_type": "bearer",
|
||||
"expire": 86400,
|
||||
}
|
||||
85
backend/src/module/api/bangumi.py
Normal file
85
backend/src/module/api/bangumi.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from module.manager import TorrentManager
|
||||
from module.models import BangumiData
|
||||
from module.security import get_current_user
|
||||
|
||||
from .log import router
|
||||
|
||||
|
||||
@router.get(
|
||||
"/api/v1/bangumi/getAll", tags=["bangumi"], response_model=list[BangumiData]
|
||||
)
|
||||
async def get_all_data(current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
with TorrentManager() as torrent:
|
||||
return torrent.search_all()
|
||||
|
||||
|
||||
@router.get(
|
||||
"/api/v1/bangumi/getData/{bangumi_id}", tags=["bangumi"], response_model=BangumiData
|
||||
)
|
||||
async def get_data(bangumi_id: str, current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
with TorrentManager() as torrent:
|
||||
return torrent.search_one(bangumi_id)
|
||||
|
||||
|
||||
@router.post("/api/v1/bangumi/updateRule", tags=["bangumi"])
|
||||
async def update_rule(data: BangumiData, current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
with TorrentManager() as torrent:
|
||||
return torrent.update_rule(data)
|
||||
|
||||
|
||||
@router.delete("/api/v1/bangumi/deleteRule/{bangumi_id}", tags=["bangumi"])
|
||||
async def delete_rule(bangumi_id: str, file: bool = False, current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
with TorrentManager() as torrent:
|
||||
return torrent.delete_rule(bangumi_id, file)
|
||||
|
||||
|
||||
@router.delete("/api/v1/bangumi/disableRule/{bangumi_id}", tags=["bangumi"])
|
||||
async def disable_rule(
|
||||
bangumi_id: str, file: bool = False, current_user=Depends(get_current_user)
|
||||
):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
with TorrentManager() as torrent:
|
||||
return torrent.disable_rule(bangumi_id, file)
|
||||
|
||||
|
||||
@router.get("/api/v1/bangumi/enableRule/{bangumi_id}", tags=["bangumi"])
|
||||
async def enable_rule(bangumi_id: str, current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
with TorrentManager() as torrent:
|
||||
return torrent.enable_rule(bangumi_id)
|
||||
|
||||
|
||||
@router.get("/api/v1/bangumi/resetAll", tags=["bangumi"])
|
||||
async def reset_all(current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
with TorrentManager() as torrent:
|
||||
torrent.delete_all()
|
||||
return JSONResponse(status_code=200, content={"message": "OK"})
|
||||
36
backend/src/module/api/config.py
Normal file
36
backend/src/module/api/config.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import logging
|
||||
|
||||
from fastapi import Depends, HTTPException, status
|
||||
|
||||
from module.conf import settings
|
||||
from module.models import Config
|
||||
from module.security import get_current_user
|
||||
|
||||
from .bangumi import router
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@router.get("/api/v1/getConfig", tags=["config"], response_model=Config)
|
||||
async def get_config(current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
return settings
|
||||
|
||||
|
||||
@router.post("/api/v1/updateConfig", tags=["config"])
|
||||
async def update_config(config: Config, current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
try:
|
||||
settings.save(config_dict=config.dict())
|
||||
settings.load()
|
||||
logger.info("Config updated")
|
||||
return {"message": "Success"}
|
||||
except Exception as e:
|
||||
logger.warning(e)
|
||||
return {"message": "Failed to update config"}
|
||||
54
backend/src/module/api/download.py
Normal file
54
backend/src/module/api/download.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from fastapi import Depends, HTTPException, status
|
||||
|
||||
from module.manager import SeasonCollector
|
||||
from module.models import BangumiData
|
||||
from module.models.api import *
|
||||
from module.rss import analyser
|
||||
from module.security import get_current_user
|
||||
|
||||
from .config import router
|
||||
|
||||
|
||||
@router.post("/api/v1/download/analysis", tags=["download"])
|
||||
async def analysis(link: RssLink, current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
data = analyser.link_to_data(link.rss_link)
|
||||
if data:
|
||||
return data
|
||||
else:
|
||||
return {"status": "Failed to parse link"}
|
||||
|
||||
|
||||
@router.post("/api/v1/download/collection", tags=["download"])
|
||||
async def download_collection(
|
||||
data: BangumiData, current_user=Depends(get_current_user)
|
||||
):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
if data:
|
||||
with SeasonCollector() as collector:
|
||||
if collector.collect_season(data, data.rss_link[0], proxy=True):
|
||||
return {"status": "Success"}
|
||||
else:
|
||||
return {"status": "Failed to add torrent"}
|
||||
else:
|
||||
return {"status": "Failed to parse link"}
|
||||
|
||||
|
||||
@router.post("/api/v1/download/subscribe", tags=["download"])
|
||||
async def subscribe(data: BangumiData, current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
if data:
|
||||
with SeasonCollector() as collector:
|
||||
collector.subscribe_season(data)
|
||||
return {"status": "Success"}
|
||||
else:
|
||||
return {"status": "Failed to parse link"}
|
||||
35
backend/src/module/api/log.py
Normal file
35
backend/src/module/api/log.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import os
|
||||
|
||||
from fastapi import Depends, HTTPException, Response, status
|
||||
|
||||
from module.conf import LOG_PATH
|
||||
from module.security import get_current_user
|
||||
|
||||
from .auth import router
|
||||
|
||||
|
||||
@router.get("/api/v1/log", tags=["log"])
|
||||
async def get_log(current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
if os.path.isfile(LOG_PATH):
|
||||
with open(LOG_PATH, "rb") as f:
|
||||
return Response(f.read(), media_type="text/plain")
|
||||
else:
|
||||
return Response("Log file not found", status_code=404)
|
||||
|
||||
|
||||
@router.get("/api/v1/log/clear", tags=["log"])
|
||||
async def clear_log(current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
if os.path.isfile(LOG_PATH):
|
||||
with open(LOG_PATH, "w") as f:
|
||||
f.write("")
|
||||
return {"status": "ok"}
|
||||
else:
|
||||
return Response("Log file not found", status_code=404)
|
||||
103
backend/src/module/api/program.py
Normal file
103
backend/src/module/api/program.py
Normal file
@@ -0,0 +1,103 @@
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException, status
|
||||
|
||||
from module.core import Program
|
||||
from module.security import get_current_user
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
program = Program()
|
||||
router = FastAPI()
|
||||
|
||||
|
||||
@router.on_event("startup")
|
||||
async def startup():
|
||||
program.startup()
|
||||
|
||||
|
||||
@router.on_event("shutdown")
|
||||
async def shutdown():
|
||||
program.stop()
|
||||
|
||||
|
||||
@router.get("/api/v1/restart", tags=["program"])
|
||||
async def restart(current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
try:
|
||||
program.restart()
|
||||
return {"status": "ok"}
|
||||
except Exception as e:
|
||||
logger.debug(e)
|
||||
logger.warning("Failed to restart program")
|
||||
raise HTTPException(status_code=500, detail="Failed to restart program")
|
||||
|
||||
|
||||
@router.get("/api/v1/start", tags=["program"])
|
||||
async def start(current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
try:
|
||||
return program.start()
|
||||
except Exception as e:
|
||||
logger.debug(e)
|
||||
logger.warning("Failed to start program")
|
||||
raise HTTPException(status_code=500, detail="Failed to start program")
|
||||
|
||||
|
||||
@router.get("/api/v1/stop", tags=["program"])
|
||||
async def stop(current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
return program.stop()
|
||||
|
||||
|
||||
@router.get("/api/v1/status", tags=["program"])
|
||||
async def status(current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
if not program.is_running:
|
||||
return {"status": "stop"}
|
||||
else:
|
||||
return {"status": "running"}
|
||||
|
||||
|
||||
@router.get("/api/v1/shutdown", tags=["program"])
|
||||
async def shutdown_program(current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
program.stop()
|
||||
logger.info("Shutting down program...")
|
||||
os.kill(os.getpid(), signal.SIGINT)
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
# Check status
|
||||
@router.get("/api/v1/check/downloader", tags=["check"])
|
||||
async def check_downloader_status(current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
return program.check_downloader()
|
||||
|
||||
|
||||
@router.get("/api/v1/check/rss", tags=["check"])
|
||||
async def check_rss_status(current_user=Depends(get_current_user)):
|
||||
if not current_user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
|
||||
)
|
||||
return program.check_analyser()
|
||||
83
backend/src/module/api/proxy.py
Normal file
83
backend/src/module/api/proxy.py
Normal file
@@ -0,0 +1,83 @@
|
||||
import logging
|
||||
import re
|
||||
|
||||
from fastapi.exceptions import HTTPException
|
||||
from fastapi.responses import Response
|
||||
|
||||
from module.conf import settings
|
||||
from module.network import RequestContent
|
||||
|
||||
from .download import router
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_rss_content(full_path):
|
||||
url = f"https://mikanani.me/RSS/{full_path}"
|
||||
custom_url = settings.rss_parser.custom_url
|
||||
if "://" not in custom_url:
|
||||
custom_url = f"https://{custom_url}"
|
||||
try:
|
||||
with RequestContent() as request:
|
||||
content = request.get_html(url)
|
||||
return re.sub(r"https://mikanani.me", custom_url, content)
|
||||
except Exception as e:
|
||||
logger.debug(e)
|
||||
logger.warning("Failed to get RSS content")
|
||||
raise HTTPException(status_code=500, detail="Failed to get RSS content")
|
||||
|
||||
|
||||
def get_torrent(full_path):
|
||||
url = f"https://mikanani.me/Download/{full_path}"
|
||||
try:
|
||||
with RequestContent() as request:
|
||||
return request.get_content(url)
|
||||
except Exception as e:
|
||||
logger.debug(e)
|
||||
logger.warning("Failed to get torrent")
|
||||
raise HTTPException(status_code=500, detail="Failed to get torrent")
|
||||
|
||||
|
||||
@router.get("/RSS/MyBangumi", tags=["proxy"])
|
||||
async def get_my_bangumi(token: str):
|
||||
full_path = "MyBangumi?token=" + token
|
||||
content = get_rss_content(full_path)
|
||||
return Response(content, media_type="application/xml")
|
||||
|
||||
|
||||
@router.get("/RSS/Search", tags=["proxy"])
|
||||
async def get_search_result(searchstr: str):
|
||||
full_path = "Search?searchstr=" + searchstr
|
||||
content = get_rss_content(full_path)
|
||||
return Response(content, media_type="application/xml")
|
||||
|
||||
|
||||
@router.get("/RSS/Bangumi", tags=["proxy"])
|
||||
async def get_bangumi(bangumiId: str, subgroupid: str):
|
||||
full_path = "Bangumi?bangumiId=" + bangumiId + "&subgroupid=" + subgroupid
|
||||
content = get_rss_content(full_path)
|
||||
return Response(content, media_type="application/xml")
|
||||
|
||||
|
||||
@router.get("/RSS/{full_path:path}", tags=["proxy"])
|
||||
async def get_rss(full_path: str):
|
||||
content = get_rss_content(full_path)
|
||||
return Response(content, media_type="application/xml")
|
||||
|
||||
|
||||
@router.get("/Download/{full_path:path}", tags=["proxy"])
|
||||
async def download(full_path: str):
|
||||
torrent = get_torrent(full_path)
|
||||
return Response(torrent, media_type="application/x-bittorrent")
|
||||
|
||||
|
||||
@router.get("/Home/Episode/{full_path:path}", tags=["proxy"])
|
||||
async def get_ep_info(full_path: str):
|
||||
url = f"https://mikanani.me/Home/Episode/{full_path}"
|
||||
try:
|
||||
with RequestContent() as request:
|
||||
return Response(request.get_html(url), media_type="text/html")
|
||||
except Exception as e:
|
||||
logger.debug(e)
|
||||
logger.warning("Failed to get ep info")
|
||||
raise HTTPException(status_code=500, detail="Failed to get ep info")
|
||||
37
backend/src/module/api/web.py
Normal file
37
backend/src/module/api/web.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from fastapi import Request
|
||||
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from module.conf import VERSION
|
||||
|
||||
from .proxy import router
|
||||
|
||||
if VERSION != "DEV_VERSION":
|
||||
router.mount("/assets", StaticFiles(directory="templates/assets"), name="assets")
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
# Resource
|
||||
@router.get("/favicon.svg", tags=["html"])
|
||||
def favicon():
|
||||
return FileResponse("templates/favicon.svg")
|
||||
|
||||
@router.get("/AutoBangumi.svg", tags=["html"])
|
||||
def logo():
|
||||
return FileResponse("templates/AutoBangumi.svg")
|
||||
|
||||
@router.get("/favicon-light.svg", tags=["html"])
|
||||
def favicon_light():
|
||||
return FileResponse("templates/favicon-light.svg")
|
||||
|
||||
# HTML Response
|
||||
@router.get("/{full_path:path}", response_class=HTMLResponse, tags=["html"])
|
||||
def index(request: Request):
|
||||
context = {"request": request}
|
||||
return templates.TemplateResponse("index.html", context)
|
||||
|
||||
else:
|
||||
|
||||
@router.get("/", status_code=302, tags=["html"])
|
||||
def index():
|
||||
return RedirectResponse("/docs")
|
||||
Reference in New Issue
Block a user