127 lines
4.6 KiB
Python
127 lines
4.6 KiB
Python
from fastapi import APIRouter, Depends, UploadFile, File, Form, HTTPException
|
||
from typing import Optional
|
||
from urllib.parse import urlparse
|
||
import traceback
|
||
from loguru import logger
|
||
from pydantic import BaseModel, Field, field_validator
|
||
|
||
from app.core.deps import get_current_user
|
||
from app.core.response import success_response
|
||
from app.modules.tools import service
|
||
from app.services import creator_scraper
|
||
from app.services.creator_scraper import ALLOWED_INPUT_DOMAINS
|
||
from app.services.glm_service import glm_service
|
||
|
||
router = APIRouter()
|
||
|
||
|
||
class AnalyzeCreatorRequest(BaseModel):
|
||
url: str = Field(..., description="博主主页链接(仅支持抖音/B站 https 链接)")
|
||
|
||
@field_validator("url")
|
||
@classmethod
|
||
def validate_url_format(cls, value: str) -> str:
|
||
candidate = value.strip()
|
||
if len(candidate) > 500:
|
||
raise ValueError("链接过长")
|
||
|
||
parsed = urlparse(candidate)
|
||
if parsed.scheme != "https":
|
||
raise ValueError("仅支持 https 链接")
|
||
|
||
hostname = (parsed.hostname or "").lower()
|
||
if hostname not in ALLOWED_INPUT_DOMAINS:
|
||
raise ValueError(f"不支持的域名: {hostname},仅支持抖音和B站")
|
||
|
||
return candidate
|
||
|
||
|
||
class GenerateTopicScriptRequest(BaseModel):
|
||
analysis_id: str = Field(..., min_length=8, max_length=80, description="分析结果ID")
|
||
topic: str = Field(..., min_length=2, max_length=30, description="选中的话题(2-30字)")
|
||
word_count: int = Field(..., ge=80, le=1000, description="目标字数(80-1000)")
|
||
|
||
|
||
@router.post("/extract-script")
|
||
async def extract_script_tool(
|
||
file: Optional[UploadFile] = File(None),
|
||
url: Optional[str] = Form(None),
|
||
rewrite: bool = Form(True),
|
||
custom_prompt: Optional[str] = Form(None),
|
||
current_user: dict = Depends(get_current_user),
|
||
):
|
||
"""独立文案提取工具"""
|
||
try:
|
||
result = await service.extract_script(file=file, url=url, rewrite=rewrite, custom_prompt=custom_prompt)
|
||
return success_response(result)
|
||
except ValueError as e:
|
||
raise HTTPException(400, str(e))
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
logger.error(f"Tool extract failed: {e}")
|
||
logger.error(traceback.format_exc())
|
||
msg = str(e)
|
||
if "Fresh cookies" in msg:
|
||
raise HTTPException(500, "下载失败:目标平台开启了反爬验证,请过段时间重试或直接上传视频文件。")
|
||
raise HTTPException(500, "文案提取失败,请稍后重试")
|
||
|
||
|
||
@router.post("/analyze-creator")
|
||
async def analyze_creator(
|
||
req: AnalyzeCreatorRequest,
|
||
current_user: dict = Depends(get_current_user),
|
||
):
|
||
"""分析博主内容并返回热门话题"""
|
||
try:
|
||
user_id = str(current_user.get("id") or "").strip()
|
||
if not user_id:
|
||
raise HTTPException(401, "登录状态无效,请重新登录")
|
||
|
||
creator_result = await creator_scraper.scrape_creator_titles(req.url, user_id=user_id)
|
||
titles = creator_result.get("titles") or []
|
||
topics = await glm_service.analyze_topics(titles)
|
||
|
||
analysis_id = creator_scraper.cache_titles(titles, user_id)
|
||
|
||
return success_response({
|
||
"platform": creator_result.get("platform", ""),
|
||
"creator_name": creator_result.get("creator_name", ""),
|
||
"topics": topics,
|
||
"analysis_id": analysis_id,
|
||
"fetched_count": creator_result.get("fetched_count", len(titles)),
|
||
})
|
||
except ValueError as e:
|
||
raise HTTPException(400, str(e))
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
logger.error(f"Analyze creator failed: {e}")
|
||
logger.error(traceback.format_exc())
|
||
raise HTTPException(500, "博主内容分析失败,请稍后重试")
|
||
|
||
|
||
@router.post("/generate-topic-script")
|
||
async def generate_topic_script(
|
||
req: GenerateTopicScriptRequest,
|
||
current_user: dict = Depends(get_current_user),
|
||
):
|
||
"""根据话题生成文案"""
|
||
try:
|
||
user_id = str(current_user.get("id") or "").strip()
|
||
if not user_id:
|
||
raise HTTPException(401, "登录状态无效,请重新登录")
|
||
|
||
titles = creator_scraper.get_cached_titles(req.analysis_id, user_id)
|
||
script = await glm_service.generate_script_from_topic(req.topic, req.word_count, titles)
|
||
|
||
return success_response({"script": script})
|
||
except ValueError as e:
|
||
raise HTTPException(400, str(e))
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
logger.error(f"Generate topic script failed: {e}")
|
||
logger.error(traceback.format_exc())
|
||
raise HTTPException(500, "文案生成失败,请稍后重试")
|