<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>字幕作成 | imakat.com</title>
	<atom:link href="https://imakat.com/category/it/jimaku/feed/" rel="self" type="application/rss+xml" />
	<link>https://imakat.com</link>
	<description>工夫と改善で人生をちょっと豊かに</description>
	<lastBuildDate>Thu, 08 Oct 2026 19:32:20 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://i0.wp.com/imakat.com/wp-content/uploads/2023/07/cropped-80d64ecd340db4e2ca3224859b04caed.png?fit=32%2C32&#038;ssl=1</url>
	<title>字幕作成 | imakat.com</title>
	<link>https://imakat.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">160909258</site>	<item>
		<title>【Mac】「VTT字幕生成＆かんたんエディタ」を作った(無料)。</title>
		<link>https://imakat.com/2026/08/26/29722/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Wed, 26 Aug 2026 06:59:23 +0000</pubDate>
				<category><![CDATA[字幕作成]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[vtt]]></category>
		<category><![CDATA[マルチモーダル]]></category>
		<category><![CDATA[ターミナル]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=29722</guid>

					<description><![CDATA[１　はじめに&#160; 26.10.03:エディタの改造を行ないました。内容としては、BGMや音声ファイルを合成する機能を追加しました。 今回の投稿は、前回の投稿ID-29651「VTT字幕の全自動生成」とペアになる投 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">１　<strong>はじめに</strong>&nbsp;</h2>



<p class="wp-block-paragraph">26.10.03:<a href="https://imakat.com/rd.php?id=w7wIty8o.png" target="_blank">エディタの改造</a>を行ないました。内容としては、BGMや音声ファイルを合成する機能を追加しました。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=4ijLOKVB.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=4ijLOKVB.png" alt="" style="width:308px;height:auto"/></a></figure>



<p class="wp-block-paragraph">今回の投稿は、前回の投稿<a href="https://imakat.com/2026/08/21/29651/" target="_blank">ID-29651</a>「VTT字幕の全自動生成」とペアになる投稿です。</p>



<p class="wp-block-paragraph">以下のビデオは、</p>



<p class="wp-block-paragraph">前回の投稿である動画音声ファイルからVTT字幕を生成するパートと、今回の投稿のメインであるエディタで編集するパートを一緒にしたものです。ご視聴ください。</p>


<div class="sc-dynamic-embed">
  <style>
  /* リンクの見た目を整える */
  .sc-dynamic-embed .sc-link-container { 
      display: flex; 
      margin-bottom: 10px; 
      flex-wrap: wrap; 
      align-items: center; 
  }
  .sc-dynamic-embed .sc-link { margin-bottom: 0; }
  .sc-dynamic-embed .sc-link a {
    font-size: 15px; 
    font-weight: normal;
    text-decoration: underline;
    color: #0073aa;
  }
  .sc-dynamic-embed .sc-link a:hover { text-decoration: none; color: #000; }
  
  @media (max-width: 500px) {
    .sc-dynamic-embed .sc-link a { font-size: 13px; }
  }

  .sc-dynamic-embed .imk-line {
      display: inline-block;
      width: 100%;
      border-radius: 2px;
      transition: background-color 0.1s;
  }

  .sc-dynamic-embed #subtitleOverlay,
  .sc-dynamic-embed #scSubtitleOverlay,
  .sc-dynamic-embed .overlay-cue,
  .sc-dynamic-embed .band {
      display: none !important;
      opacity: 0 !important;
      visibility: hidden !important;
      pointer-events: none !important;
  }
  </style>

  <div class="sc-link-container">
    <p class="sc-link">
      <!-- ★環境依存排除：固定ドメインをルート相対パスへ置換完了 -->
      <a href="/ds62/?drid=105" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <!-- ★環境依存排除：[dynamic_external_html] にルート相対パスを指定し、functions.phpにURL動的解決を委ねる設計 -->
  <style>
            :root{ --dr5emd-max: 1920px; }
            #subtitleOverlay, #scSubtitleOverlay, .overlay-cue, .band { display: none !important; opacity: 0 !important; }
            .imk-line { display: inline-block; width: 100%; border-radius: 2px; transition: background-color 0.1s; }
            .dr5emd-sublist details > p{ height:200px; overflow:auto; background-color:#EDF7FF; padding:2px 6px; margin:0; box-shadow:3px 3px 4px black; position: relative; }
            .dr5emd-sublist details > summary{ padding:2px 6px; width:100%; background-color:#ddd; border:none; box-shadow:3px 3px 4px black; cursor:pointer; list-style:none; }
            
                .video-wrap{position:relative;width:100%;margin:0 auto}
                #myVideo{width:100%;height:auto;min-height:200px;display:block;background:#000;}</style><div class="dr5emd-container"><div class="video-wrap"><video id="myVideo" controls poster="https://imakat.com/rd.php?id=osG6NR1G.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=QaFjV0en.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=HjJNFEwi.vtt" label="日本語" srclang="ja" kind="subtitles"></video></div><div class="dr5emd-sublist"><details translate="yes"><summary translate="yes">字幕一覧(クリック)</summary> <p translate="yes">
(<a href="#" class="imk-cue" data-seek="0:10" translate="no">00:00:10</a>) 全自動VTT字幕作成&エディター起動。<br>
(<a href="#" class="imk-cue" data-seek="0:16" translate="no">00:00:16</a>) このアプリケーションの操作をしてみます。<br>
(<a href="#" class="imk-cue" data-seek="0:25" translate="no">00:00:25</a>) フォルダの中から対象となるファイル、<br>
(<a href="#" class="imk-cue" data-seek="0:30" translate="no">00:00:30</a>) 動画または音声ファイルを見つけます。<br>
(<a href="#" class="imk-cue" data-seek="0:36" translate="no">00:00:36</a>) ここに用意してあります。<br>
(<a href="#" class="imk-cue" data-seek="0:40" translate="no">00:00:40</a>) mp4を用意してあります。<br>
(<a href="#" class="imk-cue" data-seek="0:44" translate="no">00:00:44</a>) このとき私は、<br>
(<a href="#" class="imk-cue" data-seek="0:48" translate="no">00:00:48</a>) 1080pの動画はちょっと重いので、<br>
(<a href="#" class="imk-cue" data-seek="0:53" translate="no">00:00:53</a>) 音質はそのままにして、<br>
(<a href="#" class="imk-cue" data-seek="0:56" translate="no">00:00:56</a>) 画質を360pに圧縮して、<br>
(<a href="#" class="imk-cue" data-seek="1:00" translate="no">00:01:00</a>) そういうファイルを生成しておきます。これですね。<br>
(<a href="#" class="imk-cue" data-seek="1:06" translate="no">00:01:06</a>) あえて字幕作成はこの360pの方を使います。<br>
(<a href="#" class="imk-cue" data-seek="1:13" translate="no">00:01:13</a>) そうすることで処理の負荷が軽くなって、<br>
(<a href="#" class="imk-cue" data-seek="1:17" translate="no">00:01:17</a>) エラーのリスクも減ります。<br>
(<a href="#" class="imk-cue" data-seek="1:21" translate="no">00:01:21</a>) それでは、このファイルの上で右クリックします。<br>
(<a href="#" class="imk-cue" data-seek="1:26" translate="no">00:01:26</a>) クイックアクション。<br>
(<a href="#" class="imk-cue" data-seek="1:29" translate="no">00:01:29</a>) それであらかじめ作ってある、<br>
(<a href="#" class="imk-cue" data-seek="1:34" translate="no">00:01:34</a>) 全自動VTT字幕作成&エディター起動、これを行います。<br>
(<a href="#" class="imk-cue" data-seek="1:41" translate="no">00:01:41</a>) するとターミナルが開いて、<br>
(<a href="#" class="imk-cue" data-seek="1:45" translate="no">00:01:45</a>) 字幕作成の作業が開始します。<br>
(<a href="#" class="imk-cue" data-seek="1:57" translate="no">00:01:57</a>) こんな形でどんどん進んでいきます。<br>
(<a href="#" class="imk-cue" data-seek="2:02" translate="no">00:02:02</a>) 今1/3って出てますけども、<br>
(<a href="#" class="imk-cue" data-seek="2:05" translate="no">00:02:05</a>) これは11分ぐらいの動画になるので、<br>
(<a href="#" class="imk-cue" data-seek="2:09" translate="no">00:02:09</a>) 動画を3つに分割して処理が進んでいきます。<br>
(<a href="#" class="imk-cue" data-seek="2:17" translate="no">00:02:17</a>) ご覧のように、まず今1/3ですね。1番目を実行してます。<br>
(<a href="#" class="imk-cue" data-seek="2:25" translate="no">00:02:25</a>) どんどん進んでいきますけども、今まだちょっと時間がかかってますね。<br>
(<a href="#" class="imk-cue" data-seek="2:31" translate="no">00:02:31</a>) それで今1/3が済んで、<br>
(<a href="#" class="imk-cue" data-seek="2:35" translate="no">00:02:35</a>) 次の2/3に入っています。<br>
(<a href="#" class="imk-cue" data-seek="2:45" translate="no">00:02:45</a>) こうしてターミナルを表示しておきますと、<br>
(<a href="#" class="imk-cue" data-seek="2:49" translate="no">00:02:49</a>) 進捗が分かるので便利ですね。<br>
(<a href="#" class="imk-cue" data-seek="2:53" translate="no">00:02:53</a>) もしエラーになった時は、<br>
(<a href="#" class="imk-cue" data-seek="2:57" translate="no">00:02:57</a>) この画面の文字列をコピーして、<br>
(<a href="#" class="imk-cue" data-seek="3:00" translate="no">00:03:00</a>) ご自身の生成AIにペーストして質問するか、<br>
(<a href="#" class="imk-cue" data-seek="3:06" translate="no">00:03:06</a>) あるいは私のimakat図書館のチャットに、<br>
(<a href="#" class="imk-cue" data-seek="3:11" translate="no">00:03:11</a>) ペーストして質問してもらえれば、解決に向かえると思います。<br>
(<a href="#" class="imk-cue" data-seek="3:18" translate="no">00:03:18</a>) 今2/3ですね。<br>
(<a href="#" class="imk-cue" data-seek="3:22" translate="no">00:03:22</a>) これで今2/3が終わって、<br>
(<a href="#" class="imk-cue" data-seek="3:26" translate="no">00:03:26</a>) 最後の3/3に入ってます。<br>
(<a href="#" class="imk-cue" data-seek="3:35" translate="no">00:03:35</a>) 今のところまでは順調に来てますね。<br>
(<a href="#" class="imk-cue" data-seek="3:41" translate="no">00:03:41</a>) そんな形で処理が進んでいきます。<br>
(<a href="#" class="imk-cue" data-seek="3:45" translate="no">00:03:45</a>) 今3/3もそろそろ終わろうとしています。<br>
(<a href="#" class="imk-cue" data-seek="3:58" translate="no">00:03:58</a>) はい。<br>
(<a href="#" class="imk-cue" data-seek="4:06" translate="no">00:04:06</a>) 最終的なVTTを結合し、<br>
(<a href="#" class="imk-cue" data-seek="4:10" translate="no">00:04:10</a>) 連番も再度振り付けて、<br>
(<a href="#" class="imk-cue" data-seek="4:14" translate="no">00:04:14</a>) それでファイルが完成いたしました。<br>
(<a href="#" class="imk-cue" data-seek="4:18" translate="no">00:04:18</a>) ここでVTTファイルが作られて保存されてます。<br>
(<a href="#" class="imk-cue" data-seek="4:26" translate="no">00:04:26</a>) 見てみます。<br>
(<a href="#" class="imk-cue" data-seek="4:29" translate="no">00:04:29</a>) 字幕VTTファイルと<br>
(<a href="#" class="imk-cue" data-seek="4:32" translate="no">00:04:32</a>) 字幕一覧テキストファイルが生成されています。<br>
(<a href="#" class="imk-cue" data-seek="4:38" translate="no">00:04:38</a>) それで同時にですね、<br>
(<a href="#" class="imk-cue" data-seek="4:42" translate="no">00:04:42</a>) ここに字幕かんたんエディターが起動しました。<br>
(<a href="#" class="imk-cue" data-seek="4:51" translate="no">00:04:51</a>) これはですね、字幕かんたんエディターは操作が非常に簡単で、<br>
(<a href="#" class="imk-cue" data-seek="4:56" translate="no">00:04:56</a>) エディターの操作は見たままです。<br>
(<a href="#" class="imk-cue" data-seek="5:02" translate="no">00:05:02</a>) ちょっと動かしてみます。<br>
(<a href="#" class="imk-cue" data-seek="5:23" translate="no">00:05:23</a>) それでは始めます。<br>
(<a href="#" class="imk-cue" data-seek="5:26" translate="no">00:05:26</a>) この画像ですけれども、これは日本の山村によく見かける<br>
(<a href="#" class="imk-cue" data-seek="5:31" translate="no">00:05:31</a>) 棚田の風景です。<br>
(<a href="#" class="imk-cue" data-seek="5:37" translate="no">00:05:37</a>) コンピューターのデータの管理を...<br>
(<a href="#" class="imk-cue" data-seek="5:42" translate="no">00:05:42</a>) 新規に字幕を追加したい時は、<br>
(<a href="#" class="imk-cue" data-seek="5:46" translate="no">00:05:46</a>) この上のテキストボックスですね、ここに文字列を入れます。<br>
(<a href="#" class="imk-cue" data-seek="6:00" translate="no">00:06:00</a>) 今例えば、これは適当に入れてみますけども、<br>
(<a href="#" class="imk-cue" data-seek="6:04" translate="no">00:06:04</a>) こうやって入れますと<br>
(<a href="#" class="imk-cue" data-seek="6:08" translate="no">00:06:08</a>) こんなのが入るわけですね。それで<br>
(<a href="#" class="imk-cue" data-seek="6:18" translate="no">00:06:18</a>) 今4秒で入れましたけども、これを修正して<br>
(<a href="#" class="imk-cue" data-seek="6:22" translate="no">00:06:22</a>) 4秒ではなくて8秒ぐらいにすると。<br>
(<a href="#" class="imk-cue" data-seek="6:26" translate="no">00:06:26</a>) 8秒にしてみましょうか。そうすると<br>
(<a href="#" class="imk-cue" data-seek="6:43" translate="no">00:06:43</a>) 8秒で字幕の表示が切れましたね。<br>
(<a href="#" class="imk-cue" data-seek="6:51" translate="no">00:06:51</a>) それでは始めます。<br>
(<a href="#" class="imk-cue" data-seek="6:54" translate="no">00:06:54</a>) それでこのですね、音声の波形の表示は<br>
(<a href="#" class="imk-cue" data-seek="6:58" translate="no">00:06:58</a>) 拡大縮小ができます。<br>
(<a href="#" class="imk-cue" data-seek="7:04" translate="no">00:07:04</a>) 適当な位置でですね、シークバーを止めて<br>
(<a href="#" class="imk-cue" data-seek="7:08" translate="no">00:07:08</a>) そこからスペースキーを押すとですね、開始します。<br>
(<a href="#" class="imk-cue" data-seek="7:13" translate="no">00:07:13</a>) で、もう一回スペースキーを押すと止まります。<br>
(<a href="#" class="imk-cue" data-seek="7:19" translate="no">00:07:19</a>) こういった形で確認ができます。<br>
(<a href="#" class="imk-cue" data-seek="7:24" translate="no">00:07:24</a>) それで、まあ全体にですね、一度は通しで<br>
(<a href="#" class="imk-cue" data-seek="7:45" translate="no">00:07:45</a>) 一番多い修正はですね、字幕の終了時刻の調整です。<br>
(<a href="#" class="imk-cue" data-seek="7:49" translate="no">00:07:49</a>) まだ話してるうちにですね、字幕が消えるということがありますので<br>
(<a href="#" class="imk-cue" data-seek="7:58" translate="no">00:07:58</a>) この終了時刻をですね、調整・修正します。<br>
(<a href="#" class="imk-cue" data-seek="8:03" translate="no">00:08:03</a>) これはいいかな。例えば、<br>
(<a href="#" class="imk-cue" data-seek="8:06" translate="no">00:08:06</a>) 準アクティブなデータを置きます。<br>
(<a href="#" class="imk-cue" data-seek="8:08" translate="no">00:08:08</a>) 一番下がT2長期保管段。ここはアーカイブ領域です。<br>
(<a href="#" class="imk-cue" data-seek="8:13" translate="no">00:08:13</a>) 重要なのは、それぞれの段にサイズもメーカーも異なるバラバラのHDDを割り当てられる点です。<br>
(<a href="#" class="imk-cue" data-seek="8:20" translate="no">00:08:20</a>) ま、ここで止めて、これがちょうど終わってますが、これ4分2秒の位置ですね。<br>
(<a href="#" class="imk-cue" data-seek="8:28" translate="no">00:08:28</a>) ところが実際は、終了が4分2秒になってるから、<br>
(<a href="#" class="imk-cue" data-seek="8:32" translate="no">00:08:32</a>) これはいいですね。そのまま変える必要はないですね。<br>
(<a href="#" class="imk-cue" data-seek="8:38" translate="no">00:08:38</a>) です。これをソフトウェアで1つの巨大な階層構造として管理します。<br>
(<a href="#" class="imk-cue" data-seek="8:45" translate="no">00:08:45</a>) このシステムの革新にある設計思想…<br>
(<a href="#" class="imk-cue" data-seek="8:48" translate="no">00:08:48</a>) ま、こういった形でですね、中身をチェックしていきます。<br>
(<a href="#" class="imk-cue" data-seek="8:54" translate="no">00:08:54</a>) アプリの終了は、この左上のボタンを押します。<br>
(<a href="#" class="imk-cue" data-seek="9:00" translate="no">00:09:00</a>) そうするとですね、フォルダにですね、<br>
(<a href="#" class="imk-cue" data-seek="9:05" translate="no">00:09:05</a>) 字幕VTTファイルと字幕一覧テキストファイルがですね、できます。<br>
(<a href="#" class="imk-cue" data-seek="9:13" translate="no">00:09:13</a>) この出来上がった2つのファイルを配信用フォルダにですね、<br>
(<a href="#" class="imk-cue" data-seek="9:18" translate="no">00:09:18</a>) コピペすれば完了ということになります。<br>
(<a href="#" class="imk-cue" data-seek="9:25" translate="no">00:09:25</a>) 以上です。<br>
</p> </details>
<style>
/* 汎用ベーススタイル（マイライブラリ等の単独表示・ブログ埋め込み用） */
details { font: 16px "Open Sans", Calibri, sans-serif; width: 100%; }
details > summary { padding: 2px 6px; width: 100%; background-color: #ddd; border: none; box-shadow: 3px 3px 4px black; cursor: pointer; list-style: none; }
details > p { font: 14px "Open Sans", Calibri, sans-serif; height:150px; overflow: scroll; background-color: #EDF7FF; padding: 2px 6px; margin: 0; box-shadow: 3px 3px 4px black; }

/* 動画パッケージ（mu1emd, dr5, dr6等）内での上書き専用スタイル */
.mu1emd-sublist details > p, .dr5-sublist details > p, .dr6-sublist details > p {
    display: block;
    min-height: 180px;
    max-height: 240px;
    overflow-y: auto;
    background-color: #EDF7FF;
    padding: 6px 8px;
    margin: 0;
    box-shadow: 3px 3px 4px black;
    border-radius: 0 0 5px 5px;
}
</style>
</div></div>            <script>
            document.addEventListener("DOMContentLoaded", function(){
                var video = document.getElementById("myVideo");
                if(!video) return;

                            });
            </script>
            

  <script>
  (function(){
    var me = document.currentScript;
    var wrapper = me ? me.closest('.sc-dynamic-embed') : null;

    function protectVideo() {
      var target = wrapper ? wrapper : document;
      var mediaEls = target.querySelectorAll('video, audio');
      mediaEls.forEach(function(v){
        if(v.dataset.protected === 'true') return;
        v.dataset.protected = 'true';
        v.setAttribute('controlsList', 'nodownload');
        v.oncontextmenu = function() { return false; };
        v.addEventListener('contextmenu', function(e){ e.preventDefault(); return false; }, false);
      });
    }

    function initSubtitles() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video, audio');
      var listContainer = target.querySelector('details > p');
      
      if (!video) return false; 
      
      if (video.dataset.subInit === 'true') return true; 
      video.dataset.subInit = 'true';

      var oldOverlay = target.querySelector('#subtitleOverlay') || target.querySelector('#scSubtitleOverlay');
      if (oldOverlay) {
          oldOverlay.style.setProperty('display', 'none', 'important');
          oldOverlay.innerHTML = ''; 
      }

      // 全画面・PiP・iOS全画面の判定（dr52.php準拠＋Safari presentationMode完全対応）
      function isSpecialMode() {
        var fsEl = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
        var isFs = !!fsEl;
        var isWebkitFs = (video.webkitPresentationMode === "fullscreen") || !!video.webkitDisplayingFullscreen;
        var isPiP = !!(document.pictureInPictureElement && document.pictureInPictureElement === video) || (video.webkitPresentationMode === "picture-in-picture");
        return isFs || isWebkitFs || isPiP;
      }

      // 字幕トラックの表示/非表示を更新
      function updateTrackMode() {
        var hasList = (listContainer !== null);
        var desiredMode = (isSpecialMode() || !hasList) ? "showing" : "hidden";
        try {
          if (video.textTracks && video.textTracks.length > 0) {
            for (var i = 0; i < video.textTracks.length; i++) {
              var track = video.textTracks[i];
              if ((track.kind === 'subtitles' || track.kind === 'captions') && track.mode !== desiredMode) {
                track.mode = desiredMode;
              }
            }
          }
        } catch(e) {}
      }

      // 初回の反映
      updateTrackMode();

      // 各種全画面・PiP切り替えイベントの即時検知リスナーを登録（video本体・document双方を監視）
      ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'].forEach(function(evtName) {
        document.addEventListener(evtName, updateTrackMode, false);
        video.addEventListener(evtName, updateTrackMode, false);
      });
      video.addEventListener('webkitbeginfullscreen', updateTrackMode, false);
      video.addEventListener('webkitendfullscreen', updateTrackMode, false);
      video.addEventListener('webkitpresentationmodechanged', updateTrackMode, false);
      video.addEventListener('enterpictureinpicture', updateTrackMode, false);
      video.addEventListener('leavepictureinpicture', updateTrackMode, false);
      video.addEventListener('play', updateTrackMode, false);
      video.addEventListener('pause', updateTrackMode, false);

      // 字幕一覧が存在する場合のみ、リストのフォーマット処理を行う
      if (listContainer) {
          var detailsEl = target.querySelector("details");
          if (detailsEl) {
              detailsEl.open = true; 
              var summaryEl = detailsEl.querySelector("summary");
              if (summaryEl) summaryEl.textContent = "字幕(シーン)はここをクリック";
          }

          if (!listContainer.dataset.formatted) {
              var html = listContainer.innerHTML;
              var lines = html.split(/<br\s*\/?>/i);
              var newHtml = "";
              for(var j=0; j<lines.length; j++) {
                  if(lines[j].trim() === "") continue;
                  newHtml += "<span class='imk-line'>" + lines[j] + "</span><br>";
              }
              listContainer.innerHTML = newHtml;
              listContainer.dataset.formatted = "true";
          }

          var rootSublist = target.querySelector(".dr5-sublist") || listContainer.parentElement;
          if (rootSublist) {
            rootSublist.addEventListener("click", function(e){
              var a = e.target.closest && e.target.closest("a.imk-cue[data-seek]");
              if(!a) return;
              e.preventDefault();
              var ts = a.getAttribute("data-seek");
              if(!ts) return;
              var p = ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
              var sec = (p.length===2) ? p[0]*60+p[1] : (p.length===3 ? p[0]*3600+p[1]*60+p[2]: null);
              if(sec==null) return;
              try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
            });
          }
      }

      // 再生中（時間進行時）の追従とハイライト更新
      video.addEventListener("timeupdate", function(){
        updateTrackMode();

        var hasList = (listContainer !== null);
        if (!hasList) return;

        var cues = listContainer.querySelectorAll("a.imk-cue");
        if(cues.length === 0) return;
        var cur = video.currentTime;
        var activeA = null;

        for(var i=0; i<cues.length; i++){
            var ts = cues[i].getAttribute("data-seek");
            if(!ts) continue;
            var p = ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
            var t = (p.length===2) ? p[0]*60+p[1] : (p.length===3 ? p[0]*3600+p[1]*60+p[2] : null);
            if(t !== null && cur >= t - 0.5){ activeA = cues[i]; } 
            else if(t > cur){ break; }
        }

        if(activeA){
            var activeLine = activeA.closest(".imk-line");
            if(!activeLine) activeLine = activeA;

            if(activeLine.classList.contains("active-hl")) return;

            var allLines = listContainer.querySelectorAll(".imk-line");
            for(var k=0; k<allLines.length; k++) {
                allLines[k].classList.remove("active-hl");
                allLines[k].removeAttribute("style"); 
            }
            var allLinks = listContainer.querySelectorAll("a");
            for(var m=0; m<allLinks.length; m++) {
                allLinks[m].classList.remove("active-hl");
                allLinks[m].removeAttribute("style"); 
            }

            activeLine.classList.add("active-hl");
            activeLine.style.setProperty("background-color", "#ffff00", "important");
            activeLine.style.setProperty("color", "red", "important");
            activeLine.style.setProperty("font-weight", "normal", "important");
            
            var newLinks = activeLine.querySelectorAll("a");
            for(var n=0; n<newLinks.length; n++) {
                newLinks[n].style.setProperty("color", "red", "important");
                newLinks[n].style.setProperty("text-decoration", "none", "important");
            }

            if(listContainer.offsetParent !== null){
                var containerRect = listContainer.getBoundingClientRect();
                var activeRect = activeLine.getBoundingClientRect();
                var targetScroll = listContainer.scrollTop + (activeRect.top - containerRect.top) - (listContainer.clientHeight / 2) + (activeLine.clientHeight / 2);
                listContainer.scrollTo({ top: targetScroll, behavior: "smooth" });
            }
        }
      });

      return true;
    }

    var checks = 0;
    var checkTimer = setInterval(function(){
      protectVideo();
      var success = initSubtitles();
      checks++;
      if (success || checks > 20) { 
        clearInterval(checkTimer);
      }
    }, 500); 

    if (!window.scStopAndGo) {
      window.scStopAndGo = function(event, link){
        try{
          var mediaEls = document.querySelectorAll('video, audio');
          mediaEls.forEach(function(m){
            try{
              if (!m.paused) m.pause();
              if (document.pictureInPictureElement === m && document.exitPictureInPicture) {
                document.exitPictureInPicture().catch(function(){});
              }
            }catch(e){}
          });
        }finally{
          event.preventDefault();
          setTimeout(function(){
            if (link.target === '_blank') {
              window.open(link.href, '_blank');
            } else {
              window.location.href = link.href;
            }
          }, 50);
        }
        return false;
      };
    }
  })();
  </script>
</div>



<h2 class="wp-block-heading"><strong>２　</strong>ウインドウを統合したエディタを作った</h2>



<p class="wp-block-paragraph"><a href="https://imakat.com/2025/08/28/27063/" target="_blank">ID-27063</a>「字幕かんたん作成～」にてご案内しましたが、ウインドウがバラバラに配置され使いにくさがありました。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=aPzCiDJU.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=aPzCiDJU.png" alt=""/></a></figure>



<p class="wp-block-paragraph">今回、大幅に改善しました。各ウインドウを統合して使いやすくしました。誰でも簡単にインストールできるようにしました。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=Y4ZxRyib.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=Y4ZxRyib.png" alt="" style="aspect-ratio:1.1015516470061926;width:481px;height:auto"/></a></figure>



<h2 class="wp-block-heading"><strong>３　</strong>字幕かんたんエディタの特徴</h2>



<h3 class="wp-block-heading">（１）字幕ファイルはvttを使う</h3>



<p class="wp-block-paragraph">動画アプリの殆どが字幕はsrtファイルを出力する形になっていますが、これは大手プラットフォームのプレーヤーの標準がsrt使用になっているからです。ところが私はWordpressプレーヤーを改良したハンドメイドプレーヤーでの再生を基本に考えていますので、Wordpressの字幕ファイルの標準であるvttを使うことにしています。</p>



<h3 class="wp-block-heading">（２）文字情報はプレーンテキストが基本</h3>



<p class="wp-block-paragraph">私は字幕を動画に焼き付けることは基本的にはしません。その動画の中に焼き付けられた文字の理解のためには、コンピュータの頭脳にかなり大きなエネルギーを使わせることになります。</p>



<p class="wp-block-paragraph">最近、YouTubeなどの動画そのものを生成AIに読み込ませてマルチモーダルに理解させることを売りにしているようですが、こんなことをするとユーザーそれぞれが金太郎飴のように同じことをやり出すわけで、膨大な資源の無駄遣いのように感じます。やはり文字情報はプレーンテキストで記録するのが基本だろうと考えます。</p>



<p class="wp-block-paragraph">だから私は、動画ファイルと字幕ファイルは別々に作ります。再生するときにプレーヤー上で合体させますし、ブログ記事をソース化するときも貼り付けた動画の字幕一覧情報を含めています。</p>



<h3 class="wp-block-heading">（３）字幕一覧txtファイルも同時生成する</h3>



<p class="wp-block-paragraph">字幕vttファイルと同時にセットで字幕一覧txtファイルも出力するようになっています。</p>



<p class="wp-block-paragraph">字幕一覧は、見たい場面に瞬時に移動できる点と、前後の字幕の見逃しや先読みに役に立つ点、それらに加えて、実は、翻訳した時の精度のばらつきをカバーするのに役に立ちます。どういうことかというと、Google翻訳は字幕一覧の中まで綺麗に翻訳出来るのに対して、Apple翻訳は字幕一覧の中の翻訳は使い物にならないほど酷いのにかかわらず画像内の文字列の翻訳は見事に出来るなど、得手不得手がバラバラです。いずれにしても字幕一覧があれば、その凸凹な翻訳レベルを補ってくれます。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=YipZGjwp.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=YipZGjwp.png" alt="" style="aspect-ratio:1.3249879634087627"/></a><figcaption class="wp-element-caption">字幕一覧は、前後の字幕の見逃しや、先読みに役立つ</figcaption></figure>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=byxvsGQk.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=byxvsGQk.png" alt=""/></a><figcaption class="wp-element-caption">Google翻訳は、字幕一覧まで翻訳される。</figcaption></figure>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=GEoHNMsF.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=GEoHNMsF.png" alt=""/></a><figcaption class="wp-element-caption">Apple翻訳は、字幕一覧の中を正しく翻訳できない。</figcaption></figure>



<h2 class="wp-block-heading"><strong>４　</strong>実装</h2>



<h3 class="wp-block-heading">（１）Macの条件</h3>



<p class="wp-block-paragraph">このアプリをインストールできるのは、</p>



<p class="wp-block-paragraph">Apple Silicon (M1以降) を搭載したMac</p>



<p class="wp-block-paragraph">対応OS：macOS 11 (Big Sur) 以降<br>必要なソフト：なし（完全スタンドアロン）</p>



<p class="wp-block-paragraph">Pythonの環境構築や追加ライブラリのインストールは一切不要です。ダウンロードしてすぐに使用できます。</p>



<p class="wp-block-paragraph">ちなみに私は、M2 Mac mini Sonomaです。</p>



<h3 class="wp-block-heading">（２）インストール方法</h3>



<p class="wp-block-paragraph">必須の操作：初回起動時のセキュリティ許可</p>



<p class="wp-block-paragraph">初回のみ、readme.txt に記載の「右クリック ➔ 開く」によるGatekeeper（開発元未確認）の回避操作が必要です。</p>



<div class="wp-block-cocoon-blocks-toggle-box-1 toggle-wrap toggle-box block-box not-nested-style cocoon-block-toggle"><input id="toggle-checkbox-202610030832480" class="toggle-checkbox" type="checkbox"/><label class="toggle-button" for="toggle-checkbox-202610030832480"><strong><span class="marker">関係スクリプトおよびインストーラ(クリック)</span></strong></label><div class="toggle-content">
<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>関係するスクリプトは全て公開します。<br>関係スクリプト要約：<br>「字幕かんたんエディタ」は、PyWebViewとBottleで構築されたmacOS向け字幕編集アプリケーションです。動画への字幕付け、BGMの追加・合成などの機能を持ち、WebKitのファイルスキーム制約回避やFFmpeg連携による効率的な処理を実現しています。配布は<code>build_dmg.sh</code>スクリプトで生成されるDMGパッケージを通じて行われ、<code>readme.txt</code>には初回起動や日常的な動画ファイルからの起動方法が案内されています。<br>本システムは、Gemini 3.6 Flash APIを利用した高度な自動字幕生成ワークフローも提供します。<code>multimodal_vtt_pipeline_auto.py</code>スクリプトは、入力された長尺動画をFFmpegで精密に分割し、Gemini APIで高精度なWebVTT形式の字幕を生成。同時に、ウェブページ埋め込み用の字幕一覧HTML（_listJ.txt）も自動作成します。このスクリプトは、処理完了後に「字幕かんたんエディタ.app」に動画パスを渡して自動起動し、シームレスな編集フローを実現します。<br>エディタ連携を伴わない単体で字幕を生成する<code>multimodal_vtt_pipeline.py</code>スクリプトも存在し、日本語の不自然な空白除去や話者区分の自動判断なども行います。<br>スクリプト↓</td></tr><tr><td><a href="https://imakat.com/script_list/?pubtxt=%E5%AD%97%E5%B9%95%E3%81%8B%E3%82%93%E3%81%9F%E3%82%93%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BFapp%E3%81%AE%E4%BD%9C%E6%88%90_pub.txt" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=zrU95MiY.png" alt="" style="width:120px; height:auto;"></a></td></tr><tr><td>インストーラ↓SubtitleEasyEditor.dmgをクリックしてアプリをドラッグ&amp;ドロップしてください。</td></tr><tr><td><a href="https://imakat.com/rd.php?id=s1U0WvYb.zip" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=sVrfGd4u.png" alt="" style="width:120px; height:auto;"></a></td></tr><tr><td>内容に関するお問い合わせは以下へ↓</td></tr><tr><td><a rel="noopener" target="_blank" href="https://notebooklm.google.com/notebook/aa64cc3d-0160-4433-b934-ecc5ab902287"><img decoding="async" src="https://imakat.com/rd.php?id=vZJJ3Jfw.png" alt="" style="width:200px; height:auto;"></a></td></tr><tr><td>付録：<br>動画を360p音質キープに変換.workflow<br>要約：<br>このワークフローは、入力された動画ファイルを一括して360p解像度に変換するシェルスクリプトです。入力された各ファイルについて、新しいファイル名（元のファイル名に<code>_360p</code>を付加）を生成し、動画処理を行います。<br>動画はアスペクト比を保ちつつ、高さが360ピクセルになるようにスケーリングされます（または幅が360ピクセルになるよう調整され、もう一方が-2で自動設定）。映像コーデックにはH.264（libx264）を使用し、品質はCRF 23、エンコード速度は<code>veryfast</code>プリセット、ピクセルフォーマットは<code>yuv420p</code>が設定されています。<br>音声に関しては、<code>ffprobe</code>で元の音声コーデックとビットレートを判定します。もし元の音声がAACであれば<code>copy</code>モードで劣化なく複製され、それ以外のコーデックであれば、元のビットレート（取得できない場合は256kbps）に近い値でAAC形式に再エンコードされます。最終的に、データサイズの最適化とWeb再生に適した形式で360p動画が出力されます。<br>スクリプト↓</td></tr><tr><td><a href="https://imakat.com/script_list/?pubtxt=動画を360p音質キープに変換_pub.txt" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=zrU95MiY.png" alt="" style="width:120px; height:auto;"></a></td></tr></tbody></table></figure>
</div></div>



<h2 class="wp-block-heading">５　使用方法</h2>



<p class="wp-block-paragraph">説明不要なくらい簡単です。上に貼り付けたビデオでざっくりと説明していますのでご覧ください。</p>



<h2 class="wp-block-heading">６　使用上の注意</h2>



<p class="wp-block-paragraph">私は、字幕vttファイル、字幕一覧txtファイルを新規に作成するときは、自動送信用のmmedia pmediaフォルダではない、別の静的なローカルフォルダに作成するようにしています。その後の、数分で済むような小さな修正は、自動送信用フォルダ内で行います。送信用フォルダは更新の都度、毎分そのファイルを配信所へ送信しますので、あまり処理のラッシュにならないようにしています。</p>



<h2 class="wp-block-heading">７　まとめ</h2>



<p class="wp-block-paragraph">今回の開発で、私にとって「字幕工房」は不要になりました。<br>DIYで作れてしまいます。<br>つくづくアプリ開発で、収入を得るのは、大変な時代になったと感じます。</p>



<p class="wp-block-paragraph">私としては、デジタル創作物もそれなりに広がりと奥行きがついてきました。これまでも意識してきたことではありますが、もっと、このimakatブログを、読者の皆さんの実装に役立つように、充実していきたいと思います。</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">29722</post-id>	</item>
		<item>
		<title>【Mac】ついに完全自動化！右クリック一つで20分以上の動画・音声から高精度マルチモーダルVTT字幕を全自動生成する（無料アカウント対応）</title>
		<link>https://imakat.com/2026/08/21/29651/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Fri, 21 Aug 2026 04:39:05 +0000</pubDate>
				<category><![CDATA[字幕作成]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[vtt]]></category>
		<category><![CDATA[マルチモーダル]]></category>
		<category><![CDATA[ターミナル]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=29651</guid>

					<description><![CDATA[１　はじめに&#160; 26.10.09: Google無料アカウントのユーザーは本日からFlash-Liteのみに制限されるようです。変化が目まぐるしくて困ります。下の記事は「そんなことできた時があったんだ！」と驚か [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">１　<strong>はじめに</strong>&nbsp;</h2>



<p class="wp-block-paragraph">26.10.09: Google無料アカウントのユーザーは本日からFlash-Liteのみに制限されるようです。変化が目まぐるしくて困ります。下の記事は「そんなことできた時があったんだ！」と驚かれる内容かもしれません。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=4ijLOKVB.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=4ijLOKVB.png" alt="" style="width:336px;height:auto"/></a></figure>



<p class="wp-block-paragraph">今日は、夏休みの工作のようなアプリを一つ。</p>



<p class="wp-block-paragraph"><strong>「Finderから動画・音声ファイルを右クリックするだけで、完全に結合・調整された1本のVTT字幕ファイルが自動でピコンと出来上がる」という、全自動連続パイプライン</strong>を開発しました 。これもギターコードアプリと同様、自分専用の介助ロボットづくり、ともいえます。</p>



<p class="wp-block-paragraph"><strong>無料アカウントのままで利用可能</strong>ですのでご安心ください。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=PuL9LpY9.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=PuL9LpY9.png" alt="" style="aspect-ratio:2.912400455062571;width:355px;height:auto"/></a></figure>



<h2 class="wp-block-heading"><strong>２　なぜGeminiの文字起こしは、VrewやCapCutより優れているのか？</strong></h2>



<p class="wp-block-paragraph">一般的に使われている文字起こしツールは、純粋に「音声だけ」を聴いて文字に起こす仕組みになっているようです。そのため、同音異義語や画面に映っている言葉と乖離した誤変換が起きやすく、後からの手直しが避けられません。</p>



<p class="wp-block-paragraph">しかし、Gemini 1.5 Pro（またはFlash、今回制作したPythonの中ではgemini-3.6-flashを使っています。）などGeminiモデルの最大の特徴は～「目（映像解析）」と「耳（音声解析）」の両方を同時に使うマルチモーダル処理～が可能なことです。 動画内のスライド、背後のホワイトボードの文字、話している文脈をGeminiが総合的に理解して文字起こしを行うため、専門用語や固有名詞の変換精度が非常に高く、手直しの手間がとても少なくなります。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=ZhgpN5e8.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=ZhgpN5e8.png" alt="" style="aspect-ratio:1.832694992304113;width:332px;height:auto"/></a></figure>



<h2 class="wp-block-heading"><strong>３　拡がる字幕情報の活用</strong></h2>



<p class="wp-block-paragraph"><br>さらに今回は、～「映像と音声から複数人の会話（対談やインタビュー）だと自動判断した場合のみ、字幕の先頭に [男性]： [女性]： などの話者ラベルを自動で付与する」～という命令（話者判別ロジック）をプロンプトに組み込みました。これにより、例えば、Notebookで2人の音声解説を作る→それをこのアプリでVTT形式で文字起こしする→それをそのままPodcastや音声ブログにする、あるいはさらに、そこから動画を生成することもできます。</p>



<h2 class="wp-block-heading"><strong>４　サンプルとして「聴くブログ」の字幕の出来栄えをご確認ください。</strong></h2>



<p class="wp-block-paragraph">今回のアプリで作成したサンプルです。以下の「聴くブログ」をクリックすると表示されます。内容は結構お硬いですが高校生に理解できる程度の会話です。音声と連動して表示される字幕を確認ください。サイドバーにも掲載しています。</p>



<div style="text-align: left; margin-bottom: 15px;">

<!-- 
  ★配信したいdrid（例：drid=m51）をhrefの中に記述しておくだけで、
  バナー画像の下のテキスト（題名）はスプレッドシートから自動取得され完璧に連動します。
-->
<a href="https://imakat.com/mu1emd_autoplay/?drid=m52&#038;frag=1" id="podcastBannerLink" target="_blank" style="display: block; text-align: center;">
  <img decoding="async" src="https://imakat.com/rd.php?id=uKexYnKu.png
" alt="Podcast imakat図書館" style="max-width: 100%; height: auto; border-radius: 6px;">
</a>

<!-- ★ここのテキストは、ページ読み込み時に最新の「題名」へ自動で書き換わります -->
<p id="podcastBannerTitle" style="font-size: 13px; font-weight: bold; color: #333; margin: 6px 0 0 0; line-height: 1.4; text-align: left;">
  読み込み中&#8230;
</p>

<script>
(function(){
  var link = document.getElementById('podcastBannerLink');
  var titleEl = document.getElementById('podcastBannerTitle');
  if(!link || !titleEl) return;

  // URLからdridパラメータ（例: m51）を抽出
  var dridMatch = link.href.match(/[?&]drid=([^&]+)/);
  var drid = dridMatch ? dridMatch[1] : '';

  if (drid) {
    // WordPress側プロキシ経由で最新のvideoct.jsonを取得して連動
    fetch('https://imakat.com/gd_proxy/?f=videoct.json')
      .then(function(res) { return res.json(); })
      .then(function(data) {
        var v = data.find(function(item) { return String(item.videoid) === String(drid); });
        if (v && v.title) {
          titleEl.textContent = v.title; // ★スプレッドシートの最新の「題名」を自動反映
        } else {
          titleEl.textContent = 'imakat図書館Podcast'; // 万が一見つからない場合のフォールバック
        }
      })
      .catch(function() {
        titleEl.textContent = 'imakat図書館Podcast'; // エラー時の安全フォールバック
      });
  }

  // スマホとPCでの最適な挙動制御
  var isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
  link.addEventListener('click', function(e){
    if(!isMobile) {
      e.preventDefault();
      var w = 430, h = 530;
      var left = (screen.width - w) / 2;
      var top = (screen.height - h) / 2;
      var features = 'width=' + w + ',height=' + h + ',left=' + left + ',top=' + top + ',personalbar=0,toolbar=0,scrollbars=1,resizable=1';
      var popup = window.open(this.href, 'podcast_popup', features);
      if(popup) {
        popup.focus();
      } else {
        window.open(this.href, '_blank');
      }
    }
  });
})();
</script>

</div>



<p class="wp-block-paragraph">この字幕情報の作り方ですが、Notebookで20分程度の音声解説を作りそれを今回のアプリで時刻付きの文字起こしVTTファイルを作成→次にそれをインタラクティブ・トランスクリプト(Interactive Transcript/対話型文字起こし型txtファイル)形式のtxtファイルに変換して同期させています。<br>インタラクティブ・トランスクリプト形式のtxtファイルの記述例：</p>



<pre class="wp-block-code plaintext"><code>&lt;details translate="yes"&gt;&lt;summary translate="yes"&gt;字幕一覧(クリック)&lt;/summary&gt; &lt;p translate="yes"&gt;
(&lt;a href="#" class="imk-cue" data-seek="0:16" translate="no"&gt;00:00:16&lt;/a&gt;) ディープダイブへようこそ。&lt;br&gt;
(&lt;a href="#" class="imk-cue" data-seek="0:17" translate="no"&gt;00:00:17&lt;/a&gt;) &#091;男性]：今日のテーマなんですが、ずばり「妥協なきデジタル主権への挑戦」ということで。&lt;br&gt;
(&lt;a href="#" class="imk-cue" data-seek="0:25" translate="no"&gt;00:00:25&lt;/a&gt;) &#091;女性]：デジタル主権、響きからして壮大ですよね。巨大IT企業に依存しないで、自分自身でデータを完全にコントロールするインフラをどうやって獲得するのか。&lt;br&gt;

&lt;/p&gt; &lt;/details&gt;
&lt;style&gt;
details { font: 16px "Open Sans", Calibri, sans-serif; width: 100%; }
details &gt; summary { padding: 2px 6px; width: 100%; background-color: #ddd; border: none; box-shadow: 3px 3px 4px black; cursor: pointer; list-style: none; }
details &gt; p { font: 14px "Open Sans", Calibri, sans-serif; height:150px; overflow: scroll; background-color: #EDF7FF; padding: 2px 6px; margin: 0; box-shadow: 3px 3px 4px black; }
&lt;/style&gt;</code></pre>



<p class="wp-block-paragraph">なお、このテストでは、字幕作成の対象ファイルに、動画ではなくmp3音声ファイルを選んでいますが問題なく処理されています。</p>



<p class="wp-block-paragraph">全自動作成後のVTTに対する修正箇所ですが、ロールバック機構→フォールバック機構、DR52.php→dr52.php、MyBox→mybox、 MyBox_1→mybox_1、の修正だけです。句読点もそのまま、時刻もそのままです。このサンプルは動画内に表示された文字を理解させるテストにはなっていませんが、それにしてもかなり高い精度です。</p>



<h2 class="wp-block-heading"><strong>５　無料アカウントでも20分以上の動画がエラーなく処理できる秘密</strong></h2>



<p class="wp-block-paragraph">Google AI Studio（API）の無料枠を利用する場合でも、長尺の動画は容量や処理限界の壁にぶつかります。 今回のパイプラインスクリプトは、以下の工程をすべて「Python」とMac標準ツールがバックグラウンドで全自動実行します。</p>



<p class="wp-block-paragraph"><strong>（１）自動・無音分割（FFmpeg）</strong>: 動画を読み込み、1.0秒以上の無音区間を自動で探し出し、キリの良いポイント（デフォルト4分30秒）で動画を再エンコードなし（一瞬）で自動分割。<br></p>



<p class="wp-block-paragraph"><strong>（２）クリーンな並列API送信</strong>: 分割した各動画を、それぞれ毎回クリーンな別のAIセッションにアップロードして文字起こしを依頼（AIの脳内メモリの混線を100%防止）。</p>



<p class="wp-block-paragraph"><strong>（３）自動クリーンアップ</strong>: 処理が終わった動画ファイルは、APIサーバー上から直ちに自動削除（サーバー容量を圧迫しません）。<br></p>



<p class="wp-block-paragraph"><strong>（４）自動タイムシフト＆マージ</strong>: 0秒リセットで返ってきた各字幕のタイムスタンプに、分割時のオフセット秒（ズレ）を自動で足し算して復元。<br></p>



<p class="wp-block-paragraph"><strong>（５）通し連番の再編成</strong>: 結合された巨大なVTTデータを走査し、1から始まる綺麗な通し連番に一瞬で自動振り直し。<br></p>



<p class="wp-block-paragraph"><strong>（６）デスクトップ通知</strong>: すべてが完了すると、Macの画面右上に「字幕作成が完了しました！」と通知が表示されます。<br></p>



<p class="wp-block-paragraph">人間がやることは～「右クリックして待つだけ」～です。</p>



<h2 class="wp-block-heading">６　実装手順</h2>



<p class="wp-block-paragraph">それでは、実装手順は以下です。</p>



<div class="wp-block-cocoon-blocks-toggle-box-1 toggle-wrap toggle-box block-box not-nested-style cocoon-block-toggle"><input id="toggle-checkbox-202610090432070" class="toggle-checkbox" type="checkbox"/><label class="toggle-button" for="toggle-checkbox-202610090432070"><strong><span class="marker">実装手順(クリック)</span></strong></label><div class="toggle-content">
<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>&#x1f6e0;&#xfe0f; <strong>実装手順</strong><br><strong>（１）Geminiの「APIキー」を無料取得する</strong><br>APIを利用するための「鍵」を入手します。<br><strong>Google AI Studioの公式サイト</strong>（ https://aistudio.google.com/ ）にアクセスします。<br>画面左上の <strong>「Get API key」</strong> ボタンを押し、プロジェクト用のキー（無料）を作成してコピーします。<br><br><strong>（２）MacにPython 3とFFmpegをインストールする</strong><br>お使いのMacのターミナル（黒い画面）を開き、以下のコマンドが通る状態にしてください。 （※すでにHomebrewが導入されている環境を推奨します）<br>brew install ffmpeg<br>pip install google-genai<br><br><strong>（３） スクリプトの保存<br>１）Python</strong><br>以下の青ボタンからPythonコードをダウンロードまたはコピーし、プログラム内の `API_KEY` の部分にご自身のAPIキーを貼り付けた上で、Macの適当なフォルダ（例: `/Users/（あなたのユーザー名）/python_scripts/` ）に **`multimodal_vtt_pipeline.py`** という名前で保存します。<br><br><strong>２）AppleScript</strong><br>同じく以下の青ボタンからAppleScriptコードをダウンロードまたはコピーして、<br>①Mac標準の <strong>「Automator」</strong> アプリを起動し、新規作成から <strong>「クイックアクション」</strong> を選択します。<br>②画面最上部の設定を以下のように設定します。<br>「ワークフローの受け取る項目:」 ➔ 「ファイルまたはフォルダ」<br>「検索対象:」 ➔ <strong>「Finder.app」</strong><br><br>③左側のアクション一覧から <strong>「AppleScriptを実行」</strong> を探し、右側のワークフローエリアにドラッグ＆ドロップします。<br>④AppleScriptのコード入力欄に、ダウンロードしたコードを貼り付けます。 （※保存したPythonスクリプトのパスやPython実行環境のパスは、ご自身の環境に合わせて適切に書き換えてください）<br>⑤名前(全自動VTT字幕作成)をつけて保存します。</td></tr><tr><td><a href="https://imakat.com/script_list/?pubtxt=%E5%85%A8%E8%87%AA%E5%8B%95%E3%81%A7%E5%8B%95%E7%94%BB%E3%81%8B%E3%82%89vtt%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B_pub.txt" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=zrU95MiY.png" alt="" style="width:120px; height:auto;"></a></td></tr><tr><td>内容に関するお問い合わせは以下へ↓</td></tr><tr><td><a rel="noopener" target="_blank" href="https://notebooklm.google.com/notebook/aa64cc3d-0160-4433-b934-ecc5ab902287"><img decoding="async" src="https://imakat.com/rd.php?id=vZJJ3Jfw.png" alt="" style="width:200px; height:auto;"></a></td></tr></tbody></table></figure>
</div></div>



<h2 class="wp-block-heading">７　<strong>運用上の注意</strong></h2>



<p class="wp-block-paragraph"><strong>（１）ターミナルが起動するようにしてあります。</strong></p>



<p class="wp-block-paragraph">私は有料AI Proですが、それでも使用制限は結構な頻度で生じます。エラーやトラブルも時々生じます。そうした異常が生じた時、ターミナルに表示された文字列をコピーして、GeminiやGemなどの生成AIに貼り付けて、その末尾を改行してその下に「エラーが生じました。対策を教えてください。」と書いて、送信することになります。<br>その時の別な方法として、logを調べてそれをターミナルにコピペする方法もありますが、分かりにくく一手間余分に掛かりますので、ターミナルを常に表示するようにしておくのが、私にとっては安心に感じています。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=oLB8YtHo.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=oLB8YtHo.png" alt="" style="aspect-ratio:0.7934048224136863;width:427px;height:auto"/></a><figcaption class="wp-element-caption">ターミナルの表示例</figcaption></figure>



<p class="wp-block-paragraph"><strong>（２）GeminiやGemなど生成AIはすぐに使えるようにしておく</strong></p>



<p class="wp-block-paragraph">ブラウザのブックマークや専用アプリなどを用意して、Gemini/Gem/生成AI、がすぐに使えるようにしておきましょう。エラーやトラブルの時、すぐにターミナルのコピペができるようにしておきましょう。</p>



<p class="wp-block-paragraph">以上</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">29651</post-id>	</item>
		<item>
		<title>「字幕かんたん作成」〜無音声動画に解説字幕をサクッと付ける方法(Automator+AppleScript)</title>
		<link>https://imakat.com/2025/08/28/27063/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Thu, 28 Aug 2025 09:32:54 +0000</pubDate>
				<category><![CDATA[字幕作成]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[vtt]]></category>
		<category><![CDATA[QuickTimePlayer]]></category>
		<category><![CDATA[TextEdit]]></category>
		<category><![CDATA[HUD]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=27063</guid>

					<description><![CDATA[これまで動画の字幕作成については、何度か記事を投稿してきましたが、その中で感じた課題のいくつかを克服するアプリを、自作しましたので紹介します。 別の投稿で紹介したいと思いますが、字幕起こしを伴う字幕作成サービスについては [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">これまで動画の字幕作成については、何度か記事を投稿してきましたが、その中で感じた課題のいくつかを克服するアプリを、自作しましたので紹介します。</p>



<p class="wp-block-paragraph">別の投稿で紹介したいと思いますが、字幕起こしを伴う字幕作成サービスについては日進月歩でして、どんどん高品質になっています。大きく分けると、ローカルで処理する方法（大抵は無料でできる）と外部サーバーで処理する方法（大抵は使用量課金かサブスク提供)がありまして、外部サーバー利用の方については激しい競争下にあるためかなり高品質で、生成AI系企業のサービスが群を抜いています。</p>



<p class="wp-block-paragraph">最近、AIの進展で、動画をアップロードするだけで音声を取り出しその内容をAIに理解させることができるようになってはきましたが、やはり字幕ファイルによる文字情報で内容を理解させる方が圧倒的に迅速で正確です。しかも動画からの読み込みは多大なリソースを消費するので、追加料金も発生するかも知れません。そんな意味で、ブログ投稿をAIに学習させる場合も、動画には字幕ファイルを別添して貼り付けるようにして、字幕ファイルからの情報吸い上げを重視すべきだと私は思います。</p>



<p class="wp-block-paragraph">そんな中で、ここで紹介する方法は、<strong>無音声の動画に解説用の字幕をサクッと付ける</strong>場合、に役立つものです。</p>


<div class="sc-dynamic-embed">
  <style>
  /* リンクの見た目を整える */
  .sc-dynamic-embed .sc-link-container { 
      display: flex; 
      margin-bottom: 10px; 
      flex-wrap: wrap; 
      align-items: center; 
  }
  .sc-dynamic-embed .sc-link { margin-bottom: 0; }
  .sc-dynamic-embed .sc-link a {
    font-size: 15px; 
    font-weight: normal;
    text-decoration: underline;
    color: #0073aa;
  }
  .sc-dynamic-embed .sc-link a:hover { text-decoration: none; color: #000; }
  
  @media (max-width: 500px) {
    .sc-dynamic-embed .sc-link a { font-size: 13px; }
  }

  .sc-dynamic-embed .imk-line {
      display: inline-block;
      width: 100%;
      border-radius: 2px;
      transition: background-color 0.1s;
  }

  .sc-dynamic-embed #subtitleOverlay,
  .sc-dynamic-embed #scSubtitleOverlay,
  .sc-dynamic-embed .overlay-cue,
  .sc-dynamic-embed .band {
      display: none !important;
      opacity: 0 !important;
      visibility: hidden !important;
      pointer-events: none !important;
  }
  </style>

  <div class="sc-link-container">
    <p class="sc-link">
      <!-- ★環境依存排除：固定ドメインをルート相対パスへ置換完了 -->
      <a href="/ds62/?drid=58_2" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <!-- ★環境依存排除：[dynamic_external_html] にルート相対パスを指定し、functions.phpにURL動的解決を委ねる設計 -->
  <style>
            :root{ --dr5emd-max: 1920px; }
            #subtitleOverlay, #scSubtitleOverlay, .overlay-cue, .band { display: none !important; opacity: 0 !important; }
            .imk-line { display: inline-block; width: 100%; border-radius: 2px; transition: background-color 0.1s; }
            .dr5emd-sublist details > p{ height:200px; overflow:auto; background-color:#EDF7FF; padding:2px 6px; margin:0; box-shadow:3px 3px 4px black; position: relative; }
            .dr5emd-sublist details > summary{ padding:2px 6px; width:100%; background-color:#ddd; border:none; box-shadow:3px 3px 4px black; cursor:pointer; list-style:none; }
            
                .video-wrap{position:relative;width:100%;margin:0 auto}
                #myVideo{width:100%;height:auto;min-height:200px;display:block;background:#000;}</style><div class="dr5emd-container"><div class="video-wrap"><video id="myVideo" controls poster="https://imakat.com/rd.php?id=TnfyWc5U.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=PjGsFuO9.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=6oBnT3B3.vtt" label="日本語" srclang="ja" kind="subtitles"></video></div><div class="dr5emd-sublist"><details translate="yes"><summary translate="yes">字幕一覧(クリック)</summary> <p translate="yes">
(<a href="#" class="imk-cue" data-seek="0:00" translate="no">00:00:00</a>) 1) 〜字幕かんたん作成〜<br>
(<a href="#" class="imk-cue" data-seek="0:20" translate="no">00:00:20</a>) 2) 今回は、Macで簡単に、動画の字幕ファイルを作成できる『字幕かんたん作成』これを紹介します。<br>
(<a href="#" class="imk-cue" data-seek="0:35" translate="no">00:00:35</a>) 3) 動画に字幕を付けるのに、編集ソフトで、コツコツと、タイムスタンプを入れるのは、とても大変ですよね。<br>
(<a href="#" class="imk-cue" data-seek="0:51" translate="no">00:00:51</a>) 4) このアプリを使えば、QuickTimePlayerで動画を再生しながら、簡単に、字幕を打ち込んでいけます。<br>
(<a href="#" class="imk-cue" data-seek="1:00" translate="no">00:01:00</a>) 5) お金は掛かりません。<br>
(<a href="#" class="imk-cue" data-seek="1:03" translate="no">00:01:03</a>) 6) Macに備わった標準アプリだけで動きます。<br>
(<a href="#" class="imk-cue" data-seek="1:12" translate="no">00:01:12</a>) 7) 通常の字幕作成では、動画の再生時間を確認して、開始時刻と終了時刻を、VTTファイルへ入力します。<br>
(<a href="#" class="imk-cue" data-seek="1:27" translate="no">00:01:27</a>) 8) サンプルをお見せします。<br>
(<a href="#" class="imk-cue" data-seek="1:33" translate="no">00:01:33</a>) 9) 例えば、このような文字列があります。<br>
(<a href="#" class="imk-cue" data-seek="1:37" translate="no">00:01:37</a>) 10) これはVTTファイルです。<br>
(<a href="#" class="imk-cue" data-seek="1:41" translate="no">00:01:41</a>) 11) 最初に、順番が入ります。　　<br>
(<a href="#" class="imk-cue" data-seek="1:46" translate="no">00:01:46</a>) 12) 次に、時　分　秒　ミリ秒、この基準に従った開始時刻<br>
(<a href="#" class="imk-cue" data-seek="1:54" translate="no">00:01:54</a>) 13) それと、同じくその基準の終了時刻を入れます。<br>
(<a href="#" class="imk-cue" data-seek="2:01" translate="no">00:02:01</a>) 14) 真ん中のところに、スペース１つ、--不等号 またひとつスペースを入れます。<br>
(<a href="#" class="imk-cue" data-seek="2:13" translate="no">00:02:13</a>) 15) その下にこうしたテキスト行を入れます。<br>
(<a href="#" class="imk-cue" data-seek="2:19" translate="no">00:02:19</a>) 16) これがVTT記述の基本形です。<br>
(<a href="#" class="imk-cue" data-seek="2:25" translate="no">00:02:25</a>) 17) このように自分でTextEditに打てば、正確にできるのですが、　　<br>
(<a href="#" class="imk-cue" data-seek="2:34" translate="no">00:02:34</a>) 18) 手間が多く、時間もかかってしまいます。<br>
(<a href="#" class="imk-cue" data-seek="2:43" translate="no">00:02:43</a>) 19) そこで開発したのが、この『字幕かんたん作成』です。<br>
(<a href="#" class="imk-cue" data-seek="2:50" translate="no">00:02:50</a>) 20) AutomatorとAppleScript_Objective-Cを組み合わせて、<br>
(<a href="#" class="imk-cue" data-seek="2:57" translate="no">00:02:57</a>) 21) QuickTimePlayerとTextEditを連携させる、<br>
(<a href="#" class="imk-cue" data-seek="3:03" translate="no">00:03:03</a>) 22) そのことで、シンプルに、効率よく、字幕ファイルを作ることができます。<br>
(<a href="#" class="imk-cue" data-seek="3:14" translate="no">00:03:14</a>) 23) その仕組みをフロー図によって説明します。<br>
(<a href="#" class="imk-cue" data-seek="3:29" translate="no">00:03:29</a>) 24) まず、FinderでVTTファイルを右クリックします。<br>
(<a href="#" class="imk-cue" data-seek="3:41" translate="no">00:03:41</a>) 25) するとクイックアクションの中にある、今回のアプリ 「字幕かんたん作成」が起動します。<br>
(<a href="#" class="imk-cue" data-seek="3:51" translate="no">00:03:51</a>) 26) スクリプトが動作し、黄色の字幕表示画面(HUDと呼びますが)それと、　<br>
(<a href="#" class="imk-cue" data-seek="4:00" translate="no">00:04:00</a>) 27) 入力Boxが表示されます。<br>
(<a href="#" class="imk-cue" data-seek="4:09" translate="no">00:04:09</a>) 28) 字幕の入力先は、macOS標準のTextEditを使います。 <br>
(<a href="#" class="imk-cue" data-seek="4:17" translate="no">00:04:17</a>) 29) 入力したテキストは、QuickTimePlayerの再生位置の時刻、タイムスタンプ、それらと一緒にVTTファイルに記入されます。<br>
(<a href="#" class="imk-cue" data-seek="4:33" translate="no">00:04:33</a>) 30) QuickTimePlayerは再生位置を提供し、NSTimerが、それに対応する時刻を、定期的にゲットします。　<br>
(<a href="#" class="imk-cue" data-seek="4:48" translate="no">00:04:48</a>) 31) 終了する場合は、終了ボタンを押すと、タイマーが止まり、HUDが閉じ、メモリーが解放されて、処理が終了します。<br>
(<a href="#" class="imk-cue" data-seek="5:08" translate="no">00:05:08</a>) 32) それでは、実際の動きをご覧ください。 <br>
(<a href="#" class="imk-cue" data-seek="5:13" translate="no">00:05:13</a>) 33) 動画の例として、「ガラポンTVの故障」を取り上げます。<br>
(<a href="#" class="imk-cue" data-seek="5:21" translate="no">00:05:21</a>) 34) 一度、動画だけをみてください。（約1分） <br>
(<a href="#" class="imk-cue" data-seek="6:28" translate="no">00:06:28</a>) 35) 動画を見ただけでは、何を伝えたいのか、さっぱりわかりませんね。<br>
(<a href="#" class="imk-cue" data-seek="6:36" translate="no">00:06:36</a>) 36) そこで、字幕を、作成していきます。<br>
(<a href="#" class="imk-cue" data-seek="6:43" translate="no">00:06:43</a>) 37) それでは、作業をやってみます。<br>
(<a href="#" class="imk-cue" data-seek="6:49" translate="no">00:06:49</a>) 38) まず、動画を右上の方に置いておきます。<br>
(<a href="#" class="imk-cue" data-seek="6:57" translate="no">00:06:57</a>) 39) すでにvttファイル名は付けてありますが、中身をクリアして作業をしてみます。<br>
(<a href="#" class="imk-cue" data-seek="7:05" translate="no">00:07:05</a>) 40) このvttファイルの上で、右クリックして、クイックアクションで、「字幕かんたん作成」を立ち上げます。<br>
(<a href="#" class="imk-cue" data-seek="7:32" translate="no">00:07:32</a>) 41) こんなように立ち上がります。<br>
(<a href="#" class="imk-cue" data-seek="7:36" translate="no">00:07:36</a>) 42) このHUDをQuickTimePlayerの下に置いて、　<br>
(<a href="#" class="imk-cue" data-seek="7:42" translate="no">00:07:42</a>) 43) 実際に、テキストを入れていきます。<br>
(<a href="#" class="imk-cue" data-seek="8:03" translate="no">00:08:03</a>) 44) ガラポンTVが異常です。全ランプが点滅を繰り返しています。<br>
(<a href="#" class="imk-cue" data-seek="8:18" translate="no">00:08:18</a>) 45) このように打ってOKを押します。<br>
(<a href="#" class="imk-cue" data-seek="8:26" translate="no">00:08:26</a>) 46) 継続秒数を入れます。<br>
(<a href="#" class="imk-cue" data-seek="8:41" translate="no">00:08:41</a>) 47) 1番目が出来上がります。<br>
(<a href="#" class="imk-cue" data-seek="8:55" translate="no">00:08:55</a>) 48) 背面の電源をOFFにしました。<br>
(<a href="#" class="imk-cue" data-seek="9:11" translate="no">00:09:11</a>) 49) OK<br>
(<a href="#" class="imk-cue" data-seek="9:13" translate="no">00:09:13</a>) 50) 4秒<br>
(<a href="#" class="imk-cue" data-seek="9:17" translate="no">00:09:17</a>) 51) 2番目が入ります。<br>
(<a href="#" class="imk-cue" data-seek="9:20" translate="no">00:09:20</a>) 52) HUDにも表示されます。<br>
(<a href="#" class="imk-cue" data-seek="9:40" translate="no">00:09:40</a>) 53) 外付けHDDを外しました。<br>
(<a href="#" class="imk-cue" data-seek="10:02" translate="no">00:10:02</a>) 54) 前の部分で、説明を入れるのを忘れたとしても、戻ることができます。<br>
(<a href="#" class="imk-cue" data-seek="10:16" translate="no">00:10:16</a>) 55) この場所で、ランプは消えました、と入れます。<br>
(<a href="#" class="imk-cue" data-seek="10:35" translate="no">00:10:35</a>) 56) 3番目として、ランプが消えた、が飛び込みます。<br>
(<a href="#" class="imk-cue" data-seek="10:46" translate="no">00:10:46</a>) 57) このような形で、入力が終了し、再生した時に、字幕がタイミングよく表示されることを確認する必要があります。その後で実際は終了します。<br>
(<a href="#" class="imk-cue" data-seek="11:02" translate="no">00:11:02</a>) 58) それでは、動画と、完成した字幕を、同時に表示してみましょう。<br>
(<a href="#" class="imk-cue" data-seek="11:17" translate="no">00:11:17</a>) 59) 字幕があることによって、その、ガラポンTVが故障している、そのように判断した理由が、より明確に、伝わるようになります。<br>
(<a href="#" class="imk-cue" data-seek="12:10" translate="no">00:12:10</a>) 60) この後、ガラポンTV社に報告して、迅速に、交換が完了しました。<br>
(<a href="#" class="imk-cue" data-seek="12:24" translate="no">00:12:24</a>) 61) このように『字幕かんたん作成』は、AutomatorとAppleScript_Objective-Cを使って、<br>
(<a href="#" class="imk-cue" data-seek="12:33" translate="no">00:12:33</a>) 62) QuickTimePlayerとTextEditを組み合わせることによって、 　<br>
(<a href="#" class="imk-cue" data-seek="12:40" translate="no">00:12:40</a>) 63) 面倒な字幕作成を、シンプルに、効率的に、してくれます。 <br>
(<a href="#" class="imk-cue" data-seek="12:46" translate="no">00:12:46</a>) 64) このスクリプトは、ブログ内で公開します。<br>
(<a href="#" class="imk-cue" data-seek="12:50" translate="no">00:12:50</a>) 65) 字幕を、できるだけ手軽に付けたい、そう思う方は、ぜひ活用してみてください。<br>
</p> </details>
<style>
details { font: 16px "Open Sans", Calibri, sans-serif; width: 100%; }
details > summary { padding: 2px 6px; width: 100%; background-color: #ddd; border: none; box-shadow: 3px 3px 4px black; cursor: pointer; list-style: none; }
details > p { font: 14px "Open Sans", Calibri, sans-serif; height:150px; overflow: scroll; background-color: #EDF7FF; padding: 2px 6px; margin: 0; box-shadow: 3px 3px 4px black; }
</style>
</div></div>            <script>
            document.addEventListener("DOMContentLoaded", function(){
                var video = document.getElementById("myVideo");
                if(!video) return;

                            });
            </script>
            

  <script>
  (function(){
    var me = document.currentScript;
    var wrapper = me ? me.closest('.sc-dynamic-embed') : null;

    function protectVideo() {
      var target = wrapper ? wrapper : document;
      var mediaEls = target.querySelectorAll('video, audio');
      mediaEls.forEach(function(v){
        if(v.dataset.protected === 'true') return;
        v.dataset.protected = 'true';
        v.setAttribute('controlsList', 'nodownload');
        v.oncontextmenu = function() { return false; };
        v.addEventListener('contextmenu', function(e){ e.preventDefault(); return false; }, false);
      });
    }

    function initSubtitles() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video, audio');
      var listContainer = target.querySelector('details > p');
      
      if (!video) return false; 
      
      if (video.dataset.subInit === 'true') return true; 
      video.dataset.subInit = 'true';

      var oldOverlay = target.querySelector('#subtitleOverlay') || target.querySelector('#scSubtitleOverlay');
      if (oldOverlay) {
          oldOverlay.style.setProperty('display', 'none', 'important');
          oldOverlay.innerHTML = ''; 
      }

      // 全画面・PiP・iOS全画面の判定（dr52.php準拠＋Safari presentationMode完全対応）
      function isSpecialMode() {
        var fsEl = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
        var isFs = !!fsEl;
        var isWebkitFs = (video.webkitPresentationMode === "fullscreen") || !!video.webkitDisplayingFullscreen;
        var isPiP = !!(document.pictureInPictureElement && document.pictureInPictureElement === video) || (video.webkitPresentationMode === "picture-in-picture");
        return isFs || isWebkitFs || isPiP;
      }

      // 字幕トラックの表示/非表示を更新
      function updateTrackMode() {
        var hasList = (listContainer !== null);
        var desiredMode = (isSpecialMode() || !hasList) ? "showing" : "hidden";
        try {
          if (video.textTracks && video.textTracks.length > 0) {
            for (var i = 0; i < video.textTracks.length; i++) {
              var track = video.textTracks[i];
              if ((track.kind === 'subtitles' || track.kind === 'captions') && track.mode !== desiredMode) {
                track.mode = desiredMode;
              }
            }
          }
        } catch(e) {}
      }

      // 初回の反映
      updateTrackMode();

      // 各種全画面・PiP切り替えイベントの即時検知リスナーを登録（video本体・document双方を監視）
      ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'].forEach(function(evtName) {
        document.addEventListener(evtName, updateTrackMode, false);
        video.addEventListener(evtName, updateTrackMode, false);
      });
      video.addEventListener('webkitbeginfullscreen', updateTrackMode, false);
      video.addEventListener('webkitendfullscreen', updateTrackMode, false);
      video.addEventListener('webkitpresentationmodechanged', updateTrackMode, false);
      video.addEventListener('enterpictureinpicture', updateTrackMode, false);
      video.addEventListener('leavepictureinpicture', updateTrackMode, false);
      video.addEventListener('play', updateTrackMode, false);
      video.addEventListener('pause', updateTrackMode, false);

      // 字幕一覧が存在する場合のみ、リストのフォーマット処理を行う
      if (listContainer) {
          var detailsEl = target.querySelector("details");
          if (detailsEl) {
              detailsEl.open = true; 
              var summaryEl = detailsEl.querySelector("summary");
              if (summaryEl) summaryEl.textContent = "字幕(シーン)はここをクリック";
          }

          if (!listContainer.dataset.formatted) {
              var html = listContainer.innerHTML;
              var lines = html.split(/<br\s*\/?>/i);
              var newHtml = "";
              for(var j=0; j<lines.length; j++) {
                  if(lines[j].trim() === "") continue;
                  newHtml += "<span class='imk-line'>" + lines[j] + "</span><br>";
              }
              listContainer.innerHTML = newHtml;
              listContainer.dataset.formatted = "true";
          }

          var rootSublist = target.querySelector(".dr5-sublist") || listContainer.parentElement;
          if (rootSublist) {
            rootSublist.addEventListener("click", function(e){
              var a = e.target.closest && e.target.closest("a.imk-cue[data-seek]");
              if(!a) return;
              e.preventDefault();
              var ts = a.getAttribute("data-seek");
              if(!ts) return;
              var p = ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
              var sec = (p.length===2) ? p[0]*60+p[1] : (p.length===3 ? p[0]*3600+p[1]*60+p[2]: null);
              if(sec==null) return;
              try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
            });
          }
      }

      // 再生中（時間進行時）の追従とハイライト更新
      video.addEventListener("timeupdate", function(){
        updateTrackMode();

        var hasList = (listContainer !== null);
        if (!hasList) return;

        var cues = listContainer.querySelectorAll("a.imk-cue");
        if(cues.length === 0) return;
        var cur = video.currentTime;
        var activeA = null;

        for(var i=0; i<cues.length; i++){
            var ts = cues[i].getAttribute("data-seek");
            if(!ts) continue;
            var p = ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
            var t = (p.length===2) ? p[0]*60+p[1] : (p.length===3 ? p[0]*3600+p[1]*60+p[2] : null);
            if(t !== null && cur >= t - 0.5){ activeA = cues[i]; } 
            else if(t > cur){ break; }
        }

        if(activeA){
            var activeLine = activeA.closest(".imk-line");
            if(!activeLine) activeLine = activeA;

            if(activeLine.classList.contains("active-hl")) return;

            var allLines = listContainer.querySelectorAll(".imk-line");
            for(var k=0; k<allLines.length; k++) {
                allLines[k].classList.remove("active-hl");
                allLines[k].removeAttribute("style"); 
            }
            var allLinks = listContainer.querySelectorAll("a");
            for(var m=0; m<allLinks.length; m++) {
                allLinks[m].classList.remove("active-hl");
                allLinks[m].removeAttribute("style"); 
            }

            activeLine.classList.add("active-hl");
            activeLine.style.setProperty("background-color", "#ffff00", "important");
            activeLine.style.setProperty("color", "red", "important");
            activeLine.style.setProperty("font-weight", "normal", "important");
            
            var newLinks = activeLine.querySelectorAll("a");
            for(var n=0; n<newLinks.length; n++) {
                newLinks[n].style.setProperty("color", "red", "important");
                newLinks[n].style.setProperty("text-decoration", "none", "important");
            }

            if(listContainer.offsetParent !== null){
                var containerRect = listContainer.getBoundingClientRect();
                var activeRect = activeLine.getBoundingClientRect();
                var targetScroll = listContainer.scrollTop + (activeRect.top - containerRect.top) - (listContainer.clientHeight / 2) + (activeLine.clientHeight / 2);
                listContainer.scrollTo({ top: targetScroll, behavior: "smooth" });
            }
        }
      });

      return true;
    }

    var checks = 0;
    var checkTimer = setInterval(function(){
      protectVideo();
      var success = initSubtitles();
      checks++;
      if (success || checks > 20) { 
        clearInterval(checkTimer);
      }
    }, 500); 

    if (!window.scStopAndGo) {
      window.scStopAndGo = function(event, link){
        try{
          var mediaEls = document.querySelectorAll('video, audio');
          mediaEls.forEach(function(m){
            try{
              if (!m.paused) m.pause();
              if (document.pictureInPictureElement === m && document.exitPictureInPicture) {
                document.exitPictureInPicture().catch(function(){});
              }
            }catch(e){}
          });
        }finally{
          event.preventDefault();
          setTimeout(function(){
            if (link.target === '_blank') {
              window.open(link.href, '_blank');
            } else {
              window.location.href = link.href;
            }
          }, 50);
        }
        return false;
      };
    }
  })();
  </script>
