Update app.py
Browse files
app.py
CHANGED
@@ -1,29 +1,31 @@
|
|
1 |
# img_bot.py
|
2 |
import discord, os, io, re, asyncio, logging, requests, replicate, subprocess
|
3 |
-
import
|
4 |
-
from PIL import Image # β NEW: μ΄λ―Έμ§ λ³νμ©
|
5 |
-
from transformers import pipeline as transformers_pipeline
|
6 |
|
7 |
# ββ νκ²½ λ³μ ββββββββββββββββββββββββββββββββββββββββββββββββ
|
8 |
TOKEN = os.getenv("DISCORD_TOKEN") # Discord λ΄ ν ν°
|
9 |
CHANNEL_ID = int(os.getenv("DISCORD_CHANNEL_ID")) # κ°μν μ±λ ID
|
10 |
-
REPL_TOKEN = (os.getenv("OPENAI_API_KEY") or "").strip() # Replicate
|
11 |
HF_TOKEN = (os.getenv("HF_TOKEN") or "").strip() # Hugging Face Personal Access Token
|
12 |
|
13 |
if not TOKEN or not CHANNEL_ID:
|
14 |
raise RuntimeError("DISCORD_TOKEN κ³Ό DISCORD_CHANNEL_ID νκ²½ λ³μλ₯Ό λͺ¨λ μ§μ νμΈμ.")
|
|
|
15 |
if not REPL_TOKEN:
|
16 |
-
raise RuntimeError(
|
|
|
|
|
17 |
|
18 |
-
|
|
|
19 |
|
20 |
# ββ λͺ¨λΈ ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
21 |
-
MODEL = "luma/ray-flash-2-540p"
|
22 |
|
23 |
# ββ λ²μ νμ΄νλΌμΈ (CPU) βββββββββββββββββββββββββββββββββββ
|
24 |
translator_kwargs = {"device": -1}
|
25 |
if HF_TOKEN:
|
26 |
-
translator_kwargs["token"] = HF_TOKEN
|
27 |
|
28 |
translator = transformers_pipeline(
|
29 |
"translation",
|
@@ -32,7 +34,7 @@ translator = transformers_pipeline(
|
|
32 |
)
|
33 |
|
34 |
def ko2en(text: str) -> str:
|
35 |
-
"""νκΈ ν¬ν¨ μ
|
36 |
if re.search(r"[κ°-ν£]", text):
|
37 |
try:
|
38 |
return translator(text, max_length=512)[0]["translation_text"].strip()
|
@@ -40,27 +42,6 @@ def ko2en(text: str) -> str:
|
|
40 |
logging.warning(f"λ²μ μ€ν¨, μλ¬Έ μ¬μ©: {e}")
|
41 |
return text
|
42 |
|
43 |
-
# ββ 보쑰: μ΄λ―Έμ§λ₯Ό PNG λ‘ λ³ν ν Replicate μ μ
λ‘λ βββββββββββ
|
44 |
-
def upload_image_as_png(url: str) -> str:
|
45 |
-
"""
|
46 |
-
Discord CDN URL β PNG νμΌλ‘ λ³ν β replicate.files.upload λ‘ μ
λ‘λ
|
47 |
-
Returns: HTTPS URL (replicate.delivery/*)
|
48 |
-
"""
|
49 |
-
try:
|
50 |
-
resp = requests.get(url, timeout=30)
|
51 |
-
resp.raise_for_status()
|
52 |
-
img = Image.open(io.BytesIO(resp.content)).convert("RGB") # webp λ± β rgb
|
53 |
-
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
54 |
-
img.save(tmp, format="PNG")
|
55 |
-
tmp_path = tmp.name
|
56 |
-
uploaded_url = replicate.files.upload(tmp_path)
|
57 |
-
return uploaded_url
|
58 |
-
finally:
|
59 |
-
# μμ νμΌ μ 리
|
60 |
-
with contextlib.suppress(Exception):
|
61 |
-
if 'tmp_path' in locals():
|
62 |
-
pathlib.Path(tmp_path).unlink(missing_ok=True)
|
63 |
-
|
64 |
# ββ λ‘κΉ
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
65 |
logging.basicConfig(
|
66 |
level=logging.INFO,
|
@@ -72,7 +53,7 @@ logging.basicConfig(
|
|
72 |
intents = discord.Intents.default()
|
73 |
intents.message_content = True # λ©μμ§ μ½ν
μΈ μ½κΈ°
|
74 |
|
75 |
-
class ImageBot(discord.Client):
|
76 |
async def on_ready(self):
|
77 |
logging.info(f"Logged in as {self.user} (id={self.user.id})")
|
78 |
# web.py λ³λ ¬ μ€ν
|
@@ -83,46 +64,21 @@ class ImageBot(discord.Client):
|
|
83 |
logging.warning(f"web.py μ€ν μ€ν¨: {e}")
|
84 |
|
85 |
async def on_message(self, message: discord.Message):
|
86 |
-
# λ΄ μμ μ
|
87 |
if message.author.id == self.user.id or message.channel.id != CHANNEL_ID:
|
88 |
return
|
89 |
|
90 |
-
|
91 |
-
|
92 |
-
if not prompt_raw and not message.attachments:
|
93 |
return
|
94 |
|
95 |
-
prompt_en = ko2en(
|
96 |
await message.channel.typing()
|
97 |
|
98 |
-
# ββ μ²¨λΆ μ΄λ―Έμ§ β start/end URL λ³ν βββββββββββββββββββββ
|
99 |
-
start_url, end_url = None, None
|
100 |
-
if message.attachments:
|
101 |
-
img_atts = [
|
102 |
-
att for att in message.attachments
|
103 |
-
if (att.content_type or "").startswith("image")
|
104 |
-
]
|
105 |
-
if img_atts:
|
106 |
-
try:
|
107 |
-
start_url = upload_image_as_png(img_atts[0].url)
|
108 |
-
if len(img_atts) > 1:
|
109 |
-
end_url = upload_image_as_png(img_atts[1].url)
|
110 |
-
except Exception as e:
|
111 |
-
logging.warning(f"μ²¨λΆ μ΄λ―Έμ§ μ²λ¦¬ μ€ν¨: {e}")
|
112 |
-
|
113 |
# ββ Replicate νΈμΆ ββββββββββββββββββββββββββββββββββ
|
114 |
def run_replicate():
|
115 |
-
|
116 |
-
|
117 |
-
input_dict["start_image_url"] = start_url
|
118 |
-
if end_url:
|
119 |
-
input_dict["end_image_url"] = end_url
|
120 |
-
# FileOutput β URL λ¬Έμμ΄λ§ λ°λλ‘ μ€μ
|
121 |
-
return replicate.run(
|
122 |
-
MODEL,
|
123 |
-
input=input_dict,
|
124 |
-
use_file_output=False
|
125 |
-
)
|
126 |
|
127 |
try:
|
128 |
output = await asyncio.get_running_loop().run_in_executor(None, run_replicate)
|
@@ -133,10 +89,16 @@ class ImageBot(discord.Client):
|
|
133 |
|
134 |
# ββ λΉλμ€ Discord μ μ‘ βββββββββββββββββββββββββββββ
|
135 |
try:
|
|
|
136 |
if isinstance(output, (list, tuple)):
|
137 |
-
output = output[0]
|
138 |
-
|
139 |
-
|
|
|
|
|
|
|
|
|
|
|
140 |
video_file = discord.File(io.BytesIO(data), filename="output.mp4")
|
141 |
await message.reply(files=[video_file])
|
142 |
except Exception as e:
|
|
|
1 |
# img_bot.py
|
2 |
import discord, os, io, re, asyncio, logging, requests, replicate, subprocess
|
3 |
+
from transformers import pipeline as transformers_pipeline # λ²μ νμ΄νλΌμΈ
|
|
|
|
|
4 |
|
5 |
# ββ νκ²½ λ³μ ββββββββββββββββββββββββββββββββββββββββββββββββ
|
6 |
TOKEN = os.getenv("DISCORD_TOKEN") # Discord λ΄ ν ν°
|
7 |
CHANNEL_ID = int(os.getenv("DISCORD_CHANNEL_ID")) # κ°μν μ±λ ID
|
8 |
+
REPL_TOKEN = (os.getenv("OPENAI_API_KEY") or "").strip() # Replicate ν ν°(λμΌ λ³μ μ¬μ©)
|
9 |
HF_TOKEN = (os.getenv("HF_TOKEN") or "").strip() # Hugging Face Personal Access Token
|
10 |
|
11 |
if not TOKEN or not CHANNEL_ID:
|
12 |
raise RuntimeError("DISCORD_TOKEN κ³Ό DISCORD_CHANNEL_ID νκ²½ λ³μλ₯Ό λͺ¨λ μ§μ νμΈμ.")
|
13 |
+
|
14 |
if not REPL_TOKEN:
|
15 |
+
raise RuntimeError(
|
16 |
+
"OPENAI_API_KEY νκ²½ λ³μμ Replicate Personal Access Token κ°μ λ£μ΄μ£ΌμΈμ."
|
17 |
+
)
|
18 |
|
19 |
+
# Replicate λΌμ΄λΈλ¬λ¦¬κ° μ°Έμ‘°νλλ‘ ν ν° μ£Όμ
|
20 |
+
os.environ["REPLICATE_API_TOKEN"] = REPL_TOKEN
|
21 |
|
22 |
# ββ λͺ¨λΈ ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
23 |
+
MODEL = "luma/ray-flash-2-540p" # ν
μ€νΈ-ν¬-λΉλμ€ λͺ¨λΈ (540p)
|
24 |
|
25 |
# ββ λ²μ νμ΄νλΌμΈ (CPU) βββββββββββββββββββββββββββββββββββ
|
26 |
translator_kwargs = {"device": -1}
|
27 |
if HF_TOKEN:
|
28 |
+
translator_kwargs["token"] = HF_TOKEN # μΈμ¦ ν ν° μ λ¬
|
29 |
|
30 |
translator = transformers_pipeline(
|
31 |
"translation",
|
|
|
34 |
)
|
35 |
|
36 |
def ko2en(text: str) -> str:
|
37 |
+
"""νκΈ ν¬ν¨ μ μμ΄ λ²μ, κ·Έλ μ§ μμΌλ©΄ μλ¬Έ λ°ν."""
|
38 |
if re.search(r"[κ°-ν£]", text):
|
39 |
try:
|
40 |
return translator(text, max_length=512)[0]["translation_text"].strip()
|
|
|
42 |
logging.warning(f"λ²μ μ€ν¨, μλ¬Έ μ¬μ©: {e}")
|
43 |
return text
|
44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
# ββ λ‘κΉ
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
46 |
logging.basicConfig(
|
47 |
level=logging.INFO,
|
|
|
53 |
intents = discord.Intents.default()
|
54 |
intents.message_content = True # λ©μμ§ μ½ν
μΈ μ½κΈ°
|
55 |
|
56 |
+
class ImageBot(discord.Client): # μ΄λ¦ μ μ§
|
57 |
async def on_ready(self):
|
58 |
logging.info(f"Logged in as {self.user} (id={self.user.id})")
|
59 |
# web.py λ³λ ¬ μ€ν
|
|
|
64 |
logging.warning(f"web.py μ€ν μ€ν¨: {e}")
|
65 |
|
66 |
async def on_message(self, message: discord.Message):
|
67 |
+
# λ΄ μμ μ λ©μμ§ νΉμ λμ μλ μ±λ β 무μ
|
68 |
if message.author.id == self.user.id or message.channel.id != CHANNEL_ID:
|
69 |
return
|
70 |
|
71 |
+
prompt = message.content.strip()
|
72 |
+
if not prompt:
|
|
|
73 |
return
|
74 |
|
75 |
+
prompt_en = ko2en(prompt) # νκΈμ΄λ©΄ μμ΄λ‘ λ³ν
|
76 |
await message.channel.typing()
|
77 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
# ββ Replicate νΈμΆ ββββββββββββββββββββββββββββββββββ
|
79 |
def run_replicate():
|
80 |
+
# νμν κ²½μ° input λμ
λ리μ duration, seed λ± μΆκ° κ°λ₯
|
81 |
+
return replicate.run(MODEL, input={"prompt": prompt_en})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
|
83 |
try:
|
84 |
output = await asyncio.get_running_loop().run_in_executor(None, run_replicate)
|
|
|
89 |
|
90 |
# ββ λΉλμ€ Discord μ μ‘ βββββββββββββββββββββββββββββ
|
91 |
try:
|
92 |
+
# replicate.run κ²°κ³Όκ° list/tuple μ΄λ©΄ 첫 λ²μ§Έ μμ μ¬μ©
|
93 |
if isinstance(output, (list, tuple)):
|
94 |
+
output = output[0]
|
95 |
+
|
96 |
+
# URL(str) β λ€μ΄λ‘λ, bytes/file-like β κ·Έλλ‘
|
97 |
+
if isinstance(output, str):
|
98 |
+
data = requests.get(output).content
|
99 |
+
else:
|
100 |
+
data = output.read() if hasattr(output, "read") else output
|
101 |
+
|
102 |
video_file = discord.File(io.BytesIO(data), filename="output.mp4")
|
103 |
await message.reply(files=[video_file])
|
104 |
except Exception as e:
|