1. はじめに

韓国最大のポータルサイトである Naver は、独自の動画配信プラットフォーム「Naver TV」および「Naver Blog Video」を運営しています。これらのプラットフォームは、コンテンツ保護のために独自のAPI構造とストリーミング仕様を採用しています。

本記事では、Naver の動画配信仕様をリバースエンジニアリングし、Webベースのダウンローダー(twittervideodownloaderx.com/naver_downloader)がどのようにデータを取得・再構成しているのか、その裏側の技術スタックを徹底解説します。

 

2. Naver TVの動画配信アーキテクチャ

Naverの動画は、主に HLS (HTTP Live Streaming) プロトコルを使用して配信されています。

2.1 配信の仕組み

1.  Video ID (vid) と Key (outKey) の特定: 動画ページ内の埋め込みスクリプトから、メタデータ取得に必要な識別子を抽出します。

2.  APIリクエスト: apis.naver.com/rmcnmv/rmcnmv/vod/play/v2.0/ に対し、特定のパラメータを送信します。

3.  マニフェスト(M3U8)の取得: APIのレスポンスから、解像度別のインデックスファイルURLを取得します。

 

3. 動的APIの解析とシグネチャの生成

Naver TVのAPIは、単純にURLを叩くだけではアクセスできません。一部の動画では、リクエストの正当性を証明するためのシグネチャやタイムスタンプが要求されます。

3.1 認証パラメータの抽出

ダウンローダーは、HTMLのDOM解析を通じて、以下の要素を特定します。

·         vid: 動画のユニークID

·         inKey: 認証用キー

·         coverImage: サムネイル情報

3.2 TypeScriptによるメタデータ取得の実装

TypeScript

interface NaverVideoResponse {

  videos: {

    list: Array<{

      encodingOption: { name: string; width: number; height: number };

      source: string; // m3u8 or mp4 url

    }>;

  };

}

 

async function fetchNaverMetadata(vid: string, key: string): Promise<NaverVideoResponse> {

  const apiUrl = `https://apis.naver.com/rmcnmv/rmcnmv/vod/play/v2.0/${vid}?key=${key}`;

  const response = await fetch(apiUrl, {

    headers: {

      'User-Agent': 'Mozilla/5.0...',

      'Referer': 'https://tv.naver.com/'

    }

  });

  return await response.json();

}

 

4. HLS(M3U8)ストリームのパースとセグメント処理

Naverは多くの場合、.ts(Transport Stream)セグメントに分割された動画を配信します。

4.1 M3U8ファイルの構造

M3U8ファイルには、以下のように複数のセグメントURLが記述されています。

Plaintext

#EXTM3U

#EXT-X-TARGETDURATION:10

#EXTINF:10.0,

segment_001.ts

#EXTINF:10.0,

segment_002.ts

4.2 セグメントの並列ダウンロード

ブラウザのフェッチ機能を最大限に活用するため、Promise.all を用いてセグメントを並列取得し、スループットを向上させます。

TypeScript

async function downloadSegments(segmentUrls: string[]) {

  const chunks = await Promise.all(

    segmentUrls.map(url => fetch(url).then(res => res.arrayBuffer()))

  );

  // バイナリの結合

  const totalLength = chunks.reduce((acc, curr) => acc + curr.byteLength, 0);

  const result = new Uint8Array(totalLength);

  let offset = 0;

  for (const chunk of chunks) {

    result.set(new Uint8Array(chunk), offset);

    offset += chunk.byteLength;

  }

  return result;

}

 

5. クライアントサイドでの再構築:WebAssemblyの利用

ダウンロードした複数の .ts ファイルを、ユーザーが扱いやすい単一の .mp4 に変換する必要があります。サーバーサイドで行うと帯域コストが膨大になるため、Naver Downloader では FFmpeg.wasm を採用しています。

5.1 FFmpeg.wasmによるコンテナ変換

TSからMP4への変換(再エンコードなし)をブラウザ上で行うロジックです。

JavaScript

import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';

 

const ffmpeg = createFFmpeg({ log: true });

 

async function convertTsToMp4(tsData) {

  await ffmpeg.load();

  ffmpeg.FS('writeFile', 'input.ts', tsData);

  // 再エンコードを避け、コピーのみを行うことで高速化

  await ffmpeg.run('-i', 'input.ts', '-c', 'copy', 'output.mp4');

  const data = ffmpeg.FS('readFile', 'output.mp4');

  return new Blob([data.buffer], { type: 'video/mp4' });

}

 

6. フロントエンドUXとパフォーマンス最適化

技術者として、ツールの「使い心地」を支えるフロントエンド設計にも触れます。

6.1 進捗状況の可視化

大規模な動画の場合、ダウンロードには時間がかかります。ReadableStream を活用して、詳細なパーセンテージを表示する実装が重要です。

6.2 Service Workersによるキャッシュ管理

頻繁に使用されるライブラリ(FFmpeg.wasmのコアなど)は数MBのサイズがあるため、Service Workersでキャッシュし、リピートアクセスの高速化を図ります。

 

7. セキュリティと法的配慮

技術記事をQiitaで公開する上で、コンプライアンスに関する言及は不可欠です。

·         私的使用の範囲: 著作権法に基づき、個人的なバックアップ目的での利用を前提とする設計。

·         レートリミットへの配慮: サーバーへ過度な負荷をかけないためのインターバル処理の実装。

·         Referer制限の回避: NaverのAPIはRefererチェックを行っているため、適切なヘッダー管理が必要。

 

8. 結論

Naver動画のダウンロード技術は、現代のストリーミング配信における「API解析」「バイナリ操作」「クライアントサイド・コンピューティング」の集大成と言えます。

twittervideodownloaderx.com/naver_downloader のようなツールは、これらの技術を組み合わせることで、複雑なプロセスを一般ユーザーに意識させないシームレスな体験を提供しています。