<?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/tag/%E3%82%AF%E3%82%A4%E3%83%83%E3%82%AF%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3/feed/" rel="self" type="application/rss+xml" />
	<link>https://imakat.com</link>
	<description>工夫と改善で人生をちょっと豊かに</description>
	<lastBuildDate>Tue, 10 Mar 2026 06:43:10 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</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>【Gemini/ChatGPT】スクリプトや記事を日本語要約＋保存するMac用スクリプト</title>
		<link>https://imakat.com/2025/10/12/27668/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Sat, 11 Oct 2025 23:34:29 +0000</pubDate>
				<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[Automator]]></category>
		<category><![CDATA[AppleScript]]></category>
		<category><![CDATA[ChatGPT]]></category>
		<category><![CDATA[クイックアクション]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=27668</guid>

					<description><![CDATA[26.1.15追記：【Gemini】Gemini版を追加。文字列をコピーした後、マウスの右クリックで→サービス、から起動する形です。無料でも使えます。 【ChatGPT】～無料版でも動く、ローカル完結の超シンプル連携法～ [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>26.1.15追記：<br><strong>【<span class="fz-14px"><span class="fz-16px"><span class="fz-18px">Gemini】</span></span></span></strong>Gemini版を追加。文字列をコピーした後、マウスの右クリックで→サービス、から起動する形です。無料でも使えます。<br></p>



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



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td><a rel="noopener" target="_blank" href="https://docs.google.com/document/d/1lML0yZwWbv-y8o1QPXb3zBTGEBuSimvBTgOTFrW_RjY/edit?usp=sharing">Gemini APIキー取得方法と活用<span class="fa fa-external-link external-icon anchor-icon"></span></a></td></tr><tr><td>要約：このAppleScriptスクリプトは、ユーザーが選択したテキストをGoogle Gemini APIで要約し、その結果と元の本文をファイルに保存する自動化ツールです。右クリックで選択されたテキストを受け取り、処理前に内容をプレビューして続行を確認します。スクリプトはPython3とcurlコマンドを使い、Gemini APIにアクセス。<code>MY_API_KEY</code>を用いて、利用可能なモデルから<code>generateContent</code>をサポートするモデルを自動選択し、指定された文字数（MAX_SUMMARY_CHARS）で日本語の要約をリクエストします。Geminiからの出力は、要約文とその後に続く元の本文で構成されます。処理が完了すると、<code>SAVE_DIR</code>にタイムスタンプと要約の最初の行から生成されたタイトル（TITLE_MAX_CHARSで制限、ファイル名として安全化）を含むUTF-8形式のテキストファイルとして保存されます。処理状況やエラーは通知でユーザーに伝えられます。<br>Automator<br>ワークフローが受け取る項目：テキスト<br>選択肢：すべてのアプリケーション</td></tr><tr><td><a href="https://imakat.com/script_list/?pubtxt=geminiを使った日本語要約_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>



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



<p><strong>【<span class="fz-14px"><span class="fz-16px"><span class="fz-18px">ChatGPT】</span></span></span></strong><br><strong>～無料版でも動く、ローカル完結の超シンプル連携法～</strong><br>ChatGPTでテキストを要約したいとき、毎回アプリを開いてコピペするのは意外と手間です。そこで、AppleScriptとChatGPTデスクトップアプリを連携させて、「クリップボードの内容を自動でChatGPTに貼り付け→要約→テキストファイルに保存」までを数クリックで完結させる仕組みを紹介します。ChatGPTの無料版でも動作し、APIキーや外部サービスは不要です。</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; /* dynamicと合わせた基本サイズ */
    font-weight: normal;
    text-decoration: underline;
    color: #0073aa;
  }
  .sc-dynamic-embed .sc-link a:hover { text-decoration: none; color: #000; }
  
  /* ★スマホ画面（幅500px以下）の時は文字を縮小して統一感を出す */
  @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="https://imakat.com/ds62/?drid=62_2" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <style>
            :root{ --dr5emd-max: 1920px; }
            .video-wrap{position:relative;width:100%;margin:0 auto}
            figure.wp-block-video.aligncenter{
              width:100%;
              max-width:min(var(--dr5emd-max, 1920px), 98vw);
              margin:0 auto;
            }
            #subtitleOverlay{
              position:absolute; left:0; right:0; bottom:6%;
              padding:0 2%; text-align:center; pointer-events:none; z-index:2;
            }
            #subtitleOverlay .band{
              display:inline-block; background:rgba(0,0,0,0.35);
              padding:6px 10px; border-radius:8px; max-width:96%;
              margin:0 auto; box-shadow:0 1px 2px rgba(0,0,0,0.15);
            }
            #subtitleOverlay .overlay-cue{
              color:#fff; font-weight:600;
              font-size:clamp(16px, 3.6vw, 32px);
              line-height:1.32; white-space:pre-wrap; margin:2px 0;
              -webkit-text-stroke:.6px rgba(0,0,0,.7);
              text-shadow:-1px -1px 0 rgba(0,0,0,.6), 1px -1px 0 rgba(0,0,0,.6),
                          -1px  1px 0 rgba(0,0,0,.6), 1px  1px 0 rgba(0,0,0,.6);
            }
            @media (max-width:430px){
              #subtitleOverlay .overlay-cue{ font-size:clamp(16px, 4.2vw, 22px); }
            }
            .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;
            }
            /* ▼ 自動スクロール時のハイライト（文字の太さを標準へ変更） */
            .active-hl {
                background-color: #ffff00 !important;
                color: #ff0000 !important;
                font-weight: normal; /* 標準の太さ */
                border-bottom: 2px solid red;
                display: inline-block;
                border-radius: 2px;
            }
            </style><div class="dr5emd-container"><figure class="wp-block-video aligncenter"><div class="video-wrap"><video id="myVideo" controls controlsList="nodownload" poster="https://imakat.com/rd.php?id=2MuRdRkf.png" playsinline preload="metadata" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=ZJjPNzrn.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=dnzQfkpR.vtt" label="日本語" srclang="ja" kind="subtitles"></video><div id="subtitleOverlay" aria-hidden="true"></div></div><script>
