Class: Gemini::Video

Inherits:
Object
  • Object
show all
Defined in:
lib/gemini/video.rb

Constant Summary collapse

SUPPORTED_FORMATS =

サポートされる動画形式

%w[.mp4 .mpeg .mov .avi .flv .mpg .webm .wmv .3gp .3gpp].freeze

Instance Method Summary collapse

Constructor Details

#initialize(client:) ⇒ Video

Returns a new instance of Video.



6
7
8
# File 'lib/gemini/video.rb', line 6

def initialize(client:)
  @client = client
end

Instance Method Details

#analyze(file: nil, file_path: nil, prompt:, model: "gemini-2.5-flash", **parameters) ⇒ Object

動画ファイルを分析する(Files APIでアップロード後に分析)20MB以上のファイルや複数回利用する場合に推奨



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/gemini/video.rb', line 12

def analyze(file: nil, file_path: nil, prompt:, model: "gemini-2.5-flash", **parameters)
  # ファイルパスが指定されている場合はファイルを開く
  if file_path && !file
    file = File.open(file_path, "rb")
    close_file = true
  else
    close_file = false
  end

  begin
    raise ArgumentError, "file or file_path parameter is required" unless file

    # MIMEタイプを判定
    mime_type = parameters.delete(:mime_type) || determine_video_mime_type(file)

    # ファイルをアップロード
    upload_result = @client.files.upload(file: file)
    file_uri = upload_result["file"]["uri"]
    file_name = upload_result["file"]["name"]

    # ファイルがACTIVE状態になるまで待機
    wait_for_file_active(file_name)

    # コンテンツを生成
    raw_response = generate_video_content(
      file_uri: file_uri,
      mime_type: mime_type,
      prompt: prompt,
      model: model,
      **parameters
    )

    # レスポンスとファイル情報を返す
    {
      response: Gemini::Response.new(raw_response),
      file_uri: file_uri,
      file_name: file_name
    }
  ensure
    file.close if file && close_file
  end
end

#analyze_inline(file: nil, file_path: nil, prompt:, model: "gemini-2.5-flash", **parameters) ⇒ Object

小さい動画ファイルをインラインデータとして分析(20MB未満向け)



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/gemini/video.rb', line 102

def analyze_inline(file: nil, file_path: nil, prompt:, model: "gemini-2.5-flash", **parameters)
  # ファイルパスが指定されている場合はファイルを開く
  if file_path && !file
    file = File.open(file_path, "rb")
    close_file = true
  else
    close_file = false
  end

  begin
    raise ArgumentError, "file or file_path parameter is required" unless file

    # ファイルサイズチェック(20MB = 20 * 1024 * 1024)
    file.rewind
    file_size = file.size
    if file_size > 20 * 1024 * 1024
      raise ArgumentError, "File size exceeds 20MB. Use analyze method with Files API instead."
    end

    # MIMEタイプを判定
    mime_type = parameters.delete(:mime_type) || determine_video_mime_type(file)

    # Base64エンコード
    file.rewind
    require 'base64'
    file_data = Base64.strict_encode64(file.read)

    # リクエストパラメータを構築
    request_params = {
      contents: [{
        parts: [
          { text: prompt },
          {
            inline_data: {
              mime_type: mime_type,
              data: file_data
            }
          }
        ]
      }]
    }

    # 追加パラメータをマージ
    merge_additional_params(request_params, parameters)

    # APIリクエスト
    response = @client.json_post(
      path: "models/#{model}:generateContent",
      parameters: request_params
    )

    Gemini::Response.new(response)
  ensure
    file.close if file && close_file
  end
end

#analyze_segment(file_uri:, prompt:, start_offset: nil, end_offset: nil, model: "gemini-2.5-flash", mime_type: "video/mp4", **parameters) ⇒ Object

動画のセグメント(一部分)を分析



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/gemini/video.rb', line 192

def analyze_segment(file_uri:, prompt:, start_offset: nil, end_offset: nil, model: "gemini-2.5-flash", mime_type: "video/mp4", **parameters)
  # videoMetadataを構築
   = {}
  [:startOffset] = start_offset if start_offset
  [:endOffset] = end_offset if end_offset

  # リクエストパラメータを構築
  file_data_part = {
    file_data: {
      mime_type: mime_type,
      file_uri: file_uri
    }
  }
  file_data_part[:file_data][:video_metadata] =  unless .empty?

  request_params = {
    contents: [{
      parts: [
        { text: prompt },
        file_data_part
      ]
    }]
  }

  # 追加パラメータをマージ
  merge_additional_params(request_params, parameters)

  # APIリクエスト
  response = @client.json_post(
    path: "models/#{model}:generateContent",
    parameters: request_params
  )

  Gemini::Response.new(response)
