• 投稿日:2025/11/10
何はなくても自動化だ!YouTube自動化収益獲得チャレンジ中!中編

何はなくても自動化だ!YouTube自動化収益獲得チャレンジ中!中編

  • 1
  • -
会員ID:5kJtslYL

会員ID:5kJtslYL

この記事は約16分で読めます
要約
ということで長くなりすぎたので2分割しました~。 え?後編?それは収益化通るかの検証編だからさ……。

はじめに

前回に引き続き読んでくれている方はありがとうございます!

まだ読んでいない人は意味がなくなるので前編を読んできてください!

今回も引き続きショート動画を完全自動化するノウハウをまとめていくよ!

ということで前回の続きからGO​

3. プロジェクトをまるごと作る

def build_video_from_csv(csv_path) -> Optional[str]: title = None; desc = None; tags = None rows = [] with open(csv_path, "r", encoding="utf-8") as f: for line in f: s = line.strip() if s.startswith("#title,"): title = s[7:]; continue if s.startswith("#description,"): desc = s[13:]; continue if s.startswith("#tags,"): tags = s[6:]; continue rows.append(line) if not rows: return None df = pd.read_csv(io.StringIO("".join(rows))).fillna("") voice_segments: List[AudioSegment] = [] frames: List[ImageClip] = [] bgm_path = CFG.get("bgm_path","").strip() bgm_seg = AudioSegment.from_file(bgm_path) + int(CFG.get("bgm_gain_db",-12)) if bgm_path and os.path.exists(bgm_path) else None tmp_dir = os.path.join(".tmp_kamishibai", os.path.splitext(os.path.basename(csv_path))[0]) os.makedirs(tmp_dir, exist_ok=True) for idx, row in df.iterrows(): text = str(row.get("text","")).strip() if not text: continue image = str(row.get("image","")).strip() or None dur_str = str(row.get("duration","")).strip() se = str(row.get("se","")).strip() or None vpath = tts(text, idx, tmp_dir) if vpath and os.path.exists(vpath): vseg = effects.normalize(AudioSegment.from_file(vpath)) else: vseg = AudioSegment.silent(duration=int(1000*float(CFG.get("min_scene_sec",2.5)))) if se and os.path.exists(se): se_seg = AudioSegment.from_file(se) vseg = se_seg.overlay(vseg) if len(se_seg) >= len(vseg) else vseg.overlay(se_seg) min_sec = float(CFG.get("min_scene_sec",2.5)) max_sec = float(CFG.get("max_scene_sec",8.0)) tail = float(CFG.get("tail_silence_sec",0.15)) want = len(vseg)/1000.0 + tail if dur_str: try: want = float(dur_str) except: pass want = max(min_sec, min(max_sec, want)) add_ms = int(max(0, want*1000 - len(vseg))) if add_ms > 0: vseg = vseg + AudioSegment.silent(duration=add_ms) voice_segments.append(vseg) bg = make_bg(image) panel = render_panel(text, is_title=False) frame = Image.alpha_composite(bg.convert("RGBA"), panel).convert("RGB") fp = os.path.join(tmp_dir, f"frame_{idx}.jpg") frame.save(fp, quality=95) clip = ImageClip(fp).set_duration(want) # 線形ズーム(ケンバーンズ風) zs = float(CFG.get("zoom_start",1.02)) ze = float(CFG.get("zoom_end",1.08)) clip = clip.resize(lambda t: zs + (ze - zs) * (t / clip.duration)) frames.append(clip) if not frames: return None video = concatenate_videoclips(frames, method="compose") if voice_segments: voice = AudioSegment.silent(duration=0) for seg in voice_segments: voice += seg if bgm_seg: loops = max(1, int(math.ceil(len(voice)/len(bgm_seg)))) if len(bgm_seg) > 0 else 1 long_bgm = AudioSegment.silent(duration=0) for _ in range(loops): long_bgm += bgm_seg long_bgm = long_bgm[:len(voice)] duck_db = int(CFG.get("ducking_db",-8)) mix = (long_bgm - max(0, duck_db)).overlay(voice) else: mix = voice audio_path = os.path.join(tmp_dir, "mix_audio.mp3") mix.export(audio_path, format="mp3") video = video.set_audio(AudioFileClip(audio_path)) base = os.path.splitext(os.path.basename(csv_path))[0] outname = f"{datetime.now().strftime('%Y%m%d_%H%M')}_{base}.mp4" outpath = os.path.join(CFG["queue_dir"], outname) video.write_videofile(outpath, fps=int(CFG["fps"]), codec="libx264", audio_codec="aac", threads=4, preset="medium", bitrate="4000k") # メタ追記 title2 = (title or CFG.get("default_title","今日の紙芝居 #Shorts")).strip() desc2 = (desc or CFG.get("default_description","自動生成の紙芝居ショートです。#Shorts")).strip() tags2 = (tags or CFG.get("default_tags","#Shorts")).strip() append_captions(outpath, title2, desc2, tags2) shutil.move(csv_path, os.path.join(CFG["done_dir"], os.path.basename(csv_path))) return outpath def main(): targets = sorted(glob.glob("stories/queue/*.csv")) if not targets: print("stories/queue が空です。CSVを置いてください。") return for p in targets: try: print(f"==> 生成: {p}") out = build_video_from_csv(p) if out: print(f"出力: {out}") except Exception as e: print(f"失敗: {p} : {e}") if __name__ == "__main__": main()

kamishibai_maker.py

続きは、リベシティにログインしてからお読みください

ノウハウ図書館でできること
  • すべての記事の閲覧

  • ブックマーク

  • いいね・レビュー

  • 記事の投稿※応援会員(有料)のみ

  • ポイントの獲得※応援会員(有料)のみ

※会員登録には、新入生会員(初月30日無料)と応援会員(有料)があります

応援会員制度とは?
さらに!
  • リベシティの他の機能やサービスもご利用いただけます詳しく見る

ブックマークに追加した記事は、ブックマーク一覧ページで確認することができます。
あとから読み返したい時に便利です。

会員ID:5kJtslYL

投稿者情報

会員ID:5kJtslYL

イルカ会員

この記事に、いいねを送ろう! 参考になった記事に、
気軽にいいねを送れるようになりました!
この記事のレビュー(0

まだレビューはありません