document.addEventListener("DOMContentLoaded", function(){
  var video=document.getElementById("myVideo");
  var trackEl=video?video.querySelector("track[kind='subtitles'], track[kind='captions']"):null;
  var overlay=document.getElementById("subtitleOverlay"); if(!video||!overlay) return;
  video.addEventListener("contextmenu", function(e){ e.preventDefault(); return false; }, false);
  function setNative(mode){
    try{
      if(video.textTracks && video.textTracks.length){
        for(var i=0;i<video.textTracks.length;i++){ video.textTracks[i].mode = mode; }
      }
      if(trackEl && trackEl.track) trackEl.track.mode = mode;
    }catch(e){}
  }
  var isOverlay=true,lastSig="";
  function sig(active){if(!active||active.length===0)return"";var a=[];for(var i=0;i<active.length;i++){var c=active[i];a.push([c.startTime,c.endTime,c.text].join("|"));}return a.join("||");}
  function cueLine(c){var d=document.createElement("div");d.className="overlay-cue";d.setAttribute("translate","yes");if(typeof c.getCueAsHTML==="function")d.appendChild(c.getCueAsHTML());else d.textContent=c.text;return d;}
  function render(){
    if(!isOverlay || !trackEl || !trackEl.track) return;
    var ac=trackEl.track.activeCues,s=sig(ac); if(s===lastSig) return; lastSig=s;
    overlay.innerHTML=""; if(!ac || ac.length===0) return;
    var b=document.createElement("div"); b.className="band"; b.setAttribute("translate","yes");
    Array.from(ac).sort(function(a,b){return a.startTime-b.startTime;}).forEach(function(c){b.appendChild(cueLine(c));});
    overlay.appendChild(b);
  }
  function useOverlay(){isOverlay=true;overlay.style.display="";setNative("hidden");lastSig="";render();}
  function useNative(){isOverlay=false;overlay.style.display="none";setNative("showing");lastSig="";}
  useOverlay();
  if(trackEl){
    if(trackEl.track){ try{ trackEl.track.addEventListener("cuechange",render); }catch(e){} }
    trackEl.addEventListener("load", function(){ try{ if(trackEl.track) trackEl.track.addEventListener("cuechange",render); }catch(e){} render(); });
  }
  video.addEventListener("loadedmetadata",render);
  function handleWebkitMode(){ var m = video.webkitPresentationMode || "inline"; (m==="picture-in-picture"||m==="fullscreen") ? useNative() : useOverlay(); }
  if("webkitPresentationMode" in video){ video.addEventListener("webkitpresentationmodechanged",handleWebkitMode); handleWebkitMode(); }
  if("webkitCurrentPlaybackTargetIsWireless" in video){
    video.addEventListener("webkitcurrentplaybacktargetiswirelesschanged", function(){ video.webkitCurrentPlaybackTargetIsWireless ? useNative() : useOverlay(); });
  }
  if("pictureInPictureEnabled" in document){
    video.addEventListener("enterpictureinpicture",useNative);
    video.addEventListener("leavepictureinpicture",useOverlay);
  }
  document.addEventListener("fullscreenchange", function(){
    var fs=document.fullscreenElement;
    if(!fs) return useOverlay();
    (fs===video || (fs && fs.contains && fs.contains(video))) ? useNative() : useOverlay();
  });
});
</script>
                <figcaption></figcaption></figure><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>)  ChatGPTによる日本語要約とTXTファイル保存のかんたん化<br>
