Sora2のAPIを使用して動画のサムネイルを取得する方法

Higtyのシステムの作り方

Sora2のVideo APIは、完成した動画ごとにvariant=thumbnailを指定するとWebP形式のサムネイルを取得できます。


using System.Net.Http;
using System.Net.Http.Headers;

// videoId は生成結果のID(例: "video_abc123")
async Task DownloadThumbnailAsync(string apiKey, string videoId, string outPath = "thumbnail.webp")
{
    using var http = new HttpClient();
    var url = $"https://api.openai.com/v1/videos/{videoId}/content?variant=thumbnail";

    var req = new HttpRequestMessage(HttpMethod.Get, url);
    req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);

    using var res = await http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
    res.EnsureSuccessStatusCode();

    await using var fs = File.Create(outPath);
    await res.Content.CopyToAsync(fs);
}


画像の形式をPNGにする方法です。


using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;

async Task ConvertWebpToPngAsync(Stream webpStream, string outPath = "thumbnail.png")
{
    webpStream.Position = 0;
    using var image = await Image.LoadAsync(webpStream); // WebPを自動判別で読み込み
    await image.SaveAsync(outPath, new PngEncoder());
}

FFmpegをプロセスを作って呼び出しフレームからサムネイルを生成する方法です。

using System.Diagnostics;

async Task<byte[]> ExtractThumbWithFfmpegAsync(Stream videoStream, string ffmpegPath = "ffmpeg")
{
    // 1) ストリームを一時MP4へ
    string mp4 = Path.Combine(Path.GetTempPath(), $"sora_{Guid.NewGuid()}.mp4");
    await using (var fs = File.Create(mp4))
        await videoStream.CopyToAsync(fs);

    // 2) FFmpegで1フレーム抽出(先頭付近の代表フレーム)
    string jpg = Path.Combine(Path.GetTempPath(), $"thumb_{Guid.NewGuid()}.jpg");
    var psi = new ProcessStartInfo
    {
        FileName = ffmpegPath,
        // 先頭1秒位置を狙う。画角を軽く縮小(幅640)して圧縮ノイズを抑える例
        Arguments = $"-hide_banner -loglevel error -y -ss 00:00:01 -i \"{mp4}\" -vf \"thumbnail,scale=640:-1\" -frames:v 1 \"{jpg}\"",
        RedirectStandardError = true,
        UseShellExecute = false,
        CreateNoWindow = true
    };

    using var proc = Process.Start(psi)!;
    proc.WaitForExit();

    // 3) バイト配列で返す(必要ならファイル保存でもOK)
    var bytes = await File.ReadAllBytesAsync(jpg);

    // 4) 掃除
    try { File.Delete(mp4); File.Delete(jpg); } catch { /* ignore */ }

    return bytes;
}

以下、注意点。


・ストリーム直パイプ(stdin経由)でも可能ですが、Windows/権限周りで詰まりやすいのでまずは一時ファイル経由を推奨。

・クロスプラットフォームでGUI不要。-ss の時刻や -vf(thumbnail,scale=...)は好みに合わせて調整。

ライブラリで包みたい場合は Xabe.FFmpeg / FFmpeg.AutoGen / OpenCvSharp なども選択肢ですが、まずは生FFmpegが一番簡単。