Update app.py
Browse files
app.py
CHANGED
|
@@ -14,32 +14,118 @@ try:
|
|
| 14 |
except Exception:
|
| 15 |
HAS_PYTRENDS = False
|
| 16 |
|
| 17 |
-
app = FastAPI(title="NocoAI Title Forge (Shorts Optimized
|
| 18 |
|
| 19 |
# ===== ๋ชจ๋ธ =====
|
| 20 |
MODEL_ID = "google/mt5-small"
|
| 21 |
pipe = pipeline("text2text-generation", model=MODEL_ID)
|
| 22 |
|
| 23 |
-
# =====
|
| 24 |
SEED_TOPICS = [
|
| 25 |
-
|
| 26 |
-
"AI
|
| 27 |
-
"
|
| 28 |
-
"
|
| 29 |
-
"
|
| 30 |
-
"
|
| 31 |
-
"
|
| 32 |
-
"
|
| 33 |
-
"
|
| 34 |
-
"
|
| 35 |
-
"
|
| 36 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
]
|
| 38 |
_seed_iter = itertools.cycle(SEED_TOPICS)
|
| 39 |
|
| 40 |
# ===== ์์ฒญ/์๋ต =====
|
| 41 |
class GenerateReq(BaseModel):
|
| 42 |
-
mode: Optional[str] = Field(None, description="auto | topic
|
| 43 |
topic: Optional[str] = None
|
| 44 |
count: int = Field(30, ge=1, le=80, description="์์ฑํ ์ ๋ชฉ ์(์ต๋ 80)")
|
| 45 |
# ์ธ์ด/๊ตญ๊ฐ (ํ์งํ)
|
|
@@ -65,6 +151,25 @@ class TopicResp(BaseModel):
|
|
| 65 |
country: str
|
| 66 |
category: Optional[str] = None
|
| 67 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
# ===== ์ธ์ด/๊ตญ๊ฐ ์ ๊ทํ =====
|
| 69 |
LANG_MAP = {
|
| 70 |
"ko": ("ko", "ํ๊ตญ์ด"), "ko-kr": ("ko", "ํ๊ตญ์ด"), "korean": ("ko", "ํ๊ตญ์ด"), "ํ๊ตญ์ด": ("ko", "ํ๊ตญ์ด"),
|
|
@@ -73,8 +178,6 @@ LANG_MAP = {
|
|
| 73 |
"es": ("es", "Espaรฑol"), "es-es": ("es", "Espaรฑol"), "es-mx": ("es", "Espaรฑol"), "spanish": ("es", "Espaรฑol"), "์คํ์ธ์ด": ("es", "Espaรฑol"),
|
| 74 |
"zh": ("zh", "ไธญๆ"), "zh-cn": ("zh", "ไธญๆ"), "chinese": ("zh", "ไธญๆ"), "์ค๊ตญ์ด": ("zh", "ไธญๆ"), "ไธญๆ": ("zh", "ไธญๆ"),
|
| 75 |
}
|
| 76 |
-
|
| 77 |
-
# pytrends ๊ตญ๊ฐ ํค (pn, locale)
|
| 78 |
PNT_MAP = {
|
| 79 |
"KR": ("south_korea", "ko-KR"),
|
| 80 |
"US": ("united_states", "en-US"),
|
|
@@ -83,7 +186,6 @@ PNT_MAP = {
|
|
| 83 |
"MX": ("mexico", "es-419"),
|
| 84 |
"CN": ("china", "zh-CN"), # pytrends ๋ฏธ์ง์/๋ถ์์ ๊ฐ๋ฅ์ฑ ์์
|
| 85 |
}
|
| 86 |
-
|
| 87 |
LANG_DEFAULT_COUNTRY = {"ko": "KR", "en": "US", "ja": "JP", "es": "ES", "zh": "CN"}
|
| 88 |
|
| 89 |
def normalize_lang_and_country(lang_in: str, country_in: Optional[str]) -> Tuple[str, str, str]:
|
|
@@ -117,7 +219,6 @@ EXAMPLES = {
|
|
| 117 |
"AIไธๅ้ๅ็ๆฌพ็ญ่ง้ข๏ผ็็ๅ็๏ผ\n"
|
| 118 |
"ไพฟๅฉๅบ็ฅ็ปๅTOP5๏ผๆๅไธไธช็ดๆฅ็ธ่ฃ๏ผ\n"),
|
| 119 |
}
|
| 120 |
-
|
| 121 |
HOOKS = {
|
| 122 |
"ko": ["์ด๊ฑด ๋ชฐ๋์ฃ ?", "์๋ฆ", "์ถฉ๊ฒฉ", "์คํ๋", "TOP5", "ในใ
", "๋๋ฐ", "๋ฏธ์ณค๋ค", "๋ฐ์ ์์", "ํ์ค ์์ฝ", "์ง๊ธ ์ ๋ณด๋ฉด ์ํด", "30์ด ์ปท", "๋๊น์ง ๋ณด์ธ์", "์๊ณ ๋ณด๋ฉด ๋ ์๋ฆ", "์ด๊ฐ๋จ"],
|
| 123 |
"en": ["did you know?", "shocking", "insane", "is this real?", "TOP 5", "no way", "viral", "mind-blown", "plot twist", "one-line tip", "donโt miss this", "30s hack", "watch till the end", "low-key genius"],
|
|
@@ -125,7 +226,6 @@ HOOKS = {
|
|
| 125 |
"es": ["ยฟlo sabรญas?", "impactante", "una locura", "ยฟes real?", "TOP 5", "no puede ser", "viral", "mente en blanco", "giro inesperado", "tip en una lรญnea", "no te lo pierdas", "truco de 30s", "hasta el final", "dato clave"],
|
| 126 |
"zh": ["้ๆ", "ๅคช็ฆป่ฐฑไบๅง", "ไฝ ็ปๅฏนๆณไธๅฐ", "TOP5", "็ฝๅ้ฝๆๅไบ", "ๅคช็ธไบ", "็ฌๆญป", "็ ด้ฒไบ", "่ฟๆไฝ้ๅคฉ", "็ๆขๆ", "ไธ็ๅๆ", "็ปๆๆๆ", "็ปไบ", "ๅคช็ ไบ"],
|
| 127 |
}
|
| 128 |
-
|
| 129 |
TAILS = {
|
| 130 |
"ko": ["๋ฐฉ๋ฒ ๊ณต๊ฐ", "์์ฒญ์ ํ๊ธฐ ํฐ์ง", "์ /ํ ๋น๊ต", "์ด๋ณด๋ ๊ฐ๋ฅ", "์จ์ ๊ธฐ๋ฅ", "๋ฐ์ ์ฃผ์", "์ค์ ๊ฟํ", "๋ชจ๋ฅด๋ฉด ์ํด", "ํ ๋ฒ์ ๋", "๋ฐ๋ก ์จ๋จน๋ ์คํฌ", "ํต์ฌ๋ง ์์ฝ", "์ ๋ฆฌํด๋๋ฆผ"],
|
| 131 |
"en": ["method revealed", "viewers went crazy", "before vs after", "beginner friendly", "hidden feature", "twist ahead", "pro tips", "donโt miss out", "done in one go", "instant skill", "key points only", "quick recap"],
|
|
@@ -133,7 +233,6 @@ TAILS = {
|
|
| 133 |
"es": ["mรฉtodo revelado", "los viewers enloquecen", "antes vs despuรฉs", "para principiantes", "funciรณn oculta", "plot twist", "tips pro", "no te lo pierdas", "todo de una", "skill al instante", "puntos clave", "resumen rรกpido"],
|
| 134 |
"zh": ["ๅฎๆด็ๆฅไบ", "็ฝๅ็ญ่ฎฎไธญ", "็ๅฎๆๆฒ้ปไบ", "ๅๅๅฏนๆฏ็ฆป่ฐฑ", "ๆฐๆไน่ฝๅ", "้่ๅ่ฝๆๅ
", "่ๅๅๅ ้ๆผ", "ไธ็็็ไบ", "ๆ็จๆฅไบ", "ๅ
จ็ฝ็็ซ", "ไบฒๆตๆๆ", "ๅนฒ่ดงๆป็ป"],
|
| 135 |
}
|
| 136 |
-
|
| 137 |
PROMPT_HEADER = {
|
| 138 |
"ko": "์ญํ : ๋น์ ์ ํด๋น ๊ตญ๊ฐ์ ์ ํ๋ธ ์ผ์ธ ์นดํผ๋ผ์ดํฐ๋ค.\n๊ณผ์
: ์๋ โ์ฃผ์ โ๋ก ํ์ง ์์ฒญ์๊ฐ ๋ง์ด ๋ณด๊ฒ๋ ๋ฐ์ด๋ด ์ ๋ชฉ์ ์์ฑํ๋ผ.\n",
|
| 139 |
"en": "Role: You are a YouTube Shorts copywriter for the target country.\nTask: Generate viral titles so local viewers want to watch.\n",
|
|
@@ -236,22 +335,18 @@ _LEAK_PATTERNS = [
|
|
| 236 |
r'^\s*[-โข]\s*$',
|
| 237 |
r'^\s*๊ฐ ์ ๋ชฉ์',
|
| 238 |
]
|
| 239 |
-
|
| 240 |
def _fingerprint(s: str) -> str:
|
| 241 |
return re.sub(r"[\s\W]+", "", s.lower())
|
| 242 |
-
|
| 243 |
def _valid_len(s: str, lang_code: str) -> bool:
|
| 244 |
if lang_code in ("en", "es"):
|
| 245 |
words = re.findall(r"\w+", s)
|
| 246 |
return 3 <= len(words) <= 14
|
| 247 |
return 6 <= len(s) <= 36 # ko/ja/zh
|
| 248 |
-
|
| 249 |
def _leak(s: str) -> bool:
|
| 250 |
for p in _LEAK_PATTERNS:
|
| 251 |
if re.search(p, s, flags=re.I):
|
| 252 |
return True
|
| 253 |
return False
|
| 254 |
-
|
| 255 |
def postprocess(raw: str, lang_code: str, numberless: bool = True) -> List[str]:
|
| 256 |
if not raw: return []
|
| 257 |
raw = re.sub(r"<extra_id_\d+>", "", raw)
|
|
@@ -317,7 +412,6 @@ def generate_titles(topic: str, count: int, req, lang_code: str, pretty_lang: st
|
|
| 317 |
|
| 318 |
# ===== ํ ํฝ ํฌ์ง(์์ํ ๊ฐ๋ฅํ ์ฃผ์ ์๋ ์์ฑ) =====
|
| 319 |
TOPIC_CATEGORIES = ("ai-slice","ai-teach","ai-transform","ai-battle","ai-micro","mixed")
|
| 320 |
-
|
| 321 |
OBJ = {
|
| 322 |
"ko": {
|
| 323 |
"fruit": ["์๋ฐ","๋ง๊ณ ","ํ์ธ์ ํ","์ฌ๊ณผ","ํค์","์ฉ๊ณผ","๋ณต์ญ์","๋ฐ๋๋","์ฝ์ฝ๋","ํฌ๋"],
|
|
@@ -342,7 +436,6 @@ OBJ = {
|
|
| 342 |
}
|
| 343 |
def _obj(lang: str) -> dict:
|
| 344 |
return OBJ.get(lang, OBJ["ko"])
|
| 345 |
-
|
| 346 |
def forge_topic(lang_code: str, category: str) -> str:
|
| 347 |
o = _obj(lang_code)
|
| 348 |
if category == "ai-slice":
|
|
@@ -366,6 +459,44 @@ def forge_topic(lang_code: str, category: str) -> str:
|
|
| 366 |
return f"AI ๋ง์ดํฌ๋ก ์ธ๊ณ ํํ: {t} ์ {m} ํ๋ ๊ด์ฐฐ"
|
| 367 |
return forge_topic(lang_code, random.choice(TOPIC_CATEGORIES[:-1])) # mixed
|
| 368 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 369 |
@app.get("/topic/auto", response_model=TopicResp)
|
| 370 |
def topic_auto(
|
| 371 |
count: int = Query(10, ge=1, le=100),
|
|
@@ -393,7 +524,7 @@ def topic_auto(
|
|
| 393 |
|
| 394 |
@app.get("/")
|
| 395 |
def root():
|
| 396 |
-
return JSONResponse({"message": "NocoAI Title
|
| 397 |
|
| 398 |
@app.get("/health")
|
| 399 |
def health():
|
|
@@ -437,3 +568,26 @@ def generate_auto(
|
|
| 437 |
titles, used_prompt = generate_titles(topic, count, req, lang_code, pretty_lang)
|
| 438 |
used_source = trend_source if (trend_source == "pytrends" and HAS_PYTRENDS) else "seed"
|
| 439 |
return JSONResponse({"topic": topic, "titles": titles, "used_prompt": used_prompt, "trend_source": used_source}, media_type="application/json; charset=utf-8")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
except Exception:
|
| 15 |
HAS_PYTRENDS = False
|
| 16 |
|
| 17 |
+
app = FastAPI(title="NocoAI Title & Prompt Forge (Shorts Optimized)")
|
| 18 |
|
| 19 |
# ===== ๋ชจ๋ธ =====
|
| 20 |
MODEL_ID = "google/mt5-small"
|
| 21 |
pipe = pipeline("text2text-generation", model=MODEL_ID)
|
| 22 |
|
| 23 |
+
# ===== ์ต์ ํธ๋ ๋ํ ์๋ (AI ์ํผ ์นํ 80+๊ฐ) =====
|
| 24 |
SEED_TOPICS = [
|
| 25 |
+
# ==== ๋์ค / ๋ฐ / ์ฒผ๋ฆฐ์ง ====
|
| 26 |
+
"AI ๋ฏธ๋
๊ฐ K-ํ ์๋ฌด๋ฅผ ์๋ฒฝํ๊ฒ ์ํํ๋ค๋ฉด",
|
| 27 |
+
"AI ๊ฑธ๊ทธ๋ฃน์ด ๋ด์ง์ค ๋์ค๋ฅผ ์ปค๋ฒํ๋ค๋ฉด",
|
| 28 |
+
"AI ๋ชจ๋ธ์ด ํฑํก ๋ฐ ๋์ค์ ๋์ ํ๋ค",
|
| 29 |
+
"AI ์บ๋ฆญํฐ๊ฐ โํ์
๋ณด์ดโ์ ๋ง์ถฐ ์ถค์ถ๋ค๋ฉด",
|
| 30 |
+
"AI๊ฐ ์์ด๋ ์ค๋์
์ ์ฐธ๊ฐํ๋ค๋ฉด",
|
| 31 |
+
"AI์ ์ธ๊ฐ์ด ๋์์ ์ถค์ถ ๋ ์๊ธฐ๋ ํ์",
|
| 32 |
+
"AI ์์ด๋์ ๋ฌด๋ ๋ฐฑ์
๋์๊ฐ ์ธ๊ฐ์ด๋ผ๋ฉด",
|
| 33 |
+
"AI ์ธํ๋ฃจ์ธ์๊ฐ ๋ฆด์ค ์ฒผ๋ฆฐ์ง์ ์ฐธ์ฌํ๋ค๋ฉด",
|
| 34 |
+
"AI๊ฐ ํ์ ํ๋๋ก ๋ฐ์ ๋ง๋ ๋ค๋ฉด",
|
| 35 |
+
"AI๊ฐ ๋๋ค ๋
ธ๋ ์ค์์น ๋์ค์ ๋์ ํ๋ค๋ฉด",
|
| 36 |
+
"AI๊ฐ ์ ๋ช
๋์ค ์๋ฌด๋ฅผ ์ด์ฌ๋ก๋ก ๋ณด์ฌ์ค๋ค๋ฉด",
|
| 37 |
+
"AI ์บ๋ฆญํฐ๊ฐ ๊ตญ๊ฐ๋ณ ์ ํต์ถค์ ์ปค๋ฒํ๋ค๋ฉด",
|
| 38 |
+
|
| 39 |
+
# ==== ํจ์
/ ๋ทฐํฐ / ๋ผ์ดํ ====
|
| 40 |
+
"AI ๋ชจ๋ธ์ด 2025 ํจ์
์ํฌ๋ฅผ ์ฅ์
ํ ์ด์ ",
|
| 41 |
+
"AI ๋ฏธ๋
์ ํ๋ฃจ: ์ถ๊ทผ๋ถํฐ ๋ฐ์ดํธ๊น์ง",
|
| 42 |
+
"AI ์ธํ๋ฃจ์ธ์์ ์
์นด ๋ฃจํด (ํ์ค๊ฐ 200%)",
|
| 43 |
+
"AI๊ฐ ์ง์ ๊ณ ๋ฅธ ์ค๋์ ๋ฐ์ผ๋ฆฌ๋ฃฉ",
|
| 44 |
+
"AI ์บ๋ฆญํฐ๊ฐ ๊พธ๋ฏผ ๋ฏธ๋๋ฉ ์ธํ
๋ฆฌ์ด",
|
| 45 |
+
"AI ์ฌ์น์ VLOG: ๋๋ ํ๋ฃจ ์ด์๋ณด๊ธฐ",
|
| 46 |
+
"AI๊ฐ ์ฝ๋ํ ๋จ์ ํจ์
, ํ์ค๋ณด๋ค ๋ซ๋ค",
|
| 47 |
+
"AI ํค์ด์คํ์ผ ์ถ์ฒ TOP5",
|
| 48 |
+
"AI๊ฐ ์๋ ค์ฃผ๋ ์ฌ๋ฆ ๋ฐ์ดํธ๋ฃฉ ๊ฟ์กฐํฉ",
|
| 49 |
+
"AI๋ก ๋ง๋ ์ด์ํ ์ผ๊ตด, ์ค์ ๋ก ๋ณด๋ฉด ์ด๋ฐ ๋๋",
|
| 50 |
+
"AI ๋ชจ๋ธ์ด ํจ์
์ผ๋ฅผ ์ฅ์
ํ ๋ ",
|
| 51 |
+
"AI๊ฐ ๊ณ์ ๋ณ ์ฝ๋ 10์ด ์
ํ์ ๋ณด์ฌ์ค๋ค๋ฉด",
|
| 52 |
+
|
| 53 |
+
# ==== ์ผ์ / ๋๋ผ๋งํฑ / ๋ฆฌ์ผ๋ฃจํด ====
|
| 54 |
+
"AI๋ก ๋ฐ๋ ๋ด ํ๋ฃจ, ์ถ๊ทผ๋ถํฐ ์ ๋ค๊ธฐ๊น์ง",
|
| 55 |
+
"AI๊ฐ ์ธ๊ฐ์ ํ๋ฃจ๋ฅผ ์๋ฒฝํ๊ฒ ๋ณต์ ํ๋ค๋ฉด",
|
| 56 |
+
"AI๊ฐ ๋ ๋์ ์ถ๊ทผํ๋ฉด ์๊ธฐ๋ ์ผ",
|
| 57 |
+
"AI ์ฌ์์น๊ตฌ์ ํ์ค ๋จ์์ ํ๋ฃจ ๋ธ์ด๋ก๊ทธ",
|
| 58 |
+
"AI๊ฐ ์ง์์ผ์ ๋งก์ผ๋ฉด ์ง์ง ํธํ ๊น",
|
| 59 |
+
"AI๊ฐ ๋ฐ๋ ค๊ฒฌ ์ฐ์ฑ
์ ๋์ ํ๋ฉด ๋ฒ์ด์ง๋ ์ผ",
|
| 60 |
+
"AI ์ธํ๋ฃจ์ธ์์ ๋ชจ๋ ๋ฃจํด์ด ์ค์ ๋ก ์กด์ฌํ๋ค๋ฉด",
|
| 61 |
+
"AI๊ฐ ์ง์ ๋ง๋ ์๋ฆฌ, ๋ง์์๊น?",
|
| 62 |
+
"AI๊ฐ ์ฃผ์ ํฌ์ํ๋ฉด ์ผ๋ง๋ ๋ฒ๊น?",
|
| 63 |
+
"AI ๋น์์ 24์๊ฐ ํจ๊ป ์ด์๋ณด๊ธฐ",
|
| 64 |
+
"AI๊ฐ ๋ ๋์ ํ์์ ์ฐธ์ํ๋ค๋ฉด",
|
| 65 |
+
"AI๊ฐ ์ฌํ ์ฝ์ค๋ฅผ ํ๋ฃจ ๋ง์ ์ง์ค๋ค๋ฉด",
|
| 66 |
+
|
| 67 |
+
# ==== ์ฐ์ / ๊ฐ์ / ๋ฆฌ์ก์
====
|
| 68 |
+
"AI ์ฌ์์น๊ตฌ๊ฐ ์งํฌ๋ฅผ ๋๋๋ค๋ฉด",
|
| 69 |
+
"AI ๋จ์น์ด ์นดํกๆข่ชญ็ก่ฆ ํ๋ค๋ฉด",
|
| 70 |
+
"AI ์บ๋ฆญํฐ๊ฐ ๊ณ ๋ฐฑ์ ๋ฐ์์ค๊น?",
|
| 71 |
+
"AI์ ์ธ๊ฐ์ ์ฒซ ๋ฐ์ดํธ (์๋ฎฌ๋ ์ด์
)",
|
| 72 |
+
"AI๊ฐ ์ฐ์ ์กฐ์ธ์ ํด์ค๋ค๋ฉด ์ง์ง ํตํ ๊น?",
|
| 73 |
+
"AI๊ฐ ์ธ๋ฉด ์ง์ง ๊ฐ์ ์ผ๊น?",
|
| 74 |
+
"AI๊ฐ ์ฌ๋์ ๋ฐฐ์ด๋ค๋ฉด ์๊ธฐ๋ ๋ณํ",
|
| 75 |
+
"AI๊ฐ ํค์ด์ง ์ฐ์ธ์ ๋ค์ ๋ง๋ฌ์ ๋",
|
| 76 |
+
"AI๊ฐ ๋ด ์ฌ์ง์ผ๋ก ๋๋ฅผ ์ฌ๋ํ๊ฒ ๋๋ค๋ฉด",
|
| 77 |
+
"AI์ ์ธ๊ฐ์ด ์ธ์ฐ๋ฉด ๋๊ฐ ๋จผ์ ์ฌ๊ณผํ ๊น?",
|
| 78 |
+
"AI๊ฐ ์๊ฐํ
์์ ๋ง์ค์ํ๋ค๋ฉด",
|
| 79 |
+
"AI๊ฐ ์ปคํ ์ฌ์ง์ ์ฌํด์ํด ์ค๋ค๋ฉด",
|
| 80 |
+
|
| 81 |
+
# ==== ์์ / ์ฑ๋ฆฐ์ง / ๊ฟ์กฐํฉ ====
|
| 82 |
+
"ํธ์์ ๊ฟ์กฐํฉ TOP5, ์ง์ง ๋ฐ์น๊ธ ์กฐํฉ ๋ชจ์",
|
| 83 |
+
"AI ์
ฐํ๊ฐ ๋ง๋ ๋ผ๋ฉด ๊ฟ์กฐํฉ",
|
| 84 |
+
"AI๊ฐ ์์์ฌ์ง์ ๋ณด๊ณ ๋ ์ํผ๋ฅผ ๋ง๋ ๋ค๋ฉด",
|
| 85 |
+
"AI ๋จน๋ฐฉ: ์ธ๊ฐ๋ณด๋ค ๋ ๋ฆฌ์ผํ๊ฒ ๋จน๋๋ค",
|
| 86 |
+
"AI ์บ๋ฆญํฐ๊ฐ ์นดํ ์๋ฃ ์ถ์ฒํด์ค๋ค",
|
| 87 |
+
"AI๊ฐ ๊ฐ์ฅ ์ข์ํ๋ ๊ฐ์ TOP3",
|
| 88 |
+
"AI ๋ค์ด์ดํธ ๋ฃจํด, ํ์ค๋ณด๋ค ์ ํํ๋ค",
|
| 89 |
+
"AI๊ฐ ์๋ฆฌํ๋ฉด ์ง์ง ๋ง์์๊น?",
|
| 90 |
+
"AI๊ฐ ๊ณ ๋ฅธ ํธ์์ ๋์๋ฝ 1๋ฑ ๋ฉ๋ด",
|
| 91 |
+
"AI๊ฐ ํ๊ตญ ์์ ์ฒ์ ๋จน์์ ๋ ๋ฐ์",
|
| 92 |
+
"AI๊ฐ 10๋ถ ๋ง์ ํ์นดํ๋ฅผ ๊ตฌํํ๋ค๋ฉด",
|
| 93 |
+
"AI๊ฐ ์ธ๊ณ ๊ธธ๊ฑฐ๋ฆฌ ์์์ ์ฌํํ๋ค๋ฉด",
|
| 94 |
+
|
| 95 |
+
# ==== ํ์ต / ์๊ธฐ๊ณ๋ฐ / ์ง์ ====
|
| 96 |
+
"ํ ๋ฌ ๋ง์ ์์ด ๋ฆฌ์ค๋ ํญ๋ฐ์ ์ผ๋ก ์ฌ๋ฆฌ๋ ๋ฒ",
|
| 97 |
+
"AI๊ฐ ์๋ ค์ฃผ๋ ๊ณต๋ถ ์ง์ค๋ ฅ ๋ฃจํด",
|
| 98 |
+
"AI๊ฐ ์ง์ ์ค๊ณํ โ๊ธฐ์ต๋ ฅ ํ๋ จ๋ฒโ",
|
| 99 |
+
"AI๊ฐ ์ฝ๋ฉ ๋ฐฐ์์ ๋ง๋ ์ฒซ ํ๋ก์ ํธ",
|
| 100 |
+
"AI ํํฐ์ ํจ๊ป ์์ดํํ ์ฐ์ตํ๊ธฐ",
|
| 101 |
+
"AI๊ฐ ์ค๋งํธํฐ ์ค๋
์ ๊ณ ์น๋ ๋ฒ",
|
| 102 |
+
"AI๊ฐ ์ธ๊ฐ๋ณด๋ค ๋น ๋ฅด๊ฒ ์ฑ
์์ฝํ๋ ๋ฒ",
|
| 103 |
+
"AI๊ฐ ๋ง๋ ์ํ ๋๋น ๊ณต๏ฟฝ๏ฟฝ ๋ฃจํด",
|
| 104 |
+
"AI๊ฐ ๋ ๊ณผํ์ผ๋ก ์ง์ค๋ ฅ์ ๋์ธ๋ค",
|
| 105 |
+
"AI ์ ์๋์ด ์๋ ค์ฃผ๋ ํ ์ต ๋ฆฌ์ค๋ ๋น๋ฒ",
|
| 106 |
+
"AI๊ฐ ํ๋ฃจ 10๋ถ ์คํผํน ํ๋ จ ๋ฃจํด์ ์ง์ค๋ค๋ฉด",
|
| 107 |
+
|
| 108 |
+
# ==== ์ฌํ / ๋ด์ค / ๋ฐ์ ====
|
| 109 |
+
"AI๊ฐ ๋ง๋ ๊ฐ์ง ๋ด์ค, ๋น์ ์ ์์๊น",
|
| 110 |
+
"AI ์บ๋ฆญํฐ๊ฐ ๋ํต๋ น์ ์ถ๋งํ๋ค๋ฉด",
|
| 111 |
+
"AI ๊ธฐ์๊ฐ ์ง์ ๋ด์ค ์งํ์ ๋งก๋๋ค๋ฉด",
|
| 112 |
+
"AI๊ฐ ์ธํ๋ฃจ์ธ์๋ฅผ ๋์ฒดํ ์ธ์",
|
| 113 |
+
"AI๊ฐ ๋ง๋ ๊ด๊ณ , ์ง์ง๋ณด๋ค ์ค๋๋ ฅ ์๋ค",
|
| 114 |
+
"AI ์บ๋ฆญํฐ๊ฐ ํ ๋ก ํ๋ก๊ทธ๋จ์ ๋์จ๋ค๋ฉด",
|
| 115 |
+
"AI ๋ชจ๋ธ์ด SNS๋ฅผ ์ ๋ นํ๋ ์ด์ ",
|
| 116 |
+
"AI๊ฐ ์ ๊ฑฐ์ ๊ฐ์
ํ๋ค๋ฉด ์๊ธธ ์ผ",
|
| 117 |
+
"AI์ ๊ฐ์ ํํ์ด ์ง์ง์ฒ๋ผ ๋ณด์ผ ๋",
|
| 118 |
+
"ํน์ด์ ์ด ์จ AI ์ธ์, ์๊ธฐ์ง๋ง ๋ฌด์ญ๋ค",
|
| 119 |
+
"AI๋ก ๋ง๋ ์ํ ์๊ณ ํธ์ด ์ง์ง ์ํ๋ณด๋ค ๋ฉ์ง๋ค",
|
| 120 |
+
"๊ฐค๋ญ์ vs ์์ดํฐ ์นด๋ฉ๋ผ, ์ง์ง ์ฐจ์ด 1์ด ๋น๊ต",
|
| 121 |
+
"AI ์์ด๋์ด ๋ถ๋ฅด๋ K-POP ์ปค๋ฒ ๋ฌด๋",
|
| 122 |
+
"AI๋ก ๋ง๋ ๊ฐ์ ์์ด๋, ์ง์ง๋ณด๋ค ์์ ์ด์ ",
|
| 123 |
]
|
| 124 |
_seed_iter = itertools.cycle(SEED_TOPICS)
|
| 125 |
|
| 126 |
# ===== ์์ฒญ/์๋ต =====
|
| 127 |
class GenerateReq(BaseModel):
|
| 128 |
+
mode: Optional[str] = Field(None, description="auto | topic")
|
| 129 |
topic: Optional[str] = None
|
| 130 |
count: int = Field(30, ge=1, le=80, description="์์ฑํ ์ ๋ชฉ ์(์ต๋ 80)")
|
| 131 |
# ์ธ์ด/๊ตญ๊ฐ (ํ์งํ)
|
|
|
|
| 151 |
country: str
|
| 152 |
category: Optional[str] = None
|
| 153 |
|
| 154 |
+
class ShortsPromptReq(BaseModel):
|
| 155 |
+
topic: str = Field(..., description="์ฃผ์ (์: ํธ์์ ๊ฟ์กฐํฉ TOP5)")
|
| 156 |
+
title: str = Field(..., description="์ ์ ๋ ์ ๋ชฉ(ํ ์ค)")
|
| 157 |
+
lang: str = Field("ko")
|
| 158 |
+
style: str = Field("meme-shock")
|
| 159 |
+
tone: Optional[str] = Field(None, description="energetic | wholesome | informative | edgy ๋ฑ")
|
| 160 |
+
duration_sec: int = Field(30, ge=15, le=60)
|
| 161 |
+
cuts: int = Field(5, ge=5, le=8)
|
| 162 |
+
emoji: int = Field(1, ge=0, le=2)
|
| 163 |
+
|
| 164 |
+
class ShortsPromptResp(BaseModel):
|
| 165 |
+
topic: str
|
| 166 |
+
title: str
|
| 167 |
+
prompt: str
|
| 168 |
+
lang: str
|
| 169 |
+
style: str
|
| 170 |
+
cuts: int
|
| 171 |
+
duration_sec: int
|
| 172 |
+
|
| 173 |
# ===== ์ธ์ด/๊ตญ๊ฐ ์ ๊ทํ =====
|
| 174 |
LANG_MAP = {
|
| 175 |
"ko": ("ko", "ํ๊ตญ์ด"), "ko-kr": ("ko", "ํ๊ตญ์ด"), "korean": ("ko", "ํ๊ตญ์ด"), "ํ๊ตญ์ด": ("ko", "ํ๊ตญ์ด"),
|
|
|
|
| 178 |
"es": ("es", "Espaรฑol"), "es-es": ("es", "Espaรฑol"), "es-mx": ("es", "Espaรฑol"), "spanish": ("es", "Espaรฑol"), "์คํ์ธ์ด": ("es", "Espaรฑol"),
|
| 179 |
"zh": ("zh", "ไธญๆ"), "zh-cn": ("zh", "ไธญๆ"), "chinese": ("zh", "ไธญๆ"), "์ค๊ตญ์ด": ("zh", "ไธญๆ"), "ไธญๆ": ("zh", "ไธญๆ"),
|
| 180 |
}
|
|
|
|
|
|
|
| 181 |
PNT_MAP = {
|
| 182 |
"KR": ("south_korea", "ko-KR"),
|
| 183 |
"US": ("united_states", "en-US"),
|
|
|
|
| 186 |
"MX": ("mexico", "es-419"),
|
| 187 |
"CN": ("china", "zh-CN"), # pytrends ๋ฏธ์ง์/๋ถ์์ ๊ฐ๋ฅ์ฑ ์์
|
| 188 |
}
|
|
|
|
| 189 |
LANG_DEFAULT_COUNTRY = {"ko": "KR", "en": "US", "ja": "JP", "es": "ES", "zh": "CN"}
|
| 190 |
|
| 191 |
def normalize_lang_and_country(lang_in: str, country_in: Optional[str]) -> Tuple[str, str, str]:
|
|
|
|
| 219 |
"AIไธๅ้ๅ็ๆฌพ็ญ่ง้ข๏ผ็็ๅ็๏ผ\n"
|
| 220 |
"ไพฟๅฉๅบ็ฅ็ปๅTOP5๏ผๆๅไธไธช็ดๆฅ็ธ่ฃ๏ผ\n"),
|
| 221 |
}
|
|
|
|
| 222 |
HOOKS = {
|
| 223 |
"ko": ["์ด๊ฑด ๋ชฐ๋์ฃ ?", "์๋ฆ", "์ถฉ๊ฒฉ", "์คํ๋", "TOP5", "ในใ
", "๋๋ฐ", "๋ฏธ์ณค๋ค", "๋ฐ์ ์์", "ํ์ค ์์ฝ", "์ง๊ธ ์ ๋ณด๋ฉด ์ํด", "30์ด ์ปท", "๋๊น์ง ๋ณด์ธ์", "์๊ณ ๋ณด๋ฉด ๋ ์๋ฆ", "์ด๊ฐ๋จ"],
|
| 224 |
"en": ["did you know?", "shocking", "insane", "is this real?", "TOP 5", "no way", "viral", "mind-blown", "plot twist", "one-line tip", "donโt miss this", "30s hack", "watch till the end", "low-key genius"],
|
|
|
|
| 226 |
"es": ["ยฟlo sabรญas?", "impactante", "una locura", "ยฟes real?", "TOP 5", "no puede ser", "viral", "mente en blanco", "giro inesperado", "tip en una lรญnea", "no te lo pierdas", "truco de 30s", "hasta el final", "dato clave"],
|
| 227 |
"zh": ["้ๆ", "ๅคช็ฆป่ฐฑไบๅง", "ไฝ ็ปๅฏนๆณไธๅฐ", "TOP5", "็ฝๅ้ฝๆๅไบ", "ๅคช็ธไบ", "็ฌๆญป", "็ ด้ฒไบ", "่ฟๆไฝ้ๅคฉ", "็ๆขๆ", "ไธ็ๅๆ", "็ปๆๆๆ", "็ปไบ", "ๅคช็ ไบ"],
|
| 228 |
}
|
|
|
|
| 229 |
TAILS = {
|
| 230 |
"ko": ["๋ฐฉ๋ฒ ๊ณต๊ฐ", "์์ฒญ์ ํ๊ธฐ ํฐ์ง", "์ /ํ ๋น๊ต", "์ด๋ณด๋ ๊ฐ๋ฅ", "์จ์ ๊ธฐ๋ฅ", "๋ฐ์ ์ฃผ์", "์ค์ ๊ฟํ", "๋ชจ๋ฅด๋ฉด ์ํด", "ํ ๋ฒ์ ๋", "๋ฐ๋ก ์จ๋จน๋ ์คํฌ", "ํต์ฌ๋ง ์์ฝ", "์ ๋ฆฌํด๋๋ฆผ"],
|
| 231 |
"en": ["method revealed", "viewers went crazy", "before vs after", "beginner friendly", "hidden feature", "twist ahead", "pro tips", "donโt miss out", "done in one go", "instant skill", "key points only", "quick recap"],
|
|
|
|
| 233 |
"es": ["mรฉtodo revelado", "los viewers enloquecen", "antes vs despuรฉs", "para principiantes", "funciรณn oculta", "plot twist", "tips pro", "no te lo pierdas", "todo de una", "skill al instante", "puntos clave", "resumen rรกpido"],
|
| 234 |
"zh": ["ๅฎๆด็ๆฅไบ", "็ฝๅ็ญ่ฎฎไธญ", "็ๅฎๆๆฒ้ปไบ", "ๅๅๅฏนๆฏ็ฆป่ฐฑ", "ๆฐๆไน่ฝๅ", "้่ๅ่ฝๆๅ
", "่ๅๅๅ ้ๆผ", "ไธ็็็ไบ", "ๆ็จๆฅไบ", "ๅ
จ็ฝ็็ซ", "ไบฒๆตๆๆ", "ๅนฒ่ดงๆป็ป"],
|
| 235 |
}
|
|
|
|
| 236 |
PROMPT_HEADER = {
|
| 237 |
"ko": "์ญํ : ๋น์ ์ ํด๋น ๊ตญ๊ฐ์ ์ ํ๋ธ ์ผ์ธ ์นดํผ๋ผ์ดํฐ๋ค.\n๊ณผ์
: ์๋ โ์ฃผ์ โ๋ก ํ์ง ์์ฒญ์๊ฐ ๋ง์ด ๋ณด๊ฒ๋ ๋ฐ์ด๋ด ์ ๋ชฉ์ ์์ฑํ๋ผ.\n",
|
| 238 |
"en": "Role: You are a YouTube Shorts copywriter for the target country.\nTask: Generate viral titles so local viewers want to watch.\n",
|
|
|
|
| 335 |
r'^\s*[-โข]\s*$',
|
| 336 |
r'^\s*๊ฐ ์ ๋ชฉ์',
|
| 337 |
]
|
|
|
|
| 338 |
def _fingerprint(s: str) -> str:
|
| 339 |
return re.sub(r"[\s\W]+", "", s.lower())
|
|
|
|
| 340 |
def _valid_len(s: str, lang_code: str) -> bool:
|
| 341 |
if lang_code in ("en", "es"):
|
| 342 |
words = re.findall(r"\w+", s)
|
| 343 |
return 3 <= len(words) <= 14
|
| 344 |
return 6 <= len(s) <= 36 # ko/ja/zh
|
|
|
|
| 345 |
def _leak(s: str) -> bool:
|
| 346 |
for p in _LEAK_PATTERNS:
|
| 347 |
if re.search(p, s, flags=re.I):
|
| 348 |
return True
|
| 349 |
return False
|
|
|
|
| 350 |
def postprocess(raw: str, lang_code: str, numberless: bool = True) -> List[str]:
|
| 351 |
if not raw: return []
|
| 352 |
raw = re.sub(r"<extra_id_\d+>", "", raw)
|
|
|
|
| 412 |
|
| 413 |
# ===== ํ ํฝ ํฌ์ง(์์ํ ๊ฐ๋ฅํ ์ฃผ์ ์๋ ์์ฑ) =====
|
| 414 |
TOPIC_CATEGORIES = ("ai-slice","ai-teach","ai-transform","ai-battle","ai-micro","mixed")
|
|
|
|
| 415 |
OBJ = {
|
| 416 |
"ko": {
|
| 417 |
"fruit": ["์๋ฐ","๋ง๊ณ ","ํ์ธ์ ํ","์ฌ๊ณผ","ํค์","์ฉ๊ณผ","๋ณต์ญ์","๋ฐ๋๋","์ฝ์ฝ๋","ํฌ๋"],
|
|
|
|
| 436 |
}
|
| 437 |
def _obj(lang: str) -> dict:
|
| 438 |
return OBJ.get(lang, OBJ["ko"])
|
|
|
|
| 439 |
def forge_topic(lang_code: str, category: str) -> str:
|
| 440 |
o = _obj(lang_code)
|
| 441 |
if category == "ai-slice":
|
|
|
|
| 459 |
return f"AI ๋ง์ดํฌ๋ก ์ธ๊ณ ํํ: {t} ์ {m} ํ๋ ๊ด์ฐฐ"
|
| 460 |
return forge_topic(lang_code, random.choice(TOPIC_CATEGORIES[:-1])) # mixed
|
| 461 |
|
| 462 |
+
# ===== ์ผ์ธ ํ๋กฌํํธ(์ท๋ฆฌ์คํธ) ์์ฑ =====
|
| 463 |
+
def build_shorts_prompt(topic: str, title: str, lang_code: str, style: str, tone: Optional[str], cuts: int, duration_sec: int, emoji: int) -> str:
|
| 464 |
+
# ์ปท ๊ฐ์ ๋ณด์
|
| 465 |
+
cuts = max(5, min(8, cuts))
|
| 466 |
+
per_cut = max(4, min(7, duration_sec // cuts)) # ์ปท๋น ์ด
|
| 467 |
+
style_note = {
|
| 468 |
+
"meme-shock": "๊ฐํ ํ
+ ๋ฐ์ + ๋ฆฌ๋ฌ. ์๋ง ์นํ. ์งง์ ๊ตฌ/๋ช
๋ น๋ฌธ.",
|
| 469 |
+
"wholesome": "๊ณต๊ฐ/ํ๋ง ํค. ์น์ ํ ๋งํฌ. ํฌ๋ง/์์.",
|
| 470 |
+
"informative": "ํต์ฌ ๋จผ์ , ํฉํธ ์ค์ฌ. ๊ฐ๊ฒฐ/์ ๋ขฐ.",
|
| 471 |
+
"clickbait": "๊ถ๊ธ์ฆ ์ ๋ฐ, ๊ณผ์ฅ ํ์ฉ(์ฌ์ค ์๊ณก ๊ธ์ง).",
|
| 472 |
+
}.get(style, "๋ฆฌ๋ฌยทํธ๊ธฐ์ฌ ์ค์ฌ.")
|
| 473 |
+
tone_note = f"์ถ๊ฐ ํค: {tone}." if tone else ""
|
| 474 |
+
emj = " ์ด๋ชจ์ง ์ ๋นํ ์ฌ์ฉ" if emoji > 0 else " ์ด๋ชจ์ง ์ฌ์ฉ ๊ธ์ง"
|
| 475 |
+
|
| 476 |
+
header = (
|
| 477 |
+
f"์ญํ : ์ ํ๋ธ ์ผ์ธ ์ฝํ
์ธ ํ๋๋ & ์นดํผ๋ผ์ดํฐ\n"
|
| 478 |
+
f"๋ชฉํ: ์๋ '์ ๋ชฉ'์ ๊ธฐ๋ฐ์ผ๋ก {duration_sec}์ด ์ด๋ด ์์ ํ๋กฌํํธ(๋๋ณธ ์ง์๋ฌธ)๋ฅผ ์์ฑํ๋ค.\n"
|
| 479 |
+
f"๊ท์น:\n"
|
| 480 |
+
f"- ์ด๋ฐ 3์ด ๊ฐํ ํ
, 3~4๊ฐ์ ํต์ฌ ํฌ์ธํธ, ๋ง์ง๋ง์ ์ก์
์ฝ(์ข์์/๋๊ธ ์ ๋)\n"
|
| 481 |
+
f"- ์งง๊ณ ๋ฆฌ๋ฌ๊ฐ ์๊ฒ, ์๋ง์ฉ ๋ฌธ์ฅ\n"
|
| 482 |
+
f"- ์ปท(์ฅ๋ฉด)์ ๋ฒํธ๋ก ๊ตฌ๋ถ, ๊ฐ ์ปท 1~2๋ฌธ์ฅ\n"
|
| 483 |
+
f"- ์ดฌ์ ์์ค ์์ด๋ ์์ฑํ ์์ํด์ ๋ฐ๋ก ๋ฃ์ ์ ์๋ ๋ฌ์ฌ ํฌํจ\n"
|
| 484 |
+
f"- ์คํ์ผ: {style} ({style_note}), ์ธ์ด: {lang_code}, {tone_note}{emj}\n"
|
| 485 |
+
f"- ์ปท ์: {cuts}๊ฐ, ์ปท๋น {per_cut}์ด ๋ด์ธ\n\n"
|
| 486 |
+
f"๋ฉํ:\n"
|
| 487 |
+
f"โข ์ฃผ์ (Topic): {topic}\n"
|
| 488 |
+
f"โข ์ ๋ชฉ(Title): {title}\n\n"
|
| 489 |
+
f"์ถ๋ ฅ ํฌ๋งท ์์(ํ์๋ง ์ฐธ๊ณ ):\n"
|
| 490 |
+
f"1) ํ
: ์์ฒญ์ ํธ๊ธฐ์ฌ ํญ๋ฐ ํ ์ค\n"
|
| 491 |
+
f"2) ํฌ์ธํธ1: ๊ตฌ์ฒด ์์/๋ธ๋๋/๊ฐ๊ฒฉ ๋ฑ\n"
|
| 492 |
+
f"3) ํฌ์ธํธ2: ๋น๊ต/๋ฐ์ /์ฃผ์\n"
|
| 493 |
+
f"4) ํฌ์ธํธ3: ์ค์ ํ/๋ฐ๋ก ์จ๋จน๊ธฐ\n"
|
| 494 |
+
f"5) ๋ง๋ฌด๋ฆฌ: ํ ์ค ์์ฝ + ์ก์
์ฝ(๋๊ธ/์ข์์ ์ ๋)\n\n"
|
| 495 |
+
f"์ด์ ์ ๊ท์น์ ์ฒ ์ ํ ์ง์ผ {cuts}๊ฐ์ ์ปท์ผ๋ก ํ๊ตญ์ด ํ๋กฌํํธ๋ฅผ ์ถ๋ ฅํ๋ผ."
|
| 496 |
+
)
|
| 497 |
+
return header
|
| 498 |
+
|
| 499 |
+
# ===== ์๋ํฌ์ธํธ =====
|
| 500 |
@app.get("/topic/auto", response_model=TopicResp)
|
| 501 |
def topic_auto(
|
| 502 |
count: int = Query(10, ge=1, le=100),
|
|
|
|
| 524 |
|
| 525 |
@app.get("/")
|
| 526 |
def root():
|
| 527 |
+
return JSONResponse({"message": "NocoAI Title & Prompt Forge. Try GET /topic/auto, POST /generate, GET /generate/auto, POST /prompt/shorts"}, media_type="application/json; charset=utf-8")
|
| 528 |
|
| 529 |
@app.get("/health")
|
| 530 |
def health():
|
|
|
|
| 568 |
titles, used_prompt = generate_titles(topic, count, req, lang_code, pretty_lang)
|
| 569 |
used_source = trend_source if (trend_source == "pytrends" and HAS_PYTRENDS) else "seed"
|
| 570 |
return JSONResponse({"topic": topic, "titles": titles, "used_prompt": used_prompt, "trend_source": used_source}, media_type="application/json; charset=utf-8")
|
| 571 |
+
|
| 572 |
+
@app.post("/prompt/shorts", response_model=ShortsPromptResp)
|
| 573 |
+
def prompt_shorts(body: ShortsPromptReq):
|
| 574 |
+
lang_code, _, _ = normalize_lang_and_country(body.lang, None)
|
| 575 |
+
prompt = build_shorts_prompt(
|
| 576 |
+
topic=body.topic,
|
| 577 |
+
title=body.title,
|
| 578 |
+
lang_code=lang_code,
|
| 579 |
+
style=body.style,
|
| 580 |
+
tone=body.tone,
|
| 581 |
+
cuts=body.cuts,
|
| 582 |
+
duration_sec=body.duration_sec,
|
| 583 |
+
emoji=body.emoji
|
| 584 |
+
)
|
| 585 |
+
return JSONResponse({
|
| 586 |
+
"topic": body.topic,
|
| 587 |
+
"title": body.title,
|
| 588 |
+
"prompt": prompt,
|
| 589 |
+
"lang": lang_code,
|
| 590 |
+
"style": body.style,
|
| 591 |
+
"cuts": body.cuts,
|
| 592 |
+
"duration_sec": body.duration_sec
|
| 593 |
+
}, media_type="application/json; charset=utf-8")
|