(<a href="#" class="imk-cue" data-seek="0:20" translate="no">00:00:20</a>)   今回は、ChatGPTの活用ということで、わかりやすい使い方を紹介します。<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:47" translate="no">00:00:47</a>)  それが簡単にできれば結構便利です。<br>
(<a href="#" class="imk-cue" data-seek="0:52" translate="no">00:00:52</a>)  ちょっとやってみましょうか。<br>
(<a href="#" class="imk-cue" data-seek="0:55" translate="no">00:00:55</a>)  今、ChatGPTでPythonのスクリプトを書いてもらったとします。<br>
(<a href="#" class="imk-cue" data-seek="1:05" translate="no">00:01:05</a>)  こういうものですね。<br>
(<a href="#" class="imk-cue" data-seek="1:09" translate="no">00:01:09</a>)  このPythonのスクリプトが、迷子になった場合、その中身が全く分からなくなってしまうので、<br>
(<a href="#" class="imk-cue" data-seek="1:19" translate="no">00:01:19</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:36" translate="no">00:01:36</a>)  今ここに、Pythonスクリプトが出来上がっていたとします。<br>
(<a href="#" class="imk-cue" data-seek="1:42" translate="no">00:01:42</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="1:49" translate="no">00:01:49</a>)  次に、作っておいた、CHATGPT用の要約、TXT保存のアプリですね、<br>
(<a href="#" class="imk-cue" data-seek="2:02" translate="no">00:02:02</a>)  これをSTREAM DECKから起動します。<br>
(<a href="#" class="imk-cue" data-seek="2:09" translate="no">00:02:09</a>)  そうすると、この内容を保存しますか？ということで、　<br>
(<a href="#" class="imk-cue" data-seek="2:15" translate="no">00:02:15</a>)  スクリプトの先頭の120字をこのように紹介してくれます。 　　<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:25" translate="no">00:02:25</a>)  続行を押します。<br>
(<a href="#" class="imk-cue" data-seek="2:29" translate="no">00:02:29</a>)  次に、ChatGPTによる要約を挿入しますか？と聞かれます。<br>
(<a href="#" class="imk-cue" data-seek="2:35" translate="no">00:02:35</a>)  はい、を選びます。<br>
(<a href="#" class="imk-cue" data-seek="2:38" translate="no">00:02:38</a>)  そうするとChatGPTが走ります。<br>
(<a href="#" class="imk-cue" data-seek="2:43" translate="no">00:02:43</a>)  非常に短い時間で、今のスクリプトの内容を日本語で300文字以内で、説明してくれます。<br>
(<a href="#" class="imk-cue" data-seek="2:58" translate="no">00:02:58</a>)  自動処理についての説明、となっています。　　<br>
(<a href="#" class="imk-cue" data-seek="3:04" translate="no">00:03:04</a>)  次にこれを、ChatGPTによって要約された文章をコピーします。　<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:23" translate="no">00:03:23</a>)  すると、この先頭に出来上がります。　<br>
(<a href="#" class="imk-cue" data-seek="3:36" translate="no">00:03:36</a>)  先頭に、日時が入っています。次に、要約が入っています。そしてその下に、ずっとスクリプトが入っています。<br>
(<a href="#" class="imk-cue" data-seek="3:51" translate="no">00:03:51</a>)  という形の保存用のファイルが出来上がります。<br>
(<a href="#" class="imk-cue" data-seek="3:59" translate="no">00:03:59</a>)  次の活用例を紹介します。<br>
(<a href="#" class="imk-cue" data-seek="4:05" translate="no">00:04:05</a>)  今ご覧のように、英文があります。　<br>
(<a href="#" class="imk-cue" data-seek="4:11" translate="no">00:04:11</a>)  この英文を日本語に翻訳して、日本語で300字以内に要約したものを作ります。　<br>
(<a href="#" class="imk-cue" data-seek="4:22" translate="no">00:04:22</a>)  そして、日時と要約とこの本文ですね。これを３点セットにして一つのファイルを作ります。<br>
(<a href="#" class="imk-cue" data-seek="4:34" translate="no">00:04:34</a>)  やってみます。<br>
(<a href="#" class="imk-cue" data-seek="4:39" translate="no">00:04:39</a>)  このようにして選択します。コピーを押します。次に、　STREAM DECKのアプリを起動します。<br>
(<a href="#" class="imk-cue" data-seek="4:45" translate="no">00:04:45</a>)  コピーを押します。<br>
(<a href="#" class="imk-cue" data-seek="4:49" translate="no">00:04:49</a>)  次に、SREAM DECKのアプリを起動します。<br>
(<a href="#" class="imk-cue" data-seek="4:58" translate="no">00:04:58</a>)  この内容を保存しますか？ということで文章の先頭の120字の紹介があります。　<br>
(<a href="#" class="imk-cue" data-seek="5:06" translate="no">00:05:06</a>)  これで間違いなしということで続行します。<br>
(<a href="#" class="imk-cue" data-seek="5:10" translate="no">00:05:10</a>)  ChatGPTによる要約を挿入しますか？はい。<br>
(<a href="#" class="imk-cue" data-seek="5:20" translate="no">00:05:20</a>)  この場合、時々、すぐに起動せずに下のボックスに英文が入ってしまうことがありますので、　　　<br>
(<a href="#" class="imk-cue" data-seek="5:28" translate="no">00:05:28</a>)  その場合は、焦らずに、右側のこのボタンを押します。<br>
(<a href="#" class="imk-cue" data-seek="5:40" translate="no">00:05:40</a>)  そうすると速やかに、翻訳がされます。<br>
(<a href="#" class="imk-cue" data-seek="5:49" translate="no">00:05:49</a>)  これは、記事の無断転載を禁止するような説明になっています。<br>
(<a href="#" class="imk-cue" data-seek="5:57" translate="no">00:05:57</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:17" translate="no">00:06:17</a>)  そうしますと先頭に、一つ出来上がっています。　<br>
(<a href="#" class="imk-cue" data-seek="6:27" translate="no">00:06:27</a>)  内容を確認します。<br>
(<a href="#" class="imk-cue" data-seek="6:32" translate="no">00:06:32</a>)  日時、次に要約した文章、その下に英文の本文が入っています。　<br>
(<a href="#" class="imk-cue" data-seek="6:45" translate="no">00:06:45</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:59" translate="no">00:06:59</a>)  URLですね。<br>
(<a href="#" class="imk-cue" data-seek="7:04" translate="no">00:07:04</a>)  ご覧のように、一つURLがあったとします。<br>
(<a href="#" class="imk-cue" data-seek="7:09" translate="no">00:07:09</a>)  このURLの中身を、300文字以内の日本語で要約して、日時、要約、本文と、いや本文といっても、このURLだけですが。<br>
(<a href="#" class="imk-cue" data-seek="7:27" translate="no">00:07:27</a>)  それを一つのファイルにします。<br>
(<a href="#" class="imk-cue" data-seek="7:31" translate="no">00:07:31</a>)  やってみます。<br>
(<a href="#" class="imk-cue" data-seek="7:38" translate="no">00:07:38</a>)  コピー<br>
(<a href="#" class="imk-cue" data-seek="7:46" translate="no">00:07:46</a>)  ChatGPT用要約TXT保存のアプリを起動します。<br>
(<a href="#" class="imk-cue" data-seek="7:52" translate="no">00:07:52</a>)  この内容を保存しますか？続行。<br>
(<a href="#" class="imk-cue" data-seek="7:58" translate="no">00:07:58</a>)  ChatGPTによる要約を挿入しますか？はい。<br>
(<a href="#" class="imk-cue" data-seek="8:18" translate="no">00:08:18</a>)  要約が済みました。<br>
(<a href="#" class="imk-cue" data-seek="8:23" translate="no">00:08:23</a>)  どうもスマホを公共の充電器に接続すると、データを盗まれる危険があるので注意してください、という文章ですね。<br>
(<a href="#" class="imk-cue" data-seek="8:43" translate="no">00:08:43</a>)  この文章をコピーします。　<br>
(<a href="#" class="imk-cue" data-seek="8:51" translate="no">00:08:51</a>)  そして取得を押します。<br>
(<a href="#" class="imk-cue" data-seek="8:56" translate="no">00:08:56</a>)  そうすると、一番上に今の内容が入ってきました。<br>
(<a href="#" class="imk-cue" data-seek="9:05" translate="no">00:09:05</a>)  ご覧のように、一番上に、日時、要約、内容、まあ時々文章が変になっている場合もあるのですかね。　　　<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:24" translate="no">00:09:24</a>)  その辺は、愛嬌ですね。<br>
(<a href="#" class="imk-cue" data-seek="9:29" translate="no">00:09:29</a>)  それからURLと、いう形になります。<br>
(<a href="#" class="imk-cue" data-seek="9:37" translate="no">00:09:37</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><script>
(function(){
  var root=document.querySelector(".dr5emd-sublist");
  var video=document.getElementById("myVideo");
  if(!root || !video) return;
  function parseTs(ts){
    if(!ts) return null;
    var p=ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
    if(p.length===2) return p[0]*60 + p[1];
    if(p.length===3) return p[0]*3600 + p[1]*60 + p[2];
    return null;
  }
  root.addEventListener("click", function(e){
    var a=e.target.closest && e.target.closest("a.imk-cue[data-seek]");
    if(!a || !root.contains(a)) return;
    e.preventDefault();
    var sec = parseTs(a.getAttribute("data-seek"));
    if(sec==null) return;
    try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
  });
  video.addEventListener("timeupdate", function(){
    var listContainer = root.querySelector("details > p");
    if(!listContainer) return;
    var cues = listContainer.querySelectorAll("a.imk-cue");
    if(cues.length === 0) return;
    var cur = video.currentTime;
    var active = null;
    for(var i=0; i<cues.length; i++){
        var t = parseTs(cues[i].getAttribute("data-seek"));
        if(t !== null && cur >= t - 0.5){
            active = cues[i];
        } else if(t > cur){
            break;
        }
    }
    if(active){
        if(active.classList.contains("active-hl")) return;
        var old = listContainer.querySelectorAll(".active-hl");
        for(var k=0; k<old.length; k++) old[k].classList.remove("active-hl");
        active.classList.add("active-hl");
        if(listContainer.offsetParent !== null){
            var containerRect = listContainer.getBoundingClientRect();
            var activeRect = active.getBoundingClientRect();
            var targetScroll = listContainer.scrollTop + (activeRect.top - containerRect.top) - (listContainer.clientHeight / 2) + (active.clientHeight / 2);
            listContainer.scrollTo({ top: targetScroll, behavior: "smooth" });
        }
    }
  });
})();
</script>
                </div>

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

    /* -----------------------------------------------
       1. 動画の保護機能（右クリック禁止・DL防止）
       ----------------------------------------------- */
    function protectVideo() {
      var target = wrapper ? wrapper : document;
      var mediaEls = target.querySelectorAll('video');
      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);
      });
    }

    /* -----------------------------------------------
       2. 字幕制御＆ハイライト機能（iPhone全画面 完全対応版）
       ----------------------------------------------- */
    function initSubtitles() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video');
      var listContainer = target.querySelector('details > p');
      
      if (!video || !listContainer) 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 = ''; 
      }

      function isSpecialMode() {
        var isFs = !!(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement);
        var isPiP = !!(document.pictureInPictureElement && document.pictureInPictureElement === video) || (video.webkitPresentationMode === "picture-in-picture");
        var isIOSFs = !!video.webkitDisplayingFullscreen; 
        return isFs || isPiP || isIOSFs;
      }

      try {
        if(video.textTracks && video.textTracks.length > 0){
          for(var i=0; i<video.textTracks.length; i++){
             if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions'){
                 video.textTracks[i].mode = "hidden";
             }
          }
        }
      } catch(e){}

      video.addEventListener("webkitbeginfullscreen", function() {
          try {
              if(video.textTracks && video.textTracks.length > 0) {
                  for(var i=0; i<video.textTracks.length; i++){
                      if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') video.textTracks[i].mode = "showing";
                  }
              }
          } catch(e){}
      });
      video.addEventListener("webkitendfullscreen", function() {
          try {
              if(video.textTracks && video.textTracks.length > 0) {
                  for(var i=0; i<video.textTracks.length; i++){
                      if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') video.textTracks[i].mode = "hidden";
                  }
              }
          } catch(e){}
      });

      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";
      }

      function parseTs(ts){
        if(!ts) return null;
        var p = ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
        if(p.length===2) return p[0]*60 + p[1];
        if(p.length===3) return p[0]*3600 + p[1]*60 + p[2];
        return null;
      }

      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 sec = parseTs(a.getAttribute("data-seek"));
          if(sec==null) return;
          try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
        });
      }

      video.addEventListener("timeupdate", function(){
        var desiredMode = isSpecialMode() ? "showing" : "hidden";
        try {
            if(video.textTracks && video.textTracks.length > 0){
                for(var i=0; i<video.textTracks.length; i++){
                    if((video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') && video.textTracks[i].mode !== desiredMode) {
                        video.textTracks[i].mode = desiredMode;
                    }
                }
            }
        } catch(e){}

        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 t = parseTs(cues[i].getAttribute("data-seek"));
            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(); // ★dynamic2は保護を維持
      var success = initSubtitles();
      checks++;
      if (success || checks > 20) { 
        clearInterval(checkTimer);
      }
    }, 500); 

    /* -----------------------------------------------
       3. 画面遷移時の停止機能
       ----------------------------------------------- */
    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>



<h4 class="wp-block-heading">■ スクリプトの流れ</h4>



<ol class="wp-block-list">
<li style="font-style:normal;font-weight:700">クリップボードのテキストを取得</li>



<li style="font-style:normal;font-weight:700">ChatGPTアプリを自動起動</li>



<li style="font-style:normal;font-weight:700">要約プロンプトを自動入力（例：「以下のテキストを日本語で400字以内に要約してください」）</li>



<li style="font-style:normal;font-weight:700">ChatGPTが要約を生成</li>



<li style="font-style:normal;font-weight:700">ユーザーが要約をコピー</li>



<li style="font-style:normal;font-weight:700">指定フォルダにテキストファイルとして自動保存</li>
</ol>



<p>１のクリップボードのテキスト取得がミソになっています。マウス右クリックでコピーしたものです。保存ファイル名は「YYYYMMDD-HHMMSS_タイトル.txt」という形式で、<br>例：/Volumes/NO3_SSD/Dropbox/dropbox_1/ChatGPTによる要約と本文/ に保存されます。</p>



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



<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>スクリプトエディタで、例えば&#8221;Save_Clipboard_with_Summary&#8221;と名前つけて、スクリプトファイル&#8221;Save_Clipboard_with_Summary.scpt&#8221;として保存します。</td></tr><tr><td style="width:10%">(3)</td><td>(a)Stream Deckからは&#8221;Save_Clipboard_with_Summary.scpt&#8221;が直接呼べます。<br>(b)クイックアクションからの起動は、Automator→クイックアクション→ワークフローが受け取る現在の項目：テキスト、使用できるアプリケーション：任意のアプリケーション→「AppleScriptを実行」のボックスを置く→(4)<br></td></tr><tr><td>(4)</td><td><strong>run script (POSIX file &#8220;/&lt;あなたのファイルパス&gt;/Save_Clipboard_with_Summary.scpt&#8221;)</strong></td></tr><tr><td>(5)</td><td>アプリ名を例えば&#8221;ChatGPT要約付きTXT保存&#8221;として保存します。</td></tr><tr><td colspan="2"><a href="https://imakat.com/?pubtxt=ChatGPT連携_要約保存Mac用スクリプト_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 colspan="2">Windowsへ実装したい場合は、ChatGPTに上のtxtファイルを添付して、「同じ働きをWindowsで実装するスクリプト作成と実装手順を指示ください」としてください。</td></tr></tbody></table></figure>



<h4 class="wp-block-heading">■ 操作方法</h4>



<p>使い方はとても簡単です。<br>要約したいテキストをコピーし、このアプリを実行するだけ。<br>ChatGPTが起動して自動で要約を生成し、その結果をコピーすればファイルが自動保存されます。<br>これで「ChatGPTで要約→保存」までが一瞬で完了します。</p>



<p>さらに、右クリック（コンテキストメニュー）から呼び出せる「クイックアクション」として登録しておくと、<br>ブラウザやメモアプリ上でテキストを選択 → コピー → 右クリックで「要約して保存」を実行、<br>という直感的な操作が可能になります。<br>Mac標準のAutomatorで簡単に登録できるため、日常の作業フローに自然に組み込めます。</p>



<h4 class="wp-block-heading">■ ChatGPTの文字列のコピーが扱えない！裏ワザあります。</h4>



<p>ブラウザ、テキストエディット、メモなどにある文章は、コピーを行なった後、このアプリをMacのキーボードショートカットに登録して、要約保存は、下記のアクセス許可を経て使用できるようになります。私は、「ChatGPT要約付きTXT保存」アプリに⌥(Option)+ファンクション(F)1を登録しました。</p>



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



<p><br>しかし、本家本元のChatGPTに表示される文字や文章は、コピーした後、アプリが起動しないという問題が生じます。その場合の裏ワザです。</p>



<p>それは、文字列のコピーは、Web版ChatGPTから行い、アプリを起動してインストール版ChatGPTで要約させ、最後に保存する流れで連携する方法です。以下のような挙動になります。</p>



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



<p><br><br></p>



<h4 class="wp-block-heading">■ 注意：アクセス許可が必要な場合があります</h4>



<p>クイックアクションを使って、Safari・Chrome・PDFビューアなど<br>さまざまなアプリからテキストをコピーして要約に取り込む場合、<br>まれに「エラーが出る」「テキストが取得できない」ことがあります。</p>



<p>これは、そのアプリのテキスト内容へのアクセスが macOS で制限されているためです。<br>その場合は以下の手順でアクセスを許可すると解決します。</p>



<p><strong>対処方法：<br>1. 「システム設定」→「プライバシーとセキュリティ」を開く<br>2. 左側のメニューから「アクセシビリティ」または「フルディスクアクセス」を選択<br>3. 使用しているアプリ（例：Automator.app、ChatGPT.app、または実行中のスクリプトランチャー）を追加してオンにする</strong></p>



<p>この設定を行うと、ブラウザ・PDF・Word・メモなど、<br>どのアプリ上でもクイックアクションを安定して利用できるようになります。<br>特にAutomator経由でスクリプトを動かす場合は「Automator.app」へのアクセス許可が重要です。</p>



<h4 class="wp-block-heading">■ TextEditでクイックアクションが出てこない場合</h4>



<p>TextEditでテキストを選択しても、右クリックメニューにクイックアクションが表示されない場合があります。<br>これは、TextEditがリッチテキスト（装飾付き）モードになっているとAutomatorが対象外とみなすためです。</p>



<p><strong>&#x1f4a1; 対処方法：<br>1. メニューの「フォーマット」→「標準テキストにする（⌘⇧T）」を選択し、プレーンテキストモードに切り替える。<br>2. 再度右クリック → 「クイックアクション」を確認。</strong></p>



<p>すると、「ChatGPT要約付きTXT保存」などのワークフローが表示されます。</p>



<h4 class="wp-block-heading">&#x2699;&#xfe0f; Automatorの設定で特に重要なポイント！</h4>



<p>Automatorでクイックアクションを作成するときは、<br>上部の設定欄にある</p>



<p>「ワークフローが受け取る現在の項目」</p>



<p>の指定を 必ず「テキスト」に設定してください。<br>ここが「リッチテキスト」や「ファイル」のままだと、<br>TextEditではメニューに表示されません。</p>



<p><strong>正しい設定：<br>• ワークフローが受け取る現在の項目：&#x1f7e2;「テキスト」<br>• 使用できるアプリケーション：&#x1f7e2;「任意のアプリケーション」</strong></p>



<p>この設定を間違えると、メモでは動くのにTextEditでは出ない、という現象が起こります。</p>



<p>&#x1f538;ポイント：Automatorの「受け取る項目」は“入力タイプの宣言”に相当します。<br>TextEditがプレーンテキストを出すときだけ、この条件に一致します。</p>



<h4 class="wp-block-heading">&#x1f9ed; Stream Deckでの活用（おすすめ）</h4>



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



<p>クイックアクションも十分便利ですが、Stream Deckを持っている方は、こちらを使うのがおすすめです。</p>



<p>Stream Deckは、右クリックメニューではなくAppleScriptやShellスクリプトを直接起動できるランチャーです。<br>そのため、ChatGPTアプリのようなWebView上のテキストでも問題なく実行できます。<br>つまり、どんなアプリや画面上でも「コピー → ボタン一発」で要約＋保存が可能になります。</p>



<p><strong>&#x25b6; Stream Deckでの設定例<br>1. Stream Deckアプリを開く<br>2. 「System」→「Open」または「AppleScript実行」アクションを追加<br>3. あなたのスクリプトファイル（例：Save_Clipboard_with_Summary.scpt）のパスを指定<br>4. ボタンに「要約して保存」などの名前やアイコンを設定</strong></p>



<p>これで、どんなアプリ上でも：</p>



<p>テキストをコピー → Stream Deckボタンを押す → ChatGPTが自動要約 → Dropboxに保存<br>という流れをワンアクションで実行できます。</p>



<p><strong>&#x25b6; 併用のすすめ<br>• 普段は右クリックのクイックアクションで十分<br>• ChatGPTアプリやPDFなど、クイックアクションが反応しない場合はStream Deckで実行</strong></p>



<p>両方に同じスクリプトを指定しておけば、操作感を統一しつつ、どんな環境でも動く構成になります。</p>



<h4 class="wp-block-heading">■ 利用のポイント</h4>



<p><strong>• 無料版（GPT-3.5）でも問題なく動作します。<br>• 英語記事や長文を正確に要約したい場合は、GPT-4またはGPT-5（ChatGPT Plus）を推奨。<br>• すべてローカルで完結し、外部通信やAPI設定は不要。<br>• 要約と手入力タイトルのどちらにも対応し、ファイルは日時順に整理されます。</strong></p>



<h4 class="wp-block-heading">■ 応用例</h4>



<p>プログラミングスクリプトの日本語要約もさることながら、</p>



<p>•<strong> 英語→日本語要約版<br>• 翻訳＋保存版<br>• URL要約版(&#x1f449;「質問してみましょう」のボックスにURLが入った状態で待ちになる場合は、右のボタンを押して動作させる)</strong></p>



<p>ChatGPTアプリを直接操作する感覚で自動化できるため、<br>誰でもすぐに試せる実用的なAI活用法です。<br>Macユーザーなら今日からすぐ導入できます。</p>



<p>以上<br><br></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">27668</post-id>	</item>
		<item>
		<title>【Macクイックアクション】線画イラストを切り抜く方法　その３</title>
		<link>https://imakat.com/2025/05/29/26297/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Thu, 29 May 2025 06:18:00 +0000</pubDate>
				<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[クイックアクション]]></category>
		<category><![CDATA[画像加工]]></category>
		<category><![CDATA[Python3]]></category>
		<category><![CDATA[シェルスクリプト]]></category>
		<category><![CDATA[Automator]]></category>
		<category><![CDATA[ChatGPT]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=26297</guid>

					<description><![CDATA[課題 Macのクイックアクションには標準で「背景を削除」があるのですが、どうも精度が今ひとつです。 画像ファイルの上で、右クリック→クイックアクション、「背景を削除」を選びます。非常に便利です。 普通の写真、動物や人物の [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h5 class="wp-block-heading">課題</h5>



<p>Macのクイックアクションには標準で「背景を削除」があるのですが、どうも精度が今ひとつです。</p>



<p>画像ファイルの上で、右クリック→クイックアクション、「背景を削除」を選びます。非常に便利です。</p>



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



<p>普通の写真、動物や人物の映った普通の写真からの、背景削除は綺麗に出来ます。ここまでは問題ないです。</p>



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



<p>ところが、私は、線だけで描いたイラストをよく使うのですが、その背景削除は、なかなか上手にいきません。下のように、白塗りは不正確、線も不正確です。</p>



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



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



<p>ということで、これまで、線画イラストを切り抜く方法の、その１、その２と紹介してきましたが、今回は、その３です。Macを使う人にとっては、一番、使いやすいかもしれません。</p>



<p>まず最初に、ChatGPTにスクリプトの記述を依頼します。</p>



<p>以下が、依頼文(プロンプト）です。</p>



<h5 class="wp-block-heading">プロンプト</h5>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td><strong>MacのAutomator,Python、シェルスクリプト、その他必要な言語を使って、下記フローの記述をお願いします。<br><br>１　MacのPDFまたは画像ファイル(pdf png jpg jpeg gif)の上で右クリックしてクイックアクションを起動する。クイックアクション名を&#8221;PDFまたは画像から切り抜き画像の生成&#8221;とする。<br>２　PDFファイルはJPG画像に変換して、他はそのままで、画像1とする。<br>３　画像1から、輪郭線と線だけを元の色(黒色)、不透明で抽出し、それ以外は、完全に透明にした画像(線と輪郭線だけの透過PNG画像)を作成する。注意点として、線の中は中空にせず線の色で塗り埋めること。これをPNG画像2とする。<br>４　PNG画像2から、線によって閉じた輪郭線（外周線)を検出して、その輪郭線の内側を白塗りにして、白塗り以外の部分を透明にして、PNG画像3を生成する。<br>５　PNG画像３を下にして、PNG画像2を上にして、重ねて、PNG画像４を生成する。PNG画像４が完成した画像となる。<br>６　PNG画像４、を読み込んだファイルと同じフォルダに書き込む。ファイル名は”読み込んだファイルのファイル名から拡張子を除いた部分&#8221;&amp;&#8221;_cropped.png&#8221;とする。<br>７　画像１、PNG画像２、PNG画像３、は削除する。<br>以上</strong></td></tr></tbody></table></figure>



<h5 class="wp-block-heading">スクリプト</h5>



<p>何回か修正、微調整を行なって、正しく処理されるようになったスクリプトの記述は以下です。そのままコピペで使えますが、<span class="blue">python3のパスは書き換えてください</span>。<strong>Automator、シェルスクリプト、Python3</strong>、を利用しています。</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>要約：PDFまたは画像から自動で切り抜き画像を作成する Automator ワークフローの内容である。入力ファイルの拡張子に応じて処理を分岐し、PDFは300dpiのJPEGに変換、GIFは最初のフレームをJPEG化、その他の画像はコピーして処理用JPEGとする。その後、Python スクリプトにファイル情報を引き渡し、OpenCV で画像を2倍拡大、濃い線部分を抽出して太らせ、線画のみの PNG（_lineonly）、外周塗りつぶし PNG（_filled）を生成。さらに両者を合成して最終的な切り抜き PNG（_cropped）を出力する。最後に中間ファイルを削除して処理を終了する。</td></tr><tr><td>クイックアクション<br>ワークフローが受け取る現在の項目：ファイルまたはフォルダ<br>検索対象：Finder.app<br>シェル：/bin/bash<br>入力の引き渡し方法：引数として<br>「シェルスクリプトを実行」を挿入</td></tr><tr><td><a href="https://imakat.com/?pubtxt=PDFまたは画像から切り抜き画像の生成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>



<h5 class="wp-block-heading">結果</h5>



<p>このスクリプトを実行します。<br>PDFまたは画像ファイルの上で右クリックして、クイックアクションから、選択します。</p>



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



<p>ファイル名の末尾に、_cropped.pngと入ったファイルが出来るので、これをクリックして確認します。</p>



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



<p><br>私の個人的な設定ですが、スクリーンショットはPDFで保存するようにしています。その理由は、PDFファイルですと図形や文字などを追記することができ、その追記も後から修正ができるからです。そのため、画像ファイルでもPDFでも、切り抜きができるようにしたかったのです。<br><br>以上です。<br></p>



<p></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">26297</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[Appleスクリプト]]></category>
		<category><![CDATA[クイックアクション]]></category>
		<category><![CDATA[字幕生成]]></category>
		<category><![CDATA[文字起こし]]></category>
		<category><![CDATA[CapCut]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Automator]]></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>23.12.26更新：CapCutのMacへのダウンロード版を使っていたら、PROへの参加（即ち有料)をしないと、SRT字幕ファイルのダウンロードが出来なくなりました。ところが、<strong>オンライン版は、ダウンロードが可能でした</strong>。しかし、いつか不可能になるかも知れません。</p>



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



<p><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; /* dynamicと合わせた基本サイズ */
    font-weight: normal;
    text-decoration: underline;
    color: #0073aa;
  }
  .sc-dynamic-embed .sc-link a:hover { text-decoration: none; color: #000; }
  
  /* ★スマホ画面（幅500px以下）の時は文字を縮小して統一感を出す */
  @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="https://imakat.com/ds62/?drid=59" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <style>
            :root{ --dr5emd-max: 1920px; }
            .video-wrap{position:relative;width:100%;margin:0 auto}
            figure.wp-block-video.aligncenter{
              width:100%;
              max-width:min(var(--dr5emd-max, 1920px), 98vw);
              margin:0 auto;
            }
            #subtitleOverlay{
              position:absolute; left:0; right:0; bottom:6%;
              padding:0 2%; text-align:center; pointer-events:none; z-index:2;
            }
            #subtitleOverlay .band{
              display:inline-block; background:rgba(0,0,0,0.35);
              padding:6px 10px; border-radius:8px; max-width:96%;
              margin:0 auto; box-shadow:0 1px 2px rgba(0,0,0,0.15);
            }
            #subtitleOverlay .overlay-cue{
              color:#fff; font-weight:600;
              font-size:clamp(16px, 3.6vw, 32px);
              line-height:1.32; white-space:pre-wrap; margin:2px 0;
              -webkit-text-stroke:.6px rgba(0,0,0,.7);
              text-shadow:-1px -1px 0 rgba(0,0,0,.6), 1px -1px 0 rgba(0,0,0,.6),
                          -1px  1px 0 rgba(0,0,0,.6), 1px  1px 0 rgba(0,0,0,.6);
            }
            @media (max-width:430px){
              #subtitleOverlay .overlay-cue{ font-size:clamp(16px, 4.2vw, 22px); }
            }
            .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;
            }
            /* ▼ 自動スクロール時のハイライト（文字の太さを標準へ変更） */
            .active-hl {
                background-color: #ffff00 !important;
                color: #ff0000 !important;
                font-weight: normal; /* 標準の太さ */
                border-bottom: 2px solid red;
                display: inline-block;
                border-radius: 2px;
            }
            </style><div class="dr5emd-container"><figure class="wp-block-video aligncenter"><div class="video-wrap"><video id="myVideo" controls controlsList="nodownload" poster="https://imakat.com/rd.php?id=YlVSEDLF.png" playsinline preload="metadata" 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 id="subtitleOverlay" aria-hidden="true"></div></div><script>