end

#analyze_with_file_uri(file_uri:, prompt:, model: "gemini-2.5-flash", mime_type: "video/mp4", **parameters) ⇒ Object

アップロード済みのファイルURIを使用して分析



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/gemini/video.rb', line 56

def analyze_with_file_uri(file_uri:, prompt:, model: "gemini-2.5-flash", mime_type: "video/mp4", **parameters)
  raw_response = generate_video_content(
    file_uri: file_uri,
    mime_type: mime_type,
    prompt: prompt,
    model: model,
    **parameters
  )

  Gemini::Response.new(raw_response)
end

#analyze_youtube(url:, prompt:, model: "gemini-2.5-flash", **parameters) ⇒ Object

YouTube URLから動画を分析(公開動画のみ)



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/gemini/video.rb', line 69

def analyze_youtube(url:, prompt:, model: "gemini-2.5-flash", **parameters)
  # YouTube URLのバリデーション
  unless valid_youtube_url?(url)
    raise ArgumentError, "Invalid YouTube URL. Only public YouTube videos are supported."
  end

  # リクエストパラメータを構築
  request_params = {
    contents: [{
      parts: [
        { text: prompt },
        {
          file_data: {
            file_uri: url
          }
        }
      ]
    }]
  }

  # 追加パラメータをマージ
  merge_additional_params(request_params, parameters)

  # APIリクエスト
  response = @client.json_post(
    path: "models/#{model}:generateContent",
    parameters: request_params
  )

  Gemini::Response.new(response)
end

#ask(file: nil, file_path: nil, file_uri: nil, youtube_url: nil, question:, model: "gemini-2.5-flash", **parameters) ⇒ Object

動画に関する質問に回答



229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/gemini/video.rb', line 229

def ask(file: nil, file_path: nil, file_uri: nil, youtube_url: nil, question:, model: "gemini-2.5-flash", **parameters)
  if youtube_url
    analyze_youtube(url: youtube_url, prompt: question, model: model, **parameters)
  elsif file_uri
    analyze_with_file_uri(file_uri: file_uri, prompt: question, model: model, **parameters)
  elsif file || file_path
    result = analyze(file: file, file_path: file_path, prompt: question, model: model, **parameters)
    result[:response]
  else
    raise ArgumentError, "file, file_path, file_uri, or youtube_url is required"
  end
end

#describe(file: nil, file_path: nil, file_uri: nil, youtube_url: nil, model: "gemini-2.5-flash", language: "ja", **parameters) ⇒ Object

動画の説明を取得するヘルパーメソッド



160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/gemini/video.rb', line 160

def describe(file: nil, file_path: nil, file_uri: nil, youtube_url: nil, model: "gemini-2.5-flash", language: "ja", **parameters)
  prompt = language == "ja" ? "この動画の内容を詳しく説明してください。" : "Describe this video in detail."

  if youtube_url
    analyze_youtube(url: youtube_url, prompt: prompt, model: model, **parameters)
  elsif file_uri
    analyze_with_file_uri(file_uri: file_uri, prompt: prompt, model: model, **parameters)
  elsif file || file_path
    result = analyze(file: file, file_path: file_path, prompt: prompt, model: model, **parameters)
    result[:response]
  else
    raise ArgumentError, "file, file_path, file_uri, or youtube_url is required"
  end
end

#extract_timestamps(file: nil, file_path: nil, file_uri: nil, youtube_url: nil, query:, model: "gemini-2.5-flash", **parameters) ⇒ Object

タイムスタンプを抽出するヘルパーメソッド



176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/gemini/video.rb', line 176

def extract_timestamps(file: nil, file_path: nil, file_uri: nil, youtube_url: nil, query:, model: "gemini-2.5-flash", **parameters)
  prompt = "動画内で「#{query}」が登場するタイムスタンプを全て抽出してください。MM:SS形式で出力してください。"

  if youtube_url
    analyze_youtube(url: youtube_url, prompt: prompt, model: model, **parameters)
  elsif file_uri
    analyze_with_file_uri(file_uri: file_uri, prompt: prompt, model: model, **parameters)
  elsif file || file_path
    result = analyze(file: file, file_path: file_path, prompt: prompt, model: model, **parameters)
    result[:response]
  else
    raise ArgumentError, "file, file_path, file_uri, or youtube_url is required"
  end
end