動画の選択肢を「高画質」の一語だけで並べると、縦長動画や同じ解像度でビットレートが違うファイルを区別できません。この記事では、取得済みの動画メタデータを表示用の候補に変換する小さな JavaScript 関数を作ります。ネットワーク取得や X の内部 API の実装は扱いません。
私は Twitter Viewer を開発しています。twitter video downloader は公開ポストの動画を MP4 として保存するためのページです。以下のコードは選択 UI の考え方を示す独立したサンプルで、本番コードの抜粋ではありません。
高さだけで品質を決めない
横長の 1280×720 と縦長の 720×1280 は同じ画素数です。高さで降順にすると縦長が上位になりますが、情報量が必ず多いとは言えません。候補の並び順には幅と高さの積を使い、表示には両方の寸法を出します。
ビットレートは同じ寸法の候補を並べる補助情報として扱います。コーデックや元動画が違うファイルの品質を、ビットレートだけで比較することはできません。また、メタデータが欠けた候補に架空の値を補わず、「不明」として扱います。
export function videoChoices(variants) {
const finitePositive = value =>
typeof value === 'number' && Number.isFinite(value) && value > 0;
const seen = new Set();
return variants
.filter(v => v.mime === 'video/mp4' && typeof v.url === 'string')
.filter(v => {
if (seen.has(v.url)) return false;
seen.add(v.url);
return true;
})
.map(v => {
const knownSize = finitePositive(v.width) && finitePositive(v.height);
const bitrate = finitePositive(v.bitrate) ? v.bitrate : null;
const label = knownSize ? `${v.width}×${v.height}` : '解像度不明';
return {
url: v.url,
pixels: knownSize ? v.width * v.height : 0,
bitrate,
label: bitrate ? `${label} / ${Math.round(bitrate / 1000)} kbps` : label
};
})
.sort((a, b) => b.pixels - a.pixels || (b.bitrate ?? 0) - (a.bitrate ?? 0));
}
この関数の入力は、取得処理が正規化したデータです。実際の応答に mime や width があるとは仮定せず、取得処理と表示処理の間でスキーマを決めます。URL の重複除去も完全一致だけに限定しています。クエリ文字列を勝手に削ると、署名付き URL を壊す可能性があるためです。
サンプルデータで並び順を確かめる
choices.mjs に関数を保存し、次の内容を choices.test.mjs に保存すると、Node.js の標準テストで確認できます。
import test from 'node:test';
import assert from 'node:assert/strict';
import { videoChoices } from './choices.mjs';
test('同じ画素数ならビットレートで並ぶ', () => {
const result = videoChoices([
{ url: 'https://example.test/a', mime: 'video/mp4', width: 720, height: 1280, bitrate: 800000 },
{ url: 'https://example.test/b', mime: 'video/mp4', width: 1280, height: 720, bitrate: 1200000 },
{ url: 'https://example.test/c', mime: 'application/x-mpegURL' },
{ url: 'https://example.test/b', mime: 'video/mp4' }
]);
assert.equal(result.length, 2);
assert.equal(result[0].label, '1280×720 / 1200 kbps');
});
test('未知の解像度を表示できる', () => {
assert.equal(videoChoices([{ url: 'https://example.test/a', mime: 'video/mp4' }])[0].label, '解像度不明');
});
実行コマンドは node --test choices.test.mjs です。通信をしないテストなので、取得先の状態とは独立して UI の規則を確認できます。
ファイルサイズは推定と明記する
平均ビットレートと再生時間が分かれば、bitrate * seconds / 8 でバイト数の概算を計算できます。ただし音声を含むか、ビットレートの値が平均か最大か、コンテナのオーバーヘッドがあるかによって差が出ます。正確なサイズが必要なら取得元の信頼できるサイズ情報やダウンロード済みファイルを確認します。
選択ボタンには「1280×720 / 約 12 MB」のように寸法と概算を表示し、不明な値は隠さず不明と示します。「最高画質」が元動画を超える品質を意味しないことも伝えます。提供される候補が一つなら、存在しない上位解像度を選ばせる必要はありません。