<?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>WordPress | imakat.com</title>
	<atom:link href="https://imakat.com/category/it/wordpress-ja/feed/" rel="self" type="application/rss+xml" />
	<link>https://imakat.com</link>
	<description>工夫と改善で人生をちょっと豊かに</description>
	<lastBuildDate>Wed, 17 Jun 2026 00:11:39 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</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>WordPress | 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>Adobeキャラは無料で使えるか？！</title>
		<link>https://imakat.com/2026/06/04/29138/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Thu, 04 Jun 2026 07:56:04 +0000</pubDate>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[Adobe character animator]]></category>
		<category><![CDATA[パペット]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=29138</guid>

					<description><![CDATA[最近、自作パペットを作ってきましたが、考え方は「大手依存からの脱却」です。素晴らしい製品を生み出してきたAdobeには感謝の気持ちは大いに持ちながらも、年齢相応というのがあるじゃないですか。高齢者になったら、やっぱり節約 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">最近、自作パペットを作ってきましたが、考え方は「大手依存からの脱却」です。素晴らしい製品を生み出してきたAdobeには感謝の気持ちは大いに持ちながらも、年齢相応というのがあるじゃないですか。高齢者になったら、やっぱり節約、断捨離、やれる範囲に縮めていく。そういうもんです。</p>



<p class="wp-block-paragraph">それで、さあいよいよ、Adobeキャラとおさらばしようとして、ちょっと触ってみて、あれ〜〜意外。まだ、使えるじゃん。と気づいたのです。</p>



<p class="wp-block-paragraph">Adobeのソフトは、どうしても「毎月の課金が必要」というイメージがありませんか？</p>



<p class="wp-block-paragraph">でも実は、<strong>無料の会員状態でも「Adobe Character Animator」でキャラクターを動かすことができるのですね。</strong></p>



<p class="wp-block-paragraph">今回は、私が実際に検証してみた様子を動画とともにお届けします。</p>



<h2 class="wp-block-heading"><strong>&#x1f3ac; まずは検証動画をご覧ください！</strong></h2>


<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="https://imakat.com/ds62/?drid=82" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <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=rhWMmQjW.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=04Cq75XY.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=Vt8jJZxf.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>) 〜Adobeキャラは無料で使えるか〜<br>
(<a href="#" class="imk-cue" data-seek="0:05" translate="no">00:00:5.</a>) 普通に使えてますね。実際私は今、どういう状態かと言うと、<br>
(<a href="#" class="imk-cue" data-seek="0:10" translate="no">00:00:10</a>) これ、Adobeのですよ。この無料会員という状態ですね。<br>
(<a href="#" class="imk-cue" data-seek="0:20" translate="no">00:00:20</a>) プランを表示…何のこっちゃい。まあ別に何も今、入ってる状態ではないんですよね。<br>
(<a href="#" class="imk-cue" data-seek="0:30" translate="no">00:00:30</a>) そっか。この下の方にですね、このCharacter Animatorがいくつか置いてありますけど、<br>
(<a href="#" class="imk-cue" data-seek="0:45" translate="no">00:00:45</a>) これがね、今開いてるのはこれなんですけども、使えてるんですよね、これね。<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:08" translate="no">00:01:08</a>) 私はこれ、アカウントってのを見た時に、無料会員って格好になってると思うんですよ今。<br>
(<a href="#" class="imk-cue" data-seek="1:19" translate="no">00:01:19</a>) これアカウント管理って見てみようかな。今は私は…あ、だってAdobe Express、<br>
(<a href="#" class="imk-cue" data-seek="1:34" translate="no">00:01:34</a>) 購入になってるから入ってないでしょ今。プランとお支払い、プラン…<br>
(<a href="#" class="imk-cue" data-seek="1:45" translate="no">00:01:45</a>) 有料プランがありません。これね、有料プランがありませんっていう状態なんですよ。<br>
(<a href="#" class="imk-cue" data-seek="1:54" translate="no">00:01:54</a>) なんとなく動作がおかしいというか。で、もう一回、この状態にして、OKにして、<br>
(<a href="#" class="imk-cue" data-seek="2:16" translate="no">00:02:16</a>) フィルターを、クロマキーを入れて、そうだね、それで閉じると。で、この状態にして、<br>
(<a href="#" class="imk-cue" data-seek="2:33" translate="no">00:02:33</a>) ちょっとこれを引いて、角を取っていく。角を取っていく。<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:13" translate="no">00:03:13</a>) これで今、普通に録音録画できてるわけだから。これ今ね、有料プランがありませんということで、<br>
(<a href="#" class="imk-cue" data-seek="3:24" translate="no">00:03:24</a>) 有料プランがない状態、無料会員になってるわけですね。それでも、このAdobeパペットがですね、<br>
(<a href="#" class="imk-cue" data-seek="3:36" translate="no">00:03:36</a>) 使えてるわけですよ。まあ何かと、Dr.AppleSmithを表示して、ちゃんと一応敬意を表してるっていう格好になるわけですね。<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:50" translate="no">00:03:50</a>) ただいつどこでパタッと使えなくなるか分かりません。<br>
(<a href="#" class="imk-cue" data-seek="3:54" translate="no">00:03:54</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 = ''; 
      }

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

      // 初期状態のセット（リストがなければ最初から showing に）
      try {
        if(video.textTracks && video.textTracks.length > 0){
          for(var i=0; i<video.textTracks.length; i++){
             if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions'){
                 video.textTracks[i].mode = (!listContainer) ? "showing" : "hidden";
             }
          }
        }
      } catch(e){}

      // 字幕一覧が存在する場合のみ、リストのフォーマット処理を行う
      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(_){}
            });
          }
      }

      // ★ dr52.php と全く同じ「常時監視」のロジック
      // これにより、Safariが読み込み遅延を起こしても確実にONになります
      video.addEventListener("timeupdate", function(){
        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++){
                    if((video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') && video.textTracks[i].mode !== desiredMode) {
                        video.textTracks[i].mode = desiredMode;
                    }
                }
            }
        } catch(e){}

        // リストがない場合はこれ以降のハイライト処理をスキップ
        if (!hasList) return;

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

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

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

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

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

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

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

      return true;
    }

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

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



<h2 class="wp-block-heading"><strong>&#x1f4a1; 検証：有料プランなしでも本当に動くの？</strong></h2>



<p class="wp-block-paragraph">現在、私のAdobeアカウントは<strong>有料プランを解約済み</strong>です。</p>



<p class="wp-block-paragraph">アカウント管理画面を確認しても、はっきりと「有料プランがありません」と表示されています。</p>



<p class="wp-block-paragraph">「じゃあ、ソフトは開けないよね？」と思いきや……</p>



<p class="wp-block-paragraph">Creative Cloudのアプリ一覧から「Character Animator」を開いてみると、なんと<strong>普通に起動できてしまいました！</strong></p>



<h2 class="wp-block-heading"><strong>&#x1f6e0;&#xfe0f; 無料状態でどこまでできる？</strong></h2>



<p class="wp-block-paragraph">実際にソフトを触ってみたところ、ただ起動するだけではなく、以下のような操作も問題なく行えました。</p>



<ul class="wp-block-list">
<li><strong>パペットの操作:</strong> 今回は「Dr. AppleSmith」というキャラクター（パペット）を使用。ちゃんと動きます！</li>



<li><strong>録音・録画:</strong> 普通に録音・録画機能が使えます。</li>



<li><strong>クロマキー処理:</strong> フィルターからクロマキーを入れて、グリーンバックを透過する設定も可能。四隅の角を取って調整する作業もできました。</li>
</ul>



<p class="wp-block-paragraph">動画内で私がやっているように、無料会員であっても、基本的なアニメーション制作の環境は整っているようです。</p>



<h2 class="wp-block-heading"><strong>&#x1f9d0; なぜ無料で使えるの？</strong></h2>



<p class="wp-block-paragraph">実は、画面の右上をよく見ると「スターター」という文字が表示されています。</p>



