🌟

iiif-prezi3を使って、動画に字幕を設定する

2024/10/09に公開

概要

iiif-prezi3を使って、動画に字幕を設定する方法に関する備忘録です。

字幕の作成

OpenAIのAPIを使用して字幕ファイルを作成しました。動画のファイルを音声ファイルに変換しています。

from openai import OpenAI
from pydub import AudioSegment
from dotenv import load_dotenv

class VideoClient:

    def __init__(self):
        load_dotenv(verbose=True)
        api_key = os.getenv("OPENAI_API_KEY")
        self.client = OpenAI(api_key=api_key)

    def get_transcriptions(self, input_movie_path):
        audio = AudioSegment.from_file(input_movie_path)

        # 一時ファイルにオーディオを書き込む
        with tempfile.NamedTemporaryFile(suffix=".mp3") as temp_audio_file:
            audio.export(temp_audio_file.name, format="mp3")  # MP3形式でエクスポート
            temp_audio_file.seek(0)  # ファイルポインタを先頭に戻す

            # Whisper APIでトランスクリプトを取得
            with open(temp_audio_file.name, "rb") as audio_file:

                # Whisper APIでトランスクリプトを取得
                transcript = self.client.audio.transcriptions.create(
                    model="whisper-1", 
                    file=audio_file,
                    response_format="vtt"
                )

                return transcript

使用するデータ

『県政ニュース 第1巻』(県立長野図書館)を使用します。

https://www.ro-da.jp/shinshu-dcommons/library/02FT0102974177

マニフェストファイルへの反映

以下の記事などを参考に、マニフェストファイルが作成済みであるとします。

https://zenn.dev/nakamura196/articles/bf748ba5868e86

以下のようなスクリプトにより、マニフェストファイルにvttファイルを追加します。

from iiif_prezi3 import Manifest, AnnotationPage, Annotation, ResourceItem, config, HomepageItem, KeyValueString

#| export
class IiifClient:

    def load_manifest(self, manifest_path):
        with open(manifest_path, "r") as f:
            manifest_json = json.load(f)

        manifest = Manifest(**manifest_json)

        return manifest


    def add_vtt(self, manifest_simple_path):
        manifest = self.load_manifest(manifest_simple_path)

        vtt_url = f"{self.prefix}/video.vtt"

        canvas = manifest.items[0]

        vtt_anno_page = AnnotationPage(id=f"{canvas.id}/page2")
        canvas.annotations = [
            vtt_anno_page,
        ]

        vtt_body = ResourceItem(id=vtt_url, type="Text", format="text/vtt")
        vtt_anno = Annotation(
            id=f"{vtt_anno_page.id}/a1",
            motivation="supplementing",
            body=vtt_body,
            target=canvas.id,
            label = "WebVTT Transcript (machine-generated)"
        )        
        vtt_anno_page.add_item(vtt_anno)

        with open(f"{self.input_dir}/manifest_vtt.json", "w") as f:
            f.write(manifest.json(indent=2))

以下のようなマニフェストファイルが作成されます。

https://d1u7hq8ziluwl9.cloudfront.net/sdcommons_npl-02FT0102974177/manifest.json

{
  "@context": "http://iiif.io/api/presentation/3/context.json",
  "id": "https://d1u7hq8ziluwl9.cloudfront.net/sdcommons_npl-02FT0102974177/manifest.json",
  "type": "Manifest",
  "label": {
    "ja": [
      "県政ニュース 第1巻"
    ]
  },
  "requiredStatement": {
    "label": {
      "ja": [
        "Attribution"
      ]
    },
    "value": {
      "ja": [
        "『県政ニュース 第1巻』(県立長野図書館)を改変"
      ]
    }
  },
  "homepage": [
    {
      "id": "https://www.ro-da.jp/shinshu-dcommons/library/02FT0102974177",
      "type": "Text",
      "label": {
        "ja": [
          "信州デジタルコモンズ 県立長野図書館所蔵資料"
        ]
      }
    },
    {
      "id": "https://jpsearch.go.jp/item/sdcommons_npl-02FT0102974177",
      "type": "Text",
      "label": {
        "ja": [
          "ジャパンサーチ"
        ]
      }
    }
  ],
  "items": [
    {
      "id": "https://d1u7hq8ziluwl9.cloudfront.net/sdcommons_npl-02FT0102974177/canvas",
      "type": "Canvas",
      "height": 480,
      "width": 640,
      "duration": 619.61962,
      "items": [
        {
          "id": "https://d1u7hq8ziluwl9.cloudfront.net/sdcommons_npl-02FT0102974177/canvas/page",
          "type": "AnnotationPage",
          "items": [
            {
              "id": "https://d1u7hq8ziluwl9.cloudfront.net/sdcommons_npl-02FT0102974177/canvas/page/annotation",
              "type": "Annotation",
              "motivation": "painting",
              "body": {
                "id": "https://d1u7hq8ziluwl9.cloudfront.net/sdcommons_npl-02FT0102974177/video.mp4",
                "type": "Video",
                "height": 480,
                "width": 640,
                "duration": 619.61962,
                "format": "video/mp4"
              },
              "target": "https://d1u7hq8ziluwl9.cloudfront.net/sdcommons_npl-02FT0102974177/canvas"
            }
          ]
        }
      ],
      "annotations": [
        {
          "id": "https://d1u7hq8ziluwl9.cloudfront.net/sdcommons_npl-02FT0102974177/canvas/page2",
          "type": "AnnotationPage",
          "items": [
            {
              "id": "https://d1u7hq8ziluwl9.cloudfront.net/sdcommons_npl-02FT0102974177/canvas/page2/a1",
              "type": "Annotation",
              "label": {
                "ja": [
                  "WebVTT Transcript (machine-generated)"
                ]
              },
              "motivation": "supplementing",
              "body": {
                "id": "https://d1u7hq8ziluwl9.cloudfront.net/sdcommons_npl-02FT0102974177/video.vtt",
                "type": "Text",
                "format": "text/vtt"
              },
              "target": "https://d1u7hq8ziluwl9.cloudfront.net/sdcommons_npl-02FT0102974177/canvas"
            }
          ]
        }
      ]
    }
  ]
}

ビューアでの表示例

ビューアでの表示例は以下です。Theseus Viewerを使用しています。

https://theseusviewer.org/?iiif-content=https://d1u7hq8ziluwl9.cloudfront.net/sdcommons_npl-02FT0102974177/manifest_vtt.json

まとめ

IIIFを用いた動画ファイルへの字幕の設定にあたり、参考になりましたら幸いです。

Discussion