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

<channel>
	<title>メディアアセット | imakat.com</title>
	<atom:link href="https://imakat.com/tag/%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E3%82%A2%E3%82%BB%E3%83%83%E3%83%88/feed/" rel="self" type="application/rss+xml" />
	<link>https://imakat.com</link>
	<description>工夫と改善で人生をちょっと豊かに</description>
	<lastBuildDate>Tue, 10 Mar 2026 03:08:42 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://i0.wp.com/imakat.com/wp-content/uploads/2023/07/cropped-80d64ecd340db4e2ca3224859b04caed.png?fit=32%2C32&#038;ssl=1</url>
	<title>メディアアセット | imakat.com</title>
	<link>https://imakat.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">160909258</site>	<item>
		<title>第３章：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[RFC]]></category>
		<category><![CDATA[識別子]]></category>
		<category><![CDATA[ストリーミング]]></category>
		<category><![CDATA[直リンク]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[トークンリンク]]></category>
		<category><![CDATA[Dropbox]]></category>
		<category><![CDATA[Webホスティング]]></category>
		<category><![CDATA[メディアアセット]]></category>
		<category><![CDATA[NFC]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=25930</guid>

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



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



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



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



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



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



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



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



<p>よく見かける「拡張子なしのランダム識別子」も確かにセキュリティ的な利点はありますが、その一方でファイル内容の把握や管理は困難になります。</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>WordPressサーバー側で、phpスクリプトによって、選択された再生サーバー番号に合わせて、送り込んだJSONファイルとの照合を行ったりエンコード後のURLを生成するなどの作業を行っています。</p>



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



<p>直リンク方式(ファイルパス方式)の場合に、ファイルパスのエンコードが必要になりますが、ややこしい問題は、そのエンコードの方式が、サーバー会社によって異なる点です。各社が提供したエンコード後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><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>結局、そのクラウドストレージがその内部にWebサーバーを建てること(Webホスティング)を許可しているかどうか、Webサーバーを建てることができれば、直リンク方式の開放ができる可能性が高いです。次章で深掘りしますが、残念ながら、例えば、Dropbox、Googleドライブ、iCloudドライブ、Oneドライブなど主要大手は、ストレージの中にWebサーバーを建てることができなくなっているわけです。その中で、Dropboxはraw=1により表示が可能、pCloudはパブリックフォルダにより表示が可能、と珍しい存在になっているわけです。</p>



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



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



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



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



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



<p>以下が再生までの処理フローです：</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>これにより、<strong>複数の配信先を動的に切り替えられる柔軟な配信構造</strong>が実現します。たとえ1つのサーバーに不具合が起きても、他のサーバーで代替再生が可能になります。</p>



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



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



<p>&#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"><a href="https://imakat.com/media_library1" target="_blank">&#x1f517; 目次ページへ戻る</a></p>



<p></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[メディアアセット]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Dropbox]]></category>
		<category><![CDATA[MAM]]></category>
		<guid isPermaLink="false">https://imakat.com/?p=25796</guid>

					<description><![CDATA[メディアライブラリを作ろう ポイント解説： 作成するスクリプトは、結構、分量があります。ChatGPTを使い、ゆっくり進めるのがいいです。以下のスクリプトをChatGPTへコピペして、（１）「このスクリプトの解説をしてく [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>メディアライブラリを作ろう</p>


<div class="sc-dynamic-embed">
  <style>
  /* リンクの見た目を整える */
  .sc-dynamic-embed .sc-link-container { 
      display: flex; 
      gap: 12px; 
      margin-bottom: 10px; 
      flex-wrap: wrap; 
      align-items: center; 
  }
  .sc-dynamic-embed .sc-link { margin-bottom: 0; }
  .sc-dynamic-embed .sc-link a {
    font-size: 15px;
    font-weight: normal;
    text-decoration: underline;
    color: #0073aa;
  }
  .sc-dynamic-embed .sc-link a:hover { text-decoration: none; color: #000; }
  
  /* ★変更：ダウンロードボタンの基本サイズを小さくし、文字の折り返しを防止 */
  .sc-dynamic-embed .dl-btn a {
    font-size: 12px !important;
    color: #d9534f;
    font-weight: bold;
    text-decoration: none;
    background: #fdf0ef;
    padding: 4px 6px;
    border-radius: 4px;
    border: 1px solid #d9534f;
    white-space: nowrap; 
  }
  .sc-dynamic-embed .dl-btn a:hover { background: #d9534f; color: #fff; }

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

  /* 行梱包時の基本スタイル */
  .sc-dynamic-embed .imk-line {
      display: inline-block;
      width: 100%;
      border-radius: 2px;
      transition: background-color 0.1s;
  }

  /* 古い枠内字幕ボックスを強制消去 */
  .sc-dynamic-embed #subtitleOverlay,
  .sc-dynamic-embed #scSubtitleOverlay,
  .sc-dynamic-embed .overlay-cue,
  .sc-dynamic-embed .band {
      display: none !important;
      opacity: 0 !important;
      visibility: hidden !important;
      pointer-events: none !important;
  }
  </style>

  <div class="sc-link-container">
    <p class="sc-link">
      <a href="https://imakat.com/ds62/?drid=46" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
    <p class="sc-link dl-btn">
      <a href="#" id="imk-dynamic-dl-btn" target="_blank" rel="noopener" download style="display: none;">
        📥 動画をダウンロード
      </a>
    </p>
  </div>

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

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

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

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

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

      if (video.dataset.subInit === 'true') return true; 
      video.dataset.subInit = 'true';

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

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

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

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

      var detailsEl = target.querySelector("details");
      if (detailsEl) {
          detailsEl.open = true; 
          var summaryEl = detailsEl.querySelector("summary");
          if (summaryEl) summaryEl.textContent = "字幕(シーン)はここをクリック";
      }

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

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

      var rootSublist = target.querySelector(".dr5-sublist") || listContainer.parentElement;
      if (rootSublist) {
        rootSublist.addEventListener("click", function(e){
          var a = e.target.closest && e.target.closest("a.imk-cue[data-seek]");
          if(!a) return;
          e.preventDefault();
          var sec = parseTs(a.getAttribute("data-seek"));
          if(sec==null) return;
          try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
        });
      }

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

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

        for(var i=0; i<cues.length; i++){
            var t = parseTs(cues[i].getAttribute("data-seek"));
            if(t !== null && cur >= t - 0.5){ activeA = cues[i]; } 
            else if(t > cur){ break; }
        }

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

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

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

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

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

      return true;
    }

    /* -----------------------------------------------
       監視タイマー（URL抜き出し＆ボタン表示を継続的に実行）
       ----------------------------------------------- */
    var checks = 0;
    var checkTimer = setInterval(function(){
      setupDownloadButton();
      var success = initSubtitles();
      checks++;
      if (success || checks > 20) { 
        clearInterval(checkTimer);
      }
    }, 500); 

    /* -----------------------------------------------
       3. 画面遷移時の停止機能
       ----------------------------------------------- */
    if (!window.scStopAndGo) {
      window.scStopAndGo = function(event, link){
        try{
          var mediaEls = document.querySelectorAll('video, audio');
          mediaEls.forEach(function(m){
            try{
              if (!m.paused) m.pause();
              if (document.pictureInPictureElement === m && document.exitPictureInPicture) {
                document.exitPictureInPicture().catch(function(){});
              }
            }catch(e){}
          });
        }finally{
          event.preventDefault();
          setTimeout(function(){
            if (link.target === '_blank') {
              window.open(link.href, '_blank');
            } else {
              window.location.href = link.href;
            }
          }, 50);
        }
        return false;
      };
    }
  })();
  </script>
</div>



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



<p>作成するスクリプトは、結構、分量があります。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;<a rel="noopener" target="_blank" href="https://docs.google.com/drawings/d/1JgcOPS8GESxG34LDFSUE1i3CVe0PSM6sAyZrfqkV0cc/edit?usp=sharing">＜マイライブラリ生成更新処理＞<span class="fa fa-external-link external-icon anchor-icon"></span></a><br>mediaLibrary.gsは、Dropbox等のメディアファイル情報をF1シートで一元管理し、その変更を自動反映するGASです。ファイル名・パスから一意なwpidexを生成し、sb付き一時ファイル名や?v=付きURL、NFC差、連続スラッシュなどを正規化して突合します。doPostはmodeにより、ライブラリのフル再構築やvideoembed.jsonのみ更新、通常の作業行追加＋処理を切り替えます。processWorkingRecordはF3のパス変更履歴を反映しつつ、K=ADD行をADD2に更新し、JSON生成とライブラリ整理、pcloudidシートへの同期を行います。cleanMediaLibraryはDEL行や重複を除去し、更新日時で並べ替え、フラグをFALSEに戻します。videoembed関連は「動画パッケージ」シートから埋め込みHTMLを集約し、XserverのPHPにJSONをPOST送信します。</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></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></tbody></table></figure>



<p>上の処理は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><strong>第3章では、「<strong>YouTube（大手）に依存しないで配信する</strong>」</strong>についてご紹介します。<br></p>



<p>&#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"><a href="https://imakat.com/media_library1" target="_blank">&#x1f517; 目次ページへ戻る</a></p>



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

					<description><![CDATA[メディアライブラリを作ろう はじめに： いまの時代、動画はたくさん出回っています。でも、その動画を作るために使った素材――たとえば写真や音声、字幕や説明文など――は、紐づいて保存されているでしょうか。 最近の動画編集ツー [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>メディアライブラリを作ろう</p>


<div class="sc-dynamic-embed">
  <style>
  /* リンクの見た目を整える */
  .sc-dynamic-embed .sc-link-container { 
      display: flex; 
      gap: 12px; 
      margin-bottom: 10px; 
      flex-wrap: wrap; 
      align-items: center; 
  }
  .sc-dynamic-embed .sc-link { margin-bottom: 0; }
  .sc-dynamic-embed .sc-link a {
    font-size: 15px;
    font-weight: normal;
    text-decoration: underline;
    color: #0073aa;
  }
  .sc-dynamic-embed .sc-link a:hover { text-decoration: none; color: #000; }
  
  /* ★変更：ダウンロードボタンの基本サイズを小さくし、文字の折り返しを防止 */
  .sc-dynamic-embed .dl-btn a {
    font-size: 12px !important;
    color: #d9534f;
    font-weight: bold;
    text-decoration: none;
    background: #fdf0ef;
    padding: 4px 6px;
    border-radius: 4px;
    border: 1px solid #d9534f;
    white-space: nowrap; 
  }
  .sc-dynamic-embed .dl-btn a:hover { background: #d9534f; color: #fff; }

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

  /* 行梱包時の基本スタイル */
  .sc-dynamic-embed .imk-line {
      display: inline-block;
      width: 100%;
      border-radius: 2px;
      transition: background-color 0.1s;
  }

  /* 古い枠内字幕ボックスを強制消去 */
  .sc-dynamic-embed #subtitleOverlay,
  .sc-dynamic-embed #scSubtitleOverlay,
  .sc-dynamic-embed .overlay-cue,
  .sc-dynamic-embed .band {
      display: none !important;
      opacity: 0 !important;
      visibility: hidden !important;
      pointer-events: none !important;
  }
  </style>

  <div class="sc-link-container">
    <p class="sc-link">
      <a href="https://imakat.com/ds62/?drid=45" target="_blank"
         onclick="return scStopAndGo(event, this);">
        👉低画質・枠外字幕はこちら
      </a>
    </p>
    <p class="sc-link dl-btn">
      <a href="#" id="imk-dynamic-dl-btn" target="_blank" rel="noopener" download style="display: none;">
        📥 動画をダウンロード
      </a>
    </p>
  </div>

  <style>
            :root{ --dr5emd-max: 1920px; }
            .video-wrap{position:relative;width:100%;margin:0 auto}
            figure.wp-block-video.aligncenter{
              width:100%;
              max-width:min(var(--dr5emd-max, 1920px), 98vw);
              margin:0 auto;
            }
            #subtitleOverlay{
              position:absolute; left:0; right:0; bottom:6%;
              padding:0 2%; text-align:center; pointer-events:none; z-index:2;
            }
            #subtitleOverlay .band{
              display:inline-block; background:rgba(0,0,0,0.35);
              padding:6px 10px; border-radius:8px; max-width:96%;
              margin:0 auto; box-shadow:0 1px 2px rgba(0,0,0,0.15);
            }
            #subtitleOverlay .overlay-cue{
              color:#fff; font-weight:600;
              font-size:clamp(16px, 3.6vw, 32px);
              line-height:1.32; white-space:pre-wrap; margin:2px 0;
              -webkit-text-stroke:.6px rgba(0,0,0,.7);
              text-shadow:-1px -1px 0 rgba(0,0,0,.6), 1px -1px 0 rgba(0,0,0,.6),
                          -1px  1px 0 rgba(0,0,0,.6), 1px  1px 0 rgba(0,0,0,.6);
            }
            @media (max-width:430px){
              #subtitleOverlay .overlay-cue{ font-size:clamp(16px, 4.2vw, 22px); }
            }
            .dr5emd-sublist details > p{
              height:200px; overflow:auto; background-color:#EDF7FF;
              padding:2px 6px; margin:0; box-shadow:3px 3px 4px black;
              position: relative;
            }
            .dr5emd-sublist details > summary{
              padding:2px 6px; width:100%;
              background-color:#ddd; border:none;
              box-shadow:3px 3px 4px black; cursor:pointer; list-style:none;
            }
            /* ▼ 自動スクロール時のハイライト（文字の太さを標準へ変更） */
            .active-hl {
                background-color: #ffff00 !important;
                color: #ff0000 !important;
                font-weight: normal; /* 標準の太さ */
                border-bottom: 2px solid red;
                display: inline-block;
                border-radius: 2px;
            }
            </style><div class="dr5emd-container"><figure class="wp-block-video aligncenter"><div class="video-wrap"><video id="myVideo" controls controlsList="nodownload" poster="https://imakat.com/rd.php?id=SNebSNKN.png" playsinline preload="metadata" 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"></video><div id="subtitleOverlay" aria-hidden="true"></div></div><script>
document.addEventListener("DOMContentLoaded", function(){
  var video=document.getElementById("myVideo");
  var trackEl=video?video.querySelector("track[kind='subtitles'], track[kind='captions']"):null;
  var overlay=document.getElementById("subtitleOverlay"); if(!video||!overlay) return;
  video.addEventListener("contextmenu", function(e){ e.preventDefault(); return false; }, false);
  function setNative(mode){
    try{
      if(video.textTracks && video.textTracks.length){
        for(var i=0;i<video.textTracks.length;i++){ video.textTracks[i].mode = mode; }
      }
      if(trackEl && trackEl.track) trackEl.track.mode = mode;
    }catch(e){}
  }
  var isOverlay=true,lastSig="";
  function sig(active){if(!active||active.length===0)return"";var a=[];for(var i=0;i<active.length;i++){var c=active[i];a.push([c.startTime,c.endTime,c.text].join("|"));}return a.join("||");}
  function cueLine(c){var d=document.createElement("div");d.className="overlay-cue";d.setAttribute("translate","yes");if(typeof c.getCueAsHTML==="function")d.appendChild(c.getCueAsHTML());else d.textContent=c.text;return d;}
  function render(){
    if(!isOverlay || !trackEl || !trackEl.track) return;
    var ac=trackEl.track.activeCues,s=sig(ac); if(s===lastSig) return; lastSig=s;
    overlay.innerHTML=""; if(!ac || ac.length===0) return;
    var b=document.createElement("div"); b.className="band"; b.setAttribute("translate","yes");
    Array.from(ac).sort(function(a,b){return a.startTime-b.startTime;}).forEach(function(c){b.appendChild(cueLine(c));});
    overlay.appendChild(b);
  }
  function useOverlay(){isOverlay=true;overlay.style.display="";setNative("hidden");lastSig="";render();}
  function useNative(){isOverlay=false;overlay.style.display="none";setNative("showing");lastSig="";}
  useOverlay();
  if(trackEl){
    if(trackEl.track){ try{ trackEl.track.addEventListener("cuechange",render); }catch(e){} }
    trackEl.addEventListener("load", function(){ try{ if(trackEl.track) trackEl.track.addEventListener("cuechange",render); }catch(e){} render(); });
  }
  video.addEventListener("loadedmetadata",render);
  function handleWebkitMode(){ var m = video.webkitPresentationMode || "inline"; (m==="picture-in-picture"||m==="fullscreen") ? useNative() : useOverlay(); }
  if("webkitPresentationMode" in video){ video.addEventListener("webkitpresentationmodechanged",handleWebkitMode); handleWebkitMode(); }
  if("webkitCurrentPlaybackTargetIsWireless" in video){
    video.addEventListener("webkitcurrentplaybacktargetiswirelesschanged", function(){ video.webkitCurrentPlaybackTargetIsWireless ? useNative() : useOverlay(); });
  }
  if("pictureInPictureEnabled" in document){
    video.addEventListener("enterpictureinpicture",useNative);
    video.addEventListener("leavepictureinpicture",useOverlay);
  }
  document.addEventListener("fullscreenchange", function(){
    var fs=document.fullscreenElement;
    if(!fs) return useOverlay();
    (fs===video || (fs && fs.contains && fs.contains(video))) ? useNative() : useOverlay();
  });
});
</script>
                <figcaption></figcaption></figure><div class="dr5emd-sublist"><details><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:19">00:00:19</a>)  メディアライブラリを作ろうというテーマで、解説を始めます。どちらかというと、地味なテーマで、かっこよくも、美しくもないです。<br>
(<a href="#" class="imk-cue" data-seek="0:36">00:00:36</a>)  しかし、例えば、動画の配信は、ほとんどの方が、YouTubeやTikTokを使っていますね。<br>
(<a href="#" class="imk-cue" data-seek="0:48">00:00:48</a>)  やたら再生回数、いいね、登録を求められて、そういうやり方は、自分にとっては風呂敷を広げ過ぎです。 <br>
(<a href="#" class="imk-cue" data-seek="1:03">00:01:03</a>)  そんなことよりも、地味にITやパソコンをやってるマイナーな層に、配信できればそれでいいと思っています。<br>
(<a href="#" class="imk-cue" data-seek="1:16">00:01:16</a>)  だから、YouTubeを使わないで、自分のホームページ、Web、ブログから動画や画像を配信することができるようにしたいわけです。<br>
(<a href="#" class="imk-cue" data-seek="1:32">00:01:32</a>)  コマーシャルなど入らないように配信したいです。<br>
(<a href="#" class="imk-cue" data-seek="1:39">00:01:39</a>)  そう望まれる方は、このシリーズが役に立つのではと思います。<br>
(<a href="#" class="imk-cue" data-seek="1:45">00:01:45</a>)  私は、WordPressを使いますが、DNSが設定できてWeb配信ができれば、他のやり方でも、構造的には同じことかと思います。<br>
(<a href="#" class="imk-cue" data-seek="2:04">00:02:04</a>)  そういった普通の作業方法は、自分のパソコンで動画や画像の作成をして、それをレンタルサーバーへアップロードする、この流れが正しいです。<br>
(<a href="#" class="imk-cue" data-seek="2:20">00:02:20</a>)  ところで、動画や画像などはメディアアセットと呼びますが、メディアアセットは、最近はDropboxなどのクラウドストレージで作業をして保管をしていることも多くなっています。<br>
(<a href="#" class="imk-cue" data-seek="2:43">00:02:43</a>)  クラウドストレージは、そのままリンクを共有できるものが多く、それをブログに貼り付けて公開する方法も、一般化しています。<br>
(<a href="#" class="imk-cue" data-seek="2:57">00:02:57</a>)  ただし気掛かりな点は、クラウドストレージもレンタルサーバーも、いつ撤退するか分からない点です。<br>
(<a href="#" class="imk-cue" data-seek="3:09">00:03:09</a>)  その対処は、完璧な方法はありませんが、ひと所に依存しない、複数使えるようにしておくことだ、と考えます。<br>
(<a href="#" class="imk-cue" data-seek="3:25">00:03:25</a>)  今回のような、ハンドメイドの配信システムを持つメリットは、やはり、いつでも自由に手直しができることです。<br>
(<a href="#" class="imk-cue" data-seek="3:37">00:03:37</a>)  結局、趣味の内容をブログにするので、記事も動画画像も頻繁に手直ししますから。<br>
(<a href="#" class="imk-cue" data-seek="3:47">00:03:47</a>)  私の作ったものは、メディアライブラリと動画パッケージ、動画ライブラリですが、頻繁に手直しする人のための道具です。<br>
(<a href="#" class="imk-cue" data-seek="4:02">00:04:02</a>)  図を見ていただきたいですが、<br>
(<a href="#" class="imk-cue" data-seek="4:05">00:04:05</a>)  動画や画像といったメディアアセットは、中には大容量の、500MBを超えるようなものもあり、入れ物が必要になります。<br>
(<a href="#" class="imk-cue" data-seek="4:22">00:04:22</a>)  サーバーに保管します。<br>
(<a href="#" class="imk-cue" data-seek="4:28">00:04:28</a>)  しかし、そのメディアアセットをマネジメントする、管理するための情報は、私の場合、Googleドライブ内のSpreadsheetで行っています。<br>
(<a href="#" class="imk-cue" data-seek="4:44">00:04:44</a>)  アセット自体の管理と、運用するためのメタ情報の管理は、分離することができます。<br>
(<a href="#" class="imk-cue" data-seek="4:55">00:04:55</a>)  図書館の本の場合は、貸し出すためのサービスが、本を貸し出すための窓口が各図書館に必要になりますが、　<br>
(<a href="#" class="imk-cue" data-seek="5:08">00:05:08</a>)  動画や画像といったメディア情報は、その置き場所ごとに管理の仕組みを設ける必要はなくて、<br>
(<a href="#" class="imk-cue" data-seek="5:20">00:05:20</a>)  一括してまとめてコントロールすることができます。<br>
(<a href="#" class="imk-cue" data-seek="5:27">00:05:27</a>)  アセット自体の管理と、運用するためのメタ情報の管理は、分離することができます。<br>
(<a href="#" class="imk-cue" data-seek="5:39">00:05:39</a>)  ただし、メタ情報のメンテナンスは瞬時にできますが、アセットの移動は、何分かかかる場合もあります。そこにはどうしてもタイムラグが生じます。<br>
(<a href="#" class="imk-cue" data-seek="6:02">00:06:02</a>)  ブログは修正されたが、動画や画像がまだ届いていない、といったタイムラグもどうしても生じます。<br>
(<a href="#" class="imk-cue" data-seek="6:14">00:06:14</a>)  それでは、次は、実際に、Macの画面操作を見ていきます。<br>
</p> </details>
<style>
details { font: 16px "Open Sans", Calibri, sans-serif; width: 100%; }
details > summary { padding: 2px 6px; width: 100%; background-color: #ddd; border: none; box-shadow: 3px 3px 4px black; cursor: pointer; list-style: none; }
details > p { font: 14px "Open Sans", Calibri, sans-serif; height:150px; overflow: scroll; background-color: #EDF7FF; padding: 2px 6px; margin: 0; box-shadow: 3px 3px 4px black; }
</style>
</div><script>
(function(){
  var root=document.querySelector(".dr5emd-sublist");
  var video=document.getElementById("myVideo");
  if(!root || !video) return;
  function parseTs(ts){
    if(!ts) return null;
    var p=ts.trim().split(":").map(function(x){return parseInt(x,10)||0;});
    if(p.length===2) return p[0]*60 + p[1];
    if(p.length===3) return p[0]*3600 + p[1]*60 + p[2];
    return null;
  }
  root.addEventListener("click", function(e){
    var a=e.target.closest && e.target.closest("a.imk-cue[data-seek]");
    if(!a || !root.contains(a)) return;
    e.preventDefault();
    var sec = parseTs(a.getAttribute("data-seek"));
    if(sec==null) return;
    try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
  });
  video.addEventListener("timeupdate", function(){
    var listContainer = root.querySelector("details > p");
    if(!listContainer) return;
    var cues = listContainer.querySelectorAll("a.imk-cue");
    if(cues.length === 0) return;
    var cur = video.currentTime;
    var active = null;
    for(var i=0; i<cues.length; i++){
        var t = parseTs(cues[i].getAttribute("data-seek"));
        if(t !== null && cur >= t - 0.5){
            active = cues[i];
        } else if(t > cur){
            break;
        }
    }
    if(active){
        if(active.classList.contains("active-hl")) return;
        var old = listContainer.querySelectorAll(".active-hl");
        for(var k=0; k<old.length; k++) old[k].classList.remove("active-hl");
        active.classList.add("active-hl");
        if(listContainer.offsetParent !== null){
            var containerRect = listContainer.getBoundingClientRect();
            var activeRect = active.getBoundingClientRect();
            var targetScroll = listContainer.scrollTop + (activeRect.top - containerRect.top) - (listContainer.clientHeight / 2) + (active.clientHeight / 2);
            listContainer.scrollTo({ top: targetScroll, behavior: "smooth" });
        }
    }
  });
})();
</script>
                </div>

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

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

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

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

      if (video.dataset.subInit === 'true') return true; 
      video.dataset.subInit = 'true';

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

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

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

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

      var detailsEl = target.querySelector("details");
      if (detailsEl) {
          detailsEl.open = true; 
          var summaryEl = detailsEl.querySelector("summary");
          if (summaryEl) summaryEl.textContent = "字幕(シーン)はここをクリック";
      }

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

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

      var rootSublist = target.querySelector(".dr5-sublist") || listContainer.parentElement;
      if (rootSublist) {
        rootSublist.addEventListener("click", function(e){
          var a = e.target.closest && e.target.closest("a.imk-cue[data-seek]");
          if(!a) return;
          e.preventDefault();
          var sec = parseTs(a.getAttribute("data-seek"));
          if(sec==null) return;
          try{ video.currentTime = sec; if(video.paused) video.play(); }catch(_){}
        });
      }

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

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

        for(var i=0; i<cues.length; i++){
            var t = parseTs(cues[i].getAttribute("data-seek"));
            if(t !== null && cur >= t - 0.5){ activeA = cues[i]; } 
            else if(t > cur){ break; }
        }

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

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

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

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

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

      return true;
    }

    /* -----------------------------------------------
       監視タイマー（URL抜き出し＆ボタン表示を継続的に実行）
       ----------------------------------------------- */
    var checks = 0;
    var checkTimer = setInterval(function(){
      setupDownloadButton();
      var success = initSubtitles();
      checks++;
      if (success || checks > 20) { 
        clearInterval(checkTimer);
      }
    }, 500); 

    /* -----------------------------------------------
       3. 画面遷移時の停止機能
       ----------------------------------------------- */
    if (!window.scStopAndGo) {
      window.scStopAndGo = function(event, link){
        try{
          var mediaEls = document.querySelectorAll('video, audio');
          mediaEls.forEach(function(m){
            try{
              if (!m.paused) m.pause();
              if (document.pictureInPictureElement === m && document.exitPictureInPicture) {
                document.exitPictureInPicture().catch(function(){});
              }
            }catch(e){}
          });
        }finally{
          event.preventDefault();
          setTimeout(function(){
            if (link.target === '_blank') {
              window.open(link.href, '_blank');
            } else {
              window.location.href = link.href;
            }
          }, 50);
        }
        return false;
      };
    }
  })();
  </script>
</div>



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



<p>いまの時代、動画はたくさん出回っています。<br>でも、その動画を作るために使った素材――たとえば写真や音声、字幕や説明文など――は、紐づいて保存されているでしょうか。</p>



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



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



<p>時代は生成AIですね。</p>



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



<p></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>さて、これからの時代、動画などのメディアコンテンツの作り方は、どんな考え方に傾いていくでしょうか。</p>



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



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



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



<p>前者は、何が原因でどういう条件でこの結果となったかが分からないので、何を大事に残したらいいかが分からない。素材軽視、意外性重視、偶然好み、結果だけあればいい。完成物だけ持って飛び出していく人。やりっぱなしで、でも元ネタはここにあるはず、となると捨てられず、家はゴミ屋敷。</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>いいえ全員そうだとは言いませんが、概してそう。</p>



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



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



<p></p>



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



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



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



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



<p>具体的に私の例で言うと、</p>



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



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



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



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



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



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



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



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



<p>でも、どれも難しいプログラミングではなく、<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>そんな方にとって、きっとヒントになるシリーズになると思います。</p>



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



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



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



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



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



<p>&#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"><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>
	</channel>
</rss>