document.addEventListener("DOMContentLoaded", function(){
  var video=document.getElementById("myVideo");
  var trackEl=video?video.querySelector("track[kind='subtitles'], track[kind='captions']"):null;
  var overlay=document.getElementById("subtitleOverlay"); if(!video||!overlay) return;
  video.addEventListener("contextmenu", function(e){ e.preventDefault(); return false; }, false);
  function setNative(mode){
    try{
      if(video.textTracks && video.textTracks.length){
        for(var i=0;i<video.textTracks.length;i++){ video.textTracks[i].mode = mode; }
      }
      if(trackEl && trackEl.track) trackEl.track.mode = mode;
    }catch(e){}
  }
  var isOverlay=true,lastSig="";
  function sig(active){if(!active||active.length===0)return"";var a=[];for(var i=0;i<active.length;i++){var c=active[i];a.push([c.startTime,c.endTime,c.text].join("|"));}return a.join("||");}
  function cueLine(c){var d=document.createElement("div");d.className="overlay-cue";d.setAttribute("translate","yes");if(typeof c.getCueAsHTML==="function")d.appendChild(c.getCueAsHTML());else d.textContent=c.text;return d;}
  function render(){
    if(!isOverlay || !trackEl || !trackEl.track) return;
    var ac=trackEl.track.activeCues,s=sig(ac); if(s===lastSig) return; lastSig=s;
    overlay.innerHTML=""; if(!ac || ac.length===0) return;
    var b=document.createElement("div"); b.className="band"; b.setAttribute("translate","yes");
    Array.from(ac).sort(function(a,b){return a.startTime-b.startTime;}).forEach(function(c){b.appendChild(cueLine(c));});
    overlay.appendChild(b);
  }
  function useOverlay(){isOverlay=true;overlay.style.display="";setNative("hidden");lastSig="";render();}
  function useNative(){isOverlay=false;overlay.style.display="none";setNative("showing");lastSig="";}
  useOverlay();
  if(trackEl){
    if(trackEl.track){ try{ trackEl.track.addEventListener("cuechange",render); }catch(e){} }
    trackEl.addEventListener("load", function(){ try{ if(trackEl.track) trackEl.track.addEventListener("cuechange",render); }catch(e){} render(); });
  }
  video.addEventListener("loadedmetadata",render);
  function handleWebkitMode(){ var m = video.webkitPresentationMode || "inline"; (m==="picture-in-picture"||m==="fullscreen") ? useNative() : useOverlay(); }
  if("webkitPresentationMode" in video){ video.addEventListener("webkitpresentationmodechanged",handleWebkitMode); handleWebkitMode(); }
  if("webkitCurrentPlaybackTargetIsWireless" in video){
    video.addEventListener("webkitcurrentplaybacktargetiswirelesschanged", function(){ video.webkitCurrentPlaybackTargetIsWireless ? useNative() : useOverlay(); });
  }
  if("pictureInPictureEnabled" in document){
    video.addEventListener("enterpictureinpicture",useNative);
    video.addEventListener("leavepictureinpicture",useOverlay);
  }
  document.addEventListener("fullscreenchange", function(){
    var fs=document.fullscreenElement;
    if(!fs) return useOverlay();
    (fs===video || (fs && fs.contains && fs.contains(video))) ? useNative() : useOverlay();
  });
});
</script>
                <figcaption></figcaption></figure><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><script>