<p class="wp-block-paragraph">Adobe Character Animatorには、誰でも無料で基本的な機能を使える「スターターモード」が用意されているんです。（※Adobeアカウントの無料登録自体は必要です）</p>



<p class="wp-block-paragraph">これなら、「ちょっとキャラを動かしてみたい」「VTuberみたいなことをお試しでやってみたい」という人でも気軽に始められますね！スターターモードといっても、Adobeはセンスがいいから、どれも使い物になりますよ。</p>



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



<h2 class="wp-block-heading"><strong>&#x26a0;&#xfe0f; 使う上での注意点</strong></h2>



<p class="wp-block-paragraph">現時点では無料で快適に使えていますが、いくつか気をつけておきたい点もあります。</p>



<ol class="wp-block-list">
<li><strong>いつ仕様が変わるか分からない:</strong> Adobe側のアップデートや仕様変更により、突然使えなくなる機能が出てくる可能性はゼロではありません。</li>



<li><strong>ウォーターマーク（透かし）の可能性:</strong> 今後の書き出し時に、無料版特有のロゴやウォーターマークが入るようになる可能性も考えられます。</li>
</ol>



<h2 class="wp-block-heading"><strong>&#x1f4dd; まとめ</strong></h2>



<p class="wp-block-paragraph"><strong>結論：無料のAdobe会員状態でも、Character Animator（Starterモード）は使えます！</strong></p>



<p class="wp-block-paragraph">いつまでこの状態で使えるかは分かりませんが、気になっている方は無料で使える今のうちにぜひ試してみてください！<br>とはいうものの、私は、自作パペットに舵を切っていきます。</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">29138</post-id>	</item>
		<item>
		<title>【脱Vimeo】Bunny CDNへ移行しました 〜ひと所に依存しない究極の自前配信システム〜</title>
		<link>https://imakat.com/2026/06/01/29030/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Sun, 31 May 2026 20:50:48 +0000</pubDate>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[自動トランスコード]]></category>
		<category><![CDATA[vimeo]]></category>
		<category><![CDATA[bunny]]></category>
		<category><![CDATA[cloudflare]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=29030</guid>

					<description><![CDATA[動画は、Bunny CDNを使っています。プレーヤーはWordPress備え付けをカスタマイズしています。 はじめに 今回は、少しマニアックな動画配信の裏側の話題です。 長年愛用してきたVimeoですが、3月頃に「Plu [&#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="https://imakat.com/ds62/?drid=83" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <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=V5sZrECn.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=ngym69ui.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=jh8hMw5r.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>) 〜vimeoからbunnyへの乗り換え〜<br>
(<a href="#" class="imk-cue" data-seek="0:05" translate="no">00:00:05</a>) 皆さんこんにちは。本日は私が長年愛用してきたVimeoから離れ、<br>
(<a href="#" class="imk-cue" data-seek="0:11" translate="no">00:00:11</a>) 特定のサービスに依存しない独自の動画配信システムを構築した理由と、その全貌についてお話しします。<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:25" translate="no">00:00:25</a>) 動画配信において、YouTubeやVimeoといった大手プラットフォームは非常に便利です。<br>
(<a href="#" class="imk-cue" data-seek="0:32" translate="no">00:00:32</a>) しかし、突然の規約変更や料金体系の改定に大きく振り回されるリスクを常に抱えています。<br>
(<a href="#" class="imk-cue" data-seek="0:40" translate="no">00:00:40</a>) また、機能面での限界もありました。例えば、Vimeoの専用プレーヤーでは、<br>
(<a href="#" class="imk-cue" data-seek="0:48" translate="no">00:00:48</a>) 字幕一覧から動画の特定のシーンへ連動してジャンプする、といった<br>
(<a href="#" class="imk-cue" data-seek="0:52" translate="no">00:00:52</a>) 私が理想とする自由な視聴体験を作ることができませんでした。<br>
(<a href="#" class="imk-cue" data-seek="0:55" translate="no">00:00:55</a>) プラットフォームの都合に縛られず、自分自身のメディアライブラリをコントロールしたい。<br>
(<a href="#" class="imk-cue" data-seek="1:00" translate="no">00:01:00</a>) これが自前インフラへの乗り換えを決断した最大の理由です。<br>
(<a href="#" class="imk-cue" data-seek="1:04" translate="no">00:01:04</a>) そこで、私が新しい配信インフラとして選んだのがBunny CDNです。<br>
(<a href="#" class="imk-cue" data-seek="1:10" translate="no">00:01:10</a>) このサービスの最大の魅力は、視聴者の環境に合わせて画質を調整する全自動トランスコード機能を持ちながら、<br>
(<a href="#" class="imk-cue" data-seek="1:18" translate="no">00:01:18</a>) ストリーミング配信用の.m3u8ファイルのダイレクトURL、つまり直リンクを解放してくれる点にあります。<br>
(<a href="#" class="imk-cue" data-seek="1:28" translate="no">00:01:28</a>) これにより、大手の専用プレーヤーを強制されることなく、自作のテンプレート、dr52.phpなどを使って、<br>
(<a href="#" class="imk-cue" data-seek="1:34" translate="no">00:01:34</a>) 動画の再生時間に合わせて字幕がハイライトし、クリックでそのシーンへジャンプできる、<br>
(<a href="#" class="imk-cue" data-seek="1:40" translate="no">00:01:40</a>) 完全に自由でインタラクティブな視聴体験を実現できるようになりました。<br>
(<a href="#" class="imk-cue" data-seek="1:48" translate="no">00:01:48</a>) ではなぜ、同じような機能を持つ強力なライバルであるCloudflare Streamではなく、Bunny CDNを選んだのでしょうか。<br>
(<a href="#" class="imk-cue" data-seek="1:58" translate="no">00:01:58</a>) その理由は、圧倒的なコストパフォーマンスの違いにあります。<br>
(<a href="#" class="imk-cue" data-seek="2:03" translate="no">00:02:03</a>) Cloudflareは動画の再生時間に対して課金されますが、Bunny CDNはデータ転送量に対する課金です。<br>
(<a href="#" class="imk-cue" data-seek="2:11" translate="no">00:02:11</a>) つまり、視聴者が途中で動画を止めたり、スマホなどの小さな画面で低画質モードで再生したりした場合、<br>
(<a href="#" class="imk-cue" data-seek="2:19" translate="no">00:02:19</a>) Bunny CDNなら消費データ量が減るため、インフラ費用を劇的に安く抑えることができます。<br>
(<a href="#" class="imk-cue" data-seek="2:25" translate="no">00:02:25</a>) 個人開発者にとって、このコストの安さは右に出るものがありません。<br>
(<a href="#" class="imk-cue" data-seek="2:30" translate="no">00:02:30</a>) そして最後に、運用を支える裏側のシステムです。複雑な作業は一切必要ありません。<br>
(<a href="#" class="imk-cue" data-seek="2:38" translate="no">00:02:38</a>) ユーザーは手元のスマホやPCからAppSheetを開き、青ボタンを押して、動画のステータスを「処理待ち」に変更するだけです。<br>
(<a href="#" class="imk-cue" data-seek="2:46" translate="no">00:02:46</a>) あとはMacのバックグラウンドで待機しているPythonスクリプト、f1_bunny_factory.pyが1分ごとに自動検知し、<br>
(<a href="#" class="imk-cue" data-seek="2:54" translate="no">00:02:54</a>) Bunny CDNへのアップロードを実行します。<br>
(<a href="#" class="imk-cue" data-seek="2:59" translate="no">00:02:59</a>) 新しいストリーミングURLが生成されると自動的にスプレッドシートが書き換えられ、古いデータは自動で削除されます。<br>
(<a href="#" class="imk-cue" data-seek="3:07" translate="no">00:03:07</a>) 人間の判断と裏側の自動化が完璧に連携した、まさに究極のハンドメイド配信システムの完成です。<br>
(<a href="#" class="imk-cue" data-seek="3:16" translate="no">00:03:16</a>) 特定のプラットフォームへの依存をなくし、視聴者には最高の体験を提供し、なおかつインフラ費用を極限まで安く抑える。<br>
(<a href="#" class="imk-cue" data-seek="3:25" translate="no">00:03:25</a>) Vimeoからの乗り換えは、動画の管理から視聴者の体験まで、すべてを自分自身でデザインできる自由を手に入れるための最高の決断でした。<br>
(<a href="#" class="imk-cue" data-seek="3:34" translate="no">00:03:34</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 = ''; 
      }

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

      // 初期状態のセット（リストがなければ最初から showing に）
      try {
        if(video.textTracks && video.textTracks.length > 0){
          for(var i=0; i<video.textTracks.length; i++){
             if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions'){
                 video.textTracks[i].mode = (!listContainer) ? "showing" : "hidden";
             }
          }
        }
      } catch(e){}

      // 字幕一覧が存在する場合のみ、リストのフォーマット処理を行う
      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(_){}
            });
          }
      }

      // ★ dr52.php と全く同じ「常時監視」のロジック
      // これにより、Safariが読み込み遅延を起こしても確実にONになります
      video.addEventListener("timeupdate", function(){
        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++){
                    if((video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') && video.textTracks[i].mode !== desiredMode) {
                        video.textTracks[i].mode = desiredMode;
                    }
                }
            }
        } catch(e){}

        // リストがない場合はこれ以降のハイライト処理をスキップ
        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>