</div>



<h3 class="wp-block-heading">ポイント解説</h3>



<p class="wp-block-paragraph"><strong>１　VTTで編集ができます</strong>：<br>簡易な字幕作成ツールとしては、「字幕工房」が優秀であるという見方は変わりませんが、SRTファイルでの書き出しです。私はWordPressブログに掲載する動画が殆どですので、毎回VTTファイルへ変換することになり面倒です。「字幕工房」に限らず、動画作成アプリで出力される字幕は、殆どがSRTファイルなのですね。</p>



<p class="wp-block-paragraph"><strong>２　ローカルファイルの更新をそのまま反映します</strong>：<br>動画共有サービスに共通することですが、字幕もそのほかのメディアアセットもローカルからアップロードした時点で切り離されてしまうので、ローカルファイルを更新しても自動では反映されません。図の下段は、今運用しているマイ・メディアライブラリの機能ですが、ここでもその威力が発揮されます。ローカルでVTTファイルをTextEditで開いて手直しすれば、そのまま自動的に、ブログの表示が変更されます（数分のタイムラグはあり）。</p>



<a rel="noopener" target="_blank" href="https://docs.google.com/drawings/d/1GXuQ_5Vtufv7YH8_SwkfyAMHxzctFyzNb4J4dmJ-MDc/edit?usp=sharing" 
>
<img decoding="async" src="https://docs.google.com/drawings/d/e/2PACX-1vSpM7uCAK9FehOqeogKeJoeCMs1929xNU45hsx1tk_CUlhO9S8CH30BgcnNMx4URJ6nH83dm9uGP2lS/pub?w=1440&#038;h=1080"
></a>



