fantos commited on
Commit
9e12771
Β·
verified Β·
1 Parent(s): 8b70177

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +27 -65
app.py CHANGED
@@ -1,29 +1,31 @@
1
  # img_bot.py
2
  import discord, os, io, re, asyncio, logging, requests, replicate, subprocess
3
- import tempfile, pathlib, contextlib
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 Personal Access Token
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("OPENAI_API_KEY ν™˜κ²½ λ³€μˆ˜μ— Replicate Personal Access Token 값을 λ„£μ–΄μ£Όμ„Έμš”.")
 
 
17
 
18
- os.environ["REPLICATE_API_TOKEN"] = REPL_TOKEN # Replicate λΌμ΄λΈŒλŸ¬λ¦¬μ— 토큰 μ£Όμž…
 
19
 
20
  # ── λͺ¨λΈ ────────────────────────────────────────────────────
21
- MODEL = "luma/ray-flash-2-540p" # μ΅œμ‹  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
- prompt_raw: str = (message.content or "").strip()
91
- # ν”„λ‘¬ν”„νŠΈΒ·μ΄λ―Έμ§€ λͺ¨λ‘ μ—†μœΌλ©΄ λ¬΄μ‹œ
92
- if not prompt_raw and not message.attachments:
93
  return
94
 
95
- prompt_en = ko2en(prompt_raw)
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
- input_dict = {"prompt": prompt_en} # ν•„μˆ˜
116
- if start_url:
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] # URL λ¬Έμžμ—΄
138
- # URL β†’ bytes λ‹€μš΄λ‘œλ“œ
139
- data = requests.get(output, timeout=60).content
 
 
 
 
 
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: