[{"data":1,"prerenderedAt":661},["ShallowReactive",2],{"content-/video-batch-generation-plan":3,"all-pages-for-dir":638,"related-/video-batch-generation-plan":639,"og-image-/video-batch-generation-plan":660},{"id":4,"title":5,"body":6,"category":622,"concepts":622,"description":623,"extension":624,"meta":625,"navigation":561,"ogImage":622,"path":626,"project_name":622,"published":627,"publishedAt":628,"seo":629,"source":622,"stem":630,"tags":631,"todo":622,"unpublished":627,"updatedAt":628,"__hash__":637},"pages/2025-12/2025-12-16/video-batch-generation-plan.md","QQQ銘柄 動画バッチ生成・R2配信計画",{"type":7,"value":8,"toc":598},"minimark",[9,13,17,25,48,52,57,112,125,128,164,168,215,218,267,274,277,296,299,303,313,317,385,389,395,399,405,408,411,415,422,452,456,459,472,476,510,514,520,524,527,544,547,550,594],[10,11,5],"h1",{"id":12},"qqq銘柄-動画バッチ生成r2配信計画",[14,15,16],"h2",{"id":16},"背景",[18,19,20,24],"p",{},[21,22,23],"code",{},"/financial-quiz/proportional-animation-qqq","ページでは、100銘柄の財務アニメーションを動画としてダウンロードできる機能がある。現在はブラウザ上でリアルタイム生成しているが、以下の理由から事前生成に切り替える。",[26,27,28,36,42],"ol",{},[29,30,31,35],"li",{},[32,33,34],"strong",{},"ユーザー体験向上",": 即座にダウンロード開始",[29,37,38,41],{},[32,39,40],{},"一貫性",": 誰がダウンロードしても同じ動画",[29,43,44,47],{},[32,45,46],{},"負荷軽減",": ブラウザでの重いレンダリング処理を排除",[14,49,51],{"id":50},"cloudflare-r2-料金体系","Cloudflare R2 料金体系",[53,54,56],"h3",{"id":55},"無料枠月額","無料枠（月額）",[58,59,60,73],"table",{},[61,62,63],"thead",{},[64,65,66,70],"tr",{},[67,68,69],"th",{},"項目",[67,71,72],{},"無料枠",[74,75,76,85,93,101],"tbody",{},[64,77,78,82],{},[79,80,81],"td",{},"ストレージ",[79,83,84],{},"10 GB",[64,86,87,90],{},[79,88,89],{},"Class A操作（書き込み）",[79,91,92],{},"100万リクエスト",[64,94,95,98],{},[79,96,97],{},"Class B操作（読み込み）",[79,99,100],{},"1,000万リクエスト",[64,102,103,106],{},[79,104,105],{},"エグレス（転送量）",[79,107,108,111],{},[32,109,110],{},"無料","（制限なし）",[113,114,115],"blockquote",{},[18,116,117,120,121,124],{},[32,118,119],{},"重要",": 無料枠は",[32,122,123],{},"アカウント単位","で適用される。バケット単位やプロジェクト単位ではなく、Cloudflareアカウント全体で10GBを共有。",[53,126,127],{"id":127},"有料になる条件",[58,129,130,139],{},[61,131,132],{},[64,133,134,136],{},[67,135,69],{},[67,137,138],{},"超過料金",[74,140,141,148,156],{},[64,142,143,145],{},[79,144,81],{},[79,146,147],{},"$0.015/GB/月",[64,149,150,153],{},[79,151,152],{},"Class A操作",[79,154,155],{},"$4.50/100万リクエスト",[64,157,158,161],{},[79,159,160],{},"Class B操作",[79,162,163],{},"$0.36/100万リクエスト",[53,165,167],{"id":166},"r2の技術的な制限","R2の技術的な制限",[58,169,170,179],{},[61,171,172],{},[64,173,174,176],{},[67,175,69],{},[67,177,178],{},"制限",[74,180,181,189,199,207],{},[64,182,183,186],{},[79,184,185],{},"バケット数/アカウント",[79,187,188],{},"100万個",[64,190,191,194],{},[79,192,193],{},"ストレージ容量/バケット",[79,195,196],{},[32,197,198],{},"無制限",[64,200,201,204],{},[79,202,203],{},"単一オブジェクトサイズ",[79,205,206],{},"最大5TiB",[64,208,209,212],{},[79,210,211],{},"カスタムドメイン/バケット",[79,213,214],{},"50個",[53,216,217],{"id":217},"本プロジェクトの見積もり",[58,219,220,229],{},[61,221,222],{},[64,223,224,226],{},[67,225,69],{},[67,227,228],{},"値",[74,230,231,239,247,259],{},[64,232,233,236],{},[79,234,235],{},"銘柄数",[79,237,238],{},"100",[64,240,241,244],{},[79,242,243],{},"1銘柄あたりサイズ",[79,245,246],{},"約3MB（MP4）",[64,248,249,254],{},[79,250,251],{},[32,252,253],{},"合計",[79,255,256],{},[32,257,258],{},"約300MB",[64,260,261,264],{},[79,262,263],{},"無料枠に対する割合",[79,265,266],{},"3%",[18,268,269,270,273],{},"→ ",[32,271,272],{},"余裕で無料枠内","。エグレス無料のため、ダウンロード数が増えても追加コストなし。",[18,275,276],{},"参考:",[278,279,280,289],"ul",{},[29,281,282],{},[283,284,288],"a",{"href":285,"rel":286},"https://developers.cloudflare.com/r2/pricing/",[287],"nofollow","Cloudflare R2 Pricing",[29,290,291],{},[283,292,295],{"href":293,"rel":294},"https://developers.cloudflare.com/r2/reference/limits/",[287],"Cloudflare R2 Limits",[14,297,298],{"id":298},"技術設計",[53,300,302],{"id":301},"_1-動画生成フロー","1. 動画生成フロー",[304,305,310],"pre",{"className":306,"code":308,"language":309},[307],"language-text","financial-data.ts (データ)\n    ↓ [Node.js スクリプト]\nCanvas + Chart.js でレンダリング\n    ↓ [FFmpeg]\nWebM → MP4 変換・最適化\n    ↓ [wrangler r2]\nCloudflare R2 にアップロード\n","text",[21,311,308],{"__ignoreMap":312},"",[53,314,316],{"id":315},"_2-動画仕様","2. 動画仕様",[58,318,319,327],{},[61,320,321],{},[64,322,323,325],{},[67,324,69],{},[67,326,228],{},[74,328,329,337,345,353,361,369,377],{},[64,330,331,334],{},[79,332,333],{},"解像度",[79,335,336],{},"1920 × 1080",[64,338,339,342],{},[79,340,341],{},"フォーマット",[79,343,344],{},"MP4 (H.264)",[64,346,347,350],{},[79,348,349],{},"フレームレート",[79,351,352],{},"30fps",[64,354,355,358],{},[79,356,357],{},"期間数",[79,359,360],{},"11期（2015〜LTM）",[64,362,363,366],{},[79,364,365],{},"各期間表示時間",[79,367,368],{},"1.5秒",[64,370,371,374],{},[79,372,373],{},"総時間",[79,375,376],{},"約17秒",[64,378,379,382],{},[79,380,381],{},"予想サイズ",[79,383,384],{},"5〜15MB/ファイル",[53,386,388],{"id":387},"_3-ファイル構成","3. ファイル構成",[304,390,393],{"className":391,"code":392,"language":309},[307],"R2 bucket: financial-videos\n├── AAPL.mp4\n├── GOOGL.mp4\n├── MSFT.mp4\n├── ... (100銘柄)\n└── metadata.json  # 生成日時、バージョン情報\n",[21,394,392],{"__ignoreMap":312},[53,396,398],{"id":397},"_4-ダウンロードurl構成","4. ダウンロードURL構成",[304,400,403],{"className":401,"code":402,"language":309},[307],"https://r2.eurekapu.com/financial-videos/{ticker}.mp4\n",[21,404,402],{"__ignoreMap":312},[18,406,407],{},"または Cloudflare Pages 経由でプロキシ。",[14,409,410],{"id":410},"実装ステップ",[53,412,414],{"id":413},"step-1-canvas直接レンダリングスクリプト","Step 1: Canvas直接レンダリングスクリプト",[18,416,417,418,421],{},"Node.js + ",[21,419,420],{},"canvas"," パッケージで、Vue コンポーネントの描画ロジックを再現。",[304,423,427],{"className":424,"code":425,"language":426,"meta":312,"style":312},"language-bash shiki shiki-themes vitesse-light vitesse-light","cd apps/web\nnode scripts/generate-financial-videos.mjs\n","bash",[21,428,429,442],{"__ignoreMap":312},[430,431,434,438],"span",{"class":432,"line":433},"line",1,[430,435,437],{"class":436},"sz8Xr","cd",[430,439,441],{"class":440},"sdGka"," apps/web\n",[430,443,445,449],{"class":432,"line":444},2,[430,446,448],{"class":447},"senZ8","node",[430,450,451],{"class":440}," scripts/generate-financial-videos.mjs\n",[53,453,455],{"id":454},"step-2-webm-mp4-変換","Step 2: WebM → MP4 変換",[18,457,458],{},"Python + FFmpeg で一括変換。",[304,460,462],{"className":424,"code":461,"language":426,"meta":312,"style":312},"python scripts/convert-to-mp4.py\n",[21,463,464],{"__ignoreMap":312},[430,465,466,469],{"class":432,"line":433},[430,467,468],{"class":447},"python",[430,470,471],{"class":440}," scripts/convert-to-mp4.py\n",[53,473,475],{"id":474},"step-3-r2-アップロード","Step 3: R2 アップロード",[304,477,479],{"className":424,"code":478,"language":426,"meta":312,"style":312},"wrangler r2 object put financial-videos/{ticker}.mp4 \\\n  --file ./videos/{ticker}.mp4\n",[21,480,481,502],{"__ignoreMap":312},[430,482,483,486,489,492,495,498],{"class":432,"line":433},[430,484,485],{"class":447},"wrangler",[430,487,488],{"class":440}," r2",[430,490,491],{"class":440}," object",[430,493,494],{"class":440}," put",[430,496,497],{"class":440}," financial-videos/{ticker}.mp4",[430,499,501],{"class":500},"snbK4"," \\\n",[430,503,504,507],{"class":432,"line":444},[430,505,506],{"class":500},"  --file",[430,508,509],{"class":440}," ./videos/{ticker}.mp4\n",[53,511,513],{"id":512},"step-4-フロントエンド変更","Step 4: フロントエンド変更",[18,515,516,519],{},[21,517,518],{},"AnimationExportButton.vue"," を修正し、事前生成動画への直リンクに変更。",[14,521,523],{"id":522},"代替案-puppeteer-による生成","代替案: Puppeteer による生成",[18,525,526],{},"Canvas直接レンダリングが複雑な場合の代替案として、以下がある。",[26,528,529,532,535,538,541],{},[29,530,531],{},"ローカルでdev server起動",[29,533,534],{},"Puppeteerで各銘柄ページを開く",[29,536,537],{},"「ダウンロード」ボタンをクリック",[29,539,540],{},"WebMファイルを収集",[29,542,543],{},"FFmpegでMP4に変換",[18,545,546],{},"メリット: 既存のレンダリングロジックをそのまま使用\nデメリット: 時間がかかる（100銘柄 × 約30秒 = 約50分）",[14,548,549],{"id":549},"次のアクション",[26,551,554,564,570,576,582,588],{"className":552},[553],"contains-task-list",[29,555,558,563],{"className":556},[557],"task-list-item",[559,560],"input",{"disabled":561,"type":562},true,"checkbox"," R2 バケット作成",[29,565,567,569],{"className":566},[557],[559,568],{"disabled":561,"type":562}," 動画生成スクリプト実装",[29,571,573,575],{"className":572},[557],[559,574],{"disabled":561,"type":562}," MP4変換スクリプト実装",[29,577,579,581],{"className":578},[557],[559,580],{"disabled":561,"type":562}," R2 アップロードスクリプト実装",[29,583,585,587],{"className":584},[557],[559,586],{"disabled":561,"type":562}," フロントエンド修正",[29,589,591,593],{"className":590},[557],[559,592],{"disabled":561,"type":562}," デプロイ・検証",[595,596,597],"style",{},"html pre.shiki code .sz8Xr, html code.shiki .sz8Xr{--shiki-default:#998418;--shiki-dark:#998418}html pre.shiki code .sdGka, html code.shiki .sdGka{--shiki-default:#B56959;--shiki-dark:#B56959}html pre.shiki code .senZ8, html code.shiki .senZ8{--shiki-default:#59873A;--shiki-dark:#59873A}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .snbK4, html code.shiki .snbK4{--shiki-default:#A65E2B;--shiki-dark:#A65E2B}",{"title":312,"searchDepth":444,"depth":444,"links":599},[600,601,608,614,620,621],{"id":16,"depth":444,"text":16},{"id":50,"depth":444,"text":51,"children":602},[603,605,606,607],{"id":55,"depth":604,"text":56},3,{"id":127,"depth":604,"text":127},{"id":166,"depth":604,"text":167},{"id":217,"depth":604,"text":217},{"id":298,"depth":444,"text":298,"children":609},[610,611,612,613],{"id":301,"depth":604,"text":302},{"id":315,"depth":604,"text":316},{"id":387,"depth":604,"text":388},{"id":397,"depth":604,"text":398},{"id":410,"depth":444,"text":410,"children":615},[616,617,618,619],{"id":413,"depth":604,"text":414},{"id":454,"depth":604,"text":455},{"id":474,"depth":604,"text":475},{"id":512,"depth":604,"text":513},{"id":522,"depth":444,"text":523},{"id":549,"depth":444,"text":549},null,"100銘柄の財務アニメーション動画を事前生成し、Cloudflare R2から配信する計画","md",{},"/video-batch-generation-plan",false,"2025-12-16T00:00:00.000Z",{"title":5,"description":623},"2025-12/2025-12-16/video-batch-generation-plan",[632,633,634,635,636],"cloudflare","r2","video","batch-generation","ffmpeg","-sPQmYi0TKTjibnIiENT6HkCMswTv8LnFGF6Mo4NJrQ",[],[640,644,648,652,656],{"title":641,"path":642,"publishedAt":643},"ずんだもん音声解説を財務3表コクピットに組み込む：132ステップ一括生成とR2デプロイ","/voicevox-zundamon-audio-r2","2026-05-06T00:00:00.000Z",{"title":645,"path":646,"publishedAt":647},"Cloudflare R2の動画が再生できない原因はWordPress時代のShift_JISファイル名だった話とCDNキャッシュバスター対応","/r2-video-shiftjis-corruption","2026-05-04T00:00:00.000Z",{"title":649,"path":650,"publishedAt":651},"Cloudflare R2カスタムドメイン × Web Audio API: ブラウザのメディアキャッシュが引き起こすCORS地獄","/cloudflare-r2-cors-web-audio-api","2026-03-17T00:00:00.000Z",{"title":653,"path":654,"publishedAt":655},"音声ファイル変換パイプライン（WAV→Opus）","/audio-wav-to-opus-pipeline","2025-12-12T00:00:00.000Z",{"title":657,"path":658,"publishedAt":659},"開発環境のバックアップを仕上げ、復旧手順をスキル化してGitHubで版管理する","/claude-env-backup-and-restore-skill","2026-07-30T00:00:00.000Z","https://log.eurekapu.com/og/blog/video-batch-generation-plan.png?v=2025-12-16T00%3A00%3A00.000Z&title=QQQ%E9%8A%98%E6%9F%84%20%E5%8B%95%E7%94%BB%E3%83%90%E3%83%83%E3%83%81%E7%94%9F%E6%88%90%E3%83%BBR2%E9%85%8D%E4%BF%A1%E8%A8%88%E7%94%BB&author=Kei%20Komatsu&sig=d0f5155a662de1eb",1785654977331]