<div class="wp-block-cocoon-blocks-toggle-box-1 toggle-wrap toggle-box block-box not-nested-style cocoon-block-toggle"><input id="toggle-checkbox-202606170911230" class="toggle-checkbox" type="checkbox"/><label class="toggle-button" for="toggle-checkbox-202606170911230"><strong><span class="marker">コードと要約</span><span class="marker">(クリック)</span></strong></label><div class="toggle-content">
<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>要約：本システムは、AppSheetとPythonスクリプトが連携し、Googleスプレッドシート上のメタデータに基づき動画・音楽ファイルをBunny CDNへ自動アップロード・管理する。 AppSheet側では、動画ファイル（mp4等）で「bn低画質動画単独生成=TRUE」かつ「bnステータス=処理待ち」の条件を満たすと、「Bunnyファイル生成更新」アクションが起動する。 MacのLaunchAgent (`com.XXXXXX.bunny_factory.plist`) により60秒ごとに実行されるPythonスクリプト`f1_bunny_factory.py`は、スプレッドシートを監視。システム設定のマスターSWが「ON」の場合に、「処理待ち」のレコードを検出する。 検出されたファイルは拡張子に応じ処理される。動画ファイルはBunny Streamへアップロードされ、動画IDとストリーミングURL（高画質・低画質）を生成。音楽ファイルはBunny Storageへアップロードされ、エンコードされたファイル名と直接アクセスURLを生成する。 アップロード後、スプレッドシートは新しいURLなどで更新され、「処理待ち」ステータスが「完了」となる。同時に、スプレッドシートに残る古い動画IDやファイル名が存在すれば、Bunny CDNからそれらを削除し、ログに記録する。これにより、Bunny CDN上のコンテンツが自動で最新に保たれる。</td></tr><tr><td><a href="https://imakat.com/script_list/?pubtxt=bunny%E6%9B%B4%E6%96%B0%E3%83%91%E3%82%A4%E3%83%97%E3%83%A9%E3%82%A4%E3%83%B3_pub.txt" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=zrU95MiY.png" alt="" style="width:120px; height:auto;"></a></td></tr><tr><td>公開スクリプトに関するお問い合わせは以下へ↓</td></tr><tr><td><a rel="noopener" target="_blank" href="https://notebooklm.google.com/notebook/aa64cc3d-0160-4433-b934-ecc5ab902287"><img decoding="async" src="https://imakat.com/rd.php?id=NgFHFYXH.png" alt="" style="width:120px; height:auto;"></a></td></tr></tbody></table></figure>
</div></div>



<p class="wp-block-paragraph">動画は、<a rel="noopener" target="_blank" href="https://bunny.net">Bunny CDN<span class="fa fa-external-link external-icon anchor-icon"></span></a>を使っています。プレーヤーはWordPress備え付けをカスタマイズしています。</p>



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



<h3 class="wp-block-heading"><strong>はじめに</strong></h3>



<p class="wp-block-paragraph">今回は、少しマニアックな動画配信の裏側の話題です。 長年愛用してきたVimeoですが、3月頃に「Plusプランが廃止の方向」という記事を目にしました。それに伴い、現在月額750円程度の料金が、1,200円程度に値上がりする方向だそうです。私の動画配信の利用レベルからすると、これは明らかな無駄遣いになってしまいます。 「いよいよVimeoの代替を探す時が来た」と決断し、日頃から頼りにしているGeminiなどのAIと相談しながら、今回の移行作業を進めてきました。</p>



<h3 class="wp-block-heading">１　コスパ最強のCDN選び：Bunny vs Cloudflare</h3>



<p class="wp-block-paragraph">移行先を検討する上で、最終的な候補に残ったのは「Bunny CDN（Bunny Stream）」と「Cloudflare Stream」の2つでした。両者のコストパフォーマンスを比較した結果、私はBunny CDNを選択しました。</p>



<p class="wp-block-paragraph">Bunny CDNの最大の魅力は、月額の最低料金がたったの1ドル（約150円）であることです。 支払いの仕組みは「デポジット制」を採用しており、自動チャージと手動チャージが選べます。感覚としては交通系ICカードのSuicaと同じです。私は手動チャージにしてあります。デポジット残高が少なくなると通知が来るはずです。 これによって、Vimeoをこのまま放置していれば月額1,200円かかるところを、Bunny CDNなら月額150〜300円程度にまで節約できそうです。小規模な個人運営にとって、このコスト差は圧倒的です。</p>



<h3 class="wp-block-heading">２　料金だけじゃない！「自動トランスコード」と「直リンク」の解放</h3>



<p class="wp-block-paragraph">Bunny CDNを選んだ理由は、単に安いからだけではありません。 Vimeoの大きなメリットであった「視聴者の環境に合わせて画質を調整する自動トランスコード機能」を、Bunny CDNもしっかりと備えています。</p>



<p class="wp-block-paragraph">さらに決定的な違いが、ストリーミング配信用（.m3u8）の「直リンクURL（ダイレクトURL）」がそのまま使える点です。 Vimeoのような大手サービスでは、専用のプレーヤーを通すことが強制され、カスタマイズが困難でした（例えば、字幕一覧から特定のシーンへジャンプするなど）。しかし、直リンクが解放されているBunny CDNなら、自作のテンプレート（dr52.phpなど）を使って、オリジナルの自由な配信の仕組みを完全にコントロールすることができます。</p>



<h3 class="wp-block-heading">３　英語の壁は「AIのサポート」で乗り越える</h3>



<p class="wp-block-paragraph">Bunny CDNを導入する上での唯一の難点は、サービス画面やマニュアルがすべて「英語」であることです。 しかし、今の時代、GeminiやChatGPTといった生成AIにサポートしてもらえば、英語はまったくハンデになりません。設定で分からないことがあればAIに聞き、PythonやAppSheetのシステム構築もAIと二人三脚で進めることができます。この強力なサポートがあれば、高齢者であっても最先端のインフラを構築することは十分に可能です。</p>



<h3 class="wp-block-heading">４　複数の配信所を切り替える「マイライブラリ」の完成</h3>



<p class="wp-block-paragraph">今回の移行以前からですが、私が構築した「マイライブラリ」システムは、状況に応じて複数の配信サーバーを瞬時に切り替えられるようになっています。</p>



<p class="wp-block-paragraph">それぞれのサーバーには特徴的な役割があります。</p>



<ul class="wp-block-list">
<li><strong>Dropbox(1)</strong>：動画の製造工場からの「産直（ダイレクト配信）」。</li>



<li><strong>Bunny(2)</strong>：今回追加、自動トランスコード付きの配信所。</li>