<p class="wp-block-paragraph"><strong>３　停止位置の時刻を自動で取得します</strong>：<br>QuickTimePlayerで動画を停止した場所の時刻(時点)を自動で取得して、テキストとともに、VTTファイルへ書き込みます。さらに、書き込んだ情報を読み込んで、HUD(ディスプレイ)に即座に表示します。</p>



<p class="wp-block-paragraph">主な特徴点は以上ですが、かんたんな点では、「字幕工房」を上回っています。</p>



<p class="wp-block-paragraph"><strong>４　わずか2ステップ</strong>：</p>



<p class="wp-block-paragraph"><strong>①対象の動画をクリックして開く</strong>(デフォルトはQuickTimePlayer)。</p>



<p class="wp-block-paragraph"><strong>②フォルダの上で右クリックして、「字幕かんたん作成」を立ち上げ、VTTファイルを作る</strong>。</p>



<p class="wp-block-paragraph"><strong>だけ</strong>です。</p>



<p class="wp-block-paragraph"><strong>５　キビキビと動きます：</strong><br>字幕作成アプリを探すと、ブラウザアプリが主流になっています。その<strong>ブラウザアプリですが、欠点は、「もったりヌルヌルした動作</strong>」にあります。iPad iPhoneでは仕方ないにしても、Macでも感じるところがあるので、それはなんか疲れるわけですね。QuickTimePlayer、TextEdit、標準スクリプト、そういった標準搭載ツールは、実にキビキビと動きます。</p>



