<?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>Dropbox | imakat.com</title>
	<atom:link href="https://imakat.com/tag/dropbox/feed/" rel="self" type="application/rss+xml" />
	<link>https://imakat.com</link>
	<description>工夫と改善で人生をちょっと豊かに</description>
	<lastBuildDate>Wed, 23 Sep 2026 19:54:38 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.2</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>Dropbox | 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>DropboxからGoogleドライブへ乗り換えることにしました</title>
		<link>https://imakat.com/2026/08/04/29546/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Tue, 04 Aug 2026 13:06:42 +0000</pubDate>
				<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[Dropbox]]></category>
		<category><![CDATA[Googleドライブ]]></category>
		<category><![CDATA[フォルダパス]]></category>
		<category><![CDATA[昔の名前で出ています]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=29546</guid>

					<description><![CDATA[はじめに&#160; そろそろ届く頃かなとは思っていたところですが。 Dropboxの年額更新案内が届きました。もともと課題意識があったので準備を進めてきたことではありますが、クラウドストレージの運用体制を見直し、Goo [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading"><strong>はじめに</strong>&nbsp;</h2>



<p class="wp-block-paragraph">そろそろ届く頃かなとは思っていたところですが。</p>



<p class="wp-block-paragraph">Dropboxの年額更新案内が届きました。もともと課題意識があったので準備を進めてきたことではありますが、クラウドストレージの運用体制を見直し、Googleドライブ（Google AI Proプラン）への全面移行を進めることにしました。</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=104" 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=wkh7padF.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=Cm7I7Q9n.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=SaRGyfFo.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:06" translate="no">00:00:06</a>) ちょっと8月に入ってですね、困ったことが起きまして、<br>
(<a href="#" class="imk-cue" data-seek="0:12" translate="no">00:00:12</a>) メールで案内が来たんですけども、Dropboxのですね、年度更新が来ました。<br>
(<a href="#" class="imk-cue" data-seek="0:22" translate="no">00:00:22</a>) それがですね、今私2TBのPlusというサービスを使っているんですけども、<br>
(<a href="#" class="imk-cue" data-seek="0:34" translate="no">00:00:34</a>) それがUSドルでですね、131.87USドルということになってます。これ年間ですね。<br>
(<a href="#" class="imk-cue" data-seek="0:44" translate="no">00:00:44</a>) 131.87USドル。<br>
(<a href="#" class="imk-cue" data-seek="0:49" translate="no">00:00:49</a>) これをですね、仮に160円/USドルで計算すると、なんと21,000円ですよね。<br>
(<a href="#" class="imk-cue" data-seek="0:59" translate="no">00:00:59</a>) これ最初に私がDropboxを契約した頃っていうのは、どうだろう、月1,000円ぐらいで済んでたと思うんですよね。<br>
(<a href="#" class="imk-cue" data-seek="1:11" translate="no">00:01:11</a>) 年間21,000円ってことは、12で割ると月1,750円ってことになるんですよ。<br>
(<a href="#" class="imk-cue" data-seek="1:23" translate="no">00:01:23</a>) 本当に下手するともう2,000円っていうぐらいになってしまうので、なんとかですね、これは脱Dropboxを考えていきたいなと思っていたわけですね。<br>
(<a href="#" class="imk-cue" data-seek="1:39" translate="no">00:01:39</a>) 実はそういう思いでずっといまして、ちょっとやっぱり2TBをですね、月1,500円以上払って使い続けるのももったいないなと。<br>
(<a href="#" class="imk-cue" data-seek="1:54" translate="no">00:01:54</a>) そこへ例のGoogle AI Proがですね、月2,900円で5TBのストレージを含むというサービスを始めましたよね。<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:22" translate="no">00:02:22</a>) それはですね、M1 Mac miniですけども、一つNO3_SSDっていうのをくっつけてるんですけども、<br>
(<a href="#" class="imk-cue" data-seek="2:35" translate="no">00:02:35</a>) これの下に今Dropboxのローカルフォルダーが入ってます。<br>
(<a href="#" class="imk-cue" data-seek="2:46" translate="no">00:02:46</a>) でそれをですね、Dropboxの契約を停止して、その代わり<br>
(<a href="#" class="imk-cue" data-seek="2:57" translate="no">00:02:57</a>) Gドライブの方にですね、mybox_1というフォルダーを作って、そこへですね、全部移すと。<br>
(<a href="#" class="imk-cue" data-seek="3:10" translate="no">00:03:10</a>) 全部移すというのはクラウド側を移すということです。<br>
(<a href="#" class="imk-cue" data-seek="3:16" translate="no">00:03:16</a>) ただしですね、私の場合このDropboxという名前を使って、非常にたくさんのですね、アプリを作ってきてるんですね。<br>
(<a href="#" class="imk-cue" data-seek="3:28" translate="no">00:03:28</a>) フォルダーパス、ファイルパスにかなりたくさん使ってきてますので、これを全部ですね、mybox何がしに替えるっていうのは大変な作業なんで、<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:47" translate="no">00:03:47</a>) ローカルフォルダー名としてはこのDropboxを残す。で、Gドライブ、パソコン、マイMacmini、mybox_1へミラーリング同期すると。という形ですね、しようと思います。<br>
(<a href="#" class="imk-cue" data-seek="4:12" translate="no">00:04:12</a>) まあそんな形でですね、いよいよDropboxの脱出を図り、データを移したあとでちょっと様子を見てですね、勇気をもってDropboxを解約したいと考えています。<br>
(<a href="#" class="imk-cue" data-seek="4:33" translate="no">00:04:33</a>) あと補足しますけども、<br>
(<a href="#" class="imk-cue" data-seek="4:38" translate="no">00:04:38</a>) DropboxをやめてGoogleドライブに切り替えた時にですね、<br>
(<a href="#" class="imk-cue" data-seek="4:44" translate="no">00:04:44</a>) 今まで作ってきたアプリケーションに、<br>
(<a href="#" class="imk-cue" data-seek="4:50" translate="no">00:04:50</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:02" translate="no">00:05:02</a>) メディアアセットをコントロールするライブラリ、まあ図書館を作っていたわけですね。<br>
(<a href="#" class="imk-cue" data-seek="5:10" translate="no">00:05:10</a>) で、この図書館からですね、各<br>
(<a href="#" class="imk-cue" data-seek="5:17" translate="no">00:05:17</a>) 配信サーバーにですね、メディアアセットを送ると<br>
(<a href="#" class="imk-cue" data-seek="5:24" translate="no">00:05:24</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:35" translate="no">00:05:35</a>) と言いますのは、例えば、<br>
(<a href="#" class="imk-cue" data-seek="5:39" translate="no">00:05:39</a>) 必ずですね、この<br>
(<a href="#" class="imk-cue" data-seek="5:45" translate="no">00:05:45</a>) 登録してあるものの中にDropboxリンクっていうのを必ず登録してきました。<br>
(<a href="#" class="imk-cue" data-seek="5:54" translate="no">00:05:54</a>) WordPressのエンコードURLとかですね、pCloudのURLとか、そういうものをリンクしてきたわけですね。<br>
(<a href="#" class="imk-cue" data-seek="6:04" translate="no">00:06:04</a>) で、このDropboxリンクっていうのがですね、これから契約解除すると、<br>
(<a href="#" class="imk-cue" data-seek="6:13" translate="no">00:06:13</a>) そこがですね、エラーにならないかっていうことが非常に気になっていたわけです。<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:29" translate="no">00:06:29</a>) その中にはですね、すでにDropboxという選択肢は出てきてないです。<br>
(<a href="#" class="imk-cue" data-seek="6:39" translate="no">00:06:39</a>) 次のこのマイコードアプリ、こちらを見ると、ここにはDropboxリンクって出てますけども、<br>
(<a href="#" class="imk-cue" data-seek="6:46" translate="no">00:06:46</a>) その前の、その次の最新のテスト登録pngを見た場合、<br>
(<a href="#" class="imk-cue" data-seek="6:53" translate="no">00:06:53</a>) もうここにはDropboxリンクというものが登場してません。<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:05" translate="no">00:07:05</a>) これがファイルですね。F1マイライブラリというファイルですけども、<br>
(<a href="#" class="imk-cue" data-seek="7:15" translate="no">00:07:15</a>) スプレッドシートです。で、それのA列のところにDropboxリンクURLがあります。<br>
(<a href="#" class="imk-cue" data-seek="7:17" translate="no">00:07:17</a>) で、この今ですね、<br>
(<a href="#" class="imk-cue" data-seek="7:24" translate="no">00:07:24</a>) 一番最近登録してあるこのテスト登録pngというファイルですけども、<br>
(<a href="#" class="imk-cue" data-seek="7:34" translate="no">00:07:34</a>) それはですね、これご覧のようにDropboxリンクのところは空欄になってます。<br>
(<a href="#" class="imk-cue" data-seek="7:41" translate="no">00:07:41</a>) 空欄にしておきながら、wpidexといういわゆるユニークIDはちゃんと設定してですね、<br>
(<a href="#" class="imk-cue" data-seek="7:51" translate="no">00:07:51</a>) 配信する時にいつものようにユニークなURLを使うことができるという仕組みにですね、改造をしました。<br>
(<a href="#" class="imk-cue" data-seek="7:59" translate="no">00:07:59</a>) ま、これによってDropboxリンクのところは空欄になるんですけれども、<br>
(<a href="#" class="imk-cue" data-seek="8:07" translate="no">00:08:07</a>) Dropboxを使って配信をしなければいいだけですね。<br>
(<a href="#" class="imk-cue" data-seek="8:12" translate="no">00:08:12</a>) そういう扱い、運用をすれば問題がないということを確認しました。<br>
(<a href="#" class="imk-cue" data-seek="8:19" translate="no">00:08:19</a>) つまり、この<br>
(<a href="#" class="imk-cue" data-seek="8:24" translate="no">00:08:24</a>) WP再生URLをクリックしてですね、このようにデータが正しく表示されます。<br>
(<a href="#" class="imk-cue" data-seek="8:33" translate="no">00:08:33</a>) それでいいわけですね。<br>
(<a href="#" class="imk-cue" data-seek="8:35" translate="no">00:08:35</a>) そういう形でですね、プログラムの手直しをしたということをやってきました。<br>
(<a href="#" class="imk-cue" data-seek="8:43" translate="no">00:08:43</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>



<figure class="wp-block-image size-full is-resized"><img data-recalc-dims="1" fetchpriority="high" decoding="async" width="472" height="209" src="https://i0.wp.com/imakat.com/wp-content/uploads/2026/08/4fb0a64bd89800a8722405fa6ed6f421-1.png?resize=472%2C209&#038;ssl=1" alt="" class="wp-image-29566" style="width:207px;height:auto"/></figure>



<p class="wp-block-paragraph">長年使い込んできたDropboxからの脱却にあたり、課題となった「ローカルパスの互換性」と「自作アプリ（AppSheet / WordPress連携）への影響」をどのようにクリアしたかについて記録しておきます。</p>



<h2 class="wp-block-heading">１　<strong>乗り換えを決意した理由</strong>〜ドル建て料金は重過ぎる〜</h2>



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



<p class="wp-block-paragraph">これまでDropbox Plus（2TB）を利用していましたが、ドル建て決済（131.87 USD/年）のため近年の円安傾向も相まって年間2万円超（月換算で1,700円〜2,000円近く）の負担となっていました。</p>



<p class="wp-block-paragraph">特に個人の場合、ふつうは貿易はしてないので、支払いに充てる外貨資金があるわけでもなく、やっぱりDropboxは企業重視だなと感じます。</p>



<p class="wp-block-paragraph">そこへ、新たに登場したGoogle AI Proプランは、月額2,900円ですが、生成AIの結構幅広い機能に加えて、Dropboxの2.5倍の5TBの大容量ストレージがパッケージされています。ストレージ単価や今後の拡張性を考慮すると、Googleドライブへ集約する方が圧倒的にメリットが大きいと判断しました。</p>



<p class="wp-block-paragraph">（後日、記事を書こうと思いますが、GoogleAIPro2,900円とGoogleAIPlus725円の相互乗り換えが、Gドライブの使用量を400GB以内に管理することにより可能になることと期待します。）</p>



<h2 class="wp-block-heading">２　<strong>ローカルフォルダ構造とパスの維持工夫</strong></h2>



<p class="wp-block-paragraph">M1 Mac mini環境において、各種スクリプトや自作アプリが既存の「Dropbox」フォルダパスに深く依存しています。すべてのフォルダパスを書き換えるのは膨大な作業となるため、以下の形でミラーリング同期を設定しました。</p>



<ul class="wp-block-list">
<li>ローカル側：既存のフォルダ構造（&#8230;/Dropbox）をそのまま維持</li>



<li>クラウド側：Googleドライブ上の パソコン ＞ マイ Mac mini ＞ mybox_1 フォルダに対してミラーリング同期を実施</li>
</ul>



<p class="wp-block-paragraph">この設計により、ローカル上のスクリプトやファイルパスを変更することなく、同期先クラウドだけをDropboxからGoogleドライブへ安全に切り替えることが可能になります。</p>



<h2 class="wp-block-heading">３　<strong>自作アプリ（AppSheet・配信システム）の影響調査と改善</strong></h2>



<p class="wp-block-paragraph">最も懸念していたのが、自作のメディア管理アプリ（マイライブラリ）および配信システムへの影響です。</p>



<p class="wp-block-paragraph">これまではGoogleスプレッドシート（F1_マイライブラリ）のA列に「Dropbox共有リンク」を記録し、WordPress等のエンコードURLや各配信サーバーへと連携する仕組みを構築していました。Dropboxを解約するとこれらの共有リンクが無効化されてしまうため、以下の改修を行いました。</p>



<p class="wp-block-paragraph">改善ポイント</p>



<p class="wp-block-paragraph">①Dropboxリンク依存の脱却</p>



<ul class="wp-block-list">
<li>今後の新規登録（テスト登録.png 等）では、Dropboxリンク欄を空欄に設定。</li>
</ul>



<p class="wp-block-paragraph">②ユニークID（wpidex）による配信URL生成</p>



<ul class="wp-block-list">
<li>外部共有リンクに頼らず、システム内部のユニークID（wpidex）をベースにWordPress経由の再生URLを直接生成・配信するロジックへ変更。</li>
</ul>



<p class="wp-block-paragraph">実証実験を行い、Dropboxリンクが空欄の状態でも正しくデータが表示され、動画・画像配信に一切支障がないことを確認できました。</p>



<p class="wp-block-paragraph">結局、今回の改善手順のポイントは、<br><strong>Dropboxを解除したあとは、普通のローカルフォルダだけで運用が出来るようにすること。</strong><br>次に<br><strong>そのローカルフォルダをGoogleDriveなど別の場所にミラーリング同期すること。</strong><br></p>



<p class="wp-block-paragraph">ということになります。</p>



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



<h2 class="wp-block-heading">４　<strong>まとめと今後の運用</strong></h2>



<p class="wp-block-paragraph">ビデオの中では、フォルダ名にDropbox dropbox_1を残したまま、昭和歌謡の「昔の名前で出ています(小林旭)」として、使い続けることを決心していたのですが、気が変わりまして、できるなら、ローカルフォルダ名も、mybox mybox_1に改名したいと強く思うようになり、主にPythonアプリを７本程度、手直ししました。その結果、現在は、昔の名前は名乗らず、今の名前で出ています。</p>



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



<a rel="noopener" target="_blank" href="https://docs.google.com/drawings/d/1INrFZ_Q8zmGtWpZq7OUuv2RKofQrlrLqYylZwLLlxSY/edit?usp=sharing"  ="">
<img decoding="async" src="https://docs.google.com/drawings/d/e/2PACX-1vSmkRqDIHqNWLvNC5vzih9yDGCf0In1GISEp-aO4ZLmZm6JMxR6Qt-fnozT8hD275HJJiThM0vnKyqH/pub?w=1920&amp;h=1440"></a>



<p class="wp-block-paragraph">余談：<br>Macには、M2 MacminiとM1 Macminiがありますが、M2の意味は、現在はAppleシリコンM2のM2ではありますが意味としてはMeister2のM2、と意味付けします。将来、AppleシリコンM6 Mac miniをこのNO2_Macの位置に入れ替えたとしても、imakatシステムでは、 M2 Mac mini、と命名し続けます。M1も同様です。と言うのは、Macの機器更新の時、設定を変えることなく、前の機器の内容をそっくり移行できるからです。</p>



<p class="wp-block-paragraph">自作システムの改修とデータ移行の検証が完了したため、これで懸念事項はすべて解消されました。実際、8月30日に解約を行いました。</p>



<p class="wp-block-paragraph">長年依存してきた環境からの移行ですので心配しましたが、構造をシンプルに整理し直す良い機会となりました。同様の構成でストレージ移行を検討されている方の参考になれば幸いです。</p>



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



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">29546</post-id>	</item>
		<item>
		<title>pCloudを動画や画像の配信に使う方法</title>
		<link>https://imakat.com/2025/12/14/28025/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Sun, 14 Dec 2025 02:54:35 +0000</pubDate>
				<category><![CDATA[マイライブラリ]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[Dropbox]]></category>
		<category><![CDATA[pcloud]]></category>
		<category><![CDATA[CDN]]></category>
		<category><![CDATA[ダイレクトリンク]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=28025</guid>

					<description><![CDATA[〜pCloudはスイスにあるクラウドストレージを提供する会社〜 最近、Black Fridayなどで目にすることが増えたpCloud。スイスにあるクラウドストレージを提供する会社で、「買い切りプランも選べる、プライバシー [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><strong>〜pCloudはスイスにあるクラウドストレージを提供する会社〜</strong></p>



<p class="wp-block-paragraph">最近、Black Fridayなどで目にすることが増えたpCloud。スイスにあるクラウドストレージを提供する会社で、「買い切りプランも選べる、プライバシー重視のオンライン倉庫」です。ここで紹介しますが、何も営業的な意図はありません。</p>



<p class="wp-block-paragraph"><strong>～Dropboxが廃止したパブリックフォルダを、pCloudでは使える～</strong></p>



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



<p class="wp-block-paragraph"><br>昔からのDropboxユーザーなら記憶にあることですが、以前はパブリックフォルダがあり、ここからWeb配信が可能でした。Dropboxは企業やビジネス用途が多くて漏洩厳禁なところ、フォルダを間違えて公開するようなうっかりミスが多発することも問題だったようです。Dropboxのパブリックフォルダは「便利すぎて危険」「無料すぎて悪用される」「事業の軸と合わない」として廃止したわけです。pCloudは、パブリックフォルダは、もともと公開利用を前提に設計されていて、それを売りにしていることもあるので、一応大丈夫だとは思います。ただ、いずれにしても、クラウドストレージをメディア配信用に使うときには、２つの心得があります。それは、<strong>一つは、配信サーバーが提供する共有リンクをそのまま公開しないこと、もう一つは、一ヶ所の配信サーバーに依存しないこと</strong>です。<br>それでは、解説ビデオをご覧ください。<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=68_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=HpJbDDCm.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=PPk0v48b.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=cuPsc5PE.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>)  〜pCloudを動画や画像の配信に使う方法〜<br>
(<a href="#" class="imk-cue" data-seek="0:18" translate="no">00:00:18</a>)  今みなさんは、こうしてブログから動画をご覧になっているわけですが　<br>
(<a href="#" class="imk-cue" data-seek="0:28" translate="no">00:00:28</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:44" translate="no">00:00:44</a>)  言えるのは、最近、クラウドストレージの中で<br>
(<a href="#" class="imk-cue" data-seek="0:48" translate="no">00:00:48</a>)  このpCloudが、やけにネットで広告が目立つと思いませんか。<br>
(<a href="#" class="imk-cue" data-seek="0:57" translate="no">00:00:57</a>)  多分、買い切りがあるところが魅力なのだと思いますね。<br>
(<a href="#" class="imk-cue" data-seek="1:03" translate="no">00:01:03</a>)  私は2025年に0.5TBを約2万円で購入しました。<br>
(<a href="#" class="imk-cue" data-seek="1:13" translate="no">00:01:13</a>)  私は、このpCloudについては、パブリックフォルダが使える、<br>
(<a href="#" class="imk-cue" data-seek="1:22" translate="no">00:01:22</a>)  パブリックフォルダから動画や画像が配信できる点が、大きな魅力の一つだと思っています。<br>
(<a href="#" class="imk-cue" data-seek="1:32" translate="no">00:01:32</a>)  pCloudのテーマに入る前に、私が運用している、動画画像の配信の流れについて、説明します。<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:50" translate="no">00:01:50</a>)  動画や画像を制作するための、その元になる材料が、製造工程であるDropboxに入ります。　<br>
(<a href="#" class="imk-cue" data-seek="2:03" translate="no">00:02:03</a>)  FinalCutProや画像ソフト、音楽ソフトで、加工します。<br>
(<a href="#" class="imk-cue" data-seek="2:13" translate="no">00:02:13</a>)  そうしたメディアの完成品のうち、インターネットで配信するものを、配信用アセットと呼びますが、それを保存します。<br>
(<a href="#" class="imk-cue" data-seek="2:27" translate="no">00:02:27</a>)  さらにそれを配信所へ配送します。<br>
(<a href="#" class="imk-cue" data-seek="2:35" translate="no">00:02:35</a>)  Dropboxは、Dropbox自身から直接に配信できます。<br>
(<a href="#" class="imk-cue" data-seek="2:44" translate="no">00:02:44</a>)  産直ですね<br>
(<a href="#" class="imk-cue" data-seek="2:47" translate="no">00:02:47</a>)  他に、Xserver 、pCloud、Vimeo、YouTubeを配信所として利用できるようにしています<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:06" translate="no">00:03:06</a>)  高機能の配信所は、ユーザーの受信環境に応じて、複数の画質を自動的に切り替えてくれる、トランスコーディングという機能を持っています。<br>
(<a href="#" class="imk-cue" data-seek="3:23" translate="no">00:03:23</a>)  Vimeo、Youtubeがそれになります<br>
(<a href="#" class="imk-cue" data-seek="3:28" translate="no">00:03:28</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:47" translate="no">00:03:47</a>)  私の例だと、Dropbox Xserver pCloud、これらがそうです。<br>
(<a href="#" class="imk-cue" data-seek="3:56" translate="no">00:03:56</a>)  ふつうにクラウドストレージと呼ばれる会社群といっていいですね。<br>
(<a href="#" class="imk-cue" data-seek="4:04" translate="no">00:04:04</a>)  これらはデータを格納するのが主な役割で、配信機能はおまけと言ってもいいかも知れません。<br>
(<a href="#" class="imk-cue" data-seek="4:18" translate="no">00:04:18</a>)  ただコスパが非常にいいので、騙し騙し使えるように、　<br>
(<a href="#" class="imk-cue" data-seek="4:25" translate="no">00:04:25</a>)  高画質動画と低画質動画のどちらかを、ユーザーが選択できるようにしました。<br>
(<a href="#" class="imk-cue" data-seek="4:36" translate="no">00:04:36</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:48" translate="no">00:04:48</a>)  そこまでが、配信所までの流れです。<br>
(<a href="#" class="imk-cue" data-seek="4:52" translate="no">00:04:52</a>)  他方、ユーザーからの表示のリクエストがWordPressに入ってきます。<br>
(<a href="#" class="imk-cue" data-seek="5:00" translate="no">00:05:00</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:13" translate="no">00:05:13</a>)  そういう操作になります。<br>
(<a href="#" class="imk-cue" data-seek="5:20" translate="no">00:05:20</a>)  それでは、次に、今回の主役である、pCloudの活用のテーマに入っていきます。<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:45" translate="no">00:05:45</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:54" translate="no">00:05:54</a>)  そのリンクをメールやブログに貼り付けるわけですね。<br>
(<a href="#" class="imk-cue" data-seek="6:01" translate="no">00:06:01</a>)  ファイル名に日本語が入っていると、こんなような、%の入った、複雑な文字列になっています。<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:23" translate="no">00:06:23</a>)  さらに。。名前変更テスト。。<br>
(<a href="#" class="imk-cue" data-seek="6:28" translate="no">00:06:28</a>)  しかし、ローカル側で、うっかり、ファイルを移動したりファイル名を変更してしまうことがありますよね。<br>
(<a href="#" class="imk-cue" data-seek="6:40" translate="no">00:06:40</a>)  その時に、そのファイルを、ダイレクトリンクを通じて、別の人が利用していたりすると、<br>
(<a href="#" class="imk-cue" data-seek="6:57" translate="no">00:06:57</a>)  表示が不可能となり、迷惑をかけてしまうことになります。<br>
(<a href="#" class="imk-cue" data-seek="7:10" translate="no">00:07:10</a>)  だから、このリンク切れを防ぐ方法を考えたいわけです。人はポカを犯しますからね。<br>
(<a href="#" class="imk-cue" data-seek="7:18" translate="no">00:07:18</a>)  そこで、よく使われる方法が、不変のIDを使う方法です。<br>
(<a href="#" class="imk-cue" data-seek="7:30" translate="no">00:07:30</a>)  まあ、マイナンバーのようなものです。<br>
(<a href="#" class="imk-cue" data-seek="7:33" translate="no">00:07:33</a>)  そのマイナンバーに、さまざまな情報が登録されているような形です。<br>
(<a href="#" class="imk-cue" data-seek="7:40" translate="no">00:07:40</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:54" translate="no">00:07:54</a>)  ただマイナンバーというと、人に知られては困るものですよね。<br>
(<a href="#" class="imk-cue" data-seek="8:00" translate="no">00:08:00</a>)  ところが、この不変のIDは、マイナンバーと全く違って、公開されるということです。<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:14" translate="no">00:08:14</a>)  先におみせした、％のついたダイレクトリンクを使うのではなく、<br>
(<a href="#" class="imk-cue" data-seek="8:20" translate="no">00:08:20</a>)  独自に、不変のID で作った再生URLを、メールやブログに貼り付けて、<br>
(<a href="#" class="imk-cue" data-seek="8:28" translate="no">00:08:28</a>)  提供するようにする、ということです。<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:40" translate="no">00:08:40</a>)  このURLをクリックしますと、画像が表示されます。<br>
(<a href="#" class="imk-cue" data-seek="8:51" translate="no">00:08:51</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:29" translate="no">00:09:29</a>)  このwpidexというところに、先ほどの不変のIDが入っていますね。<br>
(<a href="#" class="imk-cue" data-seek="9:44" translate="no">00:09:44</a>)  一方、pCloudサーバー側は、ファイル１つに対して、不変のID=fileidを割り当てています。<br>
(<a href="#" class="imk-cue" data-seek="9:58" translate="no">00:09:58</a>)  この両者をカップリングすれば、いいわけです。<br>
(<a href="#" class="imk-cue" data-seek="10:08" translate="no">00:10:08</a>)  次は、さらに詳細です。<br>
(<a href="#" class="imk-cue" data-seek="10:12" translate="no">00:10:12</a>)  そのfileidを、pCloudサーバーから取得するのには、一工夫が必要になります。<br>
(<a href="#" class="imk-cue" data-seek="10:20" translate="no">00:10:20</a>)  その手順です。<br>
(<a href="#" class="imk-cue" data-seek="10:23" translate="no">00:10:23</a>)  細かいのでブログをお読みいただけばと思います。<br>
(<a href="#" class="imk-cue" data-seek="10:31" translate="no">00:10:31</a>)  最後に豆知識を一つ付けています。これも、ブログをお読みいただければと思います。<br>
(<a href="#" class="imk-cue" data-seek="10:42" translate="no">00:10: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>



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



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



<p class="wp-block-paragraph">新規追加または変更があると、wpidexをキー項目とした、Dropboxリンク、直リンク、ファイルパスを中心に成り立つF1_メディアライブラリファイルのsheet1(シート)が最初に更新され、次に以下のpcloudid(シート)が更新されます。<br></p>



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



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>このスクリプトは、Googleスプレッドシートの「pcloudid」と「sheet1」を基に、pCloud上のファイル情報を自動更新し、filedn形式の直接URLを生成・管理するGASです。sheet1(シート)に新規追加された未登録のwpidexをpcloudid(シート)へ自動追加し、needs_updateがTRUEの行のみを対象に、filepathやfileidからpCloud APIを用いてメタ情報とパスを解決します。生成したdirect_urlや更新日時を反映し、最終的に全データをlast_update順に整列したうえで、pcloud_library.jsonをGoogle Drive上の固定IDファイルへ上書き出力します。</td></tr><tr><td><a href="https://imakat.com/script_list/?pubtxt=pCloud用再生URLおよびJSON生成_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://gemini.google.com/gem/1Dp-NLSA5j5OXE3B1Ki2IaBRCvq4CGa6t?usp=sharing"><img decoding="async" src="https://imakat.com/rd.php?id=s7BMZHhB.png" alt="" style="width:120px; height:auto;"></a></td></tr></tbody></table></figure>



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



<h4 class="wp-block-heading">２　fileidの取得とダイレクトリンクの生成</h4>



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



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



<h4 class="wp-block-heading">３　＜豆知識＞　ファイル名を変更することにより、キャッシュ更新を早める</h4>



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



<p class="wp-block-paragraph">※：<a rel="noopener" target="_blank" href="https://ja.wikipedia.org/wiki/コンテンツデリバリネットワーク">CDN<span class="fa fa-external-link external-icon anchor-icon"></span></a>方式<br>ファイルパスやファイル名の変更は、一般には、好まれませんが、ファイル名の変更はファイルの更新履歴を新しくするので、新たなファイルとして読み込みが起きます。これは、更新処理のスクリプトが安定していることが必須です。<br><br>以上</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">28025</post-id>	</item>
		<item>
		<title>４カ所(米国2　大阪　欧州)からの配信テスト</title>
		<link>https://imakat.com/2025/07/19/26595/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Fri, 18 Jul 2025 20:50:26 +0000</pubDate>
				<category><![CDATA[マイライブラリ]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[pcloud]]></category>
		<category><![CDATA[Dropbox]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[Xserver]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=26595</guid>

					<description><![CDATA[下に、動画を４箇所(米国２　大阪　欧州)から配信しています。遅延など、再生の質の違いが確認できます（このページがあると自分にとっても便利)。WiFiではあまり差はないかも知れませんが、スマホの4G回線などでは、低画質動画 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=i6EX3IuA.jpg" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=i6EX3IuA.jpg" alt="" style="width:222px;height:auto"/></a></figure>



<p class="wp-block-paragraph">下に、動画を４箇所(米国２　大阪　欧州)から配信しています。遅延など、再生の質の違いが確認できます（このページがあると自分にとっても便利)。WiFiではあまり差はないかも知れませんが、スマホの4G回線などでは、低画質動画の有用性を感じられると思います。4G回線などでは制限超えや通信料金に注意ください。</p>



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



<p class="wp-block-paragraph"><strong>Dropbox(配信所：米国)</strong></p>


<div class="sc-dynamic-embed">
  <style>
  /* リンクの見た目を整える */
  .sc-dynamic-embed .sc-link-container { 
      display: flex; 
      gap: 12px; 
      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; }
  
  /* ダウンロードボタンの基本サイズを小さくし、文字の折り返しを防止 */
  .sc-dynamic-embed .dl-btn a {
    font-size: 12px !important;
    color: #d9534f;
    font-weight: bold;
    text-decoration: none;
    background: #fdf0ef;
    padding: 4px 6px;
    border-radius: 4px;
    border: 1px solid #d9534f;
    white-space: nowrap; 
  }
  .sc-dynamic-embed .dl-btn a:hover { background: #d9534f; color: #fff; }

  /* スマホ画面（幅500px以下）の時は、さらに全体を縮小して1行に収める */
  @media (max-width: 500px) {
    .sc-dynamic-embed .sc-link-container { gap: 6px; }
    .sc-dynamic-embed .sc-link a { font-size: 13px; }
    .sc-dynamic-embed .dl-btn a { font-size: 11px !important; padding: 3px 5px; }
  }

  /* 行梱包時の基本スタイル */
  .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=50" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
    <p class="sc-link dl-btn">
      <a href="#" id="imk-dynamic-dl-btn" target="_blank" rel="noopener" download style="display: none;">
        📥 動画をダウンロード
      </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=nGxM3S7I.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=h3lBrfkc.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=rhkYbufA.vtt" label="日本語" srclang="ja" kind="subtitles"></video></div><div class="dr5emd-sublist"><details><summary>字幕一覧(クリック)</summary> <p>
(1) 風の中の小道です。<br>
(2) 佐鳴湖東岸の遊歩道です。猛暑だからこそ嬉しい、涼しい風が通り過ぎます。<br>
(3) 佐鳴湖東岸にある龍雲寺にビオトープがあります。そこに、沢山のカニが生息しています。猛暑で姿は少ないですが。<br>
(4) ビオトープの草がモゾモゾと動いています。カニがいます。何をしているのでしょう。この辺のカニは、ベンケイガニとアカテガニです。最近、アカテガニはめっきり減ったように感じます。<br>
(5) 東岸の遊歩道にいます。西に向かって望むと、(猛暑で枯れ落ちたのか)落ち葉に満ちた公園、ヨシの群生、佐鳴湖、西岸の小山があります。<br>
(6) 草葉に囲まれた日陰から望む佐鳴湖。涼しさが伝わります。これが今回のベスト画像ですかね。<br>
(7) アメリカシデのように思えますが雑木です。ゆらゆらと気持ちよさそうに揺れています。<br>
(8) あまり手がついていない原生林のような雑木林もあります。猛暑からか鳥の声はあまり聞こえませんでした。<br>
(9) ヨシが群生しています。枯れたヨシが堆積すると水質を悪化させるため、冬は人力で刈りとっているようです。<br>
(10) 用水まで出てきたタヌキ。　顔の白毛の模様ですが、タヌキは眉と鼻でT字の形になっています。ハクビシンは鼻に縦棒１本です。　後ろ姿はまるで犬です。<br>
(11) 近くの原っぱにいたオスのキジ。首の動きがニワトリに似ています。<br>
(12) アオサギです。近くの水田では、真っ白なシラサギの方がよく見ます。<br>
(13E) 以上、おまけでした。<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;

    /* -----------------------------------------------
       1. ダウンロードボタンの自動セットアップ機能
       ----------------------------------------------- */
    function setupDownloadButton() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video');
      var dlBtn = target.querySelector('#imk-dynamic-dl-btn');

      if (video && dlBtn && dlBtn.style.display === 'none') {
        var src = video.currentSrc || video.src;
        if (!src) {
          var source = video.querySelector('source');
          if (source) src = source.src;
        }
        if (src) {
          dlBtn.href = src;
          dlBtn.style.display = 'inline-block';
        }
      }
    }

    /* -----------------------------------------------
       2. 字幕制御＆ハイライト機能（全画面・PiP完全対応版）
       ----------------------------------------------- */
    function initSubtitles() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video');
      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判定の強化（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;
    }

    /* -----------------------------------------------
       監視タイマー（URL抜き出し＆ボタン表示を継続的に実行）
       ----------------------------------------------- */
    var checks = 0;
    var checkTimer = setInterval(function(){
      setupDownloadButton();
      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 class="wp-block-paragraph">Dropboxの良い点は、トークンリンク式URLを使ってWeb公開ができる点です。Dropboxはあらゆる業種で利用されているデータストレージで、マルチメディアの制作に関わるメディアアセットももちろんOKで、素材から加工品へ、さらに完成品と安全に保存ができます。それを一つの製造会社と見立てるなら、Dropboxはその完成品の直販、産直ができるという利点があります。Google,Microsoft,Appleなどのクラウドストレージを提供する大手がありますが、彼らは、直販は嫌っていて、メディア配信会社(YouTube,TikTok,Vimeoほか）へ移し、そこから配信、Web公開という形態になります。</p>



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



<p class="wp-block-paragraph"><strong>自<s>宅サーバー(配信所：浜松)</s></strong></p>


<p>動画はまだ準備中です。</p>



<p class="wp-block-paragraph"><s>下記にあるvimeoの活用も加わっており、いよいよ自宅サーバーからのメディア配信は、予備としても、そこまで準備しておく必要はない段階になりました。停止を検討しています。</s><br><s>自宅サーバーの良い点は、手元でいつでも自由自在にメディアの加工ができること、低コストであること、ファイルパスを使った直リンクを使えること、などがあります。欠点は、家庭用の回線(光1G)から配信するので非力で、高画質(大容量)の動画再生は苦手であること、災害に弱いこと、なんでも自分でやらなければならないこと、などがあります。ただ、低画質の動画を選択出来るようにしたので一定の使用価値はあるでしょう。私は、余ったSynologyNASを活用して自宅サーバーを構築していますが、発熱の少ないパソコンなら利用可能です。<br>災害に弱い欠点はありますが（これを言ったら家にある全ての機器がそうですが)、災害がなければ低コストの扱いやすい道具です。蛇足ですが、SynologyNASには監視カメラ機能がついていますので、これを応用してライブ配信も可能です。</s></p>



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



<p class="wp-block-paragraph"><strong>Xserver(配信所：大阪)</strong></p>


<div class="sc-dynamic-embed">
  <style>
  /* リンクの見た目を整える */
  .sc-dynamic-embed .sc-link-container { 
      display: flex; 
      gap: 12px; 
      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; }
  
  /* ダウンロードボタンの基本サイズを小さくし、文字の折り返しを防止 */
  .sc-dynamic-embed .dl-btn a {
    font-size: 12px !important;
    color: #d9534f;
    font-weight: bold;
    text-decoration: none;
    background: #fdf0ef;
    padding: 4px 6px;
    border-radius: 4px;
    border: 1px solid #d9534f;
    white-space: nowrap; 
  }
  .sc-dynamic-embed .dl-btn a:hover { background: #d9534f; color: #fff; }

  /* スマホ画面（幅500px以下）の時は、さらに全体を縮小して1行に収める */
  @media (max-width: 500px) {
    .sc-dynamic-embed .sc-link-container { gap: 6px; }
    .sc-dynamic-embed .sc-link a { font-size: 13px; }
    .sc-dynamic-embed .dl-btn a { font-size: 11px !important; padding: 3px 5px; }
  }

  /* 行梱包時の基本スタイル */
  .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=52" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
    <p class="sc-link dl-btn">
      <a href="#" id="imk-dynamic-dl-btn" target="_blank" rel="noopener" download style="display: none;">
        📥 動画をダウンロード
      </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=TqGE2Td2.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=VSjIoHzr.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=RPags8Bl.vtt" label="日本語" srclang="ja" kind="subtitles"></video></div><div class="dr5emd-sublist"><details><summary>字幕一覧(クリック)</summary> <p>
(1) 風の中の小道です。<br>
(2) 佐鳴湖東岸の遊歩道です。猛暑だからこそ嬉しい、涼しい風が通り過ぎます。<br>
(3) 佐鳴湖東岸にある龍雲寺にビオトープがあります。そこに、沢山のカニが生息しています。猛暑で姿は少ないですが。<br>
(4) ビオトープの草がモゾモゾと動いています。カニがいます。何をしているのでしょう。この辺のカニは、ベンケイガニとアカテガニです。最近、アカテガニはめっきり減ったように感じます。<br>
(5) 東岸の遊歩道にいます。西に向かって望むと、(猛暑で枯れ落ちたのか)落ち葉に満ちた公園、ヨシの群生、佐鳴湖、西岸の小山があります。<br>
(6) 草葉に囲まれた日陰から望む佐鳴湖。涼しさが伝わります。これが今回のベスト画像ですかね。<br>
(7) アメリカシデのように思えますが雑木です。ゆらゆらと気持ちよさそうに揺れています。<br>
(8) あまり手がついていない原生林のような雑木林もあります。猛暑からか鳥の声はあまり聞こえませんでした。<br>
(9) ヨシが群生しています。枯れたヨシが堆積すると水質を悪化させるため、冬は人力で刈りとっているようです。<br>
(10) 用水まで出てきたタヌキ。　顔の白毛の模様ですが、タヌキは眉と鼻でT字の形になっています。ハクビシンは鼻に縦棒１本です。　後ろ姿はまるで犬です。<br>
(11) 近くの原っぱにいたオスのキジ。首の動きがニワトリに似ています。<br>
(12) アオサギです。近くの水田では、真っ白なシラサギの方がよく見ます。<br>
(13E) 以上、おまけでした。<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;

    /* -----------------------------------------------
       1. ダウンロードボタンの自動セットアップ機能
       ----------------------------------------------- */
    function setupDownloadButton() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video');
      var dlBtn = target.querySelector('#imk-dynamic-dl-btn');

      if (video && dlBtn && dlBtn.style.display === 'none') {
        var src = video.currentSrc || video.src;
        if (!src) {
          var source = video.querySelector('source');
          if (source) src = source.src;
        }
        if (src) {
          dlBtn.href = src;
          dlBtn.style.display = 'inline-block';
        }
      }
    }

    /* -----------------------------------------------
       2. 字幕制御＆ハイライト機能（全画面・PiP完全対応版）
       ----------------------------------------------- */
    function initSubtitles() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video');
      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判定の強化（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;
    }

    /* -----------------------------------------------
       監視タイマー（URL抜き出し＆ボタン表示を継続的に実行）
       ----------------------------------------------- */
    var checks = 0;
    var checkTimer = setInterval(function(){
      setupDownloadButton();
      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 class="wp-block-paragraph">Xserverの良い点は、というかWordPressのホスティングサーバーとしてXserverを使っていますので、この、ホスティングサーバーへアップロードして使う方法は、まさに教科書通りの使い方です。<br>配信用メディアの完成品はDropboxにありますので、これを自動的にXserverへ更新する仕組みにしてあります。良い点は、WordPressの指示通りに動き、安全に安定しているということに尽きます。欠点は、ストレージの利用単価が高い、ということです。単純比較すると、Dropboxは2TBで月約1000円、Xserverは0.5TBで月約1000円ですから４倍になります。しかし果たしている機能が違いますし、Xserver は無料の増量サービスもあるので、単純に損得比較はできません。</p>



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



<p class="wp-block-paragraph"><strong>pCloud(配信所：欧州(ルクセンブルグ))</strong></p>


<div class="sc-dynamic-embed">
  <style>
  /* リンクの見た目を整える */
  .sc-dynamic-embed .sc-link-container { 
      display: flex; 
      gap: 12px; 
      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; }
  
  /* ダウンロードボタンの基本サイズを小さくし、文字の折り返しを防止 */
  .sc-dynamic-embed .dl-btn a {
    font-size: 12px !important;
    color: #d9534f;
    font-weight: bold;
    text-decoration: none;
    background: #fdf0ef;
    padding: 4px 6px;
    border-radius: 4px;
    border: 1px solid #d9534f;
    white-space: nowrap; 
  }
  .sc-dynamic-embed .dl-btn a:hover { background: #d9534f; color: #fff; }

  /* スマホ画面（幅500px以下）の時は、さらに全体を縮小して1行に収める */
  @media (max-width: 500px) {
    .sc-dynamic-embed .sc-link-container { gap: 6px; }
    .sc-dynamic-embed .sc-link a { font-size: 13px; }
    .sc-dynamic-embed .dl-btn a { font-size: 11px !important; padding: 3px 5px; }
  }

  /* 行梱包時の基本スタイル */
  .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=53" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
    <p class="sc-link dl-btn">
      <a href="#" id="imk-dynamic-dl-btn" target="_blank" rel="noopener" download style="display: none;">
        📥 動画をダウンロード
      </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=EL3bj0bT.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=SOKu1QVv.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=7BELh91D.vtt" label="日本語" srclang="ja" kind="subtitles"></video></div><div class="dr5emd-sublist"><details><summary>字幕一覧(クリック)</summary> <p>
(1) 風の中の小道です。<br>
(2) 佐鳴湖東岸の遊歩道です。猛暑だからこそ嬉しい、涼しい風が通り過ぎます。<br>
(3) 佐鳴湖東岸にある龍雲寺にビオトープがあります。そこに、沢山のカニが生息しています。猛暑で姿は少ないですが。<br>
(4) ビオトープの草がモゾモゾと動いています。カニがいます。何をしているのでしょう。この辺のカニは、ベンケイガニとアカテガニです。最近、アカテガニはめっきり減ったように感じます。<br>
(5) 東岸の遊歩道にいます。西に向かって望むと、(猛暑で枯れ落ちたのか)落ち葉に満ちた公園、ヨシの群生、佐鳴湖、西岸の小山があります。<br>
(6) 草葉に囲まれた日陰から望む佐鳴湖。涼しさが伝わります。これが今回のベスト画像ですかね。<br>
(7) アメリカシデのように思えますが雑木です。ゆらゆらと気持ちよさそうに揺れています。<br>
(8) あまり手がついていない原生林のような雑木林もあります。猛暑からか鳥の声はあまり聞こえませんでした。<br>
(9) ヨシが群生しています。枯れたヨシが堆積すると水質を悪化させるため、冬は人力で刈りとっているようです。<br>
(10) 用水まで出てきたタヌキ。　顔の白毛の模様ですが、タヌキは眉と鼻でT字の形になっています。ハクビシンは鼻に縦棒１本です。　後ろ姿はまるで犬です。<br>
(11) 近くの原っぱにいたオスのキジ。首の動きがニワトリに似ています。<br>
(12) アオサギです。近くの水田では、真っ白なシラサギの方がよく見ます。<br>
(13E) 以上、おまけでした。<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;

    /* -----------------------------------------------
       1. ダウンロードボタンの自動セットアップ機能
       ----------------------------------------------- */
    function setupDownloadButton() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video');
      var dlBtn = target.querySelector('#imk-dynamic-dl-btn');

      if (video && dlBtn && dlBtn.style.display === 'none') {
        var src = video.currentSrc || video.src;
        if (!src) {
          var source = video.querySelector('source');
          if (source) src = source.src;
        }
        if (src) {
          dlBtn.href = src;
          dlBtn.style.display = 'inline-block';
        }
      }
    }

    /* -----------------------------------------------
       2. 字幕制御＆ハイライト機能（全画面・PiP完全対応版）
       ----------------------------------------------- */
    function initSubtitles() {
      var target = wrapper ? wrapper : document;
      var video = target.querySelector('video');
      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判定の強化（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;
    }

    /* -----------------------------------------------
       監視タイマー（URL抜き出し＆ボタン表示を継続的に実行）
       ----------------------------------------------- */
    var checks = 0;
    var checkTimer = setInterval(function(){
      setupDownloadButton();
      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 class="wp-block-paragraph">pCloudの良い点は、ファイルパスを使った直リンクが使えること、買い切りプランがあること、この２つがあります。私は、0.5TBで約２万円の買い切りにしました。今後、一切費用負担はないとのこと。ファイルパスを使った直リンクは、以前はDropboxは利用できましたが廃止されたため、クラウドストレージを提供する会社では、貴重な存在と言えます。pCloudの保存先には、米国と欧州がありますが、両者を比較すると、日本からの視聴は欧州の方がやや速い、アジアからの視聴は欧州の方が速い、アメリカからの視聴はやや遅い、欧州からの視聴は速い、プライバシー保護は欧州が優れている、といった評価になり、総合的な判断として、欧州を選択しました。</p>



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



<p class="wp-block-paragraph"><strong>Vimeo(配信所：米国およびCDN)</strong></p>


<div class="sc-vimeo-embed">

<p style="font-size: 14px;" class="sc-link">
<a href="https://imakat.com/vm5?movid=1115007067" target="_blank" onclick="stopVimeoBeforeNavigate(event, this)">
動画を別ページで表示(ここをクリック)
</a>
</p>

<div class="iframe-wrapper">
<iframe
src="https://player.vimeo.com/video/1115007067?title=0&byline=0&portrait=0&controls=1&speed=1&texttrack=ja&dnt=1&loop=0"
frameborder="0"
allow="autoplay; fullscreen; picture-in-picture"
allowfullscreen></iframe>
</div>

<div class="dr-vimeo-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>)  〜〜ガラポンTVが異常です。全ランプが点滅を繰り返しています。〜〜<br>
(<a href="#" class="imk-cue" data-seek="0:06" translate="no">00:00:06</a>)  背面の電源をOFFにしました。<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:20" translate="no">00:00:20</a>)  外付けHDDを外しました。<br>
(<a href="#" class="imk-cue" data-seek="0:30" translate="no">00:00:30</a>)  背面の電源をONにしました。<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:44" translate="no">00:00:44</a>)  正常なら、最初に全部が点灯して、一つずつ消えて、最後に、左端の緑だけが点灯します。<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:57" translate="no">00:00:57</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 src="https://player.vimeo.com/api/player.js"></script>

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

if (!iframe || !window.Vimeo || !Vimeo.Player) return;
var player = new Vimeo.Player(iframe);

/* --- 1. 確実に一時停止してから別ページを開く処理 --- */
window.stopVimeoBeforeNavigate = function(event, link) {
  event.preventDefault();
  player.pause().then(function() {
    setTimeout(function() {
      window.open(link.href, link.target);
    }, 50);
  }).catch(function() {
    window.open(link.href, link.target);
  });
};

/* --- 2. 字幕のON/OFF制御（Vimeo API仕様） --- */
var currentMode = "hidden";
function useNormalMode() {
  currentMode = "hidden";
  player.disableTextTrack().catch(function(e){});
}
function useSpecialMode() {
  currentMode = "showing";
  // 日本語の subtitles が無ければ captions を探す安全設計
  player.enableTextTrack('ja', 'subtitles').catch(function() {
    player.enableTextTrack('ja', 'captions').catch(function() {});
  });
}

// ロード直後は枠内字幕を非表示にする
player.ready().then(function() {
  useNormalMode();
});

// Vimeoプレーヤーの全画面・PiPイベントを監視して字幕を切り替え
player.on('fullscreenchange', function(data) {
  if (data.fullscreen) useSpecialMode();
  else useNormalMode();
});
player.on('enterpictureinpicture', function() {
  useSpecialMode();
});
player.on('leavepictureinpicture', function() {
  useNormalMode();
});

/* --- 3. 再生時間に連動した字幕一覧ハイライト＆スクロール --- */
var lines = [];
var detailsEl = target.querySelector(".dr-vimeo-sublist details");
var listContainer = target.querySelector(".dr-vimeo-sublist details > p");

function parseTs(ts){
  if(!ts) return null;
  ts = ts.trim();
  var m = ts.match(/^(\d{1,2}):([0-5]?\d)(?::([0-5]?\d))?$/);
  if(!m) {
    // mm:ss.ms などの別形式に対応
    var p = ts.split(":");
    if(p.length === 2) return parseInt(p[0],10)*60 + parseFloat(p[1]);
    if(p.length === 3) return parseInt(p[0],10)*3600 + parseInt(p[1],10)*60 + parseFloat(p[2]);
    return null;
  }
  var h = m[3] ? parseInt(m[1],10) : 0,
      mm = parseInt(m[3]?m[2]:m[1],10),
      ss = parseInt(m[3]?m[3]:m[2],10);
  return h * 3600 + mm * 60 + ss;
}

function formatLines() {
  if (!listContainer || listContainer.dataset.formatted === 'true') return;
  listContainer.dataset.formatted = 'true';
  var html = listContainer.innerHTML;
  
  // (00:00:00) などの時間タグを含んだテキスト行を1行ずつ span で梱包してインデックス化
  var re = /\((<a[^>]*data-seek="([^"]+)"[^>]*>[^<]+<\/a>)\)\s*([^<]+)<br>/gi;
  var formattedHtml = html.replace(re, function(match, aTag, ts, text){
    var sec = parseTs(ts);
    return '<span class="imk-line" data-sec="' + sec + '">' + match + '</span>';
  });
  listContainer.innerHTML = formattedHtml;
  lines = Array.prototype.slice.call(listContainer.querySelectorAll('.imk-line'));
}

// 読み込み時に details を強制展開し summary を補正
if (detailsEl) {
  detailsEl.open = true;
  var summaryEl = detailsEl.querySelector("summary");
  if (summaryEl) summaryEl.textContent = "字幕(シーン)はここをクリック";
}

// details内のAクリックでシーク
if (listContainer) {
  listContainer.addEventListener('click', function(e){
    var a = e.target.closest('a.imk-cue');
    if (!a) return;
    e.preventDefault();
    var sec = parseTs(a.getAttribute('data-seek'));
    if (sec !== null) {
      player.setCurrentTime(sec).then(function() {
        player.play().catch(function(){});
      });
    }
  });
}

// タイムアップデートで同期ハイライトスクロール
player.on('timeupdate', function(data) {
  var time = data.seconds;
  formatLines();
  if (!lines.length) return;
  
  var activeLine = null;
  for (var i = 0; i < lines.length; i++) {
    var currentSec = parseFloat(lines[i].getAttribute('data-sec'));
    var nextSec = (i < lines.length - 1) ? parseFloat(lines[i+1].getAttribute('data-sec')) : 999999;
    if (time >= currentSec && time < nextSec) {
      activeLine = lines[i];
      break;
    }
  }
  
  lines.forEach(function(l) {
    l.style.backgroundColor = '';
    l.style.fontWeight = 'normal';
  });
  
  if (activeLine) {
    activeLine.style.backgroundColor = '#FFF1A8';
    activeLine.style.fontWeight = 'bold';
    
    // スクリューボックスの自動スクロール
    var rect = activeLine.getBoundingClientRect();
    var contRect = listContainer.getBoundingClientRect();
    if (rect.top < contRect.top || rect.bottom > contRect.bottom) {
      var targetScroll = activeLine.offsetTop - listContainer.offsetTop - (contRect.height / 2) + (rect.height / 2);
      listContainer.scrollTo({
        top: targetScroll,
        behavior: "smooth"
      });
    }
  }
});
})();
</script>

<style>
.sc-vimeo-embed .iframe-wrapper {
  position: relative;
  padding-bottom: 56.25%;
  height: 0;
  overflow: visible;
}
.sc-vimeo-embed .iframe-wrapper iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
/* 行梱包時の基本スタイル（干渉防止のためクラス内で指定） */
.sc-vimeo-embed .imk-line {
  display: inline-block;
  width: 100%;
  border-radius: 2px;
  transition: background-color 0.1s;
}
/* 古い枠内字幕ボックスを強制消去 */
.sc-vimeo-embed #subtitleOverlay,
.sc-vimeo-embed #scSubtitleOverlay,
.sc-vimeo-embed .overlay-cue,
.sc-vimeo-embed .band {
  display: none !important;
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
}
.dr-vimeo-sublist details > summary {
  padding: 4px 8px;
  width: 100%;
  background-color: #ddd;
  border: none;
  box-shadow: 3px 3px 4px black;
  cursor: pointer;
  list-style: none;
  font-weight: bold;
}
</style>

</div>



<p class="wp-block-paragraph">Vimeoの良い点は、オートトランスコードであること、いいね、再生数、広告などを除くことができる、差し替えができる、などです。Vimeoのコンパネからの投稿には一手間の面倒さがありますが、私は最近、ローカルからVimeoへの直アップロードの仕組みを作ったので、Vimeoのコンパネを触ることなく、差し替えができるようになりました。但し、VimeoPLusは最安ランクなので、新規の投稿の時に、細部の設定はコンパネから行う必要があります。25年11月頃から、保存量が2TBに引き上がりました。使いやすくなっています。仕方ないことですが、字幕一覧から、シーンへ飛ぶことはできません。</p>



<p class="wp-block-paragraph">以上です。</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">26595</post-id>	</item>
		<item>
		<title>第４章：リンク切れを防ぐ仕組みほか</title>
		<link>https://imakat.com/2025/04/26/25984/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Sat, 26 Apr 2025 11:06:57 +0000</pubDate>
				<category><![CDATA[マイライブラリ]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[Webホスティング]]></category>
		<category><![CDATA[識別子]]></category>
		<category><![CDATA[直リンク]]></category>
		<category><![CDATA[トークンリンク]]></category>
		<category><![CDATA[リンク切れ]]></category>
		<category><![CDATA[Dropbox]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=25984</guid>

					<description><![CDATA[メディアライブラリを作ろう 大手YouTubeなどに依存せず、独自に配信を行うのであれば、小規模な配信所を複数確保し、さらにそれらを迅速に切り替えられる仕組みを整えることが重要です。ここまでの章では、配信所には「トークン [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">メディアライブラリを作ろう</p>



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



<p class="wp-block-paragraph">大手YouTubeなどに依存せず、独自に配信を行うのであれば、小規模な配信所を複数確保し、さらにそれらを迅速に切り替えられる仕組みを整えることが重要です。<br>ここまでの章では、配信所には「トークンリンク方式」と「直リンク方式」があり、それぞれに特徴と注意点があることを説明してきました。</p>



<ul class="wp-block-list">
<li><strong>トークンリンク方式</strong>は、大手クラウドが採用しており、セキュリティ重視。そのうちDropboxなど限られたサービスのみが配信向きです。</li>



<li><strong>直リンク方式</strong>は、Webサーバーがインストールできれば、その中で容易に構築できますが、エンコード済みURLを管理する必要があり、リンク切れ対策が不可欠です。</li>
</ul>



<p class="wp-block-paragraph">この章では、特に「リンク切れを防ぐ仕組み作り」について具体的に説明していきます。</p>



<h3 class="wp-block-heading"><strong>1. Dropboxへの依存リスク</strong>〜【脱Dropbox】は可能か〜</h3>



<p class="wp-block-paragraph">トークンリンク方式には大きな利点があります。<br>ファイルパスやファイル名を変更しても、共有リンクURL（トークンリンク）は基本的に変更されません。<br>これにより、配信側にとって運用が非常に安定します。</p>



<p class="wp-block-paragraph">しかし、<strong>ストリーミング再生に対応したトークンリンク方式</strong>を許可しているクラウドサービスは非常に少なく、現状では<strong>Dropbox</strong>がほぼ唯一の選択肢となっています。<br>このため、Dropboxに頼ってしまう、配信インフラをDropboxのみに依存するリスクが懸念されます。<br>では、Dropboxの代替となるクラウドは存在するのでしょうか？ 次に、主要なクラウドストレージの状況をまとめてみます。</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td style="background-color:#dddddd"></td><td style="background-color:#dddddd"><strong>トークンリンク方式のクラウド</strong></td><td style="background-color:#dddddd"><strong>ファイルパスから共有リンク取得</strong></td><td style="background-color:#dddddd"><strong>共有リンクからストリーミング表示可能か</strong></td><td style="width:30%;background-color:#dddddd"><strong>コメント</strong></td></tr><tr><td><img data-recalc-dims="1" decoding="async" width="150" height="150" class="wp-image-23536" style="width: 150px;" src="https://i0.wp.com/imakat.com/wp-content/uploads/2024/08/apps.23871.13668225141277943.68205d94-7cbe-41f0-893f-53305fceb682.4c98395a-28d0-4eee-9b6e-08ecd210e980.png?resize=150%2C150&#038;ssl=1" alt=""></td><td><strong>Dropbox</strong></td><td>可能 &#x2705;(API使用)</td><td>可能 &#x2705;（raw=1 などで直再生可）</td><td style="width:30%">Mac Finderパスとクラウドパスが一致、取得容易。dl=0をraw=1に変換でストリーミング。</td></tr><tr><td><img data-recalc-dims="1" decoding="async" width="150" height="134" class="wp-image-26007" style="width: 150px;" src="https://i0.wp.com/imakat.com/wp-content/uploads/2025/04/efb949124a22b7e8531beeaa646f299b.png?resize=150%2C134&#038;ssl=1" alt=""></td><td><strong>Google Drive</strong></td><td>工夫すれば可能 △</td><td>部分的に可能 △（制限あり）</td><td style="width:30%">API検索でファイルID取得可能。基本はビューアーページ誘導。ファイル種別・設定次第で挙動が変わる。</td></tr><tr><td><img data-recalc-dims="1" loading="lazy" decoding="async" width="150" height="110" class="wp-image-26009" style="width: 150px;" src="https://i0.wp.com/imakat.com/wp-content/uploads/2025/04/9cc2564f50d59bc8e0dd4add9c49cb9f.png?resize=150%2C110&#038;ssl=1" alt=""></td><td><strong>OneDrive</strong></td><td>工夫すれば可能 △</td><td>部分的に可能 △（制限あり）</td><td style="width:30%">API経由でパス整形可能。ストリーミング可否はファイル形式やブラウザに依存。</td></tr><tr><td><img data-recalc-dims="1" loading="lazy" decoding="async" width="150" height="103" class="wp-image-26008" style="width: 150px;" src="https://i0.wp.com/imakat.com/wp-content/uploads/2025/04/0950888e1f0b5b1fffae2a277d0086d7.png?resize=150%2C103&#038;ssl=1" alt=""></td><td><strong>iCloud Drive</strong></td><td>ほぼ不可能 &#x274c;</td><td>ほぼ不可能 &#x274c;</td><td style="width:30%">API経由での共有リンク取得不可。共有リンクもストリーミングに適さない。</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">このように、複数のクラウドをトークンリンク方式で使い分けることは現実的ではなく、<strong>トークンリンク方式についてはDropboxに一本化する</strong>のが最も合理的な選択だと言えます。繰り返し言います。それゆえに、Dropboxに依存したくなるのですが、サービスが永続する保証など無く、そこにリスクがあるわけです。</p>



<figure class="wp-block-image size-full is-resized"><a href="https://i0.wp.com/imakat.com/wp-content/uploads/2025/07/006fc22b73c6d589b68264a753102b7a.png?ssl=1" target="_blank"><img data-recalc-dims="1" loading="lazy" decoding="async" width="600" height="581" src="https://i0.wp.com/imakat.com/wp-content/uploads/2025/07/006fc22b73c6d589b68264a753102b7a.png?resize=600%2C581&#038;ssl=1" alt="" class="wp-image-26550" style="width:121px;height:auto" srcset="https://i0.wp.com/imakat.com/wp-content/uploads/2025/07/006fc22b73c6d589b68264a753102b7a.png?w=600&amp;ssl=1 600w, https://i0.wp.com/imakat.com/wp-content/uploads/2025/07/006fc22b73c6d589b68264a753102b7a.png?resize=500%2C484&amp;ssl=1 500w" sizes="(max-width: 600px) 100vw, 600px" /></a></figure>



<h3 class="wp-block-heading"><strong>2. <strong>直リンク方式による配信とリンク切れ対策</strong></strong></h3>



<p class="wp-block-paragraph">一方、<strong>直リンク方式</strong>ならば、Dropboxに依存せずに複数の配信所を設けることが可能です。<br>具体的な候補は以下のようなものがあります。</p>



<ul class="wp-block-list">
<li><strong>pCloud(パブリックフォルダ利用)</strong></li>



<li><strong>WordPressサーバー(Xserverなど)</strong></li>



<li><strong>自宅Webサーバー</strong></li>



<li><strong>レンタルWebサーバー（独自ドメイン）</strong></li>
</ul>



<p class="wp-block-paragraph">※pCloudは今では特異な存在で、トークンリンク方式と直リンク方式の両方を提供しています。昔のDropboxに似ています。pCloudは使い勝手のいい仕組みを持っているのですが、サーバーが欧州と北米にしかないので、遅延が生じ易いです。日本にサーバーがあればかなり有用だと思います。</p>



<p class="wp-block-paragraph">これらはいずれも、ドメイン名直下またはサブディレクトリに<strong>メディアフォルダをコピーするだけ</strong>で、新たな配信拠点をすぐに追加できます。</p>



<p class="wp-block-paragraph">ただし、ここで問題になるのが、ファイルパスやファイル名の変更に伴う<strong>リンク切れ</strong>です。</p>



<p class="wp-block-paragraph">これを防ぐために、次のような仕組みを用意します。</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td colspan="2" style="background-color:#dddddd"><strong>バックグラウンド処理</strong>(常時監視する)&#x2b07;&#xfe0f;</td></tr><tr><td colspan="2">ファイルパス、ファイル名の変更前と変更後を記録する。</td></tr><tr><td colspan="2"><a href="https://imakat.com/script_list/?pubtxt=ファイルパス変更処理_gasからpython移行_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>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td colspan="2" style="background-color:#dddddd"><strong>メディアフォルダの更新方法</strong>(第２章のパイプライン処理では、スクリプトを記述しているもののみ取り上げ)&#x2b07;&#xfe0f;</td></tr><tr><td colspan="2"><strong>Dropbox</strong>：Dropboxのローカルフォルダが全ての源流。私の場合、mmedia、pmediaという名のフォルダを設けている。この２つのフォルダを、各サーバーへコピーあるいは同期する。</td></tr><tr><td colspan="2"><s><strong>自宅サーバー</strong>：SynologyNASにWordPressサーバーを建てている。SynologyNASのCloudSyncを使いDropbox経由で同期している。</s></td></tr><tr><td colspan="2"><strong>Xserver(WordPress)</strong>：SynologyNASからXserverへ、ShellScriptのrsyncにより送り込んでいる。毎分処理。</td></tr><tr><td colspan="2"><strong>pCloud</strong>：pCloudDriveの同期機能を使い、ローカルフォルダとpCloudのPublic Folder内のフォルダと同期する。</td></tr></tbody></table></figure>



<ul class="wp-block-list">
<li><strong>ファイルパス変更記録ファイル</strong>を保持する<br>（Macで変更前ファイルパスと変更後ファイルパスを記録して、Spreadsheetを更新する）</li>



<li>ライブラリ登録の時、これを照合して、配信用URL（例：https://imakat.com/rd.php?id=Abcd0123.png）に紐づくパスやファイル名を<strong>最新のものに置き換えたJSONファイルを生成</strong>する。</li>
</ul>



<p class="wp-block-paragraph">こうすることで、仮にファイル構成やファイル名が変わっても、<strong>配信</strong><strong>URL</strong><strong>自体は変更せずに運用を継続できる</strong>ようになります。</p>



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



<h3 class="wp-block-heading"><strong>３. </strong>メディアライブラリ更新のアルゴリズム</h3>



<p class="wp-block-paragraph">メディアライブラリの更新の判断は以下のようになっています。まず最初にファイルパスが変更されたかどうかが基軸になります。ファイルパスが同じで、Dropboxリンクが変更されている場合は、メディアライブラリに登録するDropboxリンクも変更されます。Dropboxリンクが同じでファイルパスが変更されている場合は、メディアライブラリに登録するファイルパスも変更されます。この仕組みにより、ファイルパスやフォルダ名が変更されても、配信用URLは不変で運用ができるようになっています。</p>



<a rel="noopener" target="_blank" href="https://docs.google.com/drawings/d/1JgcOPS8GESxG34LDFSUE1i3CVe0PSM6sAyZrfqkV0cc/edit?usp=sharing" 
>
<img decoding="async" src="https://docs.google.com/drawings/d/e/2PACX-1vSANFz-ypSmjCU127MiCadlAinb6t1kCfcIgPHB5tiKXOw8EXyxU6RKubeYVeWA-K_QeYQ07X9Y5rz7/pub?w=960&#038;h=720"
></a>



<h3 class="wp-block-heading"><strong>４. </strong>まとめ</h3>



<ul class="wp-block-list">
<li><strong>トークンリンク方式</strong>は運用が安定していますが、Dropboxへの依存が避けられず、サービス継続性にリスクがあります。</li>



<li><strong>直リンク方式</strong>は柔軟性が高く、複数の配信拠点を設けることが可能ですが、リンク切れ対策が不可欠です。</li>



<li><strong>リンク切れを防ぐ仕組み</strong>として、ファイルの変更監視と配信用URLの動的更新が効果的です。</li>
</ul>



<p class="wp-block-paragraph"><strong>第５章〜終わりに、では、「動画パッケージの作り方」</strong>についてご紹介します。<br></p>



<p class="wp-block-paragraph">&#x27a1; <a href="https://imakat.com/2025/04/29/26044/" target="_blank">第５章を読む</a></p>



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



<p class="has-text-align-center wp-block-paragraph"><a href="https://imakat.com/media_library1" target="_blank">&#x1f517; 目次ページへ戻る</a></p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">25984</post-id>	</item>
		<item>
		<title>第３章：YouTubeなど（大手）に依存しないで配信する～複数の再生サーバーを切り替える仕組み～</title>
		<link>https://imakat.com/2025/04/25/25930/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Thu, 24 Apr 2025 20:19:59 +0000</pubDate>
				<category><![CDATA[マイライブラリ]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[NFC]]></category>
		<category><![CDATA[RFC]]></category>
		<category><![CDATA[識別子]]></category>
		<category><![CDATA[ストリーミング]]></category>
		<category><![CDATA[直リンク]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[トークンリンク]]></category>
		<category><![CDATA[Dropbox]]></category>
		<category><![CDATA[Webホスティング]]></category>
		<category><![CDATA[メディアアセット]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=25930</guid>

					<description><![CDATA[メディアライブラリを作ろう 本章では、YouTubeやTikTokなどの大手拡散型プラットフォームを使用せず、自前の仕組みで動画やメディアを配信する方法について深掘りしていきます。つまり、クラウド時代の“ハンドメイド配信 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">メディアライブラリを作ろう</p>



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



<p class="wp-block-paragraph">本章では、YouTubeやTikTokなどの大手拡散型プラットフォームを使用せず、自前の仕組みで動画やメディアを配信する方法について深掘りしていきます。つまり、クラウド時代の“ハンドメイド配信”を実現するための枠組みです。</p>



<h3 class="wp-block-heading"><strong>1. 特定の大手に依存しないということの意味</strong></h3>



<p class="wp-block-paragraph">YouTubeなどの大手は、確かに拡散力や安定性に優れていますが、同時にそのプラットフォームの規約や方針変更に大きく左右されます。広告挿入やアルゴリズムによる露出制限など、制作者の意思とは無関係な制限が突然加わる可能性も否定できません。それからYouTubeやTikTokなど大手の多くは、少し手を加えるだけで簡単に他人の動画を切り抜いて転用できる、AI自動生成物がその真偽を検証されることなく配信される、そうした緩さがあるため、どうしても信用性は低く、オモチャや娯楽の道具として一段低く扱われやすいことがあります。</p>



<p class="wp-block-paragraph">私が目指すのは、小規模ながらも自由度が高く、そして制作者の意思で完結できる配信手段です。ただし、ここには大きな課題もあります。それは“<strong>小さな仕組みほど不安定で消えやすい。小さな会社ほど資金が乏しくすぐ倒産する。</strong>”という現実です。</p>



<p class="wp-block-paragraph">この課題は、製造業におけるサプライチェーンの問題と非常によく似ています。特定の国や工場に依存しすぎると、何らかの外的要因で供給が止まるリスクがあります。そうでないにしても関税のように異常な高コストになるリスクがあります。同様に、配信インフラが一箇所に依存していると、そのサービスが終了した瞬間にすべての再生が止まってしまいます。</p>



<p class="wp-block-paragraph">だからこそ必要なのが「<strong>複数の小規模の再生サーバーを柔軟に切り替える仕組み</strong>」です。</p>



<p class="wp-block-paragraph">ここではこれ以上踏み込みませんが、そうした場における無依存性も重要ですが、一つの配信システム、一つの配信技術に依存しないという観点も重要で、例示的には自動車がマルチパスウエイと称するところの、ガソリン、電気、ハイブリッド、水素、他の複数の道筋を残し、組み合わせ、スイッチするという、この種のシステムアーキテクチャーを構築するところに本当の面白さがありそうです。</p>



<h3 class="wp-block-heading"><strong>2. 再生URLの設計と意味</strong></h3>



<p class="wp-block-paragraph">私が使っている再生URLの構造は以下の通りです：</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td><strong>https://imakat.com/rd.php?id=Abcd0123.png</strong></td></tr></tbody></table></figure>



<ul class="wp-block-list">
<li>imakat.com は私の管理しているドメイン名です。</li>



<li>rd.php は再生用の振り分けスクリプトです。</li>



<li>Abcd0123.png の部分が、一意の識別子になっています。</li>
</ul>



<p class="wp-block-paragraph">この識別子には<strong>ランダムな8文字の大小英文字数字</strong>を使い、あえて<strong>拡張子（.png や .mp4 など）を付けた形式</strong>を採用しています。<br>これにより、URLだけを見てもおおよそのファイル種別が判断できるため、運用や整理の際にとても便利です。</p>



<p class="wp-block-paragraph">よく見かける「拡張子なしのランダム識別子」も確かにセキュリティ的な利点はありますが、その一方でファイル内容の把握や管理は困難になります。</p>



<a rel="noopener" target="_blank" href="https://docs.google.com/drawings/d/173W_0PrUWcWEpCzcjsJebm-cUwZxX73yCvw54q_GTH4/edit?usp=sharing" 
>
<img decoding="async" src="https://docs.google.com/drawings/d/e/2PACX-1vSWXRozx45bwD3f3QE5AU3vUMkMPglNP1MeGljlgAUpJlZp_B4sgespmr6u5SEJH92lk39aL15h7zT8/pub?w=960&#038;h=720"
></a>



<p class="wp-block-paragraph">WordPressサーバー側で、phpスクリプトによって、選択された再生サーバー番号に合わせて、送り込んだJSONファイルとの照合を行ったりエンコード後のURLを生成するなどの作業を行っています。</p>



<h4 class="wp-block-heading">直リンク方式の場合は、ファイルパスのエンコードが必要</h4>



<p class="wp-block-paragraph">直リンク方式(ファイルパス方式)の場合に、ファイルパスのエンコードが必要になりますが、ややこしい問題は、そのエンコードの方式が、サーバー会社によって異なる点です。各社が提供したエンコード後URLと、自分がファイルパスに基づく直リンクをエンコードしたURL、両者を比較して、それが一致するエンコード方式を使用することです。一致しないと、「ファイルが見つかりません」となり再生されません。</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>mmediaまたはpmedia以下のファイルパス</td><td>/mmedia/id45_メディアライブラリ_はじめに/250411_メディアライブラリ_はじめに.mp4</td></tr><tr><td><strong>TypeA</strong><br>[NFC.RFC3986 URL encoding]方式によるエンコードをすると&#x27a1;&#xfe0f;</td><td>/mmedia/id45_%E3%83%A1%E3%83%86%E3%82%99%E3%82%A3%E3%82%A2%E3%83%A9%E3%82%A4%E3%83%95%E3%82%99%E3%83%A9%E3%83%AA_%E3%81%AF%E3%81%97%E3%82%99%E3%82%81%E3%81%AB/250411_%E3%83%A1%E3%83%86%E3%82%99%E3%82%A3%E3%82%A2%E3%83%A9%E3%82%A4%E3%83%95%E3%82%99%E3%83%A9%E3%83%AA_%E3%81%AF%E3%81%97%E3%82%99%E3%82%81%E3%81%AB.mp4</td></tr><tr><td><strong>TypeB</strong><br>[NFC,部分エンコード(ブラウザ互換型)&nbsp;]によるエンコードをすると&#x27a1;&#xfe0f;</td><td>/mmedia/id45_%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA_%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB/250411_%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA_%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB.mp4</td></tr><tr><td></td><td>&#x2b06;&#xfe0f;<strong>TypeAとTypeBとでは、よく見ると、あちこち、かなり文字が異なっています。</strong></td></tr></tbody></table></figure>



<p class="wp-block-paragraph"><br>この問題を除けば、直リンク方式の方が、今回行っているような構築は、はるかに簡単に済みます。<br>ただし現実には、直リンク方式を提供するクラウドストレージの会社は非常に少なくなり、pCloudはそのうちの1社です。</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td colspan="2">WordPress(Xserver)内のURL再生が可能な状態にする(rd.php、再生用振り分けスクリプト)</td></tr><tr><td colspan="2"><a href="https://imakat.com/?pubtxt=マイライブラリXserverのrdphp_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">結局、そのクラウドストレージがその内部にWebサーバーを建てること(Webホスティング)を許可しているかどうか、Webサーバーを建てることができれば、直リンク方式の開放ができる可能性が高いです。次章で深掘りしますが、残念ながら、例えば、Dropbox、Googleドライブ、iCloudドライブ、Oneドライブなど主要大手は、ストレージの中にWebサーバーを建てることができなくなっているわけです。その中で、Dropboxはraw=1により表示が可能、pCloudはパブリックフォルダにより表示が可能、と珍しい存在になっているわけです。</p>



<p class="wp-block-paragraph">ただし、直リンク方式は、ファイルパスやファイル名を変更するとリンク切れを起こす、という致命的欠点があります。この欠点の克服方法を、別の章で紹介することにします。</p>



<h3 class="wp-block-heading"><strong>3. リンク提供とストリーミング再生の違い</strong></h3>



<p class="wp-block-paragraph">再生URLは、<strong>動画や音声などのストリーミング再生、画像再生</strong>を行うための仕組みですが、<strong>ファイルの直接ダウンロードリンク</strong>としても応用可能です。たとえばExcelファイルの配布などにも対応できます。</p>



<p class="wp-block-paragraph">ただし、<strong>再生</strong><strong>URL</strong><strong>をダウンロード専用の仕組みとして多用するのはおすすめしません</strong>。本来の目的はストリーミングにあるため、無理に使い方を広げすぎると運用が複雑になってしまいます。</p>



<h3 class="wp-block-heading"><strong>4. </strong>再生までの処理フロー</h3>



<p class="wp-block-paragraph">以下が再生までの処理フローです：</p>



<ol class="wp-block-list">
<li>ユーザーがブラウザやメールクライアントなどから再生URLにアクセスする</li>



<li>rd.php が呼び出され、該当する識別子（例：Abcd0123.png）に紐づくサーバーを判定する</li>



<li>メディアライブラリアプリ(AppSheet)にて選ばれた再生サーバー（Dropbox、Xserver、pCloudなど）へ転送される</li>



<li>ストリーミング再生が開始される</li>
</ol>



<p class="wp-block-paragraph">これにより、<strong>複数の配信先を切り替えられる柔軟な配信構造</strong>が実現します。たとえ1つのサーバーに不具合が起きても、AppSheetから、拡張子別あるいはファイル別に再生サーバーを切り替えることが可能になっています。</p>



<h3 class="wp-block-heading">次回予告：</h3>



<p class="wp-block-paragraph"><strong>第４章では、「リンク切れを防ぐ仕組みほか」</strong>についてご紹介します。<br></p>



<p class="wp-block-paragraph">&#x27a1; <a href="https://imakat.com/2025/04/26/25984/" target="_blank">第４章を読む</a></p>



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



<p class="has-text-align-center wp-block-paragraph"><a href="https://imakat.com/media_library1" target="_blank">&#x1f517; 目次ページへ戻る</a></p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">25930</post-id>	</item>
		<item>
		<title>第2章「メディアライブラリへの登録〜パイプライン処理〜」</title>
		<link>https://imakat.com/2025/04/08/25796/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Tue, 08 Apr 2025 02:02:58 +0000</pubDate>
				<category><![CDATA[マイライブラリ]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[MAM]]></category>
		<category><![CDATA[メディアアセット]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Dropbox]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=25796</guid>

					<description><![CDATA[メディアライブラリを作ろう ポイント解説： 作成するスクリプトは、結構、分量があります。ChatGPTを使い、ゆっくり進めるのがいいです。以下のスクリプトをChatGPTへコピペして、（１）「このスクリプトの解説をしてく [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">メディアライブラリを作ろう</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=46" 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=kmelubbx.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=p5ZFXLqv.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=enPvHIg9.vtt" label="日本語" srclang="ja" kind="subtitles"></video></div><div class="dr5emd-sublist"><details><summary>字幕一覧(クリック)</summary> <p>
(<a href="#" class="imk-cue" data-seek="0:00">00:00:00</a>)  〜メディアライブラリを作ろう〜<br>
(<a href="#" class="imk-cue" data-seek="0:18">00:00:18</a>)  メディア用ライブラリですね。そのパイプライン処理をですね。やってみたいと思います。<br>
(<a href="#" class="imk-cue" data-seek="0:30">00:00:30</a>)  まずこの画面にですね。画像が入っているフォルダーがあります。<br>
(<a href="#" class="imk-cue" data-seek="0:37">00:00:37</a>)  1つこれはChatGPTで作った画像ですけれどもこの画像ですね。pmediaフォルダにコピーします。<br>
(<a href="#" class="imk-cue" data-seek="0:54">00:00:54</a>)  実はもうコピーしてあります。このように「鳥の群れ」のですね。写真が入ってます。<br>
(<a href="#" class="imk-cue" data-seek="1:05">00:01:05</a>)  次にこのファイルの上でメディアライブラリ作成を行います。<br>
(<a href="#" class="imk-cue" data-seek="1:12">00:01:12</a>)  データがPythonによってGASへ送られます。<br>
(<a href="#" class="imk-cue" data-seek="1:24">00:01:24</a>)  ちょっと待つのですがGASへ送られました。GASにデータが正常に送信されました。<br>
(<a href="#" class="imk-cue" data-seek="1:37">00:01:37</a>)  OKを押します。<br>
(<a href="#" class="imk-cue" data-seek="1:40">00:01:40</a>)  この状態ですとどうなってるかと言うとSpreadsheetを持ってきます。<br>
(<a href="#" class="imk-cue" data-seek="1:50">00:01:50</a>)  そうするとですね。Spreadsheetの1番上段にですね。もう、ChatGPT今のファイルですね。それが登録されてます。<br>
(<a href="#" class="imk-cue" data-seek="2:06">00:02:06</a>)  同時にこれに登録されていれば当然なんですが、<br>
(<a href="#" class="imk-cue" data-seek="2:14">00:02:14</a>)  メディアライブラリですね。<br>
(<a href="#" class="imk-cue" data-seek="2:16">00:02:16</a>)  AppSheetで作ったメディア用ライブラリの先頭にもですね。ChatGPTの画像が入ってきます。<br>
(<a href="#" class="imk-cue" data-seek="2:34">00:02:34</a>)  これ今現在、処理が少し時間がかかるので、<br>
(<a href="#" class="imk-cue" data-seek="2:43">00:02:43</a>)  「エラー：指定されたIDのデータが見つかりません」となりますが、少し待ちます。<br>
(<a href="#" class="imk-cue" data-seek="2:51">00:02:51</a>)  どうでしょうか。はい再生URLから「鳥の群れ」の画像が表示されました。<br>
(<a href="#" class="imk-cue" data-seek="3:04">00:03:04</a>)  これは実はいくつかの再生サーバーに置いてあるんですけれども、<br>
(<a href="#" class="imk-cue" data-seek="3:11">00:03:11</a>)  Dropboxリンクからはこのように再生されます。<br>
(<a href="#" class="imk-cue" data-seek="3:18">00:03:18</a>)  それからもう一つWPエンコード後URL、これはWordPressサーバーですが、そこからもしっかり再生されます。<br>
(<a href="#" class="imk-cue" data-seek="3:32">00:03:32</a>)  いくつか再生サーバーがあるんですけれども、それを選択をするようになってまして、<br>
(<a href="#" class="imk-cue" data-seek="3:38">00:03:38</a>)  選択番号3っていうのはですね。このWordPressサーバーから引っ張ってくることになっています。<br>
(<a href="#" class="imk-cue" data-seek="3:50">00:03:50</a>)  これですね。この再生URLこれをクリックするとWordPressサーバーから引っ張ってくるようになっています。今3番を選んでますからですね。<br>
(<a href="#" class="imk-cue" data-seek="4:07">00:04:07</a>)  このような形で処理が行われます。　<br>
(<a href="#" class="imk-cue" data-seek="4:14">00:04:14</a>)  それでは次ですね。もう一つの改良点についてですね。説明します。<br>
(<a href="#" class="imk-cue" data-seek="4:22">00:04:22</a>)  今の1つのファイルを登録したわけですね。<br>
(<a href="#" class="imk-cue" data-seek="4:28">00:04:28</a>)  このChatGPTimage..pngを登録したわけですけれども、<br>
(<a href="#" class="imk-cue" data-seek="4:34">00:04:34</a>)  このChatGPTimage..と言う抽象的な名前ですと、何のことかわからんわけですね。<br>
(<a href="#" class="imk-cue" data-seek="4:42">00:04:42</a>)  それでファイル名の中に具体的な例えば何の映像だとかそういうことをですね。書いておいたほうがいいですよね。<br>
(<a href="#" class="imk-cue" data-seek="4:54">00:04:54</a>)  ファイルの名前を変えるわけです。その場合。<br>
(<a href="#" class="imk-cue" data-seek="5:00">00:05:00</a>)  名前を変えます。<br>
(<a href="#" class="imk-cue" data-seek="5:07">00:05:07</a>)  具体的には日付、これはですね。これは「鳥の群れ」とします。名前を変えました。<br>
(<a href="#" class="imk-cue" data-seek="5:33">00:05:33</a>)  そうするとですね。中で自動処理が進んでまして、<br>
(<a href="#" class="imk-cue" data-seek="5:41">00:05:41</a>)  名前を変更する前のファイルパスそれから名前を変更した後のファイルパスをデータとして保存してます。従ってそれで置き換えができるようになってます。<br>
(<a href="#" class="imk-cue" data-seek="5:56">00:05:56</a>)  例えば今、ここにあるファイルですけどもしばらく放置しておきます。<br>
(<a href="#" class="imk-cue" data-seek="6:09">00:06:09</a>)  実際、5分に1回の処理なんですが、ちょっと5分待つことにはなるんですが、<br>
(<a href="#" class="imk-cue" data-seek="6:17">00:06:17</a>)  何もしなくていいです<br>
(<a href="#" class="imk-cue" data-seek="6:20">00:06:20</a>)  そうするとここがですね変わってきますので、少々お待ちください。<br>
(<a href="#" class="imk-cue" data-seek="6:31">00:06:31</a>)  名前が変わりました「鳥の群れ」ですね。名前が変わってます。<br>
(<a href="#" class="imk-cue" data-seek="6:40">00:06:40</a>)  名前は変わりましたが再生URLの番号ですね。識別番号は変わってません。<br>
(<a href="#" class="imk-cue" data-seek="6:50">00:06:50</a>)  従って、実際このURLを使ってWordPressに記述したとしてもですね。全然それは変更しなくてもいいわけですね。<br>
(<a href="#" class="imk-cue" data-seek="7:05">00:07:05</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>



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



<p class="wp-block-paragraph">作成するスクリプトは、結構、分量があります。ChatGPTを使い、ゆっくり進めるのがいいです。以下のスクリプトをChatGPTへコピペして、（１）「このスクリプトの解説をしてください」と、まず解説をしてもらう（２）その解説の中で、自分向けに修正する箇所を指摘して、スクリプトの記述を指示する、そのようなやり方が効率的です。表の右欄に、スクリプトをリンクします。</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td colspan="2" style="background-color:#dddddd"><strong>パイプライン処理</strong>(画面入力をトリガーに、一品料理で一連の処理を行う)</td></tr><tr><td>&#x2b07;&#xfe0f;＜マイライブラリ画面入力＞<br>Finderで選択したファイルを処理し、対応するDropbox共有リンクを自動取得して送信スクリプトに渡すAppleScript。ファイルのパスや拡張子を取得し、日本語などを除去してDropbox形式のファイル名に整形。Python仮想環境のget_dropbox_link.pyでリンクを生成し、ファイル名の整合性を確認後、send_request.pyでリンク情報を送信。</td><td><a href="https://imakat.com/script_list/?pubtxt=マイライブラリ_画面入力_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>&#x2b07;&#xfe0f;＜マイライブラリDropboxAPI認証リンク取得GAS通信＞<br>DropboxのOAuth認証設定、リンク自動取得、GASとの通信を行う3本のPythonスクリプト群。<br>①dropbox_auth_setup.pyはローカルHTTPサーバで認証コードを受け取り、Dropboxのアクセストークンとリフレッシュトークンを取得する。<br>②get_dropbox_link.pyはリフレッシュトークンを用いてDropbox APIに接続し、指定ローカルファイルの共有リンクを取得または生成し、dl=0をraw=1に変換して出力する。<br>③send_request.pyはAppleScriptから受け取ったDropboxリンクやファイル情報をGASのエンドポイントへJSON形式で送信し、更新処理を実行する。</td><td><a href="https://imakat.com/script_list/?pubtxt=マイライブラリ_dropboxAPI_Mac_GASデータ交換_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>&#x2b07;&#xfe0f;＜マイライブラリ生成更新処理＞<br>このGoogle Apps Scriptは、WordPressと連携するメディアライブラリの自動管理システムです。Webhook (`doPost`) を介して新しいメディア情報の追加や既存パスの変更通知を受け取ります。 主要機能は以下の通りです。 1. **メディア情報の一元管理**: Googleスプレッドシート（F1シート）を主データソースとし、Dropboxリンク、ファイルパス、WordPress用のURL（正規化・エンコード済み）、ユニークID（wpidex）などを自動生成・更新します。 2. **パス変更履歴の適用**: ファイルパス変更履歴シート（F3シート）を利用し、F1シートの旧パスを新パスへ自動更新します。 3. **JSONファイルの生成と同期**: F1シートのデータからWordPress連携用のJSONファイル (`dropbox_wp_library.json`) をGoogle Driveに生成・上書き保存します。 4. **動画埋め込みJSONの更新**: 別シートの動画埋め込みデータをJSON化し、外部サーバー（Xserver）へ送信して`videoembed.json`を更新します。 5. **シートの整理と重複除去**: F1シートの不要行削除、K列フラグのリセット、重複行の整理（新しい方を優先し、一部データを引き継ぐ）、更新日時順での並べ替えを行います。 6. **pCloudID連携**: F1シートの情報に基づき、pCloudID同期用シートの内容を更新し、変更があった場合は更新フラグを立てます。 `processWorkingRecord`関数が中心的な役割を担い、モードに応じた一連の処理とデータ同期を安全に実行します。多岐にわたるユーティリティ関数が処理を支援します。</td><td><a href="https://imakat.com/script_list/?pubtxt=マイライブラリ_生成更新処理_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>&#x2b07;&#xfe0f;＜メディアおよびJSONをXserverへ更新＞<br>Dropbox → Xserver 同期用のメインスクリプトは、pmedia・mmedia・meta_gd_wp_data を Homebrew 版 rsync で転送し、SSH鍵を自動ロードして毎分同期するよう LaunchAgent から実行される。ログは /tmp と $HOME/scripts/logs に記録される。<br>しかし rsync が残留してフリーズすることがあるため、毎日 3:30 に自動復旧スクリプトが動作し、pmedia/mmedia 関連の rsync プロセスを検出して kill し、メインの LaunchAgent（com.xxxxxxxxm1.sync_dropbox_xserver_m1）を unload/load し直して正常状態へ戻す仕組みとなっている。</td><td><a href="https://imakat.com/script_list/?pubtxt=メディアおよびJSONをXserverへ更新および自動復帰_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>&#x2b07;&#xfe0f;＜配信サーバー切り替えリダイレクト処理＞<br>rd.phpは、指定されたidに対応するメディア情報をJSONから取得し、該当URLへ転送または中継出力するPHPスクリプト。txt・vtt・srtなど文字ファイルの場合は、文字化け防止のためサーバー側でUTF-8へ変換して直接配信する。Dropbox・Xserver・pCloudなど複数サーバーのURLを動的に切り替え、キャッシュ防止や診断表示（?diag=1）にも対応する。</td><td><a href="https://imakat.com/script_list/?pubtxt=マイライブラリXserverのrdphp_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"></p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td colspan="2" style="background-color:#dddddd"><strong>バックグラウンド処理</strong>(常時監視する)</td></tr><tr><td colspan="2">＜ファイルパス変更処理＞<br>mmedia・pmedia フォルダを常時監視し、ファイルやフォルダの移動・リネーム（MOVED）を検出して記録する Python スクリプト。検出時には変更前・変更後パス、JST の更新時刻、更新フラグ TRUE を Google スプレッドシート（sheet1）へ追記し、その後シート全体を読み込んで (A,B) が同じ重複記録は「最も古いものだけ残す」形に正規化し、更新日時の降順で再整列して書き戻す。scripts_pub や .dropbox など特定パスは無視するフィルタを搭載。watchdog によりディレクトリを監視し続け、ログは ~/Library/Logs に記録。LaunchAgent（com.xxxxxxxx.filewatcher.plist）を用いて macOS 起動時に自動実行される構成となっている。</td></tr><tr><td colspan="2"><a href="https://imakat.com/script_list/?pubtxt=ファイルパス変更処理_gasからpython移行_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">&lt;dropboxリンクの定期的自動修復&gt;<br>このシステムはmacOS上でDropbox共有リンクの自動巡回・修復、およびリンクの有効性チェックを行うものです。`fix_dropbox_links.py`はスプレッドシートのローカルパスを基に、共有リンクをダウンロード可能な形式(raw=1)に変換し、Mac特有の濁点問題を修正してAPI経由で最新リンクを取得します。差分があればスプレッドシートを更新しフラグを立てます。この処理は`com.XXXXXX.fix_dropbox_links.plist`により1時間ごとに自動実行されます。さらに今回追加された`check_dropbox_link.py`は、任意のDropbox共有リンクが有効か（リンク切れしていないか）をAPIを用いて直接確認し、結果をログに出力します。これらのスクリプト群により、Dropbox共有リンクの継続的な整合性維持と異常検知を自動化、手動介入を減らした堅牢な運用を実現しています。<br></td></tr><tr><td colspan="2"><a href="https://imakat.com/script_list/?pubtxt=dropboxリンクの定期的自動修復_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">上の処理はMacを使った処理ですが、ChatGPTの分析では、Windowsへの移植には、以下が変更点になります。Mac、Windowsで共通して使えるGAS、AppSheetを中心に据える作り方がベターと言えます。</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td colspan="3"><strong>Windowsへの変更点</strong></td></tr><tr><td><strong>処理</strong></td><td><strong>Mac</strong></td><td><strong>Windoowsでの代替</strong></td></tr><tr><td>GUI自動処理</td><td>AppleScript</td><td>PowerShell or Python GUI</td></tr><tr><td>クリップボード</td><td><code>the clipboard</code></td><td><code>pyperclip</code>&nbsp;(Python)</td></tr><tr><td>パス処理</td><td>POSIXパス</td><td><code>os.path</code>&nbsp;(Python)</td></tr><tr><td>sed/basename</td><td>shellコマンド</td><td>Pythonの標準ライブラリ</td></tr><tr><td>GAS通信</td><td><code>requests.post</code></td><td>そのままOK</td></tr><tr><td>フォルダ監視</td><td>shell + find/comm</td><td>PowerShell +&nbsp;<code>Compare-Object</code></td></tr><tr><td>自動同期</td><td>rsync</td><td>robocopy / WinSCP</td></tr><tr><td>タスク自動実行</td><td>Automator or cron</td><td>Windowsタスクスケジューラ</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">次回予告：</h3>



<p class="wp-block-paragraph"><strong>第3章では、「<strong>YouTube（大手）に依存しないで配信する</strong>」</strong>についてご紹介します。<br></p>



<p class="wp-block-paragraph">&#x27a1; <a href="https://imakat.com/2025/04/25/25930/" target="_blank">第3章を読む</a></p>



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



<p class="has-text-align-center wp-block-paragraph"><a href="https://imakat.com/media_library1" target="_blank">&#x1f517; 目次ページへ戻る</a></p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">25796</post-id>	</item>
		<item>
		<title>第1章「メディアライブラリ構築の目的と背景」</title>
		<link>https://imakat.com/2025/04/03/25693/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Thu, 03 Apr 2025 11:45:00 +0000</pubDate>
				<category><![CDATA[マイライブラリ]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[Dropbox]]></category>
		<category><![CDATA[MAM]]></category>
		<category><![CDATA[メディアアセット]]></category>
		<category><![CDATA[Mac]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=25693</guid>

					<description><![CDATA[メディアライブラリを作ろう はじめに： いまの時代、動画はたくさん出回っています。でも、その動画を作るために使った素材――たとえば写真や音声、字幕や説明文など――は、紐づいて保存されているでしょうか。 最近の動画編集ツー [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">メディアライブラリを作ろう</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=45" 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=SNebSNKN.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=b2WjRm31.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=06LGXJgR.vtt" label="日本語" srclang="ja" kind="subtitles" default></video></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">いまの時代、動画はたくさん出回っています。<br>でも、その動画を作るために使った素材――たとえば写真や音声、字幕や説明文など――は、紐づいて保存されているでしょうか。</p>



<p class="wp-block-paragraph">最近の動画編集ツールは、ブラウザから手軽に使えるものが多くなっています。<br>素材をいくつかアップして、簡単な操作で動画が完成する。便利で楽しい仕組みになっています。<br>そして完成した動画は、そのままYouTube、TikTokなどの配信サイトにアップされ、世界中に届けられます。</p>



<h4 class="wp-block-heading">プロセスは、隠された箱の中</h4>



<p class="wp-block-paragraph">時代は生成AIですね。</p>



<p class="wp-block-paragraph">でも、その一方で、完成した動画は、「どうやって作られたのか」、「どんな素材で作られたか」は、作成者は全く分からない、原型を留めないほどに、変形させられるものも多々あります。こうなった時、それが私の作品、所有者は私、と言われても、すっと腑に落ちません。</p>



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



<h3 class="wp-block-heading">出発点は「整理整頓」：</h3>



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



<p class="wp-block-paragraph">さて、これからの時代、動画などのメディアコンテンツの作り方は、どんな考え方に傾いていくでしょうか。</p>



<h4 class="wp-block-heading">適当なものを放り込んで、ボタンを押したらヒョッコリ出来ただけ</h4>



<p class="wp-block-paragraph">ひとつは、「完成した動画があれば、素材がなにだったか、原型がどうだったかは、気にしない。レシピはアプリ会社が用意したものから選ぶだけでいい。」という考え方。早い安い美味い、インスタント即決重視。</p>



<p class="wp-block-paragraph">もうひとつは、「動画の元になった素材や、その作り方の記録も残っていて、<strong>所有権は私のもの</strong>であり再加工も私はできる」という考え方。音楽制作や文芸で著作権の大切さを感じた人にある感覚でしょうか。</p>



<p class="wp-block-paragraph">前者は、何が原因でどういう条件でこの結果となったかが分からないので、何を大事に残したらいいかが分からない。素材軽視、意外性重視、偶然好み、結果だけあればいい。完成物だけ持って飛び出していく人。やりっぱなしで、でも元ネタはここにあるはず、となると捨てられず、家はゴミ屋敷。</p>



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



<p class="wp-block-paragraph">いいえ全員そうだとは言いませんが、概してそう。</p>



<p class="wp-block-paragraph">小さい頃から言われ続けてきた整理整頓は、やはり重要です。</p>



<p class="wp-block-paragraph">完成した動画だけでなく、それを作るまでに使った素材や字幕、説明なども、あとから見返せるようにしたい。<br>再利用や別の作品への展開にも役立ちます。</p>



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



<h3 class="wp-block-heading">「フォルダ整理」の次に「ライブラリ」生成へ：</h3>



<p class="wp-block-paragraph">このシリーズでは、**「メディアライブラリ」**を、メディアコンテンツを作成保存するDropbox（私の場合)を出発点にして、身近な道具を活用しながら、どう作っていくか、これまで数回に渡り掲載してきたライブラリ生成のまとめとして、ご紹介していきます。</p>



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



<p class="wp-block-paragraph">メディアライブラリとは、図書館がサービスとして本を貸し出してくれるように、そのメディアコンテンツを配信してくれるところです。しかし図書館といえど世の中のすべての書物を扱えるわけではありません。日本の図書館には、ほとんどが日本語の書物です。同じように、ここでいうメディアライブラリは、「自分が所有している配信可能なコンテンツのうち、配信する予定のあるものを、配信できる状態にスタンバイさせたもの」となります。</p>



<p class="wp-block-paragraph">ここから先は、メディアコンテンツを流通させる手法を取り扱っていくので、メディアの中身そのものというより、メディアの属性やメタ情報に焦点を当てていきます。そしてメディアの総体をメディアアセットと呼んでいくことにします。<br><br>(※アセットとは、一般的には「資産」の意味です。「情報」に対して「情報資産」と呼ぶと、まあ意味は何となく分かりますが、しかしこの「情報資産」は、質量を持たず、瞬時に無限に複製移動出来る不思議な存在です。)</p>



<p class="wp-block-paragraph">ですから、包含関係で言うと、「自分の所有するメディアアセット⊇配信可能なアセット⊇配信する予定のあるアセット⊇配信できる状態にスタンバイしたアセット」となります。</p>



<p class="wp-block-paragraph">具体的に私の例で言うと、</p>



<p class="wp-block-paragraph">(A)自分の所有するメディアアセット(様々なデバイス):<br>iPhoneやiPadに保存した画像やイラスト、動画、音声など、FinalCutPro、Cubase、図形描画、ドキュメントなどで加工中あるいは完成したものなど。<br><br>(B)配信可能なアセット(私の場合はDropbox):<br>(A)の内で、他者にわたることを前提とした、合法なコンテンツ</p>



<p class="wp-block-paragraph">(C)配信する予定のあるアセット(私の場合は、Dropbox、Xserver、自宅サーバー、pCloud、Vimeo、YouTube):<br>(B)の内で、配信にマッチするように調整を済ませたコンテンツ</p>



<p class="wp-block-paragraph">(D)配信できる状態にスタンバイしたアセット(私の場合は、Dropbox、Xserver、自宅サーバー、pCloud、Vimeo、YouTube):<br>(C)の内で、共有URLを持って、ランチャー（発射台）にセットされたアセット<br><br>※(C)と(D)の違いは、ステータス（公開、非公開）の違い。</p>



<p class="wp-block-paragraph"><br>以上のようになります。<br>このうち、(B)について、Dropboxの中にフォルダ(mmedia pmedia)を設定しています。このフォルダの整理以降が、今シリーズの対象になります。<br><br></p>



<a rel="noopener" target="_blank" href="https://docs.google.com/drawings/d/1u3IsAxH-_6Ex4Wrjv3CgmHDINUKS6V8GCzEf3SqrKVI/edit?usp=sharing" 
>
<img decoding="async" src="https://docs.google.com/drawings/d/e/2PACX-1vR9rDTau5P09ZIvdFNlART_vbCoCyUe2lcGuCFpAtS9fslIKhNUu64IebZmZ51IG6aI3pSNl_Q86Xkg/pub?w=960&#038;h=720"
></a>



<h3 class="wp-block-heading">メディアライブラリは商社の本社機能のようなもの：</h3>



<p class="wp-block-paragraph">図書館とメディアライブラリの違いを考えてみます。<br>一言でいって、サービス部門の設置場所が、違ってきます。図書館は書籍を受け渡しするためサービス部門は各図書館内に必要です。書籍は質量がありますから。<br>それに対して、メディアライブラリは、動画、画像、テキストといった、質量がなく即座に複製移動できる、情報アセットを受け渡しするサービス部門です。従って、メディアライブラリは、アセットが複数の場所に存在していても、「何がどこにあるか」というメタ情報(台帳のようなもの)があればいいだけです。それは一ヶ所あれば十分です。</p>



<p class="wp-block-paragraph">メディアライブラリはGoogleドライブを使っています。Googleドライブには、動画、画像、テキストといったアセット自体は置きません。置くのは、そのアセットの特性や特徴を記録したアセット情報(台帳)だけです。メタデータだけです。<br>またGoogleドライブには、GASやAppSheetなどの指示命令機能がそなわっています。いわば、商社の本社機能と言っていいです。</p>



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



<h3 class="wp-block-heading">このシリーズで目指すもの：</h3>



<p class="wp-block-paragraph">このシリーズでは、DropboxやWordPressサーバーなど、<strong>すでに持っている道具、標準装備されているものをうまく使って</strong>、<br><br>誰でもできる「メディアライブラリのかたち」を提案していきます。</p>



<h3 class="wp-block-heading">こんな方におすすめ：</h3>



<p class="wp-block-paragraph">この連載は、私自身が実際にメディアライブラリを作ってきた経験をもとに、<br>できるだけわかりやすく紹介していくことを目指しています。</p>



<p class="wp-block-paragraph">パソコンは<strong>&nbsp;Mac</strong>&nbsp;を使用しており、<br>そのために&nbsp;<strong>AppleScript&nbsp;</strong>や&nbsp;<strong>Automator</strong>&nbsp;など、Mac特有の言語やアプリを使う場面も出てきます。</p>



<p class="wp-block-paragraph">でも、どれも難しいプログラミングではなく、<br>「ちょっとした工夫で整理がしやすくなる」という発想に近い内容です。</p>



<ul class="wp-block-list">
<li><strong>動画配信やメディア配信を、YouTubeに類するような大手配信サービスに依存したくない方、ひと所に依存したくない方</strong></li>



<li><strong>動画や画像を頻繁に手直ししたい方</strong></li>



<li><strong>共有URLの提供を、自分として一本化したい方</strong></li>



<li><strong>メディアコンテンツの作成からメディアアセットの管理を、一貫して行いたい方</strong></li>



<li><strong>そうした<a rel="noopener" target="_blank" href="https://ja.wikipedia.org/wiki/デジタル資産管理">メディアアセット管理システム(MAM)<span class="fa fa-external-link external-icon anchor-icon"></span></a>を、あまりお金を掛けないで、ハンドメイドで小規模に実現したい方</strong></li>
</ul>



<p class="wp-block-paragraph">そんな方にとって、きっとヒントになるシリーズになると思います。</p>



<p class="wp-block-paragraph"><br><br>次回からは、実際の構造や工夫について、図や例をまじえながらお伝えしていきます。</p>



<h3 class="wp-block-heading">準備するもの：</h3>



<p class="wp-block-paragraph">Mac、Dropbox、Googleドライブ、AppSheet、AppleScript、Python、GAS、ShellScript<br>です。DropboxおよびGoogleドライブは最下位の有料版で十分。</p>



<h3 class="wp-block-heading">次回予告：</h3>



<p class="wp-block-paragraph"><strong>第2章では、「メディアライブラリへの登録〜パイプライン処理〜</strong>」についてご紹介します。<br></p>



<p class="wp-block-paragraph">&#x27a1; <a href="https://imakat.com/2025/04/08/25796/">第2章を読む</a></p>



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



<p class="has-text-align-center wp-block-paragraph"><a href="https://imakat.com/media_library1" target="_blank">&#x1f517; 目次ページへ戻る</a></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">25693</post-id>	</item>
		<item>
		<title>DropboxのデータをGoogleドライブへ同期する〜NASのrsyncの利用〜</title>
		<link>https://imakat.com/2024/11/26/24397/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Tue, 26 Nov 2024 05:42:18 +0000</pubDate>
				<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[Dropbox]]></category>
		<category><![CDATA[SynologyNAS]]></category>
		<category><![CDATA[Googleドライブ]]></category>
		<category><![CDATA[rsync]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=24397</guid>

					<description><![CDATA[落とし穴がありそうで、なかなか手をつけられない問題 今回は、以前に後回しにしたテーマでして、SynologyNASを利用してDropboxのデータをGoogleドライブへ同期する方法についての解説になります。 簡単な気が [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h4 class="wp-block-heading">落とし穴がありそうで、なかなか手をつけられない問題<br><span id="docs-internal-guid-0103ffce-7fff-47c4-be1b-e104c5a6f81b" style="white-space: normal; caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-weight: normal;"><p dir="ltr" style="line-height: 1.38; margin-top: 0pt; margin-bottom: 0pt;"></p></span></h4>



<p class="wp-block-paragraph">今回は、以前に後回しにしたテーマでして、SynologyNASを利用してDropboxのデータをGoogleドライブへ同期する方法についての解説になります。</p>



<p class="wp-block-paragraph">簡単な気がしますが、意外にややこしい、奥が深いのが以下のような話。</p>



<p class="wp-block-paragraph">それが、Macの中のDropboxにあるAフォルダと同じ内容をGoogleドライブにあるBフォルダへ作ることです。いえ。データをコピー＆ペーストするだけなら簡単なことですが、大量にあるいは自動的にコピーさせようとすると、<strong>さてはデータを移動させてから解約を考えてるんじゃないか</strong>、勝手にさせないぞ、と囲い込みが働き、難しくなります。それもそのはず、クラウドストレージを二つも三つも好きで持っている人などいません。一つで済むのに越したことは無いのです。何度も書いてきているように、Dropbox、Googleドライブそれぞれ欠点があるからやむを得ず、両方持っているわけで、状況次第でいつ解約されてもおかしくないサービスです。</p>



<p class="wp-block-paragraph">Dropbox内に作ったフォルダのエイリアスをGoogleドライブのフォルダに持ってくるようにすると、デバイスから削除するだの、利用できなくなるだの、Dropbox外に移動するだの、恐ろしい警告が表示されます。</p>



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



<p class="wp-block-paragraph">何が起こるのかよくわかりませんが、やってはいけないことをやろうとしているのだろう、そう釘を刺されていると感じます。さすがビジネスに強いDropbox、トラブル時の責任範囲を明確にするということでしょう。上の場面では、キャンセルしました。しかし私は過去、こうした時に、先に進んでしまい、Dropbox全体ごとダウンロードをやり直した、そんな経験もあったような気がします。</p>



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



<h4 class="wp-block-heading">有料なら、サービスはあります</h4>



<p class="wp-block-paragraph">クラウドストレージ間のデータ移動や同期ですが、MultiCloudという別のクラウドサービスを使うと実現できます。しかし、無料で使えるのは、月間5GBのデータトラフィックまでです。これは少な過ぎ！有料になると年間1200GBのトラフィックまでで約7,000円年となっていますが、容量ではなくてデータトラフィックと言われても実感が沸かないわけです。私の場合すでに有料会員としてDropboxは2TB、Googleドライブは0.2TBを利用していますので、またNASも持っていますので、クラウドストレージは、今のところ増やしたくありません。</p>



<h4 class="wp-block-heading">まわりくどいが確実な方法を選んでみる</h4>



<p class="wp-block-paragraph">Mac内で、クラウドストレージ間でフォルダをコピーしたり同期しようとすると、何かと意地悪をされそうなので、<strong>迂回させることにします</strong>。SynologyNAS内で同期させます。１〜２分かかりますが、確実な方法です。</p>



<p class="wp-block-paragraph"><span class="bold-red">❶</span>　MacのDropboxフォルダを変更するとDropboxクラウドが更新される(双方向同期)。</p>



<p class="wp-block-paragraph"><span class="bold-red">❷</span>　SynologyNASのDropboxフォルダを更新する(CloudSyncによる。双方向同期)。</p>



<p class="wp-block-paragraph"><span class="bold-red">❸</span>　<strong>SynologyNASのDropboxフォルダからSynologyNASのGoogleドライブフォルダを更新する(rsyncによる。一方向同期)</strong>。</p>



<p class="wp-block-paragraph"><span class="bold-red">❹</span>　Googleドライブクラウドが更新される(CloudSyncによる。双方向同期)。</p>



<p class="wp-block-paragraph"><span class="bold-red">❺</span>　MacのGoogleドライブフォルダが更新される(双方向同期)。</p>



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



<a rel="noopener" target="_blank" href="https://docs.google.com/drawings/d/1os09BmZrWsM_C9mxjuAsrilq28pmMeqwXJC4qP4ht-0/edit?usp=sharing" 
>
<img decoding="async" src="https://docs.google.com/drawings/d/e/2PACX-1vRB8sXIxymuZagC8H8l_MHSlGamZvkkuXmhsYKkYz44fSvE8CBcjXfnCjwNOl864k_XXYTYe8B_utWb/pub?w=960&#038;h=720"
></a>



<h4 class="wp-block-heading"><span class="bold-red">❸</span>の存在を知らない人には、何が起こっているか分からない。</h4>



<p class="wp-block-paragraph">今回私は、確実にデータを移動させかつ便利に使うための仕組みを作っているわけです。しかし、<strong>「確実」「便利」という言葉の背後には、「確実、便利に悪巧みをする方法」ということもあり得る</strong>わけですね。そもそも<span class="bold-red">❸</span>は自由自在にデザインできるわけです。例えば、「元データの中に金額があったら２０％水増しして新たに書き出せ」とか、AIに解読させて、どちらかに有利に変えさせるとか、様々な悪巧みができるわけですね。見方を変えれば、「データ転がし」ですから。データ隠し、マネーロンダリング、不正改ざん、など。ここで重要なのは、そうした危険性があるということを知っておくことでしょう。<br></p>



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



<h4 class="wp-block-heading">１　rsyncの設定手順</h4>



<h5 class="wp-block-heading">シェルスクリプトファイルの作成と格納</h5>



<p class="wp-block-paragraph">dropbox_google_sync.sh</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>#!/bin/bash<br>rsync -av &#8211;delete /volume1/dropbox_1/sync_to_google/ /volume1/google_drive_1/sync_from_dropbox/ </td></tr></tbody></table></figure>



<p class="wp-block-paragraph"><br>SynologyNASにはrsyncは最初から入っているので、シェルスクリプトのファイル.shを、Macのテキストエディットなどで作成(例えばdropbox_google_sync.sh)→SynologyNASの共有フォルダに例えばbinフォルダを作る→binフォルダの中にdropbox_google_sync.shを入れる。</p>



<h5 class="wp-block-heading">タスクスケジューラの設定</h5>



<p class="wp-block-paragraph">SynologyNASのコントロールパネル→タスクスケジューラー<br>「全般」「スケジュール」「タスク設定」を以下のように設定します。</p>



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



<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=R19u0h7S.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=R19u0h7S.png" alt="" style="width:400px;height:auto"/></a></figure>



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



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



<h4 class="wp-block-heading">２　Macから共有リンクが取得できる</h4>



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



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



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



<p class="wp-block-paragraph">今回のように、送り出し側のフォルダ名を&#8221;sync_to_google&#8221;、受け取り側のフォルダ名を&#8221;sync_from_dropbox&#8221;とわかりやすくするのが賢明です。&#8221;send_&#8221;、&#8221;receive_&#8221;と頭につけるのもわかりやすいです。そうせずに、Aフォルダ、Bフォルダなど無意味な名前ですと、迷子になりそうです。</p>



<p class="wp-block-paragraph">関係を逆にして、送り出し側をGoogleドライブに設定して、受け取り側をDropboxに設定することも、もちろん可能です。</p>



<h4 class="wp-block-heading">３　その他　まとめ<br></h4>



<p class="wp-block-paragraph">私が便利に感じているフローは、各種メディアデータの加工はDropboxで行う→メタデータはGoogleドライブへ登録する、実データ(メディアコンテンツ)は配信サーバーへ送り出す→GoogleドライブでAppSheet、GASを使いメタデータを加工する→Xserverなどの外部サーバーへ送り出す。という形です。今回はそのフローの一部でもあります。</p>



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



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">24397</post-id>	</item>
		<item>
		<title>【Mac】M1 Mac miniと、M2 Mac miniを使い分けました。</title>
		<link>https://imakat.com/2024/10/30/23866/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Wed, 30 Oct 2024 06:42:38 +0000</pubDate>
				<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[Mac mini]]></category>
		<category><![CDATA[GoogleDrive]]></category>
		<category><![CDATA[Apple Intelligence]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Dropbox]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=23866</guid>

					<description><![CDATA[制作用のパソコンは、フリーズが多くなってしまう。再起動がつきもの。 音楽制作や動画制作では、アプリのバグに振り回されて、結構、頻繁にフリーズして、Macを再起動することが起きます。その作業の最中に、並行して、Hazelや [&#8230;]]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=IfXpZODc.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=IfXpZODc.png" alt="" style="width:171px;height:auto"/></a></figure>



<h5 class="wp-block-heading"><strong>制作用のパソコンは、フリーズが多くなってしまう。再起動がつきもの。</strong></h5>



<p class="wp-block-paragraph">音楽制作や動画制作では、アプリのバグに振り回されて、結構、頻繁にフリーズして、Macを再起動することが起きます。その作業の最中に、並行して、<a rel="noopener" target="_blank" href="https://note.com/fuun_inc/n/ndf99d4675c46">Hazel<span class="fa fa-external-link external-icon anchor-icon"></span></a>やAppleScriptなどのファイル更新の自動処理を行っているときに、そちらも、中断させられてしまいます。その逆もあります。いずれも、やり直しになります。</p>



<h5 class="wp-block-heading"><strong>ファイルの更新処理は切り離したい。</strong></h5>



<p class="wp-block-paragraph">やはり、制作類の作業を行うMacと、ファイル更新処理を行うMacなどのコンピュータは独立させた方が、全体としての安定性が向上します。</p>



<p class="wp-block-paragraph">但し、<a rel="noopener" target="_blank" href="https://pc.watch.impress.co.jp/docs/column/macinfo/1356257.html">クイックアクション<span class="fa fa-external-link external-icon anchor-icon"></span></a>などの、いわゆる生産性を上げるための操作は、制作作業Mac側で行い、ファイル更新処理Macの様子を別ウインドウで表示して横目で監視するようなやり方でも、問題なく使えるでしょう(例：上の写真)。</p>



<h5 class="wp-block-heading"><strong>ファイルサーバーとしても活用</strong></h5>



<p class="wp-block-paragraph">M1以降のMacはほとんど発熱もなく消費電力も小さいので、少なくともファイル更新処理Macは常時稼働で問題なく、いわばファイルサーバーのようにSynologyNASの頭脳のような位置付けで使用するのが相性が良さそうです。</p>



<h5 class="wp-block-heading"><strong>Mac miniもそうだが、Macは進歩はすれど安心して継承できる。</strong></h5>



<p class="wp-block-paragraph">つい先日、<em><a rel="noopener" target="_blank" href="https://www.apple.com/jp/newsroom/2024/10/apples-new-mac-mini-is-more-mighty-more-mini-and-built-for-apple-intelligence/">M4 Mac mini<span class="fa fa-external-link external-icon anchor-icon"></span></a></em>が発表され、どんどんコンパクトに強力になっていくことに驚いていますが、方向は、そうなるのだろうなと想像はしていました。</p>



<p class="wp-block-paragraph">つまりMacminiは、サーバーだと思えば、電源スイッチを底面にしたのもガッテンです。NASの電源スイッチなど１年に1回触るかどうかです。</p>



<p class="wp-block-paragraph">しかしよくよく考えて、最新鋭のM4 Mac miniをサーバー的に使うのは勿体無いわけで、もしM4 Mac miniを制作系の作業で使うなら、アプリ側の更新の遅れで頻繁なフリーズに悩まされて、何度も電源スイッチの入り切りを行うことになるわけですから、いっそのことスイッチ付きテーブルタップを、自分の一番使いやすい位置に取り付ける、そういう結論になるのだと思います。まあ小さい話です。</p>



<h5 class="wp-block-heading"><strong>先入先出の作業方法をApple Intelligenceに考えてもらいたい</strong>。</h5>



<p class="wp-block-paragraph">OSは今回、１世代前のSonomaにアップしました。それは１年遅れで信頼性を確保したいからです。次のOSの更新を焦るつもりはありません。ハードに依存します。ハードは、いずれは、M4 Macminiなどがメイン機になり、今あるM2 Macminiがサブになる、古いものから順に引退する、つまり先入先出になるわけです。課題は、特に音楽制作は、Mac本体内のディスク容量を食うのでM2 Macminiは1TBとしてきました。どういうことかと言うと、音楽制作のDAWは、多種多様な音源をロードして使います。そのロードに時間がかかるのです。その音源のうち音源データ部分は外部SSDなどへ逃すにしても、アプリケーション部分は本体内に残すようにしないと、ロード時間を短くできないのです。前述したように、そのDAW、音源アプリさらにはOSとハードとの間で、バグや相性不具合が生じて強制的な起動が頻発するわけです。どうにかならないかとストレスを抱えます。しかし、M4 Macmini1TBともなると、また高価格になっていくので、なかなか、ついていけませんね。&nbsp; 次は何とかメイン機のディスク容量を512GBで抑制できないか、そのように改善したいです。その診断と解決策をApple Intelligenceに考えてもらえたら助かりますね。<br></p>



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



<a rel="noopener" target="_blank" href="https://docs.google.com/drawings/d/1wR0pNTej5OTjO6YDLAl1n4Qv4aPU2XUVy0Fa9nvfzqw/edit?usp=sharing">
<img decoding="async" src="https://docs.google.com/drawings/d/e/2PACX-1vRx4i39wHFUdJESf6--9Ye-wHGA2aCyg3duM3o5cEBaHc3qfL70csV408TdEiPBee62KSTD63yaxKTl/pub?w=960&#038;h=720"
></a>



<h4 class="wp-block-heading">１　<strong>DropboxとGoogleDriveの間のデータの更新方法について</strong></h4>



<p class="wp-block-paragraph">クラウドストレージとクラウドストレージの同期を行うのにはどうするか(※1)。それまた別のクラウドストレージによるサービスもあるのですが、両方ともデータはローカルにあり、SSDの中あるいはNASの中にあるので、ローカルで更新を行う方法を選択します。<a rel="noopener" target="_blank" href="https://atmarkit.itmedia.co.jp/ait/articles/1702/02/news031.html">rsync<span class="fa fa-external-link external-icon anchor-icon"></span></a>を使う方法が一般的です。これはNASの中でもMacのSSDの中でも使用できます。NASの中で行う方法について、別の投稿で、説明するつもりです。</p>



<p class="wp-block-paragraph">更新の方向ですが、Dropboxが更新されたらGoogleDriveが更新される、from Dropbox to GoogleDriveにします。つまり、基本的なローカルでのファイル作成作業はDropboxで行うこととします。</p>



<p class="wp-block-paragraph">※１：SynologyNASのアプリにCloudSyncがありますが、これは、ローカルのフォルダやファイルに対しては、一つだけのクラウドストレージしか指定できません。</p>



<h4 class="wp-block-heading">２　<strong>GoogleDriveも使う理由</strong></h4>



<p class="wp-block-paragraph">それならDropboxだけでいいではないか、と思われるところですが、それは早計でして、GoogleDriveを使う理由は、AppSheetを使いたい、GASを使いたい、スプレッドシートを使いたい、それらのためにGoogleDriveが必要になるからです。<br>いえそれなら、GoogleDriveだけでいいではないか。と思われるでしょうが、これまた勇み足でして、字幕の扱いがうまくいかないため動画をリンクで使うのが困難。ならDropboxの方がマシということが大きいです(<a href="https://imakat.com/vm5?movid=905195687" target="_blank">解説&#x1f3a5;</a>)。</p>



<h4 class="wp-block-heading">３　Dropboxデスクトップアプリによるファイル操作がラク</h4>



<p class="wp-block-paragraph">Dropboxにある写真や動画をWordPressへリンクするためにDropboxリンクを取得しようとするとき、Dropbox.comのサイトから入るのはちょっと手間がかかり過ぎです。Dropboxのローカルファイルから右クリックで取得できれば簡単です。SynologyNASは、そのフォルダをDropboxと同期することはできますがしかし、SynologyNASのDropboxのフォルダからは、それを右クリックしてもDropboxリンクの取得はできません。Dropboxデスクトップアプリが提供されていないためです。この辺りが、SynologyNASをパソコン的に使えないと感じる部分です。</p>



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



<p class="wp-block-paragraph">その点、MacやWindowsでは、デスクトップアプリが提供されているため、以下のように、MacのFinderからでも、Dropboxリンクが取得できます。GoogleDriveでも同様に、リンク取得が可能です。</p>



<figure class="wp-block-image size-large is-resized"><a href="https://imakat.com/rd.php?id=ZZh7TyOF.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=ZZh7TyOF.png" alt="" style="width:800px;height:auto"/></a><figcaption class="wp-element-caption">M1 Mac mini  一部、高画質での提供の動画は視聴できません。</figcaption></figure>



<p class="wp-block-paragraph">M1 Mac miniは、M2 Mac miniからの画面共有(<a href="https://imakat.com/script_list/?pubtxt=M1Macの画面共有を起動する_pub.txt" target="_blank">スクリプト&#x1f4d2;</a>)の形にしています。共用のモニター、キーボード、マウスで操作しています。</p>



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



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



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">23866</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[vtt]]></category>
		<category><![CDATA[タイムコード]]></category>
		<category><![CDATA[Apple翻訳]]></category>
		<category><![CDATA[Windows翻訳]]></category>
		<category><![CDATA[Google翻訳]]></category>
		<category><![CDATA[Dropbox]]></category>
		<category><![CDATA[vimeo]]></category>
		<category><![CDATA[Appleスクリプト]]></category>
		<category><![CDATA[srt]]></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>
	</channel>
</rss>