<li><strong>Xserver(3)</strong>：安全で安定したWordPressの「メインのホスティングサーバー」であり、配信を振り分けるルーティングの中枢。</li>



<li><strong>pCloud(4)</strong>：買い切りプランを活かした「保存倉庫」。</li>
</ul>



<p class="wp-block-paragraph">もし配信にトラブルが起きても、Xserver、pCloud、Dropboxへ速やかに切り替えることができます。 ここに、強力なCDN配信所として「Bunny CDN」が加わった形になります。このような柔軟で自立したエコシステムが完成した今、私がVimeoなどの大手プラットフォームに依存し続けるメリットは殆どなくなりました。</p>



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



<h3 class="wp-block-heading">５　もし再生数が多過ぎたら、その動画をYouTubeへ隔離。など自由自在。</h3>



<p class="wp-block-paragraph">そんなことはないとは思いますが、もし動画に過度に人気や関心が集まり再生数が増大してしまい、Bunnyの料金負担が重いと感じるようになってしまったとします。具体的には月2$(300円)を超えるような場合です。その場合は、その動画を特定して、残念ながら広告が入るかも知れませんが、その動画をYouTubeへ隔離するようにしたいと思います。私にとっては例外的な出来事になりますが、さりとて、その動画は、公共の道具であるインターネットを通じて公開してきているわけですので、観たい聴きたいというニーズに対しては、応えていく必要はあると思います。</p>



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



<p class="wp-block-paragraph">また、BunnyのStreamとして保存したGB数の上限を自分で決めて、それを超えた場合は、古いものについては、Bunnyから除去して、ファイル選択もBunnyを外して、標準の拡張子別の再生に切り替えるようにすることも考えています。</p>



<p class="wp-block-paragraph">このようにマイライブラリを使うことにより、出費をコントロールしながら、自由自在に、動画の配信所の選択ができるわけです。</p>



<h3 class="wp-block-heading"><strong>おわりに：世界情勢が物語る「脱・依存」の重要性</strong></h3>



<p class="wp-block-paragraph">特定のプラットフォームにすべてを預けてしまうと、規約変更や大幅な値上げ、あるいはサービス終了の際に、自分のメディアが人質に取られたような状態になってしまいます。最近の世界情勢が物語っているように、何か一つの場所やサービスに過度に依存しないことは、情報発信においても非常に重要です。</p>



<p class="wp-block-paragraph">Vimeoからの卒業は、まさに自分の資産を自分自身でコントロールする自由を手に入れるための、最良の決断でした。これからも色々工夫して、このブログの、無料かつ非営利の配信を堅持していきます。</p>



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



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">29030</post-id>	</item>
		<item>
		<title>【Mac】Webカメラと声で動く！「自作パペットシステム」を作りました〜無料配布〜</title>
		<link>https://imakat.com/2026/04/18/28865/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Fri, 17 Apr 2026 22:01:50 +0000</pubDate>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[パペット]]></category>
		<category><![CDATA[アバター]]></category>
		<category><![CDATA[Adobeサブスク]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=28865</guid>

					<description><![CDATA[今回は、Webカメラとマイクを使って、自分の顔の動きや声に合わせて画面上の動作人形（パペット）を動かせる「自作パペットシステム」をご紹介します。「自分専用のオリジナルアバターを、もっと手軽に動かしてみたい！」という思いか [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">今回は、Webカメラとマイクを使って、自分の顔の動きや声に合わせて画面上の動作人形（パペット）を動かせる「自作パペットシステム」をご紹介します。<br>「自分専用のオリジナルアバターを、もっと手軽に動かしてみたい！」という思いからPythonで開発を進めていたシステムですが、<a href="https://imakat.com/2026/03/12/28692/" target="_blank">前回の記事の内容に改良を加え</a>、やっと誰でも簡単に使えるパッケージになりました。<br>この辺で、<strong>Adobeのパペットにバイバイして、Adobeのサブスクをキャンセルすることにします</strong>。</p>



<p class="wp-block-paragraph">今回は<strong>サンプルパペット付きのアプリ（ZIPファイル）を無料公開</strong>します。プログラミングが分からない方でもすぐに使えるように工夫したので、ぜひダウンロードして試してみてください。</p>


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

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

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

  <div class="sc-link-container">
    <p class="sc-link">
      <a href="https://imakat.com/ds62/?drid=79" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <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=CVBqwr5k.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=exd87RDG.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=0zTMlFvp.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>) 〜自作パペットサンプル起動〜<br>
(<a href="#" class="imk-cue" data-seek="0:12" translate="no">00:00:12</a>) これがですね、ブログの記事に掲載しますサンプルパペットです。<br>
(<a href="#" class="imk-cue" data-seek="0:24" translate="no">00:00:24</a>) で、これそうですね、キャラを実行。<br>
(<a href="#" class="imk-cue" data-seek="0:29" translate="no">00:00:29</a>) うん、こんな風にですね、いくつもできちゃうんですね、面白いですね。<br>
(<a href="#" class="imk-cue" data-seek="0:34" translate="no">00:00:34</a>) 例えば、ここでimakat1号の方も起動させますとね。<br>
(<a href="#" class="imk-cue" data-seek="0:43" translate="no">00:00:43</a>) そうすると、ほら、これがなんと、<br>
(<a href="#" class="imk-cue" data-seek="0:46" translate="no">00:00:46</a>) キャラクターが、サンプルが2つと、同じものが2つと、<br>
(<a href="#" class="imk-cue" data-seek="0:58" translate="no">00:00:58</a>) もう1つ、imakat1号ですね。このキャラクターも動いていると。<br>
(<a href="#" class="imk-cue" data-seek="1:07" translate="no">00:01:07</a>) これはカメラとマイクを同じものを使っているものですから、3つとも同じ動作になっていますが。<br>
(<a href="#" class="imk-cue" data-seek="1:17" translate="no">00:01:17</a>) これはカメラもマイクも一応、キャラクターごとに設定できるようにしてあるので、<br>
(<a href="#" class="imk-cue" data-seek="1:27" translate="no">00:01:27</a>) やりようによってはですね、違うキャラクターが登場して違う人が操作すると、<br>
(<a href="#" class="imk-cue" data-seek="1:36" translate="no">00:01:36</a>) ということがですね、可能になるようになっています。<br>
(<a href="#" class="imk-cue" data-seek="1:42" translate="no">00:01:42</a>) それでは、この仕組みをどのように導入していくかということについて、<br>
(<a href="#" class="imk-cue" data-seek="1:50" translate="no">00:01:50</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 = ''; 
      }

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

      // 初期状態のセット（リストがなければ最初から showing に）
      try {
        if(video.textTracks && video.textTracks.length > 0){
          for(var i=0; i<video.textTracks.length; i++){
             if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions'){
                 video.textTracks[i].mode = (!listContainer) ? "showing" : "hidden";
             }
          }
        }
      } catch(e){}

      // 字幕一覧が存在する場合のみ、リストのフォーマット処理を行う
      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(_){}
            });
          }
      }

      // ★ dr52.php と全く同じ「常時監視」のロジック
      // これにより、Safariが読み込み遅延を起こしても確実にONになります
      video.addEventListener("timeupdate", function(){
        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++){
                    if((video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') && video.textTracks[i].mode !== desiredMode) {
                        video.textTracks[i].mode = desiredMode;
                    }
                }
            }
        } catch(e){}

        // リストがない場合はこれ以降のハイライト処理をスキップ
        if (!hasList) return;

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

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

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

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

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

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

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

      return true;
    }

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

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



<h2 class="wp-block-heading"><strong>&#x2728; 自作パペットシステムの3つの特徴</strong></h2>



<h3 class="wp-block-heading"><strong>1. Webカメラとマイクだけでスルスル動く</strong></h3>



<p class="wp-block-paragraph">高価なトラッキング機材は不要です。Macの内蔵カメラ（またはWebカメラ）が自分の顔の傾きを認識し、マイクが声の大きさを拾って、パペットがリアルタイムに連動します。</p>