<h3 class="wp-block-heading">実装の手順</h3>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td style="width:10%">(1)</td><td>下の青色のボタンから、スクリプト(txtファイル)をダウンロードします。</td></tr><tr><td style="width:10%">(2)</td><td>生成AI( Gemini、ChatGPTなど)を利用すると簡単です。AIのチャット欄に、<br>①ダウンロードしたスクリプトをアップロードします。<br>②以下を記述します。<br>「私のMacに、添付したスクリプトを実装する手順を作ってください。それをtxt形式で提供ください。<br>私のMacは、[機種名][OSバージョン]です。よろしくお願いします。」</td></tr><tr><td style="width:10%">(3)</td><td>提供された、実装手順を記載したtxtファイルに基づき、生成AIとやりとりしながら、実装してください。<br>※txtファイルを開いたまま生成AIとやり取りし、修正後のtxtを再度送って「これで大丈夫ですか」と確認する方法を取れば、更新内容を逐一手元に残せるため、作業の履歴管理として便利で安心です。</td></tr><tr><td colspan="2"><a href="https://imakat.com/?pubtxt=字幕かんたん作成workflow_pub.txt" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=zrU95MiY.png" alt="" style="width:120px; height:auto;"></a></td></tr></tbody></table></figure>



<p class="wp-block-paragraph">以上です。<br><br></p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">27063</post-id>	</item>
		<item>
		<title>【WordPress】Apple Windowsの字幕表示の設定について</title>
		<link>https://imakat.com/2024/10/02/23205/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Tue, 01 Oct 2024 21:44:00 +0000</pubDate>
				<category><![CDATA[字幕作成]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[iPad]]></category>
		<category><![CDATA[スマホ]]></category>
		<category><![CDATA[字幕]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=23205</guid>

					<description><![CDATA[字幕の作成に関する問題ですが、それは、規格が統一されていないことに尽きる、と言っていいです。動画を作成する人は、それに振り回されています。 その字幕作成の悩みの一つが、スマホなどの小さな画面を使って、解説の動画を視聴して [&#8230;]]]></description>
										<content:encoded><![CDATA[<div class="sc-dynamic-embed">
  <style>
  /* リンクの見た目を整える */
  .sc-dynamic-embed .sc-link-container { 
      display: flex; 
      margin-bottom: 10px; 
      flex-wrap: wrap; 
      align-items: center; 
  }
  .sc-dynamic-embed .sc-link { margin-bottom: 0; }
  .sc-dynamic-embed .sc-link a {
    font-size: 15px; 
    font-weight: normal;
    text-decoration: underline;
    color: #0073aa;
  }
  .sc-dynamic-embed .sc-link a:hover { text-decoration: none; color: #000; }
  
  @media (max-width: 500px) {
    .sc-dynamic-embed .sc-link a { font-size: 13px; }
  }

  .sc-dynamic-embed .imk-line {
      display: inline-block;
      width: 100%;
      border-radius: 2px;
      transition: background-color 0.1s;
  }

  .sc-dynamic-embed #subtitleOverlay,
  .sc-dynamic-embed #scSubtitleOverlay,
  .sc-dynamic-embed .overlay-cue,
  .sc-dynamic-embed .band {
      display: none !important;
      opacity: 0 !important;
      visibility: hidden !important;
      pointer-events: none !important;
  }
  </style>

  <div class="sc-link-container">
    <p class="sc-link">
      <!-- ★環境依存排除：固定ドメインをルート相対パスへ置換完了 -->
      <a href="/ds62/?drid=33" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <!-- ★環境依存排除：[dynamic_external_html] にルート相対パスを指定し、functions.phpにURL動的解決を委ねる設計 -->
  <style>
            :root{ --dr5emd-max: 1920px; }
            #subtitleOverlay, #scSubtitleOverlay, .overlay-cue, .band { display: none !important; opacity: 0 !important; }
            .imk-line { display: inline-block; width: 100%; border-radius: 2px; transition: background-color 0.1s; }
            .dr5emd-sublist details > p{ height:200px; overflow:auto; background-color:#EDF7FF; padding:2px 6px; margin:0; box-shadow:3px 3px 4px black; position: relative; }
            .dr5emd-sublist details > summary{ padding:2px 6px; width:100%; background-color:#ddd; border:none; box-shadow:3px 3px 4px black; cursor:pointer; list-style:none; }
            
                .video-wrap{position:relative;width:100%;margin:0 auto}
                #myVideo{width:100%;height:auto;min-height:200px;display:block;background:#000;}</style><div class="dr5emd-container"><div class="video-wrap"><video id="myVideo" controls poster="https://imakat.com/rd.php?id=gTxuADx9.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=GUIDgaF4.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=j0ry31nq.vtt" label="日本語" srclang="ja" kind="subtitles"></video></div><div class="dr5emd-sublist"><details translate="yes"><summary translate="yes">字幕一覧(クリック)</summary> <p translate="yes">
(<a href="#" class="imk-cue" data-seek="0:00" translate="no">00:00:00</a>)  Apple Windowsの字幕表示の設定について<br>
(<a href="#" class="imk-cue" data-seek="0:10" translate="no">00:00:10</a>)  (1) Macの字幕設定方法　<br>
(<a href="#" class="imk-cue" data-seek="0:20" translate="no">00:00:20</a>)  まず最初に、WordPressブログを視聴する方への共通の対策です。<br>
(<a href="#" class="imk-cue" data-seek="0:26" translate="no">00:00:26</a>)  WordPressのカスタマイズ->追加CSSを開きます。<br>
(<a href="#" class="imk-cue" data-seek="0:30" translate="no">00:00:30</a>)  ご覧の部分のCSSを追加します。<br>
(<a href="#" class="imk-cue" data-seek="0:34" translate="no">00:00:34</a>)  このコードはブログへ添付します。字幕の背景を黒にする。字幕の背景の透過を50%にする。字幕の文字色を白にする。文字に少々影を追加する。以上4点です。<br>
(<a href="#" class="imk-cue" data-seek="0:44" translate="no">00:00:44</a>)  ここから各デバイスへの設定になります。設定は、各デバイスの「アクセシビリティ」へ行います。<br>
(<a href="#" class="imk-cue" data-seek="0:51" translate="no">00:00:51</a>)  最初はMacからです。「システム設定」->「アクセシビリティ」->「字幕」<br>
(<a href="#" class="imk-cue" data-seek="0:58" translate="no">00:00:58</a>)  先頭にある「透明な背景」を選択すると、上段にイメージが表示されます。これでいいので、このままにします。<br>
(<a href="#" class="imk-cue" data-seek="1:16" translate="no">00:01:16</a>)  MacでWordPressプレーヤーを再生します。<br>
(<a href="#" class="imk-cue" data-seek="1:28" translate="no">00:01:28</a>)  背景黒で透明度50%ですが、これが読みやすく、かつ下地も、まずまず見えます。<br>
(<a href="#" class="imk-cue" data-seek="1:41" translate="no">00:01:41</a>)  Macは以上です。<br>
(<a href="#" class="imk-cue" data-seek="1:51" translate="no">00:01:51</a>)  (2)Windowsの字幕設定方法　<br>
(<a href="#" class="imk-cue" data-seek="2:01" translate="no">00:02:01</a>)  Windowsは、「設定」->「アクセシビリティ」->「字幕」->「字幕のスタイル」と入ります。<br>
(<a href="#" class="imk-cue" data-seek="2:10" translate="no">00:02:10</a>)  Windowsは、「ビデオの設定を優先」するか否かの選択がありませんが、要するに、ここで指定した項目は上書きされます。<br>
(<a href="#" class="imk-cue" data-seek="2:17" translate="no">00:02:17</a>)  「テキスト」は白で不透明度100%、サイズは中、フォントはプロポーショナルゴシック。<br>
(<a href="#" class="imk-cue" data-seek="2:26" translate="no">00:02:26</a>)  「背景」は黒で不透明度75%<br>
(<a href="#" class="imk-cue" data-seek="2:33" translate="no">00:02:33</a>)  問題は、50%がないのですね。これだと、透けて見えないのではないかと思います。まあ仕方ないです。<br>
(<a href="#" class="imk-cue" data-seek="2:51" translate="no">00:02:51</a>)  WindowsでWordPressプレーヤーを再生します。<br>
(<a href="#" class="imk-cue" data-seek="3:05" translate="no">00:03:05</a>)  色々いじってみましたが、これが一番まともです。<br>
(<a href="#" class="imk-cue" data-seek="3:20" translate="no">00:03:20</a>)  以上がWindowsの設定です。<br>
(<a href="#" class="imk-cue" data-seek="3:31" translate="no">00:03:31</a>)  (3)iPadの字幕設定方法　<br>
(<a href="#" class="imk-cue" data-seek="3:42" translate="no">00:03:42</a>)  iPadは、「設定」->「アクセシビリティ」　->「標準字幕とバリアフリー字幕」<br>
(<a href="#" class="imk-cue" data-seek="3:51" translate="no">00:03:51</a>)  iPadは、「スタイル」で、一番上の「透明な背景」を選びます。上のイメージでは文字が小さく感じますが、実際は適当な大きさになっています。<br>
(<a href="#" class="imk-cue" data-seek="4:09" translate="no">00:04:09</a>)  iPadで、WordPressプレーヤーを再生します。<br>
(<a href="#" class="imk-cue" data-seek="4:20" translate="no">00:04:20</a>)  Macとほぼ同じ表示レベルです。読みやすく問題ないです。<br>
(<a href="#" class="imk-cue" data-seek="4:31" translate="no">00:04:31</a>)  iPadの設定は以上です。<br>
(<a href="#" class="imk-cue" data-seek="4:41" translate="no">00:04:41</a>)  (4) iPhoneの字幕設定方法　<br>
(<a href="#" class="imk-cue" data-seek="4:50" translate="no">00:04:50</a>)  (4-1) 枠内字幕と枠外字幕の比較<br>
(<a href="#" class="imk-cue" data-seek="4:58" translate="no">00:04:58</a>)  左側は、字幕を枠の外に表示して、右側は字幕を枠の中に表示しています。<br>
(<a href="#" class="imk-cue" data-seek="5:08" translate="no">00:05:08</a>)  枠外表示は、スマホなどの画面が小さい時、有効だと思います。<br>
(<a href="#" class="imk-cue" data-seek="5:21" translate="no">00:05:21</a>)  枠内表示は、文字が邪魔になるので、文字の背景を透過させる設定が求められます。<br>
(<a href="#" class="imk-cue" data-seek="5:37" translate="no">00:05:37</a>)  (4-2)字幕の背景の透明度を上げる設定<br>
(<a href="#" class="imk-cue" data-seek="5:46" translate="no">00:05:46</a>)  「設定」->「アクセシビリティ」　<br>
(<a href="#" class="imk-cue" data-seek="5:59" translate="no">00:05:59</a>)  「標準字幕とバリアフリー字幕」<br>
(<a href="#" class="imk-cue" data-seek="6:07" translate="no">00:06:07</a>)  「スタイル」->「新規スタイルを作成」を選択すると「スタイル1」ができます。<br>
(<a href="#" class="imk-cue" data-seek="6:14" translate="no">00:06:14</a>)  「スタイル1」を選択した状態で、右上の「編集」をクリックします。<br>
(<a href="#" class="imk-cue" data-seek="6:21" translate="no">00:06:21</a>)  「フォント」->「システムフォント(モノ)」->「ビデオの設定を優先」をオフ   ※オンにすると何故か明朝体になります。<br>
(<a href="#" class="imk-cue" data-seek="6:36" translate="no">00:06:36</a>)  「サイズ」->「大」を選択。 スマホは画面が小さいので、読もうと思うなら「大」がベターです。<br>
(<a href="#" class="imk-cue" data-seek="6:44" translate="no">00:06:44</a>)  文字の「カラー」->「ホワイト（デフォルト）」を選択。<br>
(<a href="#" class="imk-cue" data-seek="6:50" translate="no">00:06:50</a>)  背景の「カラー」->「ブラック（デフォルト）」->「ビデオの設定を優先」をオフ<br>
(<a href="#" class="imk-cue" data-seek="7:09" translate="no">00:07:09</a>)   背景の「不透明度」->「50%（デフォルト）」->「ビデオの設定を優先」をオフ<br>
(<a href="#" class="imk-cue" data-seek="7:21" translate="no">00:07:21</a>)   テキストの「不透明度」->「不透明（デフォルト）」<br>
(<a href="#" class="imk-cue" data-seek="7:37" translate="no">00:07:37</a>)   以上で、iPhoneの字幕の設定は終了です。<br>
(<a href="#" class="imk-cue" data-seek="7:46" translate="no">00:07:46</a>)  (5) VimeoやYouTubeの字幕の設定は、マニュアル操作。<br>
(<a href="#" class="imk-cue" data-seek="7:55" translate="no">00:07:55</a>)  Vimeoを例に、字幕を表示します。<br>
(<a href="#" class="imk-cue" data-seek="8:03" translate="no">00:08:03</a>)  デフォルトでは、黒の背景に白の文字で、透過度ゼロです。<br>
(<a href="#" class="imk-cue" data-seek="8:09" translate="no">00:08:09</a>)  どうすればいいか、ウロウロしています。<br>
(<a href="#" class="imk-cue" data-seek="8:16" translate="no">00:08:16</a>)  まあ、真っ黒も、悪くはないけど。<br>
(<a href="#" class="imk-cue" data-seek="8:26" translate="no">00:08:26</a>)  「CC」のボタンをクリック->「カスタマイズ」->「背景」<br>
(<a href="#" class="imk-cue" data-seek="8:34" translate="no">00:08:34</a>)  不透明度50%にします。<br>
(<a href="#" class="imk-cue" data-seek="8:52" translate="no">00:08:52</a>)  これなら、まあ、見やすいです。<br>
(<a href="#" class="imk-cue" data-seek="9:06" translate="no">00:09:06</a>)  それで問題は、ここで、このURLを再起動してみます。<br>
(<a href="#" class="imk-cue" data-seek="9:19" translate="no">00:09:19</a>)  再起動されました。<br>
(<a href="#" class="imk-cue" data-seek="9:27" translate="no">00:09:27</a>)  すると、元の、真っ黒に戻ってしまうということです。<br>
(<a href="#" class="imk-cue" data-seek="9:35" translate="no">00:09:35</a>)  Vimeoの字幕のカスタマイズは、毎回、マニュアル操作です。以上です。<br>
(<a href="#" class="imk-cue" data-seek="9:42" translate="no">00:09:42</a>)  この動画は以上です。ご視聴ありがとうございました。<br>
</p> </details>
<style>
details { font: 16px "Open Sans", Calibri, sans-serif; width: 100%; }
details > summary { padding: 2px 6px; width: 100%; background-color: #ddd; border: none; box-shadow: 3px 3px 4px black; cursor: pointer; list-style: none; }
details > p { font: 14px "Open Sans", Calibri, sans-serif; height:150px; overflow: scroll; background-color: #EDF7FF; padding: 2px 6px; margin: 0; box-shadow: 3px 3px 4px black; }
</style>
</div></div>            <script>
            document.addEventListener("DOMContentLoaded", function(){
                var video = document.getElementById("myVideo");
                if(!video) return;

                            });
            </script>
            

  <script>
  (function(){
    var me = document.currentScript;
    var wrapper = me ? me.closest('.sc-dynamic-embed') : null;

    function protectVideo() {
      var target = wrapper ? wrapper : document;
      var mediaEls = target.querySelectorAll('video, audio');
      mediaEls.forEach(function(v){
        if(v.dataset.protected === 'true') return;
        v.dataset.protected = 'true';
        v.setAttribute('controlsList', 'nodownload');
        v.oncontextmenu = function() { return false; };
        v.addEventListener('contextmenu', function(e){ e.preventDefault(); return false; }, false);
      });
    }

    function initSubtitles() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video, audio');
      var listContainer = target.querySelector('details > p');
      
      if (!video) return false; 
      
      if (video.dataset.subInit === 'true') return true; 
      video.dataset.subInit = 'true';

      var oldOverlay = target.querySelector('#subtitleOverlay') || target.querySelector('#scSubtitleOverlay');
      if (oldOverlay) {
          oldOverlay.style.setProperty('display', 'none', 'important');
          oldOverlay.innerHTML = ''; 
      }

      // 全画面・PiP・iOS全画面の判定（dr52.php準拠＋Safari presentationMode完全対応）
      function isSpecialMode() {
        var fsEl = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
        var isFs = !!fsEl;
        var isWebkitFs = (video.webkitPresentationMode === "fullscreen") || !!video.webkitDisplayingFullscreen;
        var isPiP = !!(document.pictureInPictureElement && document.pictureInPictureElement === video) || (video.webkitPresentationMode === "picture-in-picture");
        return isFs || isWebkitFs || isPiP;
      }

      // 字幕トラックの表示/非表示を更新
      function updateTrackMode() {
        var hasList = (listContainer !== null);
        var desiredMode = (isSpecialMode() || !hasList) ? "showing" : "hidden";
        try {
          if (video.textTracks && video.textTracks.length > 0) {
            for (var i = 0; i < video.textTracks.length; i++) {
              var track = video.textTracks[i];
              if ((track.kind === 'subtitles' || track.kind === 'captions') && track.mode !== desiredMode) {
                track.mode = desiredMode;
              }
            }
          }
        } catch(e) {}
      }

      // 初回の反映
      updateTrackMode();

      // 各種全画面・PiP切り替えイベントの即時検知リスナーを登録（video本体・document双方を監視）
      ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'].forEach(function(evtName) {
        document.addEventListener(evtName, updateTrackMode, false);
        video.addEventListener(evtName, updateTrackMode, false);
      });
      video.addEventListener('webkitbeginfullscreen', updateTrackMode, false);
      video.addEventListener('webkitendfullscreen', updateTrackMode, false);
      video.addEventListener('webkitpresentationmodechanged', updateTrackMode, false);
      video.addEventListener('enterpictureinpicture', updateTrackMode, false);
      video.addEventListener('leavepictureinpicture', updateTrackMode, false);
      video.addEventListener('play', updateTrackMode, false);
      video.addEventListener('pause', updateTrackMode, false);

      // 字幕一覧が存在する場合のみ、リストのフォーマット処理を行う
      if (listContainer) {
          var detailsEl = target.querySelector("details");
          if (detailsEl) {
              detailsEl.open = true; 
              var summaryEl = detailsEl.querySelector("summary");
              if (summaryEl) summaryEl.textContent = "字幕(シーン)はここをクリック";
          }

          if (!listContainer.dataset.formatted) {
              var html = listContainer.innerHTML;
              var lines = html.split(/<br\s*\/?>/i);
              var newHtml = "";
              for(var j=0; j<lines.length; j++) {
                  if(lines[j].trim() === "") continue;
                  newHtml += "<span class='imk-line'>" + lines[j] + "</span><br>";
              }
              listContainer.innerHTML = newHtml;
              listContainer.dataset.formatted = "true";
          }

          var rootSublist = target.querySelector(".dr5-sublist") || listContainer.parentElement;
          if (rootSublist) {
            rootSublist.addEventListener("click", function(e){
              var a = e.target.closest && e.target.closest("a.imk-cue[data-seek]");
              if(!a) return;
              e.preventDefault();
              var ts = a.getAttribute("data-seek");
              if(!ts) return;
              var p = ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
              var sec = (p.length===2) ? p[0]*60+p[1] : (p.length===3 ? p[0]*3600+p[1]*60+p[2]: null);
              if(sec==null) return;
              try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
            });
          }
      }

      // 再生中（時間進行時）の追従とハイライト更新
      video.addEventListener("timeupdate", function(){
        updateTrackMode();

        var hasList = (listContainer !== null);
        if (!hasList) return;

        var cues = listContainer.querySelectorAll("a.imk-cue");
        if(cues.length === 0) return;
        var cur = video.currentTime;
        var activeA = null;

        for(var i=0; i<cues.length; i++){
            var ts = cues[i].getAttribute("data-seek");
            if(!ts) continue;
            var p = ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
            var t = (p.length===2) ? p[0]*60+p[1] : (p.length===3 ? p[0]*3600+p[1]*60+p[2] : null);
            if(t !== null && cur >= t - 0.5){ activeA = cues[i]; } 
            else if(t > cur){ break; }
        }

        if(activeA){
            var activeLine = activeA.closest(".imk-line");
            if(!activeLine) activeLine = activeA;

            if(activeLine.classList.contains("active-hl")) return;

            var allLines = listContainer.querySelectorAll(".imk-line");
            for(var k=0; k<allLines.length; k++) {
                allLines[k].classList.remove("active-hl");
                allLines[k].removeAttribute("style"); 
            }
            var allLinks = listContainer.querySelectorAll("a");
            for(var m=0; m<allLinks.length; m++) {
                allLinks[m].classList.remove("active-hl");
                allLinks[m].removeAttribute("style"); 
            }

            activeLine.classList.add("active-hl");
            activeLine.style.setProperty("background-color", "#ffff00", "important");
            activeLine.style.setProperty("color", "red", "important");
            activeLine.style.setProperty("font-weight", "normal", "important");
            
            var newLinks = activeLine.querySelectorAll("a");
            for(var n=0; n<newLinks.length; n++) {
                newLinks[n].style.setProperty("color", "red", "important");
                newLinks[n].style.setProperty("text-decoration", "none", "important");
            }

            if(listContainer.offsetParent !== null){
                var containerRect = listContainer.getBoundingClientRect();
                var activeRect = activeLine.getBoundingClientRect();
                var targetScroll = listContainer.scrollTop + (activeRect.top - containerRect.top) - (listContainer.clientHeight / 2) + (activeLine.clientHeight / 2);
                listContainer.scrollTo({ top: targetScroll, behavior: "smooth" });
            }
        }
      });

      return true;
    }

    var checks = 0;
    var checkTimer = setInterval(function(){
      protectVideo();
      var success = initSubtitles();
      checks++;
      if (success || checks > 20) { 
        clearInterval(checkTimer);
      }
    }, 500); 

    if (!window.scStopAndGo) {
      window.scStopAndGo = function(event, link){
        try{
          var mediaEls = document.querySelectorAll('video, audio');
          mediaEls.forEach(function(m){
            try{
              if (!m.paused) m.pause();
              if (document.pictureInPictureElement === m && document.exitPictureInPicture) {
                document.exitPictureInPicture().catch(function(){});
              }
            }catch(e){}
          });
        }finally{
          event.preventDefault();
          setTimeout(function(){
            if (link.target === '_blank') {
              window.open(link.href, '_blank');
            } else {
              window.location.href = link.href;
            }
          }, 50);
        }
        return false;
      };
    }
  })();
  </script>
</div>



<p class="wp-block-paragraph">字幕の作成に関する問題ですが、それは、規格が統一されていないことに尽きる、と言っていいです。動画を作成する人は、それに振り回されています。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=xhUgzybc.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=xhUgzybc.png" alt="" style="width:125px;height:auto"/></a></figure>



<p class="wp-block-paragraph">その字幕作成の悩みの一つが、スマホなどの小さな画面を使って、解説の動画を視聴してもらおうとすると、字幕が邪魔になることです。その解決策として、既に、<a href="https://imakat.com/2024/03/23/20191/" target="_blank">字幕を動画枠の外に表示する方法</a>は紹介してきましたが、この動画では、字幕の背景を透過させる設定方法を紹介しています。</p>



<p class="wp-block-paragraph">なお、WordPressプレーヤーを使用する場合は、以下の方法で、デフォルトで透過率を設定することができます。</p>



<pre class="wp-block-code"><code>video::cue {
  background-color: rgba(0, 0, 0, 0.5); /* 背景を黒で透過率50% */
  color: white; /* 字幕の文字色を白に設定 */
  text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); /* 文字に影を追加 */
}</code></pre>



<p class="wp-block-paragraph">上のコードを、WordPressのカスタマイズ-&gt;追加CSS、へ追記して、公開のボタンを押します。</p>



<p class="wp-block-paragraph"></p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=Auj9lJJd.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=Auj9lJJd.png" alt=""/></a></figure>



<p class="wp-block-paragraph">以上</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">23205</post-id>	</item>
		<item>
		<title>【Mac】QuickTimePlayerとテキストエディット〜昔からある小道具を忘るべからず</title>
		<link>https://imakat.com/2024/08/08/22970/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Thu, 08 Aug 2024 02:00:00 +0000</pubDate>
				<category><![CDATA[字幕作成]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[vimeo]]></category>
		<category><![CDATA[FinalCutPro]]></category>
		<category><![CDATA[テキストエディット]]></category>
		<category><![CDATA[字幕工房]]></category>
		<category><![CDATA[QuickTimePlayer]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=22970</guid>

					<description><![CDATA[純正のQuickTimePlayerとテキストエディットだけで、かなりのことができる YouTube,SNSといった庶民による動画配信が拡大したことにより、動画作成や字幕作成、翻訳などについてのアプリやサービスは、百花繚 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h3 class="wp-block-heading">純正のQuickTimePlayerとテキストエディットだけで、かなりのことができる</h3>



<p class="wp-block-paragraph">YouTube,SNSといった庶民による動画配信が拡大したことにより、動画作成や字幕作成、翻訳などについてのアプリやサービスは、百花繚乱といった状況です。</p>



<p class="wp-block-paragraph">アプリやサービスを、取り替えては楽しむ。それは若い人は、それでいいのでしょうが、私なんかは、手作業で地味にコツコツできないものか、とすぐに考えてしまいます。</p>



<p class="wp-block-paragraph"><strong>QuickTimePlayerとテキストエディットは</strong>、Macを買えば、最初から入っています。<strong>いわば大工のノミとカンナのような小道具</strong>です。今回、感じたのは、新しいものの吸収も大事だが、古くからある道具にコツコツ慣れることも大事だろう、と思えることです。</p>



<p class="wp-block-paragraph">下のイメージ図は、動画と字幕作りのワークフローです。結局、作られたものは必ずといっていいほど、作り直しが発生するわけです。その時に、本格的に動画アプリでその修正を行うか、または、ノミ、カンナなどDIYショップで買えるような小道具で行うか。そこの選択ですが、私は、まずはDIYを選ぶ。ダメなら動画アプリなどを選ぶ。そのような順番で考えます。</p>



<a rel="noopener" target="_blank" href="https://docs.google.com/drawings/d/1bR0LVfw4O6ug_sEluP5RW557a-TceQ3OonCKBpbLKzg/edit?usp=sharing">
<img decoding="async" src="https://docs.google.com/drawings/d/e/2PACX-1vSroUBmQKujQ4nJ2AGV8dgEYFgcqllRF4x-lrOPsVPM_xtnvPV9i6WA2MorthHHHQZR366oeW66KWkK/pub?w=960&amp;h=720">
</a>



<p class="wp-block-paragraph"></p>



<div class="wp-block-cocoon-blocks-toggle-box-1 toggle-wrap toggle-box block-box not-nested-style cocoon-block-toggle"><input id="toggle-checkbox-202604150430090" class="toggle-checkbox" type="checkbox"/><label class="toggle-button" for="toggle-checkbox-202604150430090"><span class="fz-18px"> <strong><span class="marker">関係するオリジナルスクリプト一覧表(ここをクリック)</span></strong></span></label><div class="toggle-content">
<figure class="wp-block-flexible-table-block-table is-scroll-on-pc is-scroll-on-mobile"><table class="is-stacked-on-mobile" style="max-width:100%;width:auto;min-width:50%"><tbody><tr><td style="background-color:#fff7cc;width:25%"><strong>スクリプトのリンク</strong></td><td style="background-color:#f3fafe;width:50%"><strong>説明</strong></td></tr><tr><td style="background-color:#fff7cc;width:50%"><a href="https://imakat.com/?pubtxt=srtからvttを生成するworkflow_pub.txt" target="_blank">srtからvttを生成する</a>(AppleScriptクイックアクション)</td><td><a href="https://imakat.com/ds5/?drid=2">動画&#x1f3a5;(無音)</a><br>新たに作ったVTTファイルに対して、 <br> (1) 先頭行に文字列&#8221;WEBVTT&#8221;を含む行を挿入する。 <br>(2) 2行目に空白の行を挿入する。 <br>(3) 3行目以降から、読み込んだデータを書き込む。 <br> (4) &#8220;&#8211;&gt;&#8221; がある行は、行の中の&#8221;,&#8221;を&#8221;.&#8221;に置き換える。<br>※元のファイルを残します。</td></tr><tr><td style="background-color:#fff7cc"><a href="https://imakat.com/?pubtxt=vttからsrtを生成する兼srtの連番を付け直すworkflow_pub.txt" target="_blank">vttからsrtを生成する兼srtの連番を付け直す</a>(renumbered.srt)(AppleScriptクイックアクション)</td><td><a href="https://imakat.com/ds5/?drid=24" target="_blank">動画&#x1f3a5;(無音)</a><br>vttからsrtを生成します。また、新たに作ったSRTファイルに対して、<br>(1) 文字列”WEBVTT”のある行は削除する。<br>(2) &#8220;&#8211;&gt;&#8221; がある行は、行の中の&#8221;.&#8221;を&#8221;,&#8221;に置き換える。<br>(3)連番を1から振り直す。<br>※元のファイルを残します。新しいファイル名は、&#8221;元のファイル名_renumbered.srt&#8221;となります。</td></tr><tr><td style="background-color:#fff7cc"><a href="https://imakat.com/?pubtxt=字幕文章に時刻を挿入するworkflow_pub.txt" target="_blank">字幕文章に時刻を挿入するvtt</a>(timeadded.vtt)(AppleScriptクイックアクション)</td><td><a href="https://imakat.com/ds5?drid=4">動画&#x1f3a5;(無音</a><a href="https://imakat.com/ds?drid=4">)</a><br>文字列の先頭に、開始時刻(00:01:23)を追記する。私はカモメ→(00:01:23)私はカモメ※元のファイルを残します。_timeadded.vttが新しく作られます。この字幕文章の先頭に時刻情報を入れる目的は、翻訳された外国語の示す動画の位置を分かりやすくするためです。</td></tr><tr><td style="background-color:#fff7cc"><a href="https://imakat.com/?pubtxt=字幕文章の時刻を削除するworkflow_pub.txt" target="_blank">字幕文章の時刻を削除する</a>(timeremoved.vtt)(AppleScriptクイックアクション)</td><td><a href="https://imakat.com/ds5/?drid=23" target="_blank">動画(&#x1f3a5;無音)</a><br>上の「字幕文章に時刻を挿入する」を取り消しにします。※元のファイル_timeadded.vttは残します。新しいファイルは、_timeadded_timeremoved .vttになります。</td></tr><tr><td style="background-color:#fff7cc"><a href="https://imakat.com/?pubtxt=字幕文章に番号を挿入するworkflow_pub.txt" target="_blank">字幕文章に番号を挿入する</a>(numadded.vtt)(AppleScriptクイックアクション)</td><td>文字列の先頭に、番号1)を追記する。私はカモメ→1)私はカモメ※元のファイルを残します。_numadded.vttが新しく作られます。この字幕文章の先頭に番号を入れる目的は、翻訳された外国語の示す動画の位置を分かりやすくするためです。</td></tr><tr><td style="background-color:#fff7cc"><a href="https://imakat.com/?pubtxt=字幕文章の番号を削除するworkflow_pub.txt" target="_blank">字幕文章の番号を削除する</a>(numremoved.vtt)(AppleScriptクイックアクション)</td><td>上の「字幕文章に番号を挿入する」を取り消しにします。※元のファイル_numadded.vttは残します。新しいファイルは、_numadded_numremoved .vttになります。</td></tr><tr><td style="background-color:#fff7cc"><a href="https://imakat.com/?pubtxt=字幕空白詰め_vimeo_auto_generated_captions_vttの修正_pub.txt" target="_blank">vimeo_auto_generated_captions.vttの修正をする</a>(AppleScriptクイックアクション)</td><td><a href="https://imakat.com/ds5/?drid=22" target="_blank">動画&#x1f3a5;(無音)</a><br>Vimeoの字幕自動生成からダウンロードしたファイルの、文字列の空白を削除する。vttのまま。※直接上書き更新します。何度でもダウンロードできるため。</td></tr><tr><td style="background-color:#fff7cc"><a href="https://imakat.com/?pubtxt=字幕時刻の秒ずらしを行うworkflow_pub.txt" target="_blank">vttの字幕時刻の秒ずらしを行う。</a>(timeshifted.vtt)(AppleScriptクイックアクション)</td><td><a href="https://imakat.com/ds5/?drid=25" target="_blank">動画&#x1f3a5;(無音)</a><br>動画の途中で、挿入や削除を行うと、字幕ファイルの時刻にズレが生じます。挿入や削除を行った以降の動画をコピーして、その分の秒ずらしを行うことができます。<span class="bold-red">但し、文章の頭に時刻情報を入れてある(_timeadded.vtt)については、エラーになります。一度、上の「字幕文章の時刻を削除する」を実行した後で、ここを実行してください</span>。<br>※元のファイルを残します。新しいファイル名は&#8221;元のファイル名_timeshited.vtt&#8221;となります。</td></tr></tbody></table></figure>
</div></div>



<p class="wp-block-paragraph"><strong>＜クイックアクション＞</strong></p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=jUjjxyz1.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=jUjjxyz1.png" alt=""/></a></figure>



<h3 class="wp-block-heading">ポイント解説</h3>



<h4 class="wp-block-heading">１　「字幕工房」の利用</h4>



<a rel="noopener" target="_blank" href="https://www.tranquillitybase.jp/SubtitleFactory/SubtitleFactory.html">
<img decoding="async" src="https://imakat.com/rd.php?id=zZhRueuH.png
"
></a>



<p class="wp-block-paragraph">動画や字幕を修正する小道具は、ノミやカンナのようなものと言いましたが、小道具側に入れるが、ちょっと高級なもの、電動ノコギリのようなものが「字幕工房」です。<br>ナレーションを文字起こしするような場合は、Vimeo、Vrew、Filmoraなどの機能を使って字幕ファイルが自動生成されますが、内容を文字で説明する場合は、説明文を自分で打ち込むことになります。そのような場合に、「字幕工房」は向いています。</p>



<p class="wp-block-paragraph">「字幕工房」は、動画(mp4 mov)の上で、右クリック-&gt;このアプリケーションで開く-&gt;「字幕工房.app」で開けるのが非常に便利です。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=FjQdEj2y.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=FjQdEj2y.png" alt=""/></a></figure>



<p class="wp-block-paragraph">この「字幕工房」は字幕をQuickTimeプレーヤーおよびSafariと紐付けするようになっています。その紐付け状態でWordPressプレーヤーで再生させると字幕が二重になったり扱いが面倒になります。従って、私としては、動画は動画として独立、字幕は字幕として独立して作成して、配信するときに、紐付けて動作させるやり方にします。</p>



<p class="wp-block-paragraph"><br>その方法ですが、2つ注意点があります。<span class="bold">1つ目は、作成した字幕を忘れずに書き出すこと</span>。ファイル-&gt;SRT-&gt;書き出す-&gt;日本語、とすると同じフォルダにSRTが書き出されます。これを忘れると、終了の時、字幕が何も残らない結果（何も修正されない結果)になってしまいます。<strong>SRTで書き出すことが字幕の保存という意味です</strong>。<strong>作業を後日続けたい時は、「読み込む&#8230;」で行います</strong>。その上にある保存、別名で保存は、mp4.mov動画の保存であり、私の場合は使いません。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=6FIiyDGV.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=6FIiyDGV.png" alt=""/></a></figure>



<p class="wp-block-paragraph"><span class="bold">2つ目は、終了する時</span>、「動画に加えた変更を保存しますか？」と聞かれますが、動画のイメージ自体には何も変更はありませんが動画と字幕を紐付けすることは、動画にとっての変更となるでしょう。動画には何の変更もされたくありませんので<span class="bold">「保存しない」を選びます。</span></p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=chte8F3G.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=chte8F3G.png" alt=""/></a></figure>



<p class="wp-block-paragraph">上のスクリプトの説明表の中の動画は、「字幕工房」QuickTimePlayerで作成しています。</p>



<p class="wp-block-paragraph">※「字幕工房」：Mac App Storeで購入できます。買い切りで約1000円。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=3B1f4XtZ.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=3B1f4XtZ.png" alt="" style="width:167px;height:auto"/></a></figure>



<h4 class="wp-block-heading">２　Appleスクリプトによる字幕ファイルのかんたん作成</h4>



<p class="wp-block-paragraph">「字幕工房」よりもっと、かんたんな作成方法です。QuickTimePlayerで動画を表示しながら、止めた位置に、字幕テキストを書き込んでいく簡単なアプリを作成しました。そこそこ実用になります。→<a href="https://imakat.com/2025/08/28/27063/" target="_blank">「字幕かんたん作成」</a></p>



<h4 class="wp-block-heading">３　字幕文章の時刻情報(00:00:00)の挿入と削除について</h4>



<p class="wp-block-paragraph">上の表にあるように、字幕文章の先頭に秒未満をカットした時刻情報(00:00:00)を、挿入したり削除したりできます。一応、その都度、ファイル名に、timeadded、timeremovedが追記されます。なぜ、このようなものを用意しているかですが、<strong>動画内の字幕が翻訳できない場合の補助として、その字幕の位置を示すため</strong>です。例えば、Vimeoなど動画内に表示される字幕を翻訳できる場合と、WordPressのように動画内では翻訳できない場合があります。字幕一覧は翻訳されます。<br>時刻情報が文章に入っている例：<br>下は、Dropboxの動画をWordPressプレーヤーで配信している例です。ページ左上の「言語を選択」から適当な言語を選んで表示してみてください。(1)はリアルタイムで翻訳されますが、動画の外枠へ表示します。(2)は動画内の字幕は日本語ですが、字幕一覧は翻訳されます(長い動画の時は翻訳の改行が不安定になりますが)ので、時刻情報をキーとして内容を把握できます。<br><a href="https://imakat.com/ds5/?drid=10" target="_blank">(1)メールを全受信に表示しない方法＜字幕一覧なし＞ 33秒(動画&#x1f3a5;Dropbox)</a><br><a href="https://imakat.com/dr5/?drid=10" target="_blank">(2)メールを全受信に表示しない方法＜字幕一覧あり＞ 33秒(動画&#x1f3a5;Dropbox)</a></p>



<p class="wp-block-paragraph">必要に応じて、文章頭に時刻情報を入れることが可能、と捉えてください。<br></p>



<h4 class="wp-block-heading">４　その他</h4>



<p class="wp-block-paragraph">動画作成の作業を便利にするものとして、今回は、QuickTimePlayer、テキストエディット、字幕工房を具体的に取り上げましたが、網羅するならば、上に掲載したクイックアクション、それから、下にあるStream Deckの各設定が、全体像になります。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=mnpp7LoX.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=mnpp7LoX.png" alt=""/></a></figure>



<p class="wp-block-paragraph">Stream Deckに関しては、また別に取り上げます。</p>



<p class="wp-block-paragraph">以上。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=s0omPPEw.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=s0omPPEw.png" alt="" style="width:110px;height:auto"/></a></figure>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">22970</post-id>	</item>
		<item>
		<title>字幕一覧(字幕リスト)は、古いようで一番先進の動画構成方法！</title>
		<link>https://imakat.com/2024/02/11/20002/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Sat, 10 Feb 2024 19:42:46 +0000</pubDate>
				<category><![CDATA[字幕作成]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[Google翻訳]]></category>
		<category><![CDATA[Dropbox]]></category>
		<category><![CDATA[vimeo]]></category>
		<category><![CDATA[Appleスクリプト]]></category>
		<category><![CDATA[srt]]></category>
		<category><![CDATA[vtt]]></category>
		<category><![CDATA[タイムコード]]></category>
		<category><![CDATA[Apple翻訳]]></category>
		<category><![CDATA[Windows翻訳]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=20002</guid>

					<description><![CDATA[現状では、世界のどこかの人が何かのデバイスで、このブログを見て、その中の動画を視聴した時に、言葉がわからないという問題にぶつかります。ですから、そのブログの中に、言葉をわかるようにする「手掛かり」を配置しておきたいと思い [&#8230;]]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=GjFF2Rw0.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=GjFF2Rw0.png" alt="" style="width:222px;height:auto"/></a><figcaption class="wp-element-caption">「字幕一覧」は本屋の立ち読み</figcaption></figure>



<p class="wp-block-paragraph">現状では、世界のどこかの人が何かのデバイスで、このブログを見て、その中の動画を視聴した時に、言葉がわからないという問題にぶつかります。ですから、そのブログの中に、<strong>言葉をわかるようにする「手掛かり」を配置</strong>しておきたいと思います。</p>



<p class="wp-block-paragraph">ただその「手掛かり」と言っても、なかなか簡単ではありません。<br>条件が複雑過ぎるからです。</p>



<p class="wp-block-paragraph">現在、条件がどうマチマチであるか眺めてみます。まず、デバイスとしてポピュラーなものをピックアップすると、WindowsPC,Mac,iPhone,iPad,Android携帯、Androidタブレット、以上になるでしょうし、OSでポピュラーなものは、Windows,Mac,iPad/iPhone,Androidになるでしょうし、ブラウザでポピュラーなものは、Chrome,Edge,Safariになるでしょうし、さらに動画の配信側は、YouTube,Vimeo,WordPress,Dropboxなどクラウドサービス、自宅サーバーなど。。<br>こんな複雑な条件の中で、今、こうしたブログさらにその中の動画などが配信されているわけですね。ですから、全てのユーザーに、「言葉がわかるようにする」のは無理と言っていいでしょう。</p>



<p class="wp-block-paragraph">結論が遅れました。</p>



<p class="wp-block-paragraph">それほど、手の込んだ工夫ではありません。</p>



<p class="wp-block-paragraph"><strong><span class="fz-18px"><span class="marker">動画の下に、タイムコードあるいは番号が付いた「字幕一覧」を配置すべし。<br>具体的には以下です。</span></span></strong><br></p>


<div class="sc-dynamic-embed">
  <style>
  /* リンクの見た目を整える */
  .sc-dynamic-embed .sc-link-container { 
      display: flex; 
      margin-bottom: 10px; 
      flex-wrap: wrap; 
      align-items: center; 
  }
  .sc-dynamic-embed .sc-link { margin-bottom: 0; }
  .sc-dynamic-embed .sc-link a {
    font-size: 15px; 
    font-weight: normal;
    text-decoration: underline;
    color: #0073aa;
  }
  .sc-dynamic-embed .sc-link a:hover { text-decoration: none; color: #000; }
  
  @media (max-width: 500px) {
    .sc-dynamic-embed .sc-link a { font-size: 13px; }
  }

  .sc-dynamic-embed .imk-line {
      display: inline-block;
      width: 100%;
      border-radius: 2px;
      transition: background-color 0.1s;
  }

  .sc-dynamic-embed #subtitleOverlay,
  .sc-dynamic-embed #scSubtitleOverlay,
  .sc-dynamic-embed .overlay-cue,
  .sc-dynamic-embed .band {
      display: none !important;
      opacity: 0 !important;
      visibility: hidden !important;
      pointer-events: none !important;
  }
  </style>

  <div class="sc-link-container">
    <p class="sc-link">
      <!-- ★環境依存排除：固定ドメインをルート相対パスへ置換完了 -->
      <a href="/ds62/?drid=28" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <!-- ★環境依存排除：[dynamic_external_html] にルート相対パスを指定し、functions.phpにURL動的解決を委ねる設計 -->
  <style>
            :root{ --dr5emd-max: 1920px; }
            #subtitleOverlay, #scSubtitleOverlay, .overlay-cue, .band { display: none !important; opacity: 0 !important; }
            .imk-line { display: inline-block; width: 100%; border-radius: 2px; transition: background-color 0.1s; }
            .dr5emd-sublist details > p{ height:200px; overflow:auto; background-color:#EDF7FF; padding:2px 6px; margin:0; box-shadow:3px 3px 4px black; position: relative; }
            .dr5emd-sublist details > summary{ padding:2px 6px; width:100%; background-color:#ddd; border:none; box-shadow:3px 3px 4px black; cursor:pointer; list-style:none; }
            
                .video-wrap{position:relative;width:100%;margin:0 auto}
                #myVideo{width:100%;height:auto;min-height:200px;display:block;background:#000;}</style><div class="dr5emd-container"><div class="video-wrap"><video id="myVideo" controls poster="https://imakat.com/rd.php?id=AWGsA7WG.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=WLISfxH7.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=QBzEiJtv.vtt" label="日本語" srclang="ja" kind="subtitles"></video></div><div class="dr5emd-sublist"><details translate="yes"><summary translate="yes">字幕一覧(クリック)</summary> <p translate="yes">
(<a href="#" class="imk-cue" data-seek="0:00" translate="no">00:00:00</a>)  Synology NAS HDD「お下がり」交換リレー<br>
(<a href="#" class="imk-cue" data-seek="0:11" translate="no">00:00:11</a>)  これが自宅にあるNASとWebサーバーです。<br>
(<a href="#" class="imk-cue" data-seek="0:23" translate="no">00:00:23</a>)  DS220+ですけどね、最近容量不足ということで困ってました。<br>
(<a href="#" class="imk-cue" data-seek="0:30" translate="no">00:00:30</a>)  そこで大容量に変えることにしました。<br>
(<a href="#" class="imk-cue" data-seek="0:34" translate="no">00:00:34</a>)  ストレージマネージャーにこういう警告が出ています。<br>
(<a href="#" class="imk-cue" data-seek="0:39" translate="no">00:00:39</a>)  二巡すると全部が入れ替わるようになります。<br>
(<a href="#" class="imk-cue" data-seek="0:46" translate="no">00:00:46</a>)  事前にハードディスクとSATAスタンドを買っておきました。<br>
(<a href="#" class="imk-cue" data-seek="0:53" translate="no">00:00:53</a>)  それでは作業を具体的に見ていきましょう。NASの蓋を外します。<br>
(<a href="#" class="imk-cue" data-seek="1:04" translate="no">00:01:04</a>)  ディスク1とディスク2がありますが、同じ容量なんで2から抜くことにします。<br>
(<a href="#" class="imk-cue" data-seek="1:14" translate="no">00:01:14</a>)  引き抜きます。<br>
(<a href="#" class="imk-cue" data-seek="1:21" translate="no">00:01:21</a>)  Seagateの6TBです。<br>
(<a href="#" class="imk-cue" data-seek="1:39" translate="no">00:01:39</a>)  引き抜いたあと警告音が鳴ります。<br>
(<a href="#" class="imk-cue" data-seek="1:51" translate="no">00:01:51</a>)  右側が今外した6TBです。左が12TBの新品です。<br>
(<a href="#" class="imk-cue" data-seek="2:05" translate="no">00:02:05</a>)  ハードディスク取り付けパーツを付け替えます。<br>
(<a href="#" class="imk-cue" data-seek="2:21" translate="no">00:02:21</a>)  まあ滅多にやらない作業なんで手際が悪いです。<br>
(<a href="#" class="imk-cue" data-seek="2:45" translate="no">00:02:45</a>)  12TBの方にはめます。<br>
(<a href="#" class="imk-cue" data-seek="3:42" translate="no">00:03:42</a>)  やっとできました。私が不器用なだけです。誰でも簡単にできます。<br>
(<a href="#" class="imk-cue" data-seek="3:58" translate="no">00:03:58</a>)  差し込みます。<br>
(<a href="#" class="imk-cue" data-seek="4:30" translate="no">00:04:30</a>)  ディスク2のライトが点灯しました。装着まではOKです。<br>
(<a href="#" class="imk-cue" data-seek="4:39" translate="no">00:04:39</a>)  「劣化」と表示されています。新しいドライブを装着済みなので、今すぐ修復をクリックします。<br>
(<a href="#" class="imk-cue" data-seek="4:54" translate="no">00:04:54</a>)  修復中となります。最初に初期化されます。<br>
(<a href="#" class="imk-cue" data-seek="5:03" translate="no">00:05:03</a>)  それでは3番目の作業で、Webサーバーの3TBの入れ替えをします。<br>
(<a href="#" class="imk-cue" data-seek="5:25" translate="no">00:05:25</a>)  Webサーバーの蓋を外します。<br>
(<a href="#" class="imk-cue" data-seek="5:31" translate="no">00:05:31</a>)  ここにディスク1とディスク2があります。同じ容量なのでどちらから抜いても構いません。<br>
(<a href="#" class="imk-cue" data-seek="5:40" translate="no">00:05:40</a>)  2から抜いてみます。<br>
(<a href="#" class="imk-cue" data-seek="5:53" translate="no">00:05:53</a>)  ホットスワップ機能がついているもんですから、ハードディスクを抜いてもサーバーは中断しないです。そのまま動作します。<br>
(<a href="#" class="imk-cue" data-seek="6:18" translate="no">00:06:18</a>)  ハードディスク取り付けパーツを、お下がりの6TBへ付け替えます。<br>
(<a href="#" class="imk-cue" data-seek="6:38" translate="no">00:06:38</a>)  どうも不器用です。<br>
(<a href="#" class="imk-cue" data-seek="7:01" translate="no">00:07:01</a>)  6TBの方へ付けます。<br>
(<a href="#" class="imk-cue" data-seek="7:11" translate="no">00:07:11</a>)  どうも手元の小さいものがよく見えなくなってきてますね、年のせいで。<br>
(<a href="#" class="imk-cue" data-seek="8:06" translate="no">00:08:06</a>)  はい、できました。<br>
(<a href="#" class="imk-cue" data-seek="8:23" translate="no">00:08:23</a>)  6TBを差し込みます。<br>
(<a href="#" class="imk-cue" data-seek="8:47" translate="no">00:08:47</a>)  ディスク2が点灯しました。<br>
(<a href="#" class="imk-cue" data-seek="8:57" translate="no">00:08:57</a>)  お下がりの6TBは差し込み済みなので、今すぐ修復を選びます。<br>
(<a href="#" class="imk-cue" data-seek="9:06" translate="no">00:09:06</a>)  ディスク2を選びます。<br>
(<a href="#" class="imk-cue" data-seek="9:13" translate="no">00:09:13</a>)  お下がりなのでデータは入ってるんですね。まあ消していいか聞いてきますので、「いいえ」ではなく「はい」を選びます。<br>
(<a href="#" class="imk-cue" data-seek="9:33" translate="no">00:09:33</a>)  修復中になります。<br>
(<a href="#" class="imk-cue" data-seek="9:41" translate="no">00:09:41</a>)  一巡目の最後の作業ですね。お下がりの3TBをSATAスタンドに取り付けます。<br>
(<a href="#" class="imk-cue" data-seek="9:55" translate="no">00:09:55</a>)  私が買ったのはAlxumというスタンドです。<br>
(<a href="#" class="imk-cue" data-seek="10:20" translate="no">00:10:20</a>)  それではAのソケットに差し込みます。<br>
(<a href="#" class="imk-cue" data-seek="11:06" translate="no">00:11:06</a>)  Bも空いています。<br>
(<a href="#" class="imk-cue" data-seek="11:23" translate="no">00:11:23</a>)  まあ、硬いですな。<br>
(<a href="#" class="imk-cue" data-seek="11:33" translate="no">00:11:33</a>)  SATAスタンドのハードディスクはexFATでフォーマットします。<br>
(<a href="#" class="imk-cue" data-seek="11:41" translate="no">00:11:41</a>)  これで一巡目は終わりですけど、NASサーバー、Webサーバーともに修復順調に進んでます。<br>
(<a href="#" class="imk-cue" data-seek="11:53" translate="no">00:11:53</a>)  TimeMachineの設定については割愛しますね。<br>
(<a href="#" class="imk-cue" data-seek="11:58" translate="no">00:11:58</a>)  まあちょっと気分転換です。<br>
(<a href="#" class="imk-cue" data-seek="12:04" translate="no">00:12:04</a>)  家の近くにですね、タヌキが出ていました。<br>
(<a href="#" class="imk-cue" data-seek="12:10" translate="no">00:12:10</a>)  まあ本当に犬ですね。犬そっくりです。走り方も犬。<br>
(<a href="#" class="imk-cue" data-seek="12:20" translate="no">00:12:20</a>)  二巡目ですけど、やることは一巡目と同じです。<br>
(<a href="#" class="imk-cue" data-seek="12:29" translate="no">00:12:29</a>)  もう結果からお見せします。SATAスタンドに2個のハードディスクが刺さってます。<br>
(<a href="#" class="imk-cue" data-seek="12:43" translate="no">00:12:43</a>)  最後にマネージャーを確認しますと、NAS、Webサーバーともに修復中、進んでます。<br>
(<a href="#" class="imk-cue" data-seek="12:53" translate="no">00:12:53</a>)  以上ですね、一通りハードディスク交換リレー終わりました。ありがとうございます。<br>
</p> </details>
<style>
details { font: 16px "Open Sans", Calibri, sans-serif; width: 100%; }
details > summary { padding: 2px 6px; width: 100%; background-color: #ddd; border: none; box-shadow: 3px 3px 4px black; cursor: pointer; list-style: none; }
details > p { font: 14px "Open Sans", Calibri, sans-serif; height:150px; overflow: scroll; background-color: #EDF7FF; padding: 2px 6px; margin: 0; box-shadow: 3px 3px 4px black; }
</style>
</div></div>            <script>
            document.addEventListener("DOMContentLoaded", function(){
                var video = document.getElementById("myVideo");
                if(!video) return;

                            });
            </script>
            

  <script>
  (function(){
    var me = document.currentScript;
    var wrapper = me ? me.closest('.sc-dynamic-embed') : null;

    function protectVideo() {
      var target = wrapper ? wrapper : document;
      var mediaEls = target.querySelectorAll('video, audio');
      mediaEls.forEach(function(v){
        if(v.dataset.protected === 'true') return;
        v.dataset.protected = 'true';
        v.setAttribute('controlsList', 'nodownload');
        v.oncontextmenu = function() { return false; };
        v.addEventListener('contextmenu', function(e){ e.preventDefault(); return false; }, false);
      });
    }

    function initSubtitles() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video, audio');
      var listContainer = target.querySelector('details > p');
      
      if (!video) return false; 
      
      if (video.dataset.subInit === 'true') return true; 
      video.dataset.subInit = 'true';

      var oldOverlay = target.querySelector('#subtitleOverlay') || target.querySelector('#scSubtitleOverlay');
      if (oldOverlay) {
          oldOverlay.style.setProperty('display', 'none', 'important');
          oldOverlay.innerHTML = ''; 
      }

      // 全画面・PiP・iOS全画面の判定（dr52.php準拠＋Safari presentationMode完全対応）
      function isSpecialMode() {
        var fsEl = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
        var isFs = !!fsEl;
        var isWebkitFs = (video.webkitPresentationMode === "fullscreen") || !!video.webkitDisplayingFullscreen;
        var isPiP = !!(document.pictureInPictureElement && document.pictureInPictureElement === video) || (video.webkitPresentationMode === "picture-in-picture");
        return isFs || isWebkitFs || isPiP;
      }

      // 字幕トラックの表示/非表示を更新
      function updateTrackMode() {
        var hasList = (listContainer !== null);
        var desiredMode = (isSpecialMode() || !hasList) ? "showing" : "hidden";
        try {
          if (video.textTracks && video.textTracks.length > 0) {
            for (var i = 0; i < video.textTracks.length; i++) {
              var track = video.textTracks[i];
              if ((track.kind === 'subtitles' || track.kind === 'captions') && track.mode !== desiredMode) {
                track.mode = desiredMode;
              }
            }
          }
        } catch(e) {}
      }

      // 初回の反映
      updateTrackMode();

      // 各種全画面・PiP切り替えイベントの即時検知リスナーを登録（video本体・document双方を監視）
      ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'].forEach(function(evtName) {
        document.addEventListener(evtName, updateTrackMode, false);
        video.addEventListener(evtName, updateTrackMode, false);
      });
      video.addEventListener('webkitbeginfullscreen', updateTrackMode, false);
      video.addEventListener('webkitendfullscreen', updateTrackMode, false);
      video.addEventListener('webkitpresentationmodechanged', updateTrackMode, false);
      video.addEventListener('enterpictureinpicture', updateTrackMode, false);
      video.addEventListener('leavepictureinpicture', updateTrackMode, false);
      video.addEventListener('play', updateTrackMode, false);
      video.addEventListener('pause', updateTrackMode, false);

      // 字幕一覧が存在する場合のみ、リストのフォーマット処理を行う
      if (listContainer) {
          var detailsEl = target.querySelector("details");
          if (detailsEl) {
              detailsEl.open = true; 
              var summaryEl = detailsEl.querySelector("summary");
              if (summaryEl) summaryEl.textContent = "字幕(シーン)はここをクリック";
          }

          if (!listContainer.dataset.formatted) {
              var html = listContainer.innerHTML;
              var lines = html.split(/<br\s*\/?>/i);
              var newHtml = "";
              for(var j=0; j<lines.length; j++) {
                  if(lines[j].trim() === "") continue;
                  newHtml += "<span class='imk-line'>" + lines[j] + "</span><br>";
              }
              listContainer.innerHTML = newHtml;
              listContainer.dataset.formatted = "true";
          }

          var rootSublist = target.querySelector(".dr5-sublist") || listContainer.parentElement;
          if (rootSublist) {
            rootSublist.addEventListener("click", function(e){
              var a = e.target.closest && e.target.closest("a.imk-cue[data-seek]");
              if(!a) return;
              e.preventDefault();
              var ts = a.getAttribute("data-seek");
              if(!ts) return;
              var p = ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
              var sec = (p.length===2) ? p[0]*60+p[1] : (p.length===3 ? p[0]*3600+p[1]*60+p[2]: null);
              if(sec==null) return;
              try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
            });
          }
      }

      // 再生中（時間進行時）の追従とハイライト更新
      video.addEventListener("timeupdate", function(){
        updateTrackMode();

        var hasList = (listContainer !== null);
        if (!hasList) return;

        var cues = listContainer.querySelectorAll("a.imk-cue");
        if(cues.length === 0) return;
        var cur = video.currentTime;
        var activeA = null;

        for(var i=0; i<cues.length; i++){
            var ts = cues[i].getAttribute("data-seek");
            if(!ts) continue;
            var p = ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
            var t = (p.length===2) ? p[0]*60+p[1] : (p.length===3 ? p[0]*3600+p[1]*60+p[2] : null);
            if(t !== null && cur >= t - 0.5){ activeA = cues[i]; } 
            else if(t > cur){ break; }
        }

        if(activeA){
            var activeLine = activeA.closest(".imk-line");
            if(!activeLine) activeLine = activeA;

            if(activeLine.classList.contains("active-hl")) return;

            var allLines = listContainer.querySelectorAll(".imk-line");
            for(var k=0; k<allLines.length; k++) {
                allLines[k].classList.remove("active-hl");
                allLines[k].removeAttribute("style"); 
            }
            var allLinks = listContainer.querySelectorAll("a");
            for(var m=0; m<allLinks.length; m++) {
                allLinks[m].classList.remove("active-hl");
                allLinks[m].removeAttribute("style"); 
            }

            activeLine.classList.add("active-hl");
            activeLine.style.setProperty("background-color", "#ffff00", "important");
            activeLine.style.setProperty("color", "red", "important");
            activeLine.style.setProperty("font-weight", "normal", "important");
            
            var newLinks = activeLine.querySelectorAll("a");
            for(var n=0; n<newLinks.length; n++) {
                newLinks[n].style.setProperty("color", "red", "important");
                newLinks[n].style.setProperty("text-decoration", "none", "important");
            }

            if(listContainer.offsetParent !== null){
                var containerRect = listContainer.getBoundingClientRect();
                var activeRect = activeLine.getBoundingClientRect();
                var targetScroll = listContainer.scrollTop + (activeRect.top - containerRect.top) - (listContainer.clientHeight / 2) + (activeLine.clientHeight / 2);
                listContainer.scrollTo({ top: targetScroll, behavior: "smooth" });
            }
        }
      });

      return true;
    }

    var checks = 0;
    var checkTimer = setInterval(function(){
      protectVideo();
      var success = initSubtitles();
      checks++;
      if (success || checks > 20) { 
        clearInterval(checkTimer);
      }
    }, 500); 

    if (!window.scStopAndGo) {
      window.scStopAndGo = function(event, link){
        try{
          var mediaEls = document.querySelectorAll('video, audio');
          mediaEls.forEach(function(m){
            try{
              if (!m.paused) m.pause();
              if (document.pictureInPictureElement === m && document.exitPictureInPicture) {
                document.exitPictureInPicture().catch(function(){});
              }
            }catch(e){}
          });
        }finally{
          event.preventDefault();
          setTimeout(function(){
            if (link.target === '_blank') {
              window.open(link.href, '_blank');
            } else {
              window.location.href = link.href;
            }
          }, 50);
        }
        return false;
      };
    }
  })();
  </script>
</div>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=MRuDPz44.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=MRuDPz44.png" alt=""/></a></figure>



<p class="wp-block-paragraph">このような折りたたみの字幕一覧を用意しておくと、ブログの翻訳をすれば、この部分も翻訳されます。(1)(2)などの番号でもいいですが、動画の中の位置が分かりません。タイムコードなら動画のバーを近くに持っていけます。YouTubeなどで字幕の自動翻訳もありますが、<strong>速く進み過ぎて目が追いつけません</strong>。だから、自分のペースで追いたいです。逆に、<strong>急いでいるので動画の要点をサラッと知りたい</strong>ということもあります。<strong>いわば「本屋の立ち読み」です。音声の早送りより、はるかに生産性は高いです。10分の動画でも字幕一覧をみれば30秒で概略がわかります</strong>。</p>



<p class="wp-block-paragraph">AIの解読力ですが、動画を理解することもできるようになってきてはいます。しかし多大なリソース消費の問題もあり正確性も今一歩のところがあります。Geminiに質問してみましたが、動画に字幕ファイルが別添されている形が、AIとすると一番ありがたい形式、と言う返答でした。</p>



<p class="wp-block-paragraph">いずれにせよ字幕一覧形式は非常に優れています。翻訳した字幕一覧を自分のメモに貼り付けることも出来ます。字幕が邪魔なら、動画の字幕を非表示にして、この字幕一覧を折り畳めばいいです。</p>



<p class="wp-block-paragraph"><br>ちなみに、<strong>上の折りたたみ字幕一覧を、左上のGoogle翻訳で試してみてください。翻訳の途中から改行がされない現象が生ずる場合があります。これはChromeからChromeの翻訳機能を使っても同じ現象になるでしょう。<br>次に、AppleやWindowsの翻訳を試してみてください。ブラウザから利用できると思います。それを使うと、正しく改行されるはずです</strong>。</p>



<p class="wp-block-paragraph">Macで使う場合、Vimeoの動画に表示される字幕の翻訳は、Google翻訳では翻訳不可ですが、Apple翻訳ではVimeoの動画に表示される字幕を翻訳してくれます(<a href="https://imakat.com/vm/?movid=913935684">動画無音</a>)。iPad,iPhoneではそれが難しいのでWordPress固定ページに字幕一覧を読み込んで工夫することにします(<a href="https://imakat.com/vm/?movid=914144217">動画有音</a>)。</p>



<p class="wp-block-paragraph">Apple翻訳で気づいた欠点の一つは、WordPress動画プレーヤーで表示される字幕について翻訳しようとする時パカパカと点滅状態になるため(<a href="https://imakat.com/ds5/?drid=1">動画無音</a>)字幕は非表示にせざるを得ない点があります（2024.2月現在)。</p>



<p class="wp-block-paragraph">なお、WindowsのEdgeにより、vimeoのプレーヤーを直接に呼び出す場合ですが、AppleのSafariと同じく、字幕は直接に翻訳されます(<a href="https://imakat.com/vm?movid=914067338">動画無音</a>)。</p>



<p class="wp-block-paragraph">総じて、各社の字幕や翻訳の対応は、まだまだバラバラな状況と言っていいです。そこへ群がるように、各社がAI生成画像、音楽などのサービスをくっつけてきているというのが現状です。</p>



<p class="wp-block-paragraph">そこで、<strong><span class="marker">振り回されないように、字幕一覧を配置して、Apple/Windows翻訳とGoogle翻訳の両方を使い分けるようにしよう</span></strong>。というのが、この投稿の主張です。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=bKOzGEon.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=bKOzGEon.png" alt="" style="aspect-ratio:0.5021524788223858;width:130px;height:auto"/></a></figure>



<hr class="wp-block-separator has-text-color has-key-color-color has-alpha-channel-opacity has-key-color-background-color has-background is-style-wide"/>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph"><strong><span class="fz-16px"><span class="fz-18px"><span class="marker">＜字幕作成に関するオリジナルスクリプト一覧表＞</span></span></span></strong></p>



<figure class="wp-block-flexible-table-block-table is-scroll-on-pc is-scroll-on-mobile"><table class="" style="width:740px;max-width:740px"><tbody><tr><td style="background-color:#f3fafe"><strong>スクリプト名</strong></td><td style="background-color:#f3fafe"><strong>説明</strong></td><td style="background-color:#f3fafe"><strong>補足</strong></td></tr><tr><td><a href="https://imakat.com/wp-content/pmedia/scripts_pub/srtからvttを生成するworkflow_pub.txt" target="_blank">srtからvttを生成する</a></td><td><a href="https://imakat.com/ds5/?drid=2">動画(無音)</a><br>新たに作ったVTTファイルに対して、 <br> (1) 先頭行に文字列&#8221;WEBVTT&#8221;を含む行を挿入する。 <br>(2) 2行目に空白の行を挿入する。 <br>(3) 3行目以降から、読み込んだデータを書き込む。 <br> (4) &#8220;&#8211;&gt;&#8221; がある行は、行の中の&#8221;,&#8221;を&#8221;.&#8221;に置き換える。<br>※元のファイルを残します。</td><td>AppleScriptクイックアクション</td></tr><tr><td><a href="https://imakat.com/wp-content/pmedia/scripts_pub/vttからsrtを生成する兼srtの連番を付け直すworkflow_pub.txt" target="_blank">vttからsrtを生成する兼srtの連番を付け直す</a>(renumbered.srt)</td><td><a href="https://imakat.com/ds5?drid=3">動画(無音</a>)<br>新たに作ったSRTファイルに対して、<br>(1) 文字列”WEBVTT”のある行は削除する。<br>(2) &#8220;&#8211;&gt;&#8221; がある行は、行の中の&#8221;.&#8221;を&#8221;,&#8221;に置き換える。<br>(3)連番を1から振り直す。<br>※元のファイルを残します。新しいファイル名は、&#8221;元のファイル名_renumbered.srt&#8221;となります。</td><td>AppleScriptクイックアクション</td></tr><tr><td><a href="https://imakat.com/wp-content/pmedia/scripts_pub/字幕文章に時刻を挿入するworkflow_pub.txt" target="_blank">字幕文章に時刻を挿入する</a>(timeadded.vtt)</td><td><a href="https://imakat.com/ds5?drid=4">動画(無音</a><a href="https://imakat.com/ds?drid=4">)</a><br>文字列の先頭に、開始時刻(00:01:23)を追記する。私はカモメ→(00:01:23)私はカモメ※元のファイルを残します。_timeadded.vttが新しく作られます。この字幕文章の先頭に時刻情報を入れる目的は、翻訳された外国語の示す動画の位置を分かりやすくするためです。</td><td>AppleScriptクイックアクション</td></tr><tr><td><a href="https://imakat.com/wp-content/pmedia/scripts_pub/字幕文章の時刻を削除するworkflow_pub.txt" target="_blank">字幕文章の時刻を削除する</a>(timeremoved.vtt)</td><td>上の「vttからタイムありvttを生成する」を取り消しにします。※元のファイル_timeadded.vttは残します。新しいファイルは、_timeadded_timeremoved .vttになります。</td><td>AppleScriptクイックアクション</td></tr><tr><td><a href="https://imakat.com/wp-content/pmedia/scripts_pub/vttから字幕一覧用txtを生成workflow_pub.txt" target="_blank">字幕vttから字幕一覧txtを生成する</a></td><td>動画内の字幕は、多くの場合、翻訳されないか動作不安定になるが、字幕一覧txtが動画の下にあれば、翻訳が可能になります。タイムコードの位置にジャンプする_listJ.txtおよびジャンプしない_list.txtを生成します。※元のファイルは残します。新しいファイルは、_list.txtになります。なお既存の場合は上書きします。</td><td>AppleScriptクイックアクション</td></tr><tr><td><a href="#link2"><s>vttから字幕一覧用txtを生成する</s></a></td><td><a href="https://imakat.com/ds5?drid=5">動画(無音)</a><br>字幕一覧用txtは、動画の外側に配置するので、Google翻訳される。<strong>→「字幕vttから字幕一覧txtを生成する」へ集約</strong>。</td><td>AppleScriptクイックアクション</td></tr><tr><td><a href="https://imakat.com/2024/01/22/18942#link1"><s>DropboxリンクをWordPress用に変換する(クリップボード保存)</s></a></td><td><a href="https://imakat.com/ds5?drid=6">動画(無音)</a><br>Dropboxのメディアを外部リンクするために、URLに指定された変更を加える。URLの記述の中の &#8220;www.dropbox.com&#8221;は、そのまま使うようにとりあえず判断した。</td><td>AppleScriptクイックアクション</td></tr><tr><td><a href="https://imakat.com/2024/01/22/18942/#link2"><s>★WordPress動画貼り付けコード生成(クリップボード保存)</s></a></td><td><a href="https://imakat.com/ds5?drid=7">動画(無音)</a><br>WordPressのカスタムhtmlへ貼り付ける。初期画面用画像、動画本体、字幕のファイルを予め用意。→マイライブラリの運用に切り替え。</td><td><span class="red">AppleScriptアプリケーション</span></td></tr><tr><td><a href="#link1">★<s>字幕一覧用txtからカスタムhtmlを生成(クリップボード保存)</s></a></td><td><a href="https://imakat.com/ds5?drid=8">動画(無音</a><a href="https://imakat.com/ds?drid=8">)</a> <a href="https://imakat.com/ds5/?drid=9">動画(無音</a><a href="https://imakat.com/ds/?drid=9">)</a><br>字幕一覧用txtを表示するボックスレイアウトを作る。Google翻訳の表示にて改行がうまくいかない状態がある<strong>。→「字幕vttから字幕一覧txtを生成する」へ集約</strong>。</td><td>AppleScriptクイックアクション</td></tr><tr><td><a href="https://imakat.com/wp-content/pmedia/scripts_pub/字幕空白詰め_vimeo_auto_generated_captions_vttの修正_pub.txt" target="_blank">vimeo_auto_generated_captions.vttの修正をする</a></td><td>Vimeoの字幕自動生成からダウンロードしたファイルの、文字列の空白を削除する。vttのまま。※直接上書きします。何度でもダウンロードできるため。</td><td>AppleScriptクイックアクション</td></tr><tr><td><a href="https://imakat.com/2019/12/14/8731/"><s>★Vimeo登録用htmlコード生成</s></a></td><td>方法１：WordPressで、カスタムhtmlブロックへ登録して、パターンを作成しておく。<br>方法２：Stream deckなどで文字列として登録しておく。<br>方法３：Appleクイックアクションで登録しておく。など。→マイライブラリの運用に切り替え。</td><td>字幕作成とは直接に関係ありませんが、迷子にならないように。</td></tr><tr><td><a href="https://imakat.com/wp-content/pmedia/scripts_pub/字幕時刻の秒ずらしを行うworkflow_pub.txt" target="_blank">vttの字幕時刻の秒ずらしを行う。</a>(timeshifted.vtt)</td><td><a href="https://imakat.com/ds5?drid=25" target="_blank">動画(無音)</a><br>動画の途中で、挿入や削除を行うと、字幕ファイルの時刻にズレが生じます。挿入や削除を行った以降の動画をコピーして、その分の秒ずらしを行うことができます。<span class="bold-red">但し、文章の頭に時刻情報を入れてある(_timeadded.vtt)については、エラーになります。一度、上の「字幕文章の時刻を削除する」を実行した後で、ここを実行してください</span>。<br>※元のファイルを残します。新しいファイル名は&#8221;元のファイル名_timeshited.vtt&#8221;となります。</td><td>AppleScriptクイックアクション</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">ポイント解説</h3>



<h4 class="wp-block-heading">１　その他</h4>



<p class="wp-block-paragraph">WindowsPCでのこのブログにリンクした動画の再生についてです。Dropboxの動画についてですが、Dropboxの動画は、ブログに貼り付けた場合は、そのまま素直にストリーミング再生されます。しかし、URLをリンクで再生しようとした場合、ダウンロードしてからの再生を求められます。それはとても面倒なこと。その点も勘案して、Google翻訳アイコン、動画、字幕一覧の各場所をテンプレートにして、動画については、Vimeo、Dropbox、WordPressサーバー、YouTubeなどを貼り付けられるようにして、WordPressの固定ページで動画ライブラリーとしてひとまとめにするように改善中です。</p>



<p class="wp-block-paragraph">以上</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=ZrH7yK6Q.jpeg" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=ZrH7yK6Q.jpeg" alt="" style="width:146px;height:auto"/></a></figure>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">20002</post-id>	</item>
		<item>
		<title>【MacWin共通】CapCutの自動字幕起こしは、いつまで無料で使えるか？</title>
		<link>https://imakat.com/2023/12/08/18724/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Fri, 08 Dec 2023 09:03:21 +0000</pubDate>
				<category><![CDATA[字幕作成]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Automator]]></category>
		<category><![CDATA[Appleスクリプト]]></category>
		<category><![CDATA[クイックアクション]]></category>
		<category><![CDATA[字幕生成]]></category>
		<category><![CDATA[文字起こし]]></category>
		<category><![CDATA[CapCut]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=18724</guid>

					<description><![CDATA[23.12.26更新：CapCutのMacへのダウンロード版を使っていたら、PROへの参加（即ち有料)をしないと、SRT字幕ファイルのダウンロードが出来なくなりました。ところが、オンライン版は、ダウンロードが可能でした。 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<hr class="wp-block-separator has-text-color has-key-color-color has-alpha-channel-opacity has-key-color-background-color has-background is-style-wide"/>



<p class="wp-block-paragraph">23.12.26更新：CapCutのMacへのダウンロード版を使っていたら、PROへの参加（即ち有料)をしないと、SRT字幕ファイルのダウンロードが出来なくなりました。ところが、<strong>オンライン版は、ダウンロードが可能でした</strong>。しかし、いつか不可能になるかも知れません。</p>



<h5 class="wp-block-heading">CapCutの自動字幕起こしを使う。</h5>



<p class="wp-block-paragraph"><span class="fz-12px">下のビデオは、Mac版CapCutを使用した例です。</span></p>


<div class="sc-dynamic-embed">
  <style>
  /* リンクの見た目を整える */
  .sc-dynamic-embed .sc-link-container { 
      display: flex; 
      margin-bottom: 10px; 
      flex-wrap: wrap; 
      align-items: center; 
  }
  .sc-dynamic-embed .sc-link { margin-bottom: 0; }
  .sc-dynamic-embed .sc-link a {
    font-size: 15px; 
    font-weight: normal;
    text-decoration: underline;
    color: #0073aa;
  }
  .sc-dynamic-embed .sc-link a:hover { text-decoration: none; color: #000; }
  
  @media (max-width: 500px) {
    .sc-dynamic-embed .sc-link a { font-size: 13px; }
  }

  .sc-dynamic-embed .imk-line {
      display: inline-block;
      width: 100%;
      border-radius: 2px;
      transition: background-color 0.1s;
  }

  .sc-dynamic-embed #subtitleOverlay,
  .sc-dynamic-embed #scSubtitleOverlay,
  .sc-dynamic-embed .overlay-cue,
  .sc-dynamic-embed .band {
      display: none !important;
      opacity: 0 !important;
      visibility: hidden !important;
      pointer-events: none !important;
  }
  </style>

  <div class="sc-link-container">
    <p class="sc-link">
      <!-- ★環境依存排除：固定ドメインをルート相対パスへ置換完了 -->
      <a href="/ds62/?drid=59" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <!-- ★環境依存排除：[dynamic_external_html] にルート相対パスを指定し、functions.phpにURL動的解決を委ねる設計 -->
  <style>
            :root{ --dr5emd-max: 1920px; }
            #subtitleOverlay, #scSubtitleOverlay, .overlay-cue, .band { display: none !important; opacity: 0 !important; }
            .imk-line { display: inline-block; width: 100%; border-radius: 2px; transition: background-color 0.1s; }
            .dr5emd-sublist details > p{ height:200px; overflow:auto; background-color:#EDF7FF; padding:2px 6px; margin:0; box-shadow:3px 3px 4px black; position: relative; }
            .dr5emd-sublist details > summary{ padding:2px 6px; width:100%; background-color:#ddd; border:none; box-shadow:3px 3px 4px black; cursor:pointer; list-style:none; }
            
                .video-wrap{position:relative;width:100%;margin:0 auto}
                #myVideo{width:100%;height:auto;min-height:200px;display:block;background:#000;}</style><div class="dr5emd-container"><div class="video-wrap"><video id="myVideo" controls poster="https://imakat.com/rd.php?id=YlVSEDLF.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=M0wA1t9d.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=WobxnR6k.vtt" label="日本語" srclang="ja" kind="subtitles"></video></div><div class="dr5emd-sublist"><details translate="yes"><summary translate="yes">字幕一覧(クリック)</summary> <p translate="yes">
(<a href="#" class="imk-cue" data-seek="0:02" translate="no">00:00:02</a>)  それでは今回、CapCutという無料の動画編集アプリですね。<br>
(<a href="#" class="imk-cue" data-seek="0:05" translate="no">00:00:05</a>)  これを使ってですね、この中に非常に便利な字幕起こし機能が入っているんですね。<br>
(<a href="#" class="imk-cue" data-seek="0:15" translate="no">00:00:15</a>)  これを使ってみたいと思います。<br>
(<a href="#" class="imk-cue" data-seek="0:30" translate="no">00:00:30</a>)  はい、それでは最初に、すでにテストのファイルが入っていますのでこれを開いてみます。<br>
(<a href="#" class="imk-cue" data-seek="0:38" translate="no">00:00:38</a>)  これがテストのファイルですね。<br>
(<a href="#" class="imk-cue" data-seek="0:44" translate="no">00:00:44</a>)  具体的にこういう動画がすでに入っています。<br>
(<a href="#" class="imk-cue" data-seek="0:59" translate="no">00:00:59</a>)  これの文字起こしをしていきたいと思います。<br>
(<a href="#" class="imk-cue" data-seek="1:03" translate="no">00:01:03</a>)  まず最初にこの上にある「テキスト」というところを選びます。<br>
(<a href="#" class="imk-cue" data-seek="1:09" translate="no">00:01:09</a>)  その中に「自動キャプション」というところがありますので、これを選びます。<br>
(<a href="#" class="imk-cue" data-seek="1:18" translate="no">00:01:18</a>)  次に言語を選びます。<br>
(<a href="#" class="imk-cue" data-seek="1:25" translate="no">00:01:25</a>)  今、最初中国語になっていますが、これを日本語にします。日本語にしました。<br>
(<a href="#" class="imk-cue" data-seek="1:35" translate="no">00:01:35</a>)  あとは下の「作成」をクリックするだけです。そうすると自動文字起こしがスタートします。<br>
(<a href="#" class="imk-cue" data-seek="1:46" translate="no">00:01:46</a>)  やってみます。もうスタートしています。<br>
(<a href="#" class="imk-cue" data-seek="2:03" translate="no">00:02:03</a>)  早いですね。もう出来上がりました。<br>
(<a href="#" class="imk-cue" data-seek="2:07" translate="no">00:02:07</a>)  ここにこういう風に文字が入っています。<br>
(<a href="#" class="imk-cue" data-seek="2:16" translate="no">00:02:16</a>)  文字が入っているんですが、白い文字なのでちょっと見えにくいですね。<br>
(<a href="#" class="imk-cue" data-seek="2:28" translate="no">00:02:28</a>)  それでは背景をこう入れて、ONにしてみます。<br>
(<a href="#" class="imk-cue" data-seek="2:40" translate="no">00:02:40</a>)  そうすると黒い背景に白い文字になるので、こんな風にかなり見えやすくなりますね。<br>
(<a href="#" class="imk-cue" data-seek="2:54" translate="no">00:02:54</a>)  ただし文字起こしは雑なところが多いので、もちろん後で手直ししていきます。<br>
(<a href="#" class="imk-cue" data-seek="3:04" translate="no">00:03:04</a>)  手直しをしたという前提で、手直しをした後でこれを書き出す作業をやってみます。<br>
(<a href="#" class="imk-cue" data-seek="3:13" translate="no">00:03:13</a>)  ファイルというところの中にエクスポートがあります。<br>
(<a href="#" class="imk-cue" data-seek="3:20" translate="no">00:03:20</a>)  これをクリックします。<br>
(<a href="#" class="imk-cue" data-seek="3:25" translate="no">00:03:25</a>)  そうしますと、こういう画面になるんですけれども。<br>
(<a href="#" class="imk-cue" data-seek="3:34" translate="no">00:03:34</a>)  「動画のエクスポート」というところにチェックが入っています。<br>
(<a href="#" class="imk-cue" data-seek="3:40" translate="no">00:03:40</a>)  このチェックを取ると、ここにSRTというのが出てきて、これが字幕ファイルですね。<br>
(<a href="#" class="imk-cue" data-seek="3:46" translate="no">00:03:46</a>)  このSRTという字幕ファイルだけを書き出すことができます。<br>
(<a href="#" class="imk-cue" data-seek="3:55" translate="no">00:03:55</a>)  まず始めにこれをやります。やってみます。<br>
(<a href="#" class="imk-cue" data-seek="4:01" translate="no">00:04:01</a>)  はい、キャプションがエクスポートされました。<br>
(<a href="#" class="imk-cue" data-seek="4:06" translate="no">00:04:06</a>)  フォルダーを開いてみると、ここに「1206 test2」というものが出来上がっています。<br>
(<a href="#" class="imk-cue" data-seek="4:15" translate="no">00:04:15</a>)  これを開くと字幕用のSRTファイルですね。こんなように書き出されております。<br>
(<a href="#" class="imk-cue" data-seek="4:32" translate="no">00:04:32</a>)  字幕の書き出しが済んだとしますね。<br>
(<a href="#" class="imk-cue" data-seek="4:39" translate="no">00:04:39</a>)  そしたら次に、今この動画の中にこんなように字幕が入っているわけですけれども。<br>
(<a href="#" class="imk-cue" data-seek="4:57" translate="no">00:04:57</a>)  このまま動画を書き出しますと、字幕が動画の中に焼き込まれてしまいます。<br>
(<a href="#" class="imk-cue" data-seek="5:08" translate="no">00:05:08</a>)  イメージとして一体化してしまいます。<br>
(<a href="#" class="imk-cue" data-seek="5:17" translate="no">00:05:17</a>)  まあそれはもう必要ないんですね。先ほどSRTとして字幕ファイルを別に書き出しましたので。<br>
(<a href="#" class="imk-cue" data-seek="5:26" translate="no">00:05:26</a>)  この動画自体は字幕を表示しないようにしたいんです。<br>
(<a href="#" class="imk-cue" data-seek="5:32" translate="no">00:05:32</a>)  したがって、この字幕ファイルの目のマークをクリックして。<br>
(<a href="#" class="imk-cue" data-seek="5:43" translate="no">00:05:43</a>)  字幕ファイルが書き出されないようにします。<br>
(<a href="#" class="imk-cue" data-seek="5:55" translate="no">00:05:55</a>)  はい、この状態でファイルのエクスポート。<br>
(<a href="#" class="imk-cue" data-seek="6:02" translate="no">00:06:02</a>)  そして先ほどの動画のエクスポートのところ、チェックが外れていますがこれをONにします。<br>
(<a href="#" class="imk-cue" data-seek="6:14" translate="no">00:06:14</a>)  これでエクスポートをします。そうすると字幕のない動画だけが書き出されます。<br>
(<a href="#" class="imk-cue" data-seek="6:33" translate="no">00:06:33</a>)  はい、これで書き出しが終わりました。<br>
(<a href="#" class="imk-cue" data-seek="6:38" translate="no">00:06:38</a>)  フォルダーを開いてみますと、一番上の「test3 mp4」というものが書き出されました。<br>
(<a href="#" class="imk-cue" data-seek="6:52" translate="no">00:06:52</a>)  ではこれを開いてみます。<br>
(<a href="#" class="imk-cue" data-seek="7:07" translate="no">00:07:07</a>)  まあこういう風に動画ができているんですけれども。<br>
(<a href="#" class="imk-cue" data-seek="7:14" translate="no">00:07:14</a>)  ご覧のように字幕は入っていないです。<br>
(<a href="#" class="imk-cue" data-seek="7:22" translate="no">00:07:22</a>)  はい、以上、このような形です。<br>
</p> </details>
<style>
details { font: 16px "Open Sans", Calibri, sans-serif; width: 100%; }
details > summary { padding: 2px 6px; width: 100%; background-color: #ddd; border: none; box-shadow: 3px 3px 4px black; cursor: pointer; list-style: none; }
details > p { font: 14px "Open Sans", Calibri, sans-serif; height:150px; overflow: scroll; background-color: #EDF7FF; padding: 2px 6px; margin: 0; box-shadow: 3px 3px 4px black; }
</style>
</div></div>            <script>
            document.addEventListener("DOMContentLoaded", function(){
                var video = document.getElementById("myVideo");
                if(!video) return;

                            });
            </script>
            

  <script>
  (function(){
    var me = document.currentScript;
    var wrapper = me ? me.closest('.sc-dynamic-embed') : null;

    function protectVideo() {
      var target = wrapper ? wrapper : document;
      var mediaEls = target.querySelectorAll('video, audio');
      mediaEls.forEach(function(v){
        if(v.dataset.protected === 'true') return;
        v.dataset.protected = 'true';
        v.setAttribute('controlsList', 'nodownload');
        v.oncontextmenu = function() { return false; };
        v.addEventListener('contextmenu', function(e){ e.preventDefault(); return false; }, false);
      });
    }

    function initSubtitles() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video, audio');
      var listContainer = target.querySelector('details > p');
      
      if (!video) return false; 
      
      if (video.dataset.subInit === 'true') return true; 
      video.dataset.subInit = 'true';

      var oldOverlay = target.querySelector('#subtitleOverlay') || target.querySelector('#scSubtitleOverlay');
      if (oldOverlay) {
          oldOverlay.style.setProperty('display', 'none', 'important');
          oldOverlay.innerHTML = ''; 
      }

      // 全画面・PiP・iOS全画面の判定（dr52.php準拠＋Safari presentationMode完全対応）
      function isSpecialMode() {
        var fsEl = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
        var isFs = !!fsEl;
        var isWebkitFs = (video.webkitPresentationMode === "fullscreen") || !!video.webkitDisplayingFullscreen;
        var isPiP = !!(document.pictureInPictureElement && document.pictureInPictureElement === video) || (video.webkitPresentationMode === "picture-in-picture");
        return isFs || isWebkitFs || isPiP;
      }

      // 字幕トラックの表示/非表示を更新
      function updateTrackMode() {
        var hasList = (listContainer !== null);
        var desiredMode = (isSpecialMode() || !hasList) ? "showing" : "hidden";
        try {
          if (video.textTracks && video.textTracks.length > 0) {
            for (var i = 0; i < video.textTracks.length; i++) {
              var track = video.textTracks[i];
              if ((track.kind === 'subtitles' || track.kind === 'captions') && track.mode !== desiredMode) {
                track.mode = desiredMode;
              }
            }
          }
        } catch(e) {}
      }

      // 初回の反映
      updateTrackMode();

      // 各種全画面・PiP切り替えイベントの即時検知リスナーを登録（video本体・document双方を監視）
      ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'].forEach(function(evtName) {
        document.addEventListener(evtName, updateTrackMode, false);
        video.addEventListener(evtName, updateTrackMode, false);
      });
      video.addEventListener('webkitbeginfullscreen', updateTrackMode, false);
      video.addEventListener('webkitendfullscreen', updateTrackMode, false);
      video.addEventListener('webkitpresentationmodechanged', updateTrackMode, false);
      video.addEventListener('enterpictureinpicture', updateTrackMode, false);
      video.addEventListener('leavepictureinpicture', updateTrackMode, false);
      video.addEventListener('play', updateTrackMode, false);
      video.addEventListener('pause', updateTrackMode, false);

      // 字幕一覧が存在する場合のみ、リストのフォーマット処理を行う
      if (listContainer) {
          var detailsEl = target.querySelector("details");
          if (detailsEl) {
              detailsEl.open = true; 
              var summaryEl = detailsEl.querySelector("summary");
              if (summaryEl) summaryEl.textContent = "字幕(シーン)はここをクリック";
          }

          if (!listContainer.dataset.formatted) {
              var html = listContainer.innerHTML;
              var lines = html.split(/<br\s*\/?>/i);
              var newHtml = "";
              for(var j=0; j<lines.length; j++) {
                  if(lines[j].trim() === "") continue;
                  newHtml += "<span class='imk-line'>" + lines[j] + "</span><br>";
              }
              listContainer.innerHTML = newHtml;
              listContainer.dataset.formatted = "true";
          }

          var rootSublist = target.querySelector(".dr5-sublist") || listContainer.parentElement;
          if (rootSublist) {
            rootSublist.addEventListener("click", function(e){
              var a = e.target.closest && e.target.closest("a.imk-cue[data-seek]");
              if(!a) return;
              e.preventDefault();
              var ts = a.getAttribute("data-seek");
              if(!ts) return;
              var p = ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
              var sec = (p.length===2) ? p[0]*60+p[1] : (p.length===3 ? p[0]*3600+p[1]*60+p[2]: null);
              if(sec==null) return;
              try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
            });
          }
      }

      // 再生中（時間進行時）の追従とハイライト更新
      video.addEventListener("timeupdate", function(){
        updateTrackMode();

        var hasList = (listContainer !== null);
        if (!hasList) return;

        var cues = listContainer.querySelectorAll("a.imk-cue");
        if(cues.length === 0) return;
        var cur = video.currentTime;
        var activeA = null;

        for(var i=0; i<cues.length; i++){
            var ts = cues[i].getAttribute("data-seek");
            if(!ts) continue;
            var p = ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
            var t = (p.length===2) ? p[0]*60+p[1] : (p.length===3 ? p[0]*3600+p[1]*60+p[2] : null);
            if(t !== null && cur >= t - 0.5){ activeA = cues[i]; } 
            else if(t > cur){ break; }
        }

        if(activeA){
            var activeLine = activeA.closest(".imk-line");
            if(!activeLine) activeLine = activeA;

            if(activeLine.classList.contains("active-hl")) return;

            var allLines = listContainer.querySelectorAll(".imk-line");
            for(var k=0; k<allLines.length; k++) {
                allLines[k].classList.remove("active-hl");
                allLines[k].removeAttribute("style"); 
            }
            var allLinks = listContainer.querySelectorAll("a");
            for(var m=0; m<allLinks.length; m++) {
                allLinks[m].classList.remove("active-hl");
                allLinks[m].removeAttribute("style"); 
            }

            activeLine.classList.add("active-hl");
            activeLine.style.setProperty("background-color", "#ffff00", "important");
            activeLine.style.setProperty("color", "red", "important");
            activeLine.style.setProperty("font-weight", "normal", "important");
            
            var newLinks = activeLine.querySelectorAll("a");
            for(var n=0; n<newLinks.length; n++) {
                newLinks[n].style.setProperty("color", "red", "important");
                newLinks[n].style.setProperty("text-decoration", "none", "important");
            }

            if(listContainer.offsetParent !== null){
                var containerRect = listContainer.getBoundingClientRect();
                var activeRect = activeLine.getBoundingClientRect();
                var targetScroll = listContainer.scrollTop + (activeRect.top - containerRect.top) - (listContainer.clientHeight / 2) + (activeLine.clientHeight / 2);
                listContainer.scrollTo({ top: targetScroll, behavior: "smooth" });
            }
        }
      });

      return true;
    }

    var checks = 0;
    var checkTimer = setInterval(function(){
      protectVideo();
      var success = initSubtitles();
      checks++;
      if (success || checks > 20) { 
        clearInterval(checkTimer);
      }
    }, 500); 

    if (!window.scStopAndGo) {
      window.scStopAndGo = function(event, link){
        try{
          var mediaEls = document.querySelectorAll('video, audio');
          mediaEls.forEach(function(m){
            try{
              if (!m.paused) m.pause();
              if (document.pictureInPictureElement === m && document.exitPictureInPicture) {
                document.exitPictureInPicture().catch(function(){});
              }
            }catch(e){}
          });
        }finally{
          event.preventDefault();
          setTimeout(function(){
            if (link.target === '_blank') {
              window.open(link.href, '_blank');
            } else {
              window.location.href = link.href;
            }
          }, 50);
        }
        return false;
      };
    }
  })();
  </script>
</div>



<p class="wp-block-paragraph"><strong>はじめに：</strong></p>



<p class="wp-block-paragraph">一つ前の投稿で紹介したVimeoの字幕生成機能を使う方法ですが、それはあくまで暫定手段なのだろう、やはり動画編成アプリ自体に字幕起こし機能が備わっているのがベストだ、と思えています。最近、CapCutという動画編成アプリを見つけまして、これが無料で、自動字幕起こし機能がついています。しかも動画編集機能もシンプルで扱いやすいので、動画として完成させて、あちこちへアップロードして幅広く使おうと思っていました。</p>



<p class="wp-block-paragraph"><strong>CapCutはTikTokワールドの一部。無料のアプリを使うのに、あれこれ要望はできない。規約に従うのみ。：</strong></p>



<p class="wp-block-paragraph">しかしこのCapCutというアプリはTikTokの会社の製品であり、TikTokへの動画加工から配信までの流れを簡単にできるようにすることに主眼があります。著作権については、CapCutで制作した動画の所有権はTikTok会社に移り、TikTok会社の所有になった動画は、作者の許可なく再加工ができる、ということです。あの「さすらいネキ」は、どれがオリジナルか分からないくらい再加工の投稿が溢れかえっています。渡ったら最後戻ってこないと思った方がいいです。何が怖いかって、それは、悪用されることです。</p>



<p class="wp-block-paragraph"><strong>やはり、無料なのに、広告も入らない、会社ロゴの刷り込みもされない、なんて、不自然です。タダより高いものはない。</strong></p>



<p class="wp-block-paragraph">TikTokビジネス圏、TikTokワールドで、みんなで共有して楽しむ展示物として供出した、そういう位置付けとなります。<strong>例えると、夏休みの親子絵画体験会。主催者側が道具や指導の先生を準備、描いた作品を会館に展示、作品はテレビで紹介もします。そんなビジネスモデルですね。</strong>無料で道具を使わせてもらうわけですので、文句を言う立場にはない、CapCutを利用して制作した動画の扱いについては規約に従う必要があります。</p>



<p class="wp-block-paragraph"><strong>お試し的に、使ってみよう：</strong></p>



<p class="wp-block-paragraph">CapCutの利用ついては、三択です。（１）全く使わない、（２）<strong>一部機能である字幕起こしを使ってSRTファイルを作るプロセスのみを利用する</strong>、（３）動画加工も含めて全部利用する、この三つです。</p>



<p class="wp-block-paragraph">（３）は、私はオリジナルの音楽の所有権を放棄するつもりもありませんので、選びません。（２）の字幕起こし機能については15分以内等の使用制限の範囲で無料で提供される文字起こしサービスで、Google,Vrewなど他社も同様なサービスを一定の使用制限内を無料で提供しているので、それと同様に、一つの単機能として、一定の制限内で、使用できるものと今のところは捉えます。しかしながら、CapCutとしてはそれは許可しないのであれば、文字起こしは当面、単機能アプリであるVrewなどを使うことにします。つまり（１）を選んでCapCutは全く使わないことになろうと思います。</p>



<h3 class="wp-block-heading">ポイント解説</h3>



<h4 class="wp-block-heading">１　字幕のフォーマット形式はSRTがいい。ほぼ全体をカバー。</h4>



<p class="wp-block-paragraph">2023年12月時点で、アプリごとの字幕ファイルのフォーマットを調べてみました。調べ方は、既にメジャーになっているSRTまたはVTTを使っているかどうかです。</p>



<figure class="wp-block-flexible-table-block-table is-scroll-on-pc is-scroll-on-mobile"><table class="has-fixed-layout"><thead><tr><th>サーバー・アプリ</th><th>字幕フォーマット形式(SRTかVTTか）</th><th>自動文字起こし後の出力(SRTかVTTか)その他の注意点</th></tr></thead><tbody><tr><td><strong><span class="marker">Vimeo</span></strong>(サーバー)</td><td><strong><span class="marker">SRT </span></strong><br>※24.11月ごろからVTTがうまく使えなくなりました。</td><td>VTT　自動文字起こしあり。auto_generated_captions.vttという名前でダウンロードされる。</td></tr><tr><td>YouTube(サーバー)</td><td>SRT VTT</td><td>YouTube Studioの中の字幕→「字幕をダウンロード」→SBV形式。</td></tr><tr><td><strong><span class="marker">WordPress</span></strong>(サーバー)</td><td><strong><span class="marker">VTT</span></strong></td><td>自動文字起こしなし</td></tr><tr><td><span class="marker"><strong>Final Cut Pro</strong></span></td><td><span class="marker"><strong>SRT</strong></span></td><td>自動文字起こしなし</td></tr><tr><td>Davinci Resolve</td><td>SRT</td><td>SRT　自動文字起こしあり。</td></tr><tr><td>Filmora</td><td>SRT</td><td>SRT　自動文字起こしあり。無料の範囲が短い。料金体系が分かりにくい。</td></tr><tr><td>Vrew</td><td>SRT</td><td>SRT 文字起こしは月120分まで無料。</td></tr><tr><td><strong><span class="marker">CapCut</span></strong>(オンライン版)</td><td><strong><span class="marker">SRT</span></strong></td><td><strong><span class="marker">SRT</span>　15分以内　SRTで出力した後、Final Cut Proへ読み込むなど。WordPressで使うならVTTへ変換する。</strong>※QuickTime PlayerやFinal Cut Proで、ある程度、動画の成形が済んだら、動画をCapCutで読み込んでSRTを作成する。</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">以上から、SRTを基調として使い、SRT&lt;-&gt;VTT相互に変換できるようなツールを用意しておけば良さそうです。ところで、YouTubeのSBV形式ですが、SRT形式と殆ど同じです。</p>



<p class="wp-block-paragraph"><strong>一点だけ異なります。<br>SBVのタイムコードの秒とミリ秒の間は&#8221;.&#8221;です。<br>SRTのタイムコードの秒とミリ秒の間は&#8221;,&#8221;です。</strong></p>



<p class="wp-block-paragraph">以下のAppleスクリプトをMacのAutomatorのクイックアクションで動かすと一発で変換できます。</p>



<p class="wp-block-paragraph"><strong><span class="marker">SBVからSRTを生成するAppleスクリプト</span></strong></p>



<p class="wp-block-paragraph">（１）「 AppleScriptを実行」を挿入</p>



<pre class="wp-block-code"><code>on run {input, parameters}
	set <strong><span class="marker">sbv</span></strong>FilePath to POSIX path of input
	
	-- 新しいファイルに拡張子をSRTに変更してコピー
	set <strong><span class="marker">srt</span></strong>FilePath to replaceExtension(<strong><span class="marker">sbv</span></strong>FilePath, "<strong><span class="marker">srt</span></strong>")
	do shell script "cp " &amp; quoted form of <strong><span class="marker">sbv</span></strong>FilePath &amp; " " &amp; quoted form of <strong><span class="marker">srt</span></strong>FilePath
	
	return <strong><span class="marker">srt</span></strong>FilePath
end run

-- 拡張子を置き換える補助関数
on replaceExtension(filePath, newExtension)
	set AppleScript's text item delimiters to "."
	set pathItems to text items of filePath
	set lastItem to last item of pathItems
	set last item of pathItems to newExtension
	set AppleScript's text item delimiters to "."
	set newPath to pathItems as text
	return newPath
end replaceExtension</code></pre>



<p class="wp-block-paragraph">（２）「 AppleScriptを実行」を挿入</p>



<pre class="wp-block-code"><code>on run {input, parameters}
	set <strong><span class="marker">srt</span></strong>FilePath to POSIX path of input
	
	-- 秒とミリ秒の間の"<span class="marker">.</span>"を"<span class="marker">,</span>"に変更してファイルに書き込む
	do shell script "sed -i '' 's/<span class="marker">.</span>/<span class="marker">,</span>/g' " &amp; quoted form of <strong><span class="marker">srt</span></strong>FilePath
	
	return input
end run</code></pre>



<p class="wp-block-paragraph"><strong><span class="marker">SRTからSBVを生成するAppleスクリプト</span></strong><br>※実際に使うことは無いかも知れません。</p>



<p class="wp-block-paragraph">（１）「 AppleScriptを実行」を挿入</p>



<pre class="wp-block-code"><code>on run {input, parameters}
	set <strong><span class="marker">srt</span></strong>FilePath to POSIX path of input
	
	-- 新しいファイルに拡張子をSRTに変更してコピー
	set <strong><span class="marker">sbv</span></strong>FilePath to replaceExtension(<strong><span class="marker">srt</span></strong>FilePath, "<strong><span class="marker">sbv</span></strong>")
	do shell script "cp " &amp; quoted form of <strong><span class="marker">srt</span></strong>FilePath &amp; " " &amp; quoted form of <strong><span class="marker">sbv</span></strong>FilePath
	
	return <strong><span class="marker">sbv</span></strong>FilePath
end run

-- 拡張子を置き換える補助関数
on replaceExtension(filePath, newExtension)
	set AppleScript's text item delimiters to "."
	set pathItems to text items of filePath
	set lastItem to last item of pathItems
	set last item of pathItems to newExtension
	set AppleScript's text item delimiters to "."
	set newPath to pathItems as text
	return newPath
end replaceExtension</code></pre>



<p class="wp-block-paragraph">（２）「 AppleScriptを実行」を挿入</p>



<pre class="wp-block-code"><code>on run {input, parameters}
	set <strong><span class="marker">sbv</span></strong>FilePath to POSIX path of input
	
	-- 秒とミリ秒の間の"<span class="marker">,</span>"を"<span class="marker">.</span>"に変更してファイルに書き込む
	do shell script "sed -i '' 's/<span class="marker">,</span>/<span class="marker">.</span>/g' " &amp; quoted form of <strong><span class="marker">sbv</span></strong>FilePath
	
	return input
end run</code></pre>



<h4 class="wp-block-heading">２　字幕情報の各国語翻訳への対応</h4>



<p class="wp-block-paragraph">各動画に、字幕情報をクローズドキャプションとして表示することは、上記の対応で可能となりますが、その字幕情報を各国語へ翻訳したい時、動画の外側に字幕情報がテキストデータで掲載されていれば、このブログの最上段にあるGoogle翻訳の言語選択を行うことにより、その字幕情報もブログの一部として、翻訳が実行されます。具体的には、このページの上方の動画の下にある字幕情報のボックスがそれになります（すみません。雑な字幕起こしの段階で掲載しているのであまり効果的には感じられないかも知れません）。</p>



<p class="wp-block-paragraph">この字幕情報ボックスへ格納するための字幕情報を作成する、Appleスクリプト及びWordPressのカスタムhtmlブロックを紹介します。</p>



<p class="wp-block-paragraph"><strong><span class="marker">VTTファイルからWordPress表示用TXTファイルを生成</span><span class="marker"> Appleスクリプト</span></strong></p>



<p class="wp-block-paragraph"><a href="https://imakat.com/2024/02/11/20002/#link2">こちらに記載</a></p>



<p class="wp-block-paragraph"><strong><span class="marker">WordPressブログのカスタムhtmlブロック</span></strong></p>



<p class="wp-block-paragraph">上で生成したTXTファイルのデータを、下の<strong>&lt;!&#8211; 以下から書き出し &#8211;&gt;</strong>　と　<strong>&lt;!&#8211; 以上まで書き出し &#8211;&gt;</strong>の間の行に、コピー＆ペーストしてください。</p>



<pre class="wp-block-code"><code>&lt;details&gt; 
&#091;prisna-google-website-translator] 
&lt;summary&gt;字幕情報を全行表示します(クリック)&lt;/summary&gt;
&lt;p&gt;
<strong>&lt;!-- 以下から書き出し --&gt;</strong>

<strong>&lt;!-- 以上まで書き出し --&gt;</strong>
&lt;/p&gt;
&lt;/details&gt;

&lt;style&gt;
details {
  font: 16px "Open Sans", Calibri, sans-serif;
  width: 100%;
}

details &gt; summary {
  padding: 2px 6px;
 width: 100%; 
  background-color: #ddd;
  border: none;
  box-shadow: 3px 3px 4px black;
  cursor: pointer;
  list-style: none;
}

details &gt; p {
  font: 14px "Open Sans", Calibri, sans-serif;
  height:150px;
  overflow: scroll;
  background-color: #EDF7FF;
  padding: 2px 6px;
  margin: 0;
  box-shadow: 3px 3px 4px black;
}
&lt;/style&gt;</code></pre>



<p class="wp-block-paragraph">上のコピー＆ペーストは面倒なこともあると思います。txtファイルの上で右クリックをしてクイックアクションでクリップボードに書き込むスクリプトは以下です。</p>



<p class="wp-block-paragraph"><strong><span class="marker">WordPressブログのカスタムhtmlブロックをクイックアクションで生成するAppleScript</span></strong></p>



<p class="wp-block-paragraph"><a href="https://imakat.com/2024/02/11/20002/#link1">こちらに記載</a></p>



<p class="wp-block-paragraph">但し、私が記述している日本語の段階では、改行コード&lt;br&gt;は有効に働きますが、私の環境の問題か、Google翻訳の問題かわかりませんが、<strong>各国語に翻訳した時、改行がされない、空白行が生ずる、表示順番があべこべになる言語があるなど、不具合が生ずることがあります</strong>。より正確に翻訳を行いたい場合は、各ブラウザから直接呼び出せるプラグインなど、別のアプリをお試しください。改善は引き続き進めます。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=trOWsQH4.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=trOWsQH4.png" alt=""/></a></figure>



<h4 class="wp-block-heading">３　まとめ</h4>



<p class="wp-block-paragraph">実際のところ、Final Cut ProやQuickTime Player などで動画の寸法がある程度決まったら、動画を一時的に読み込んで、文字起こしをCapCutまたはVrew、またはVimeoなどで行い、そのSRTファイルを、Final Cut Proで読み込みます。SRTファイルの読み込みは、この1回だけです。その後、何かメディアを追加削除したり、トランジションを入れると、字幕が増えたり、書き換えが起きますが、字幕修正は、Final Cut Proの中で手で部分修正します。</p>



<p class="wp-block-paragraph">器材記録：</p>



<p class="wp-block-paragraph">CapCut<br>Adobe puppet Dr.AppleSmith<br>Mac mini M2 Ventura<br>BGM:オリジナル音楽</p>



<p class="wp-block-paragraph">以上です。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=qHjxqFix.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=qHjxqFix.png" alt="" style="width:169px;height:auto"/></a></figure>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">18724</post-id>	</item>
		<item>
		<title>【Mac】Vimeoの日本語自動生成を修正利用する。</title>
		<link>https://imakat.com/2023/11/18/18638/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Sat, 18 Nov 2023 08:01:49 +0000</pubDate>
				<category><![CDATA[字幕作成]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[文字起こし]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[vimeo]]></category>
		<category><![CDATA[Automator]]></category>
		<category><![CDATA[Appleスクリプト]]></category>
		<category><![CDATA[クイックアクション]]></category>
		<category><![CDATA[字幕生成]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=18638</guid>

					<description><![CDATA[私の場合、Vimeoの一番安いクラスPlusの会員、700円/月、250GBになっています。現在はもうPlusの募集は無く、新体系でStarterが登場し1,200円/月、2TBということになっています。Vimeoの有料 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<hr class="wp-block-separator has-text-color has-key-color-color has-alpha-channel-opacity has-key-color-background-color has-background is-style-wide"/>



<p class="wp-block-paragraph">私の場合、Vimeoの一番安いクラスPlusの会員、700円/月、250GBになっています。現在はもうPlusの募集は無く、<a rel="noopener" target="_blank" href="https://www.digital-farm.com/staffblog/13841">新体系<span class="fa fa-external-link external-icon anchor-icon"></span></a>でStarterが登場し1,200円/月、2TBということになっています。Vimeoの有料会員のサービスに、日本語字幕の自動生成が含まれるようになりました。これを使うのが経済的です。最近、自動生成される文字起こしも、使い物になってきていると感じます。このやり方の何がいいかというと、自分でテキストエディタなどで修正を行なって即反映できること。実は字幕データの修正は、つまり文字の移動や時刻の移動などは、アプリやWeb上で行うより、使い慣れたテキストエディタで行う方がラクです。Vimeoはそうした形での利用に向いています。例えばVimeoの自動字幕起こしを元に作った例は、こんな感じです。</p>



<p class="wp-block-paragraph"><a href="https://imakat.com/vm5/?movid=984143974" target="_blank">自動字幕起こしにvimeoを使った例_OBS~音声の設定 9分(動画&#x1f3a5;)</a></p>



<p class="wp-block-paragraph"><strong>字幕起こしについては、色々触ってみましたが、今一番コスパがいいと思うのは、Vimeoです</strong>。動画をアップロード→自動字幕起こし→srtをダウンロード→下記の文字修正→テキストエディタ上で文字直し→必要ならFinalCutProへ読み込んで尺の調整→完成アップロード、となります。<strong>Vimeoの翻訳レベルは割と高い、句読点、。が入っている点も好みです。</strong></p>



<p class="wp-block-paragraph">さらに、Vimeoは動画配信をそのまま、差し替え自由、広告なしでやってくれる点を忘れてはなりません。</p>



<p class="wp-block-paragraph">修正作業を効率化するフロー及びツールを作りましたので、以下にご紹介します。</p>



<h3 class="wp-block-heading">ポイント解説</h3>



<h4 class="wp-block-heading">１　<strong>Vimeoの日本語(自動生成）を利用しダウンロードします。</strong>ところが。</h4>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=My3YekCk.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=My3YekCk.png" alt=""/></a></figure>



<p class="wp-block-paragraph">ところが、<strong>自動生成したものは、下のように、文字と文字の間に、不規則に、空白が入っており、このまま使いたくありません</strong>。それから、<strong>auto_generated_captions.vttとしてダウンロードされます。つまりvttファイルです。それと連番が０から始まっています。srtは連番は1から始まる必要があり、Final Cut Proで使うためには変換が必要になります</strong>。これも全体の流れを複雑にしている理由でもありますが。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=Cxl7plBd.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=Cxl7plBd.png" alt="" style="width:820px;height:auto"/></a><figcaption class="wp-element-caption">Vimeoで最初に自動生成されるvttファイル( auto_generated_captions.vtt )は、上のようにかなり低品質。これを活用するか、別の文字起こしアプリを使うか迷うところだが。。</figcaption></figure>



<h4 class="wp-block-heading">２　<strong>MacのAutomatorを使い、Appleスクリプトを</strong>記述します。</h4>



<p class="wp-block-paragraph"><strong>（１）文字と文字の空白を削除する。</strong></p>



<p class="wp-block-paragraph"><strong>（２）&#8221;&#8211;&gt;&#8221;の前後の空白も削除されてしまうので、&#8221; &#8211;&gt; &#8220;に置き換える。</strong></p>



<p class="wp-block-paragraph"><strong>（３）先頭行を&#8221;WEBVTT &#8220;だけにして、２行目に空白行を挿入する。</strong></p>



<p class="wp-block-paragraph" id="link1"><strong>&nbsp;<span class="marker">Vimeo修正用　Appleスクリプト</span></strong>：コピーしてご利用ください。</p>



<p class="wp-block-paragraph" id="link1">「 AppleScriptを実行」を挿入</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>on run {input, parameters}
	set filePath to POSIX path of input
	
	-- Remove all spaces from the file
	do shell script &quot;sed -i &#39;&#39; -e &#39;s/ //g&#39; &quot; & quoted form of filePath
	
	-- Ensure the arrow has spaces on both sides
	do shell script &quot;sed -i &#39;&#39; -e &#39;s/--&gt;/ --&gt; /g&#39; &quot; & quoted form of filePath
	
	-- Ensure the first line is exactly &quot;WEBVTT&quot;
	do shell script &quot;sed -i &#39;&#39; -e &#39;1s/.*/WEBVTT/&#39; &quot; & quoted form of filePath
	
	return input
end run</code></pre></div>



<h4 class="wp-block-heading">３　<strong>クイックアクションで、vttファイルを修正します。</strong></h4>



<p class="wp-block-paragraph">コピーしたvttファイルの上で右クリック→クイックアクション→vimeoのvttの修正、を起動します。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=7s89DUsN.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=7s89DUsN.png" alt=""/></a></figure>



<p class="wp-block-paragraph">すると、以下のように、修正されます。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=X8STQYLt.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=X8STQYLt.png" alt=""/></a></figure>



<p class="wp-block-paragraph">これを元に、テキストエディットで更に手直しを行い、vttファイルを完成します。それをvimeoの字幕へアップロードします。</p>



<p class="wp-block-paragraph">上記説明とは直接には関係ありませんが、この際、あると便利なものを作っておきます。</p>



<p class="wp-block-paragraph">vttからsrt生成、srtからvtt生成についても、あちこちで変換ツールが提供されていますが、Macとして一番使いやすいのは、Finderから対象ファイルの上で右クリックで操作できるのが一番ラクです。そのためのAppleスクリプトは以下です。ご利用ください。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=3efP5rvs.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=3efP5rvs.png" alt=""/></a></figure>



<p class="wp-block-paragraph" id="link2"><span class="marker"><strong>vttからsrt生成兼連番付け直しスクリプト</strong></span></p>



<p class="wp-block-paragraph">vttからsrtを作成するときは、連番が0からではなく、1から始まるようにする必要があります。vttファイル上で作業するときに、いくつかの文章行をまとめたり削除したりしますと、当然、連番は変わっていきます。連番の付け直しの作業も行います。つまり、このスクリプトは、vttからsrtを生成するときとsrtの内容を手直しして連番を正しくしたいときに使います。_renumbered.srtが新しくできます。<br>（１）「 AppleScriptを実行」を挿入</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>-- 手順１　内容を複製した新たなSRTファイルを作成する。
on run {input, parameters}
	set srtFilePath to POSIX path of (first item of input)
	
	-- 新しいファイル名を作成
	set fileName to do shell script &quot;basename &quot; & quoted form of srtFilePath
	set baseName to text 1 thru ((offset of &quot;.&quot; in fileName) - 1) of fileName
	set newFileName to baseName & &quot;_renumbered.srt&quot;
	set newFilePath to (text 1 thru -((count fileName) + 1) of srtFilePath) & newFileName
	
	-- ファイルをコピーして新しいファイルを作成する
	do shell script &quot;cp &quot; & quoted form of srtFilePath & &quot; &quot; & quoted form of newFilePath
	
	-- 次の処理に新しいファイルのパスを渡す
	return {newFilePath}
end run</code></pre></div>



<p class="wp-block-paragraph">（２）「 AppleScriptを実行」を挿入</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>-- 手順２　新しいSRTファイルの内容を更新する。
on run {input, parameters}
	set newFilePath to POSIX path of (first item of input)
	
	-- SRTファイルの内容を読み込む
	set fileContent to read newFilePath as «class utf8»
	set fileLines to paragraphs of fileContent
	
	-- 新しい内容を生成
	set newContent to &quot;&quot;
	set counter to 1
	set lineCount to count of fileLines
	
	repeat with i from 1 to lineCount
		if (item i of fileLines) contains &quot;--&gt;&quot; then
			-- 連番を追加
			set newContent to newContent & (counter as string) & linefeed
			-- 時刻行
			set newContent to newContent & (item i of fileLines) & linefeed
			-- 文章情報行
			if i + 1 ≤ lineCount then
				set newContent to newContent & (item (i + 1) of fileLines) & linefeed
			end if
			-- 空白行
			set newContent to newContent & linefeed
			-- 連番カウンターを増加
			set counter to counter + 1
		end if
	end repeat
	
	-- ファイルに新しい内容を書き込む
	set fileHandle to open for access newFilePath with write permission
	set eof of fileHandle to 0
	write newContent to fileHandle as «class utf8»
	close access fileHandle
	
	return input
end run</code></pre></div>



<p class="wp-block-paragraph" id="link3"><strong><span class="marker">srtからvtt生成用Appleスクリプト</span></strong></p>



<p class="wp-block-paragraph">（１）「 AppleScriptを実行」を挿入</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>-- 手順１　内容を複製した新たなvttファイルを作成する。
on run {input, parameters}
	set srtFilePath to POSIX path of input
	
	-- 拡張子をVTTに置き換える
	set newFilePath to replaceExtension(srtFilePath, &quot;vtt&quot;)
	
	-- ファイル名を作成する
	set fileName to do shell script &quot;basename &quot; & quoted form of srtFilePath
	set baseName to text 1 thru ((offset of &quot;.&quot; in fileName) - 1) of fileName
	set newFileName to baseName & &quot;.vtt&quot;
	
	-- 新しいファイルのパスを生成する
	set newFilePath to (text 1 thru -((count fileName) + 1) of srtFilePath) & newFileName
	
	-- ファイルをコピーして新しいファイルを作成する
	do shell script &quot;cp &quot; & quoted form of srtFilePath & &quot; &quot; & quoted form of newFilePath
	
	-- 手順２に新しいファイルのパスを渡す
	return {newFilePath}
end run

-- 拡張子を置き換える補助関数
on replaceExtension(filePath, newExtension)
	set AppleScript&#39;s text item delimiters to &quot;.&quot;
	set pathItems to text items of filePath
	set lastItem to last item of pathItems
	set last item of pathItems to newExtension
	set AppleScript&#39;s text item delimiters to &quot;.&quot;
	set newPath to pathItems as text
	return newPath
end replaceExtension</code></pre></div>



<p class="wp-block-paragraph">（２）「 AppleScriptを実行」を挿入</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>-- 手順２   次に、手順１で新たに作ったVTTファイルに対して、
-- (1) 先頭行に文字列&quot;WEBVTT&quot;を含む行を挿入する。
-- (2) 2行目に空白の行を挿入する。
-- (3) 3行目以降から、読み込んだデータを書き込む。
-- (4) &quot;--&gt;&quot; がある行は、行の中の&quot;,&quot;を&quot;.&quot;に置き換える。

on run {input, parameters}
	set newFilePath to POSIX path of (first item of input)
	
	-- ファイル内容を読み込む
	set fileContent to read newFilePath as «class utf8»
	set fileLines to paragraphs of fileContent
	
	set newContent to &quot;WEBVTT&quot; & linefeed & linefeed
	
	-- 条件に基づいてファイル内容を更新
	repeat with i from 1 to count fileLines
		set lineText to item i of fileLines
		
		if i &gt; 1 then -- 読み込みデータの2行目以降
			if lineText contains &quot;--&gt;&quot; then -- (4)
				set lineText to textReplace(lineText, &quot;,&quot;, &quot;.&quot;)
			end if
		end if
		
		set newContent to newContent & lineText & linefeed
	end repeat
	
	-- ファイルに新しい内容を書き込む
	set fileHandle to open for access newFilePath with write permission
	set eof of fileHandle to 0
	write newContent to fileHandle as «class utf8»
	close access fileHandle
	
	return input
end run

-- 文字列中の特定の文字を置き換える
on textReplace(inputText, findText, replaceText)
	set AppleScript&#39;s text item delimiters to findText
	set textItems to text items of inputText
	set AppleScript&#39;s text item delimiters to replaceText
	set newText to textItems as text
	set AppleScript&#39;s text item delimiters to &quot;&quot;
	return newText
end textReplace</code></pre></div>



<h4 class="wp-block-heading">４　まとめ</h4>



<p class="wp-block-paragraph">補足しますが、Vimeoの<a href="https://imakat.com/rd.php?id=2ql7TIgX.png" target="_blank">自動生成は音声がある場合、作動します</a>。</p>



<p class="wp-block-paragraph">Vimeoの自動生成を使うのは、動画の尺は変えない段階まで来てから、字幕だけ付けるという場合、有効でしょう。あるいはもしFinal Cut Proで動画編集をしているなら、Final Cut Proの字幕ファイル読み込みはsrtファイルだけなので、<a href="https://imakat.com/rd.php?id=58JfqRXw.png" target="_blank">先に述べてきた通りAppleスクリプトでvtt→srtを生成して、srtを読み込みFinal Cut Proで編集する方法があります。</a></p>



<p class="wp-block-paragraph">字幕起こしアプリVrewも、Filmoraも代替方法とします。</p>



<p class="wp-block-paragraph">ただ、Vimeoの自動生成をテキストエディタで修正したものを、srtとvttを変換しながらFinal Cut Proで修正するのが、そこそこ分かりやすい方法ですが、いかんせん、何回も変換するのは面倒。</p>



<h4 class="wp-block-heading"><span class="marker">使い慣れたテキストエディタが、一番使いやすい！</span></h4>



<p class="wp-block-paragraph">さて、説明してきたように、Vimeoの自動生成ファイルはvttファイルです。それを手直ししながら全部済むなら一番ラクです。</p>



<p class="wp-block-paragraph">誰にでも操作できるツールは、Windows、Mac問わず、<strong>テキストエディタ</strong>です。正直言って、字幕起こしアプリの中で、文字を直したり、移動したり、時刻を変更する作業は、どれを使ったとしても、私には、更新に不安定さを感じますし、作業方法があれこれ変えられてしまうアプリ会社の都合も入ってきます。ところが、テキストエディタは、昔から殆ど変わりません。メニューバーも１行程度しかありません。ですから、やることは、</p>



<p class="wp-block-paragraph"><strong>「Vimeo他の自動字幕起こしを行ったら、すぐに自分のテキストエディタへダウンロードをする。<br>動画を適当なプレーヤーで表示しながら、各字幕の開始時刻、終了時刻、文字の修正を、テキストエディタへ直接書き込む。」</strong></p>



<p class="wp-block-paragraph">以上のことだけで十分です。</p>



<p class="wp-block-paragraph">さらにあると便利だと感じるのは、<strong>「時刻情報をxxx分xxx秒(いやxxx秒だけの方がわかりやすいかも)、だけズラせ」というスクリプトを動作できるようにすれば、それはさらに便利でしょう。</strong>この改善（<a rel="noopener" target="_blank" href="https://docs.google.com/document/d/1j5edxQOuu99Azs1aguE9Httft3EvzNb8KVNsOuLsgfg/edit?usp=sharing">スクリプトは完成しています。<span class="fa fa-external-link external-icon anchor-icon"></span></a>）は後日、ブログで投稿したいと思います。</p>



<p class="wp-block-paragraph">結論的に言うならば、</p>



<p class="wp-block-paragraph">簡単な字幕の修正なら、vttファイルのままで、時刻ずらしスクリプトなどを使ってテキストエディタで処理する、複雑な修正なら、連番の付け直しをしてsrtファイルに変換して、Final Cut Proに読み込んで、動画との紐付きを視覚的に確認しながら処理する、と言うことになります。</p>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">器材記録：</p>



<p class="wp-block-paragraph">Vimeo(PLUS会員)<br>Automator<br>テキストエディット<br>Mac mini M2 Ventura<br>BGM:オリジナル音楽</p>



<p class="wp-block-paragraph">以上です。</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">18638</post-id>	</item>
	</channel>
</rss>