(function(){
  var root=document.querySelector(".dr5emd-sublist");
  var video=document.getElementById("myVideo");
  if(!root || !video) return;
  function parseTs(ts){
    if(!ts) return null;
    var p=ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
    if(p.length===2) return p[0]*60 + p[1];
    if(p.length===3) return p[0]*3600 + p[1]*60 + p[2];
    return null;
  }
  root.addEventListener("click", function(e){
    var a=e.target.closest && e.target.closest("a.imk-cue[data-seek]");
    if(!a || !root.contains(a)) return;
    e.preventDefault();
    var sec = parseTs(a.getAttribute("data-seek"));
    if(sec==null) return;
    try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
  });
  video.addEventListener("timeupdate", function(){
    var listContainer = root.querySelector("details > p");
    if(!listContainer) return;
    var cues = listContainer.querySelectorAll("a.imk-cue");
    if(cues.length === 0) return;
    var cur = video.currentTime;
    var active = null;
    for(var i=0; i<cues.length; i++){
        var t = parseTs(cues[i].getAttribute("data-seek"));
        if(t !== null && cur >= t - 0.5){
            active = cues[i];
        } else if(t > cur){
            break;
        }
    }
    if(active){
        if(active.classList.contains("active-hl")) return;
        var old = listContainer.querySelectorAll(".active-hl");
        for(var k=0; k<old.length; k++) old[k].classList.remove("active-hl");
        active.classList.add("active-hl");
        if(listContainer.offsetParent !== null){
            var containerRect = listContainer.getBoundingClientRect();
            var activeRect = active.getBoundingClientRect();
            var targetScroll = listContainer.scrollTop + (activeRect.top - containerRect.top) - (listContainer.clientHeight / 2) + (active.clientHeight / 2);
            listContainer.scrollTo({ top: targetScroll, behavior: "smooth" });
        }
    }
  });
})();
</script>
                </div>

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

    /* -----------------------------------------------
       1. 動画の保護機能（右クリック禁止・DL防止）
       ----------------------------------------------- */
    function protectVideo() {
      var target = wrapper ? wrapper : document;
      var mediaEls = target.querySelectorAll('video');
      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);
      });
    }

    /* -----------------------------------------------
       2. 字幕制御＆ハイライト機能（iPhone全画面 完全対応版）
       ----------------------------------------------- */
    function initSubtitles() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video');
      var listContainer = target.querySelector('details > p');
      
      if (!video || !listContainer) 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 = ''; 
      }

      function isSpecialMode() {
        var isFs = !!(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement);
        var isPiP = !!(document.pictureInPictureElement && document.pictureInPictureElement === video) || (video.webkitPresentationMode === "picture-in-picture");
        var isIOSFs = !!video.webkitDisplayingFullscreen; 
        return isFs || isPiP || isIOSFs;
      }

      try {
        if(video.textTracks && video.textTracks.length > 0){
          for(var i=0; i<video.textTracks.length; i++){
             if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions'){
                 video.textTracks[i].mode = "hidden";
             }
          }
        }
      } catch(e){}

      video.addEventListener("webkitbeginfullscreen", function() {
          try {
              if(video.textTracks && video.textTracks.length > 0) {
                  for(var i=0; i<video.textTracks.length; i++){
                      if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') video.textTracks[i].mode = "showing";
                  }
              }
          } catch(e){}
      });
      video.addEventListener("webkitendfullscreen", function() {
          try {
              if(video.textTracks && video.textTracks.length > 0) {
                  for(var i=0; i<video.textTracks.length; i++){
                      if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') video.textTracks[i].mode = "hidden";
                  }
              }
          } catch(e){}
      });

      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";
      }

      function parseTs(ts){
        if(!ts) return null;
        var p = ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
        if(p.length===2) return p[0]*60 + p[1];
        if(p.length===3) return p[0]*3600 + p[1]*60 + p[2];
        return null;
      }

      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 sec = parseTs(a.getAttribute("data-seek"));
          if(sec==null) return;
          try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
        });
      }

      video.addEventListener("timeupdate", function(){
        var desiredMode = isSpecialMode() ? "showing" : "hidden";
        try {
            if(video.textTracks && video.textTracks.length > 0){
                for(var i=0; i<video.textTracks.length; i++){
                    if((video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') && video.textTracks[i].mode !== desiredMode) {
                        video.textTracks[i].mode = desiredMode;
                    }
                }
            }
        } catch(e){}

        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 t = parseTs(cues[i].getAttribute("data-seek"));
            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(); // ★dynamic2は保護を維持
      var success = initSubtitles();
      checks++;
      if (success || checks > 20) { 
        clearInterval(checkTimer);
      }
    }, 500); 

    /* -----------------------------------------------
       3. 画面遷移時の停止機能
       ----------------------------------------------- */
    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><strong>はじめに：</strong></p>



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



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



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



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



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



<p><strong>お試し的に、使ってみよう：</strong></p>



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



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



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



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



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



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



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



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



<p>（１）「 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>（２）「 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><strong><span class="marker">SRTからSBVを生成するAppleスクリプト</span></strong><br>※実際に使うことは無いかも知れません。</p>



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



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



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



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



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



<p>上で生成した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>上のコピー＆ペーストは面倒なこともあると思います。txtファイルの上で右クリックをしてクイックアクションでクリップボードに書き込むスクリプトは以下です。</p>



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



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



<p>但し、私が記述している日本語の段階では、改行コード&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>実際のところ、Final Cut ProやQuickTime Player などで動画の寸法がある程度決まったら、動画を一時的に読み込んで、文字起こしをCapCutまたはVrew、またはVimeoなどで行い、そのSRTファイルを、Final Cut Proで読み込みます。SRTファイルの読み込みは、この1回だけです。その後、何かメディアを追加削除したり、トランジションを入れると、字幕が増えたり、書き換えが起きますが、字幕修正は、Final Cut Proの中で手で部分修正します。</p>



<p>器材記録：</p>



<p>CapCut<br>Adobe puppet Dr.AppleSmith<br>Mac mini M2 Ventura<br>BGM:オリジナル音楽</p>



<p>以上です。</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[vimeo]]></category>
		<category><![CDATA[Automator]]></category>
		<category><![CDATA[Appleスクリプト]]></category>
		<category><![CDATA[クイックアクション]]></category>
		<category><![CDATA[字幕生成]]></category>
		<category><![CDATA[文字起こし]]></category>
		<category><![CDATA[Mac]]></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>私の場合、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><a href="https://imakat.com/vm5/?movid=984143974" target="_blank">自動字幕起こしにvimeoを使った例_OBS~音声の設定 9分(動画&#x1f3a5;)</a></p>



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



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



<p>修正作業を効率化するフロー及びツールを作りましたので、以下にご紹介します。</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>ところが、<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><strong>（１）文字と文字の空白を削除する。</strong></p>



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



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



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



<p 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>コピーした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>すると、以下のように、修正されます。</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>これを元に、テキストエディットで更に手直しを行い、vttファイルを完成します。それをvimeoの字幕へアップロードします。</p>



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



<p>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 id="link2"><span class="marker"><strong>vttからsrt生成兼連番付け直しスクリプト</strong></span></p>



<p>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>（２）「 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 id="link3"><strong><span class="marker">srtからvtt生成用Appleスクリプト</span></strong></p>



<p>（１）「 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>（２）「 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>補足しますが、Vimeoの<a href="https://imakat.com/rd.php?id=2ql7TIgX.png" target="_blank">自動生成は音声がある場合、作動します</a>。</p>



<p>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>字幕起こしアプリVrewも、Filmoraも代替方法とします。</p>



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



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



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



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



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



<p>以上のことだけで十分です。</p>



<p>さらにあると便利だと感じるのは、<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>結論的に言うならば、</p>



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



<p></p>



<p>器材記録：</p>



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



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