<p class="wp-block-paragraph">「あいうえお」の口の形にも対応しており、まばたきや視線の移動も自動で行います。</p>



<h3 class="wp-block-heading"><strong>2. 専用の設定画面（UI）でカンタン調整</strong></h3>



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



<h3 class="wp-block-heading"><strong>3. スペースキーでいつでも正面をリセット！</strong></h3>



<p class="wp-block-paragraph">使っている途中で「ちょっと顔の向きがズレてきたな…」と思ったら、<strong>スペースキーをポンッと押すだけ</strong>。その瞬間の自分の顔の位置を「真正面」として再設定（キャリブレーション）します。</p>



<h2 class="wp-block-heading"><strong>&#x1f4e5; ダウンロードと使い方</strong></h2>



<p class="wp-block-paragraph">以下のリンクから、一式がまとまったZIPファイルをダウンロードしてください。</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>&#x1f449;<strong>ダウンロード(Macのみ)(無料・許諾不要)</strong></td></tr><tr><td><a href="https://imakat.com/rd.php?id=HFOB6Eer.zip" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=ShLhgSW9.png" alt="" style="width:120px; height:auto;"></a></td></tr></tbody></table></figure>



<h3 class="wp-block-heading"><strong>使い方の手順（※Mac専用です）</strong></h3>



<p class="wp-block-paragraph">使うためには、お使いのMacに「Python」が入っている必要があります。（入っていない方は<a rel="noopener" target="_blank" href="https://www.python.org/downloads/">公式サイト<span class="fa fa-external-link external-icon anchor-icon"></span></a>からインストールしてください）</p>



<ol class="wp-block-list">
<li>ダウンロードしたZIPを解凍し、フォルダを開きます。</li>



<li>初回のみ、フォルダ内の『はじめにお読みください.txt』の手順に従って、必要なAIパーツ（MediaPipeなど）をインストールします。<strong>（※ターミナルにドラッグ＆ドロップするだけです！）</strong></li>



<li>準備ができたら、**「自作パペット起動.command」**をダブルクリック！</li>



<li>設定画面が開くので、キャラクターに「サンプル」を選んで「起動」ボタンを押せば完了です。</li>
</ol>



<p class="wp-block-paragraph"><em>※初回起動時はMacのセキュリティで弾かれることがあります。その場合はファイルを「右クリック」→「開く」を選択してください。カメラのアクセス、マイクのアクセスの許可を求められたら「許可」を選択してください。</em></p>



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



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



<h2 class="wp-block-heading"><strong>&#x1f3a8; 応用編：オリジナルのパペットを自作しよう！</strong></h2>



<p class="wp-block-paragraph">今回お配りしたフォルダの中には、run_assets/サンプル という場所に、パペットのデザインの元になった <strong>「サンプル.pxd」</strong> というファイルが入っています。<br></p>



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



<p class="wp-block-paragraph">Macの画像編集ソフト「<strong>Pixelmator Pro</strong>」をお持ちの方は、このファイルを開いて自分の好きな絵に描き換えるだけで、<strong>完全オリジナルの自作パペット</strong>を作ることができます！</p>



<p class="wp-block-paragraph">さらに、Pixelmator Proから画像を書き出してパペットを起動するまでを「ワンクリック」で全自動化する裏技（AppleScriptとAutomatorの連携）も開発しました。私は、サブスク版ではない、以前からある単発アプリを使っています。もしサブスク版で「パペット書き出し起動.app」がうまく動かなくなった場合は、Automatorで作成したAppleScript内の tell application &#8220;Pixelmator Pro&#8221; という1行を、新しいサブスク版の正確なアプリ名に書き換えるだけで解決します。</p>



<p class="wp-block-paragraph">コードや詳しい仕組みに興味がある方は、こちらのページにスクリプトを公開していますので、ぜひ覗いてみてください。</p>



<div class="wp-block-cocoon-blocks-toggle-box-1 toggle-wrap toggle-box block-box not-nested-style cocoon-block-toggle"><input id="toggle-checkbox-202605220456280" class="toggle-checkbox" type="checkbox"/><label class="toggle-button" for="toggle-checkbox-202605220456280"><strong><span class="marker">コードと要約</span><span class="marker">(クリック)</span></strong></label><div class="toggle-content">
<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>要約：このシステムは、顔認識と音声入力を利用してリアルタイムで動作するデスクトップパペットを実現します。<br><code>run_ui.py</code>は、パペットの各種設定を行うGUIアプリケーションです。キャラクターの新規作成、複製、選択、そしてボリューム閾値、グローバルスケール、顔の傾き制限、目の感度、カメラ/マイクIDといったパラメータ調整機能を提供します。これらの設定はキャラクターごとにJSONファイルとして保存されます。<br><code>run_puppet.py</code>は、UIで選択・設定されたキャラクターとパラメータに基づき、実際のパペット動作を制御するスクリプトです。OpenCVとMediaPipe Face Meshを用いてカメラ映像から顔の傾き、目の動き、口の形を検出し、マイクからの音声ボリュームと連携させてアバターの頭の回転、視線、口の動きをリアルタイムで再現します。キャリブレーションデータもキャラクターごとに保存されます。<br>Automatorスクリプトは、これらのPythonスクリプトの実行を補助します。「自作パペット起動.app」は設定UIを立ち上げ、「パペット書き出し起動.app」はPixelmator Proで開いているドキュメントの各レイヤーをPNG画像として書き出し、キャラクターごとの設定JSONを自動生成した後、該当するパペットを起動する一連の処理を行います。これにより、Pixelmator Proでの画像編集からパペット起動までがスムーズに連携されます。</td></tr><tr><td><a href="https://imakat.com/script_list/?pubtxt=自作パペット_py_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>
</div></div>



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



<p class="wp-block-paragraph">PixelmatorProによるパーツの変更をパペット起動アプリに即座に反映している動画を以下に掲載します。<br></p>



<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="https://imakat.com/ds62/?drid=78" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <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=fuNs32gP.png" playsinline preload="none" style="width:100%;height:auto;">  <source src="https://imakat.com/rd.php?id=uCHEt9eh.mp4" type="video/mp4">  <track src="https://imakat.com/rd.php?id=yf1V8irn.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>) 〜パペット生成パイプライン〜PixelmatorProからPython連携〜<br>
(<a href="#" class="imk-cue" data-seek="0:11" translate="no">00:00:11</a>) このbodyの方がですね、選ばれて書き込まれるということになります。<br>
(<a href="#" class="imk-cue" data-seek="0:18" translate="no">00:00:18</a>) で、紺のbodyの方はちょっとこう、脇にですね、よけておきます。<br>
(<a href="#" class="imk-cue" data-seek="0:25" translate="no">00:00:25</a>) さあこれで、今これファイルをですね、保存します。<br>
(<a href="#" class="imk-cue" data-seek="0:30" translate="no">00:00:30</a>) そして、「パペットの書き出し起動」を起動させます。<br>
(<a href="#" class="imk-cue" data-seek="0:36" translate="no">00:00:36</a>) そうするとダダダダダッと動いていってですね、<br>
(<a href="#" class="imk-cue" data-seek="0:42" translate="no">00:00:42</a>) なんと、あっという間に、紺の服装からベージュの服装に、お着替えができました。<br>
(<a href="#" class="imk-cue" data-seek="0:56" translate="no">00:00:56</a>) さあそれではまた今度ですね、また紺の服装に戻ってみたいと思います。<br>
(<a href="#" class="imk-cue" data-seek="1:02" translate="no">00:01:02</a>) はい今度これをずらして、紺の服装に着替えて、<br>
(<a href="#" class="imk-cue" data-seek="1:11" translate="no">00:01:11</a>) で、bodyをこっちはベージュにします。<br>
(<a href="#" class="imk-cue" data-seek="1:22" translate="no">00:01:22</a>) で、紺の方をですね、これ紺のを取り外します。bodyだけにします。<br>
(<a href="#" class="imk-cue" data-seek="1:32" translate="no">00:01:32</a>) そうすると今度入れ替わるはずですよね。はい、それではプログラムを起動させます。<br>
(<a href="#" class="imk-cue" data-seek="1:45" translate="no">00:01:45</a>) はい、どうでしょうか。入れ替わりましたね。これで入れ替わったわけです。<br>
(<a href="#" class="imk-cue" data-seek="1:57" translate="no">00:01:57</a>) こんなように簡単にですね、入れ替わる事ができます。<br>
(<a href="#" class="imk-cue" data-seek="2:02" translate="no">00:02:02</a>) やり方としては、Pixelmator Proにファイルを作って、<br>
(<a href="#" class="imk-cue" data-seek="2:07" translate="no">00:02:07</a>) そのファイル上のパーツを入れ替えるということができます。<br>
(<a href="#" class="imk-cue" data-seek="2:11" translate="no">00:02:11</a>) そしてこのアプリを起動させることによって、サッと入れ替えができるという、<br>
(<a href="#" class="imk-cue" data-seek="2:19" translate="no">00:02:19</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 = ''; 
      }

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

      // 初期状態のセット（リストがなければ最初から showing に）
      try {
        if(video.textTracks && video.textTracks.length > 0){
          for(var i=0; i<video.textTracks.length; i++){
             if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions'){
                 video.textTracks[i].mode = (!listContainer) ? "showing" : "hidden";
             }
          }
        }
      } catch(e){}

      // 字幕一覧が存在する場合のみ、リストのフォーマット処理を行う
      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(_){}
            });
          }
      }

      // ★ dr52.php と全く同じ「常時監視」のロジック
      // これにより、Safariが読み込み遅延を起こしても確実にONになります
      video.addEventListener("timeupdate", function(){
        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++){
                    if((video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') && video.textTracks[i].mode !== desiredMode) {
                        video.textTracks[i].mode = desiredMode;
                    }
                }
            }
        } catch(e){}

        // リストがない場合はこれ以降のハイライト処理をスキップ
        if (!hasList) return;

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

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

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

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

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

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

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

      return true;
    }

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

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



