Magenta49 commited on
Commit
9e63fc8
ยท
1 Parent(s): 8ff9a44

Add Hugging Face deployment workflow

Browse files
Files changed (3) hide show
  1. .github/workflows/deploy_to_hf.yml +22 -0
  2. app.py +71 -89
  3. logic_image.py +4 -3
.github/workflows/deploy_to_hf.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Deploy to Hugging Face Space
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ deploy:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - name: Checkout repository
13
+ uses: actions/checkout@v4
14
+ with:
15
+ fetch-depth: 0
16
+
17
+ - name: Push to Hugging Face Space
18
+ env:
19
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
20
+ run: |
21
+ git remote add space https://PLXR:${HF_TOKEN}@huggingface.co/spaces/PLXR/youtube_auto_image1
22
+ git push --force space main
app.py CHANGED
@@ -28,6 +28,8 @@ if 'final_audio' not in st.session_state:
28
  st.session_state['final_audio'] = None
29
  if 'shared_script' not in st.session_state:
30
  st.session_state['shared_script'] = ""
 
 
31
 
32
  # [CSS] ๋‹คํฌ๋ชจ๋“œ + ์˜ค๋ Œ์ง€ ํฌ์ธํŠธ ๋””์ž์ธ
33
  st.markdown("""
@@ -310,6 +312,7 @@ with tab2:
310
  seo_result = logic_seo.generate_seo_content(client, text_model_id, seo_script)
311
 
312
  st.session_state["seo_result"] = seo_result
 
313
  st.success("๋ถ„์„ ์™„๋ฃŒ!")
314
 
315
  c_seo1, c_seo2 = st.columns(2)
@@ -323,105 +326,84 @@ with tab2:
323
  st.markdown("##### ๐Ÿ“ ์„ค๋ช…๋ž€ (Description)")
324
  st.text_area("์„ค๋ช…๋ž€ ๊ฒฐ๊ณผ", seo_result['description'], height=200)
325
  st.divider()
326
- st.subheader("๐Ÿ–ผ๏ธ ์ธ๋„ค์ผ ์ƒ์„ฑ")
327
 
328
- seo_cached = st.session_state.get("seo_result")
329
 
330
- if not seo_cached:
331
- st.info("SEO ๋ถ„์„์„ ๋จผ์ € ์‹คํ–‰ํ•˜๋ฉด ์ธ๋„ค์ผ ์ƒ์„ฑ ๋ฒ„ํŠผ์ด ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.")
332
- else:
333
- strat_keys = list(THUMBNAIL_STRATEGIES.keys())
334
- sel_strat = st.selectbox("์ธ๋„ค์ผ ์ „๋žต ์„ ํƒ", strat_keys, index=0)
335
 
336
- # ์ธ๋„ค์ผ ํ…์ŠคํŠธ(๋ฉ”์ธ ํƒ€์ดํ‹€) ํ›„๋ณด: SEO ์ถ”์ฒœ ์ œ๋ชฉ 1๊ฐœ๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ
337
- default_thumb_text = seo_cached["titles"][0] if seo_cached.get("titles") else ""
338
- thumb_text = st.text_input("์ธ๋„ค์ผ ๋ฉ”์ธ ๋ฌธ๊ตฌ(์›ํ•˜๋ฉด ์ˆ˜์ •)", value=default_thumb_text)
339
-
340
- if st.button("๐Ÿง  ์ธ๋„ค์ผ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ", key="thumb_prompt_btn"):
341
- if not api_key:
342
- st.error("API Key ํ•„์š”")
343
- else:
344
- client = genai.Client(api_key=api_key)
345
-
346
- strategy_block = THUMBNAIL_STRATEGIES[sel_strat]
347
-
348
- prompt = f"""
349
- ๋„ˆ๋Š” ์œ ํŠœ๋ธŒ ์ธ๋„ค์ผ ๊ธฐํš์ž๋‹ค.
350
- ์•„๋ž˜ '๋Œ€๋ณธ'๊ณผ '์ „๋žต'์„ ์ฐธ๊ณ ํ•ด์„œ, ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ชจ๋ธ์— ๋„ฃ์„ '์ธ๋„ค์ผ ํ”„๋กฌํ”„ํŠธ'๋ฅผ ๋งŒ๋“ ๋‹ค.
351
-
352
- [๋Œ€๋ณธ]
353
- {seo_script[:8000]}
354
-
355
- [์ „๋žต]
356
- {strategy_block}
357
-
358
- [์ถ”๊ฐ€ ์กฐ๊ฑด]
359
- - ์ธ๋„ค์ผ ์ด๋ฏธ์ง€ ์•ˆ์— ํ…์ŠคํŠธ๋ฅผ ์ง์ ‘ ๊ทธ๋ ค ๋„ฃ์ง€ ๋งˆ๋ผ(๊ธ€์ž ์ƒ์„ฑ ๊ธˆ์ง€).
360
- - ๋Œ€์‹  'ํ…์ŠคํŠธ๋ฅผ ๋„ฃ์„ ์ž๋ฆฌ'๋ฅผ ๊ตฌ๋„๋กœ ํ™•๋ณดํ•ด๋ผ(์ƒ๋‹จ/ํ•˜๋‹จ ์—ฌ๋ฐฑ, ์•ˆ์ „์˜์—ญ).
361
- - ๊ตญ๊ฐ€/๊ตญ๊ธฐ/๋Œ€ํ†ต๋ น/์ฒญ์™€๋Œ€/๊ตญํšŒ์˜์‚ฌ๋‹น ๋“ฑ ํŠน์ • ๊ตญ๊ฐ€ ์ƒ์ง•์ด ์ž๋™์œผ๋กœ ๋‚˜์˜ค์ง€ ์•Š๊ฒŒ,
362
- ์ •์น˜ ์ƒ์ง•๋ฌผ์€ ์ถ”์ƒ์  ์€์œ (์‹ค๋ฃจ์—ฃ, ์กฐ๋ช…, ๊ตฐ์ค‘, ๋ฌด๋Œ€)๋กœ ์ฒ˜๋ฆฌํ•ด๋ผ.
363
- - ํ™”๋ฉด๋น„๋Š” {aspect_ratio}.
364
- - ์ถœ๋ ฅ์€ "ํ”„๋กฌํ”„ํŠธ ํ…์ŠคํŠธ 1๊ฐœ"๋งŒ. JSON/๋งˆํฌ๋‹ค์šด ๊ธˆ์ง€.
365
-
366
- [์‚ฌ์šฉ์ž๊ฐ€ ๋„ฃ์„ ๋ฉ”์ธ ๋ฌธ๊ตฌ(์ฐธ๊ณ ๋งŒ)]
367
- {thumb_text}
368
- """.strip()
369
-
370
- res = client.models.generate_content(model=text_model_id, contents=prompt)
371
- thumb_prompt = (getattr(res, "text", "") or "").strip()
372
- st.session_state["thumb_prompt"] = thumb_prompt
373
- st.success("์ธ๋„ค์ผ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์™„๋ฃŒ!")
374
- st.text_area("์ƒ์„ฑ๋œ ์ธ๋„ค์ผ ํ”„๋กฌํ”„ํŠธ", value=thumb_prompt, height=180)
375
 
376
- # ํ”„๋กฌํ”„ํŠธ๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ๋…ธ์ถœ
377
- thumb_prompt_cached = st.session_state.get("thumb_prompt", "")
378
- if thumb_prompt_cached:
379
- if st.button("๐ŸŽจ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€ 1์žฅ ์ƒ์„ฑ", key="thumb_img_btn", type="primary"):
380
  if not api_key:
381
  st.error("API Key ํ•„์š”")
 
 
382
  else:
383
  client = genai.Client(api_key=api_key)
384
-
385
- # ์ด๋ฏธ์ง€ ์ƒ์„ฑ (generate_images ์šฐ์„ , ์—†์œผ๋ฉด generate_content fallback)
386
- img_bytes = None
387
- try:
388
- if hasattr(client.models, "generate_images"):
389
- img_res = client.models.generate_images(
390
- model=image_model_id,
391
- prompt=thumb_prompt_cached
392
- )
393
- # logic_image์— ์žˆ๋Š” ์•ˆ์ „ ์ถ”์ถœ ํ•จ์ˆ˜๊ฐ€ ์—†๋‹ค๋ฉด ๊ฐ„๋‹จํžˆ parts์—์„œ ๋ฝ‘๊ธฐ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  try:
395
- cand = img_res.candidates[0]
396
- part = cand.content.parts[0]
397
- img_bytes = part.inline_data.data if part.inline_data else None
 
 
 
 
 
 
 
398
  except:
399
- img_bytes = None
400
  else:
401
- img_res = client.models.generate_content(
402
- model=image_model_id,
403
- contents=thumb_prompt_cached
404
- )
405
- try:
406
- cand = img_res.candidates[0]
407
- part = cand.content.parts[0]
408
- img_bytes = part.inline_data.data if part.inline_data else None
409
- except:
410
- img_bytes = None
411
- except Exception as e:
412
- st.error(f"์ด๋ฏธ์ง€ ์ƒ์„ฑ ์‹คํŒจ: {e}")
413
-
414
- if img_bytes:
415
- st.image(Image.open(io.BytesIO(img_bytes)), use_container_width=True)
416
- st.download_button(
417
- "โฌ‡๏ธ ์ธ๋„ค์ผ ๋‹ค์šด๋กœ๋“œ",
418
- data=img_bytes,
419
- file_name="thumbnail.png",
420
- mime="image/png",
421
- use_container_width=True
422
- )
423
- else:
424
- st.error("์ด๋ฏธ์ง€ ๋ฐ”์ดํŠธ๋ฅผ ์ถ”์ถœํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. (๋ชจ๋ธ ์‘๋‹ต ๊ตฌ์กฐ ํ™•์ธ ํ•„์š”)")
425
 
426
  # ----------------------------------------------------------------
427
  # [TAB 3] AI ์„ฑ์šฐ (TTS)
@@ -478,4 +460,4 @@ with tab3:
478
  if final_wav:
479
  st.success("์˜ค๋””์˜ค ์ƒ์„ฑ ์™„๋ฃŒ!")
480
  st.audio(final_wav, format="audio/wav")
481
- st.download_button("๋‹ค์šด๋กœ๋“œ (WAV)", final_wav, "full_audio.wav", "audio/wav")
 
28
  st.session_state['final_audio'] = None
29
  if 'shared_script' not in st.session_state:
30
  st.session_state['shared_script'] = ""
31
+ if 'thumbnail_results' not in st.session_state:
32
+ st.session_state['thumbnail_results'] = []
33
 
34
  # [CSS] ๋‹คํฌ๋ชจ๋“œ + ์˜ค๋ Œ์ง€ ํฌ์ธํŠธ ๋””์ž์ธ
35
  st.markdown("""
 
312
  seo_result = logic_seo.generate_seo_content(client, text_model_id, seo_script)
313
 
314
  st.session_state["seo_result"] = seo_result
315
+ st.session_state["thumbnail_results"] = []
316
  st.success("๋ถ„์„ ์™„๋ฃŒ!")
317
 
318
  c_seo1, c_seo2 = st.columns(2)
 
326
  st.markdown("##### ๐Ÿ“ ์„ค๋ช…๋ž€ (Description)")
327
  st.text_area("์„ค๋ช…๋ž€ ๊ฒฐ๊ณผ", seo_result['description'], height=200)
328
  st.divider()
 
329
 
330
+ st.subheader("๐Ÿ–ผ๏ธ ์ธ๋„ค์ผ ์ƒ์„ฑ")
331
 
332
+ seo_cached = st.session_state.get("seo_result")
 
 
 
 
333
 
334
+ if not seo_cached:
335
+ st.info("SEO ๋ถ„์„์„ ๋จผ์ € ์‹คํ–‰ํ•˜๋ฉด ์ธ๋„ค์ผ ์ƒ์„ฑ ๋ฒ„ํŠผ์ด ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.")
336
+ else:
337
+ default_thumb_text = seo_cached["titles"][0] if seo_cached.get("titles") else ""
338
+ thumb_text = st.text_input("์ธ๋„ค์ผ ๋ฉ”์ธ ๋ฌธ๊ตฌ(์ฐธ๊ณ ์šฉ)", value=default_thumb_text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
 
340
+ if st.button("๐ŸŽจ ์ธ๋„ค์ผ 3์ข… ๋™์‹œ ์ƒ์„ฑ", key="thumb_img_btn", type="primary"):
 
 
 
341
  if not api_key:
342
  st.error("API Key ํ•„์š”")
343
+ elif not seo_script:
344
+ st.warning("๋Œ€๋ณธ ํ•„์š”")
345
  else:
346
  client = genai.Client(api_key=api_key)
347
+ progress_text = "์ธ๋„ค์ผ 3์ข… ์ƒ์„ฑ ์ค‘..."
348
+ my_bar = st.progress(0, text=progress_text)
349
+
350
+ strategy_items = list(THUMBNAIL_STRATEGIES.items())
351
+ temp_results = [None] * len(strategy_items)
352
+
353
+ with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
354
+ future_to_idx = {
355
+ executor.submit(
356
+ logic_image.process_thumbnail_task,
357
+ i,
358
+ key,
359
+ strategy_text,
360
+ seo_script,
361
+ thumb_text,
362
+ client,
363
+ text_model_id,
364
+ image_model_id,
365
+ aspect_ratio
366
+ ): i
367
+ for i, (key, strategy_text) in enumerate(strategy_items)
368
+ }
369
+ completed = 0
370
+ for future in concurrent.futures.as_completed(future_to_idx):
371
+ idx, prompt, img = future.result()
372
+ strategy_key = strategy_items[idx][0]
373
+ temp_results[idx] = (strategy_key, prompt, img)
374
+ completed += 1
375
+ my_bar.progress(completed / len(strategy_items), text=f"{strategy_key} ์™„๋ฃŒ!")
376
+
377
+ st.session_state['thumbnail_results'] = temp_results
378
+ my_bar.empty()
379
+ st.rerun()
380
+
381
+ if st.session_state.get('thumbnail_results'):
382
+ cols = st.columns(3)
383
+ for col, item in zip(cols, st.session_state['thumbnail_results']):
384
+ if item is None:
385
+ continue
386
+ strategy_key, prompt_text, img_data = item
387
+ with col:
388
+ st.markdown(f"**{strategy_key}**")
389
+ if img_data:
390
  try:
391
+ image = Image.open(io.BytesIO(img_data))
392
+ st.image(image, use_container_width=True)
393
+ st.download_button(
394
+ label="โฌ‡๏ธ ๋‹ค์šด๋กœ๋“œ",
395
+ data=img_data,
396
+ file_name=f"thumbnail_{strategy_key.split('.')[0].lower()}.png",
397
+ mime="image/png",
398
+ key=f"thumb_dl_{strategy_key}",
399
+ use_container_width=True
400
+ )
401
  except:
402
+ st.error("์ด๋ฏธ์ง€ ์˜ค๋ฅ˜")
403
  else:
404
+ st.warning("์ด๋ฏธ์ง€ ์—†์Œ")
405
+ with st.expander("ํ”„๋กฌํ”„ํŠธ ๋ณด๊ธฐ"):
406
+ st.write(prompt_text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
 
408
  # ----------------------------------------------------------------
409
  # [TAB 3] AI ์„ฑ์šฐ (TTS)
 
460
  if final_wav:
461
  st.success("์˜ค๋””์˜ค ์ƒ์„ฑ ์™„๋ฃŒ!")
462
  st.audio(final_wav, format="audio/wav")
463
+ st.download_button("๋‹ค์šด๋กœ๋“œ (WAV)", final_wav, "full_audio.wav", "audio/wav")
logic_image.py CHANGED
@@ -141,7 +141,8 @@ def process_scene_task(
141
  img_bytes = None
142
 
143
  return index, final_prompt, img_bytes
144
- def process_thumbnail_task(
 
145
  index,
146
  strategy_key,
147
  strategy_text,
@@ -151,7 +152,7 @@ def process_scene_task(
151
  text_model_id,
152
  image_model_id,
153
  aspect_ratio,
154
- reference_image=None
155
  ):
156
  """
157
  return (idx, final_prompt, image_bytes)
@@ -177,6 +178,7 @@ def process_scene_task(
177
  [ํ•„์ˆ˜ ์กฐ๊ฑด]
178
  - ์ถœ๋ ฅ์€ ๋ฌด์กฐ๊ฑด ํ•œ๊ตญ์–ด๋งŒ. ์˜์–ด/๋กœ๋งˆ์ž ๊ธˆ์ง€.
179
  - ์ด๋ฏธ์ง€ ์•ˆ์— ๊ธ€์ž/์ž๋ง‰/๋ฐฐ๋„ˆ/๋กœ๊ณ  ์ ˆ๋Œ€ ์ƒ์„ฑ ๊ธˆ์ง€.
 
180
  - ๋Œ€์‹  ํ…์ŠคํŠธ๋ฅผ ๋‚˜์ค‘์— ๋„ฃ์„ ์ˆ˜ ์žˆ๊ฒŒ ์ƒ๋‹จ ๋˜๋Š” ํ•˜๋‹จ์— ๋„“์€ ์—ฌ๋ฐฑ(์•ˆ์ „์˜์—ญ)์„ ํ™•๋ณด.
181
  - ํŠน์ • ๊ตญ๊ฐ€ ์ƒ์ง• ์ž๋™์ƒ์„ฑ ๊ธˆ์ง€: ํƒœ๊ทน๊ธฐ, ์ฒญ์™€๋Œ€, ๊ตญํšŒ์˜์‚ฌ๋‹น, ๋Œ€ํ†ต๋ น, ํ›ˆ์žฅ/ํœ˜์žฅ, ์„ ๊ฑฐ ํฌ์Šคํ„ฐ, ๊ตญ๊ธฐ๋ฅ˜ ์ „๋ถ€ ๊ธˆ์ง€.
182
  - ์ •์น˜์ธ์„ ์‹ค์กด ์ธ๋ฌผ์ฒ˜๋Ÿผ ํŠน์ •ํ•˜์ง€ ๋ง๊ณ , ์ต๋ช… ์บ๋ฆญํ„ฐ/์‹ค๋ฃจ์—ฃ/์ƒ์ง•์  ์—ฐ์ถœ๋กœ ์ฒ˜๋ฆฌ.
@@ -215,4 +217,3 @@ def process_scene_task(
215
  img_bytes = None
216
 
217
  return index, final_prompt, img_bytes
218
-
 
141
  img_bytes = None
142
 
143
  return index, final_prompt, img_bytes
144
+
145
+ def process_thumbnail_task(
146
  index,
147
  strategy_key,
148
  strategy_text,
 
152
  text_model_id,
153
  image_model_id,
154
  aspect_ratio,
155
+ reference_image=None,
156
  ):
157
  """
158
  return (idx, final_prompt, image_bytes)
 
178
  [ํ•„์ˆ˜ ์กฐ๊ฑด]
179
  - ์ถœ๋ ฅ์€ ๋ฌด์กฐ๊ฑด ํ•œ๊ตญ์–ด๋งŒ. ์˜์–ด/๋กœ๋งˆ์ž ๊ธˆ์ง€.
180
  - ์ด๋ฏธ์ง€ ์•ˆ์— ๊ธ€์ž/์ž๋ง‰/๋ฐฐ๋„ˆ/๋กœ๊ณ  ์ ˆ๋Œ€ ์ƒ์„ฑ ๊ธˆ์ง€.
181
+ - ์ „๋žต์— ํฌํ•จ๋œ ํ…์ŠคํŠธ ์˜ค๋ฒ„๋ ˆ์ด ์ง€์‹œ๋Š” ๋ชจ๋‘ ๋ฌด์‹œํ•˜๊ณ , ํ…์ŠคํŠธ๊ฐ€ ๋“ค์–ด๊ฐ€์ง€ ์•Š๊ฒŒ ๊ตฌ์„ฑ.
182
  - ๋Œ€์‹  ํ…์ŠคํŠธ๋ฅผ ๋‚˜์ค‘์— ๋„ฃ์„ ์ˆ˜ ์žˆ๊ฒŒ ์ƒ๋‹จ ๋˜๋Š” ํ•˜๋‹จ์— ๋„“์€ ์—ฌ๋ฐฑ(์•ˆ์ „์˜์—ญ)์„ ํ™•๋ณด.
183
  - ํŠน์ • ๊ตญ๊ฐ€ ์ƒ์ง• ์ž๋™์ƒ์„ฑ ๊ธˆ์ง€: ํƒœ๊ทน๊ธฐ, ์ฒญ์™€๋Œ€, ๊ตญํšŒ์˜์‚ฌ๋‹น, ๋Œ€ํ†ต๋ น, ํ›ˆ์žฅ/ํœ˜์žฅ, ์„ ๊ฑฐ ํฌ์Šคํ„ฐ, ๊ตญ๊ธฐ๋ฅ˜ ์ „๋ถ€ ๊ธˆ์ง€.
184
  - ์ •์น˜์ธ์„ ์‹ค์กด ์ธ๋ฌผ์ฒ˜๋Ÿผ ํŠน์ •ํ•˜์ง€ ๋ง๊ณ , ์ต๋ช… ์บ๋ฆญํ„ฐ/์‹ค๋ฃจ์—ฃ/์ƒ์ง•์  ์—ฐ์ถœ๋กœ ์ฒ˜๋ฆฌ.
 
217
  img_bytes = None
218
 
219
  return index, final_prompt, img_bytes