<h2 class="wp-block-heading"><strong>&#x1f4a1; OBSでリアルタイム配信に使いたい方へ</strong></h2>



<p class="wp-block-paragraph">このパペットシステムは、OBS Studioを使ってリアルタイム配信などの画面に合成することも可能です。</p>



<p class="wp-block-paragraph">ウィンドウキャプチャでパペットの画面を取り込み、背景（グリーンバック等）を透過させればOKです。</p>



<p class="wp-block-paragraph"><strong>【ワンポイントアドバイス：音ズレの直し方】</strong></p>



<p class="wp-block-paragraph">映像の処理には少し時間がかかるため、OBS上で「声（マイク）」に対して「パペットの口の動き（映像）」がわずかに遅れて見えることがあります。</p>



<p class="wp-block-paragraph">その場合は、OBSの「オーディオの詳細プロパティ」から、<strong>マイクの「同期オフセット」を「200ms〜500ms」ほどプラス</strong>に設定して、声をわざと遅らせて映像に合わせると自然になります。私は今500msに設定しています。</p>



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



<h2 class="wp-block-heading"><strong>おわりに</strong></h2>



<p class="wp-block-paragraph">自分が描いたイラストが、自分の動きに合わせて滑らかに動く体験は、何度やっても感動します。それではご自由に利用ください。</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">28865</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="https://imakat.com/ds62/?drid=68_2" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <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 = ''; 
      }

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

      // 初期状態のセット（リストがなければ最初から showing に）
      try {
        if(video.textTracks && video.textTracks.length > 0){
          for(var i=0; i<video.textTracks.length; i++){
             if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions'){
                 video.textTracks[i].mode = (!listContainer) ? "showing" : "hidden";
             }
          }
        }
      } catch(e){}

      // 字幕一覧が存在する場合のみ、リストのフォーマット処理を行う
      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(_){}
            });
          }
      }

      // ★ dr52.php と全く同じ「常時監視」のロジック
      // これにより、Safariが読み込み遅延を起こしても確実にONになります
      video.addEventListener("timeupdate", function(){
        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++){
                    if((video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') && video.textTracks[i].mode !== desiredMode) {
                        video.textTracks[i].mode = desiredMode;
                    }
                }
            }
        } catch(e){}

        // リストがない場合はこれ以降のハイライト処理をスキップ
        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>第５章〜終わりに：動画パッケージの作り方</title>
		<link>https://imakat.com/2025/04/29/26044/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Tue, 29 Apr 2025 04:41:31 +0000</pubDate>
				<category><![CDATA[マイライブラリ]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[ショートコード]]></category>
		<category><![CDATA[識別子]]></category>
		<category><![CDATA[直リンク]]></category>
		<category><![CDATA[トークンリンク]]></category>
		<category><![CDATA[ファイルパス]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=26044</guid>

					<description><![CDATA[メディアライブラリを作ろう 24年3月「動的サイトを作ってみた」、同12月「動画ショートコード」を作成し、動画配信のためのデータ作成方法を解説してきました。 その後、少し手直しを行ったため、改めて「動画パッケージの作り方 [&#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=kSLZHN3D.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=kSLZHN3D.png" alt="" style="width:237px;height:auto"/></a></figure>



<p class="wp-block-paragraph"><a href="https://imakat.com/2024/03/23/20191/" target="_blank">24年3月「動的サイトを作ってみた」</a>、<a href="https://imakat.com/2024/12/22/24182/" target="_blank">同12月「動画ショートコード」</a>を作成し、動画配信のためのデータ作成方法を解説してきました。</p>



<p class="wp-block-paragraph">その後、少し手直しを行ったため、改めて「動画パッケージの作り方」の解説をします。</p>



<p class="wp-block-paragraph">なお「動画パッケージ」は、自作のメディアライブラリの中の一つのメニューです。</p>



<h3 class="wp-block-heading"><strong>1</strong>　スクリプトの変更点</h3>



<h4 class="wp-block-heading">ファイルパス入力を不要に：</h4>



<p class="wp-block-paragraph">当初は、各URLファイルを入力した後、そのファイルパスを別途手で入力していました。これは手間なので、事前にメディアライブラリの中にメディアアセットとして登録済みの場合は、再生URLを登録するだけで、spreadsheetに対して、存在するファイルパスが自動的に呼び出されるよう改良しました。</p>



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



<h3 class="wp-block-heading">２　アプリ運用変更点　</h3>



<h4 class="wp-block-heading">個別指定による再生サーバーの優先：</h4>



<p class="wp-block-paragraph">第3章で説明したように、再生URLに対しては個別に指定された再生サーバーが優先されます。個別指定がなければ、拡張子別の選択番号が適用されます。</p>



<p class="wp-block-paragraph">メディアアセットごとにこの再生選択番号が設定され、例えば、以下のようになります。</p>



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



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>https://imakat.com/rd.php?id=5XipZRMG.png</td><td>サーバー選択番号：３</td></tr></tbody></table></figure>



<h4 class="wp-block-heading">拡張子別サーバー選択：</h4>



<p class="wp-block-paragraph">現状、下のように、３：Xserver(WordPress)、４：pCloud、を選択しています。</p>



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



<h4 class="wp-block-heading">所在場所に「dynamic」を追加：</h4>



<p class="wp-block-paragraph">次に、動画パッケージの設定に入ります。以前にも説明した通り、動画はいくつかのメディアアセットを集めて作ります。このライブラリでは、初期画像ファイルURL、高画質動画ファイルURL、低画質動画ファイルURL、字幕ファイルURL、字幕一覧ファイルURL、説明ファイルURLの6種類です。基本的には、これらの再生URLを登録します。</p>



<p class="wp-block-paragraph">動画パッケージの設定では、各URLを登録すると同時に再生サーバーも自動決定されます。例えば、初期画像はpCloudから表示、動画はDropboxから表示、字幕はXserverから表示、字幕一覧はpCloudから表示、説明はXserverから表示といったように、自由に設定が出来ます。<strong>動的に自由に場所が変更可能なので、呼び名をdynamicとしました</strong>。<br><strong>ただし、字幕vttファイルについては、WordPressサーバーの中に置くようにします(そうしないと、Windowsの場合は字幕が表示されません)</strong>。</p>



<p class="wp-block-paragraph">なお、再生URLを入力せずに、サーバー別のURLを直接入力することもできます。その場合は、個別選択、拡張子別選択の影響は受けずに、再生されます。</p>



<p class="wp-block-paragraph">基礎情報として、いくつかに分類してありますが、この意図は、埋め込みコードの形式の違いをはっきりさせるためです。dynamic、wordpressサーバー、は、いずれもWordPressサーバーのHTML5動画プレーヤーを使用しますので、どれを選んでも同じ結果になります。Vimeo、YouTubeはそれぞれの動画プレーヤーを使うのでhtmlスクリプトも異なります。</p>



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



<h3 class="wp-block-heading">３　ショートコードの生成</h3>



<p class="wp-block-paragraph">WordPressの投稿には、</p>



<ul class="wp-block-list">
<li>カスタムHTMLブロックに埋め込む方法</li>



<li>ショートコードブロックに記載する方法<br></li>
</ul>



<p class="wp-block-paragraph">の二つがあり、表示される内容は同じです。</p>



<p class="wp-block-paragraph">phpスクリプト</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td style="background-color:#dddddd"><strong><a rel="noopener" target="_blank" href="https://docs.google.com/document/d/1rtcT8lfSgcHDKIzLEsMVpjzNBIZO8TrmHFGkTgBBrNA/edit?usp=sharing">動画ショートコードの例<span class="fa fa-external-link external-icon anchor-icon"></span></a></strong></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>(WordPressサーバー内)</td></tr><tr><td colspan="2">指定された videoid に基づいて videoembed.json を取得し、対応するレコードの embedCode を実行して動画を埋め込む。videoid が指定されていない、JSONが取得できない、または該当レコードが見つからない場合は「動画はまだ準備中です。」と表示する。<br></td></tr><tr><td colspan="2"><a href="https://imakat.com/script_list/?pubtxt=動画ショートコードの生成php_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>



<h3 class="wp-block-heading">４　動画を使う目的に応じて選択する</h3>



<p class="wp-block-paragraph">動画のグルーピングは、広く公開したいときは、Vimeo、YouTubeを選択する、ブログの記事用については、ブログは文章や画像で説明を済ませたいところですが、実際の作業や動きを見せて補強した方がいい時は多々あります。その時はdynamicを使う、といった使い分けになります。大事なのは、視聴者の立場で考えること。例えば、ブログ記事の補強で動画を使う場合、大抵は、数分〜10分程度におさまると思いますが、<strong>内容が数分程度のものを見てもらうのに、その前に広告を長々と見てもらうというのは、それはむごいこと、ある意味失礼なことだと、私は、感じます</strong>。だから、そうした動画はdynamic、長くなった場合はVimeoを選びます。<br>YouTubeは、やむを得ない場合使う、という順位に考えています。</p>



<h3 class="wp-block-heading">5　マイライブラリを２ページ並べる</h3>



<p class="wp-block-paragraph">動画パッケージを作成する手順は、最初に、メディアアセットをマイライブラリに登録することから始めます。次に、その登録されたアセットの再生URL(rd.phpの文字列のある)を、動画パッケージに登録します。この作業は、画面を２分割して、左側に「メディアアセット情報」、右側に「動画パッケージ」を配置すると便利です。それをクリックで表示するAppleScriptを紹介します。</p>



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



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>SafariでAppSheetの2つのURLを左右に並べて表示するAppleScript。メインディスプレイのサイズを取得し、画面を左右に分割して各URLをそれぞれの位置に開く。Safariを起動後、左側に1つ目のURLを新規ウインドウで開き、続いて右側に2つ目のURLを開く。もし新規ウインドウが作成されずタブ化された場合は、システムイベント経由で新規ウインドウを強制的に生成し、右側に配置する。</td></tr><tr><td><a href="https://imakat.com/script_list/?pubtxt=マイライブラリ画面２分割applescript_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>



<h3 class="wp-block-heading">終わりに</h3>



<p class="wp-block-paragraph">ここまで、メディアライブラリの作成から活用までの流れを説明してきました。</p>



<p class="wp-block-paragraph">基本の考え方は「ひとつの場所に依存しない」です。<strong>脱YouTube、脱Dropboxです</strong>。</p>



<p class="wp-block-paragraph">どうしても、人は、「この指とまれ」「このボール、あなたなら奪えるよ」と誘われると、群がる習性があります。そこを狙ったが如く、突然、天災、人災は起こり、その集まりを破壊します。あるいは、物事は、新しく生み出されてしばらくすると、それが普及して大衆化します。大衆化した次は飽和して衰退します。万物同じ原理で動いています。YouTubeは今は儲かっているでしょうが類似のサービスは間違いなく拡大します。YouTubeが未来ずっと存在することは難しいでしょう。<br></p>



<p class="wp-block-paragraph">&#x27a1;<a href="https://imakat.com/2025/04/03/25693/" 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">26044</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[トークンリンク]]></category>
		<category><![CDATA[リンク切れ]]></category>
		<category><![CDATA[Dropbox]]></category>
		<category><![CDATA[Webホスティング]]></category>
		<category><![CDATA[識別子]]></category>
		<category><![CDATA[直リンク]]></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" 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[ストリーミング]]></category>
		<category><![CDATA[直リンク]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[トークンリンク]]></category>
		<category><![CDATA[Dropbox]]></category>
		<category><![CDATA[Webホスティング]]></category>
		<category><![CDATA[メディアアセット]]></category>
		<category><![CDATA[NFC]]></category>
		<category><![CDATA[RFC]]></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>



<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つのサーバーに不具合が起きても、他のサーバーで代替再生が可能になります。</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[Mac]]></category>
		<category><![CDATA[Dropbox]]></category>
		<category><![CDATA[MAM]]></category>
		<category><![CDATA[メディアアセット]]></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="https://imakat.com/ds62/?drid=46" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <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 = ''; 
      }

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

      // 初期状態のセット（リストがなければ最初から showing に）
      try {
        if(video.textTracks && video.textTracks.length > 0){
          for(var i=0; i<video.textTracks.length; i++){
             if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions'){
                 video.textTracks[i].mode = (!listContainer) ? "showing" : "hidden";
             }
          }
        }
      } catch(e){}

      // 字幕一覧が存在する場合のみ、リストのフォーマット処理を行う
      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(_){}
            });
          }
      }

      // ★ dr52.php と全く同じ「常時監視」のロジック
      // これにより、Safariが読み込み遅延を起こしても確実にONになります
      video.addEventListener("timeupdate", function(){
        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++){
                    if((video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') && video.textTracks[i].mode !== desiredMode) {
                        video.textTracks[i].mode = desiredMode;
                    }
                }
            }
        } catch(e){}

        // リストがない場合はこれ以降のハイライト処理をスキップ
        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[MAM]]></category>
		<category><![CDATA[メディアアセット]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Dropbox]]></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="https://imakat.com/ds62/?drid=45" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
  </div>

  <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 = ''; 
      }

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

      // 初期状態のセット（リストがなければ最初から showing に）
      try {
        if(video.textTracks && video.textTracks.length > 0){
          for(var i=0; i<video.textTracks.length; i++){
             if(video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions'){
                 video.textTracks[i].mode = (!listContainer) ? "showing" : "hidden";
             }
          }
        }
      } catch(e){}

      // 字幕一覧が存在する場合のみ、リストのフォーマット処理を行う
      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(_){}
            });
          }
      }

      // ★ dr52.php と全く同じ「常時監視」のロジック
      // これにより、Safariが読み込み遅延を起こしても確実にONになります
      video.addEventListener("timeupdate", function(){
        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++){
                    if((video.textTracks[i].kind === 'subtitles' || video.textTracks[i].kind === 'captions') && video.textTracks[i].mode !== desiredMode) {
                        video.textTracks[i].mode = desiredMode;
                    }
                }
            }
        } catch(e){}

        // リストがない場合はこれ以降のハイライト処理をスキップ
        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>【WordPress】ヘッダーの背景画像を曜日で変える</title>
		<link>https://imakat.com/2024/11/07/24072/</link>
		
		<dc:creator><![CDATA[imakat]]></dc:creator>
		<pubDate>Wed, 06 Nov 2024 23:24:53 +0000</pubDate>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[デジタル]]></category>
		<category><![CDATA[ものづくり]]></category>
		<category><![CDATA[自宅サーバー]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=24072</guid>

					<description><![CDATA[今回の小改善ですが、おそらく、プラグインの中に出来るものがあるとは思いますが、割と簡単なので、ChatGPTに依頼しながらphpで作成することにします。 WordPressのテーマに無料のCocoonを使用していますが、 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">今回の小改善ですが、おそらく、プラグインの中に出来るものがあるとは思いますが、割と簡単なので、ChatGPTに依頼しながらphpで作成することにします。</p>



<p class="wp-block-paragraph">WordPressのテーマに無料の<a rel="noopener" target="_blank" href="https://wp-cocoon.com">Cocoon<span class="fa fa-external-link external-icon anchor-icon"></span></a>を使用していますが、使い続けさせていただき感謝を申し上げる次第ですが、とても分かりやすくできているので初めての人でもすぐに馴染めます。子テーマへは、自分なりのカスタマイズも行えるようになっており中級者にも満足いけるものと思います。</p>



<p class="wp-block-paragraph">ページのデザインには、たくさんの着替えが選べるようになっています。</p>



<p class="wp-block-paragraph">ブログで一番目立つ場所は、やはりヘッダーだろうと思います。このヘッダーには、ロゴやキャッチフレーズが自由に差し替えられます。ヘッダーの背景画像も差し替えが出来ます。</p>



<p class="wp-block-paragraph">そこで思ったのは、このヘッダーの背景画像が自動的に差し替えが出来たらいい、というか、手元の、Macやパソコンの画像ファイルを書き換えたら、そのままWordPressの更新がされたらラク、そう考えました。ちなみに、キャッチフレーズも日替わりはどうかと考えてみましたが、SEOに悪影響がありそうなのと、そもそもあまり面白味がないので、やめました。</p>



<figure class="wp-block-image size-large"><a href="https://imakat.com/rd.php?id=vepBJ8fH.png" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=vepBJ8fH.png" alt=""/></a><figcaption class="wp-element-caption">imakat_header_imageフォルダに各曜日の画像.pngを保存する。</figcaption></figure>



<h4 class="wp-block-heading">3つの更新方法</h4>



<div class="wp-block-cocoon-blocks-sticky-box blank-box block-box sticky">
<p class="wp-block-paragraph"><strong>No１　＜単純に、外部WordPressサーバーへFTP更新する＞</strong>XserverにあるWordPressサーバーへ、FTPで更新する。Hazelでimakat_header_imageフォルダ(フォルダ名は任意)を監視して、更新されたら、Transmit5アプリでFTPを起動させて、Xserverへ更新することも可能。phpの構造は３方法とも同じで、URLが異なるのみ。URLは、XserverにあるWordPressサーバーに登録したhead-insert.phpの中に書き込む。<br><strong>画像のURL：https://imakat.com/&#8230;&#8230;/imakat_header_image/monday_image.png</strong></p>
</div>



<div class="wp-block-cocoon-blocks-sticky-box blank-box block-box sticky">
<p class="wp-block-paragraph"><strong>No２　＜自宅WordPressサーバーのフォルダを更新する＞</strong>自宅のSynologyNASにあるWordPressサーバーのimakat_header_imageフォルダをCloudSyncでDropbox同期する。Macのimakat_header_imageフォルダをDropbox同期する。Mac上で曜日別画像.pngの更新を行うと自宅WordPressサーバーも更新される。phpの構造は３方法とも同じで、URLが異なるのみ。URLは、XserverにあるWordPressサーバーに登録したhead-insert.phpの中に書き込む。<br><strong>画像のURL：https://imakat.synology.me/&#8230;&#8230;./imakat_header_image/monday_image.png</strong></p>
</div>



<div class="wp-block-cocoon-blocks-sticky-box blank-box block-box sticky">
<p class="wp-block-paragraph"><strong>No３　＜Dropboxの画像ファイルを更新する＞</strong>フォルダの中に作ったimakat_header_imageフォルダの中の曜日別画像.pngのDropboxリンクを取得する。phpの構造は３方法とも同じで、URLが異なるのみ。但しこのDropboxのURLは共有トークンリンクURLで非常に長いランダムな文字列になっている。URLは、XserverにあるWordPressサーバーに登録したhead-insert.phpの中に書き込む。<br><strong>画像のURL：https://www.dropbox.com/scl/fi/xxxxx&#8230;xxxxxx/monday_image.png?rlkey=yyyy&#8230;..yyyyy&amp;raw=1</strong></p>
</div>



<p class="wp-block-paragraph">以上ですが、No１はオーソドックスな方法で、間違いは少ないです。No２は、自宅サーバーがある場合は、Macのフォルダに画像ファイルを更新するだけで、Web配信まで連動していくのでシンプルです。No３はMacのDropboxフォルダにある画像ファイルを更新するだけで、Web配信まで連動していくので、一見、シンプルですがしかし画像のURLは複雑なトークンリンクになっています。</p>



<h4 class="wp-block-heading">No３のデメリット</h4>



<p class="wp-block-paragraph">No３のデメリットを言います。No１とNo２は、URLからフォルダ階層が明示されるので迷子にならないですが、No３はURLは暗号化された文字列でありフォルダ階層も分かりません。従って、URLとファイルパスをコンビにしてどこかに記録しておかないと迷子になります。上書きは結構、頻繁に行うことになると思います。もし、<strong>上書きに失敗して新規にリンク取得となると、また、head-insert.phpを書き換える必要が生じます。</strong>これは起こり得ることだと思います。</p>



<h4 class="wp-block-heading">当面は、No２を利用</h4>



<p class="wp-block-paragraph">No３を別の寸評をすると「壊れやすい華奢」。No１とNo２は「少々乱暴でも壊れない」。</p>



<p class="wp-block-paragraph">私としては、ブログは、読みこみ許可、書き込み禁止で公開するのを基本と考えていまして、ファイルの所在位置が分かってしまうことは問題ではありません。それゆえ２を選択することにします。</p>



<h4 class="wp-block-heading">具体的な設定</h4>



<p class="wp-block-paragraph"> URLで共有できるメディアサーバーがあれば、その中にxxxxx_header_imageフォルダを設定します。パーミッションを、管理者以外は読み込みのみ、URLを知っている人全員可、に設定します。</p>



<p class="wp-block-paragraph">日本時間での曜日の変更になります。</p>



<p class="wp-block-paragraph">画像は、pngでもjpgでも問題ありません。画像のサイズは、ヘッダーのデザインによりますが、0.5MB程度に調整するのがいいと思います。<br></p>



<h5 class="wp-block-heading">header-insert.phpへの記述</h5>



<p class="wp-block-paragraph">Cocoonの場合は、テーマCocoon child→tmp-user→head-insert.phpになります。</p>



<figure class="wp-block-flexible-table-block-table"><table class="has-fixed-layout"><tbody><tr><td>Cocoonの場合は、テーマCocoon child→tmp-user→head-insert.phpになります。</td></tr><tr><td><a href="" target="_blank"></a><a href="" target="_blank"></a><a href="https://imakat.com/script_list/?pubtxt=cocoonのヘッダー部コンテンツ上部のカスタマイズ_head-insertphp_pub.txt" target="_blank"><img decoding="async" src="https://imakat.com/rd.php?id=zrU95MiY.png" alt="" style="width:120px; height:auto;"></a></td></tr></tbody></table></figure>



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