Class: CloudflareStreamApi

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
app/services/cloudflare_stream_api.rb

Constant Summary collapse

ACCOUNT_ID =
'79b7f58cf035093b5ad11747df30369a'
BASE_URL =
'https://api.cloudflare.com/client/v4'
STREAM_BASE_URL =
"#{BASE_URL}/accounts/#{ACCOUNT_ID}/stream"

Instance Method Summary collapse

Constructor Details

#initializeCloudflareStreamApi

Returns a new instance of CloudflareStreamApi.



10
11
12
# File 'app/services/cloudflare_stream_api.rb', line 10

def initialize
  @token = CF_STREAM_AND_IMAGE_TOKEN
end

Instance Method Details

#copy_from_url(url, meta: {}) ⇒ Hash

Upload a video from a URL to Cloudflare Stream (URL copy).
Cloudflare will fetch and process the video asynchronously.

Parameters:

  • url (String)

    Public URL of the video to import

  • meta (Hash) (defaults to: {})

    Optional metadata (name, requireSignedURLs, etc.)

Returns:

  • (Hash)

    { success: true, uid: '...', data: ... } or { success: false, error: '...' }



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'app/services/cloudflare_stream_api.rb', line 182

def copy_from_url(url, meta: {})
  payload = { url: url, meta: meta }.compact
  response = make_request(:post, "#{STREAM_BASE_URL}/copy", payload.to_json)

  if response&.success?
    body = JSON.parse(response.body)
    result = body['result']
    { success: true, uid: result['uid'], data: result }
  else
    error = response ? response.body : 'no response'
    { success: false, error: error }
  end
rescue StandardError => e
  Rails.logger.error "CloudflareStreamApi#copy_from_url failed: #{e.message}"
  { success: false, error: e.message }
end

#delete_captions(cloudflare_uid, language) ⇒ Object

Delete captions for a video and language



126
127
128
129
130
131
132
133
134
# File 'app/services/cloudflare_stream_api.rb', line 126

def delete_captions(cloudflare_uid, language)
  endpoint = "#{STREAM_BASE_URL}/#{cloudflare_uid}/captions/#{language}"

  response = make_request(:delete, endpoint)
  response&.success?
rescue StandardError => e
  Rails.logger.error "Failed to delete captions for video #{cloudflare_uid}: #{e.message}"
  false
end

#delete_mp4_download(cloudflare_uid, download_type) ⇒ Object

Delete specific type of video download for a video

Parameters:

  • cloudflare_uid (String)

    the Cloudflare video UID

  • download_type (String)

    either 'default' or 'audio'



51
52
53
54
55
56
57
58
59
60
61
# File 'app/services/cloudflare_stream_api.rb', line 51

def delete_mp4_download(cloudflare_uid, download_type)
  raise ArgumentError, "Invalid download type: #{download_type}" unless %w[default audio].include?(download_type)

  endpoint = "#{STREAM_BASE_URL}/#{cloudflare_uid}/downloads/#{download_type}"

  response = make_request(:delete, endpoint)
  response&.success?
rescue StandardError => e
  Rails.logger.error "Failed to delete #{download_type} download for video #{cloudflare_uid}: #{e.message}"
  false
end

#delete_video(cloudflare_uid) ⇒ Object

Delete a video from Cloudflare Stream



200
201
202
203
204
205
206
207
208
# File 'app/services/cloudflare_stream_api.rb', line 200

def delete_video(cloudflare_uid)
  endpoint = "#{STREAM_BASE_URL}/#{cloudflare_uid}"

  response = make_request(:delete, endpoint)
  response&.success?
rescue StandardError => e
  Rails.logger.error "Failed to delete Cloudflare video #{cloudflare_uid}: #{e.message}"
  false
end

#enable_mp4_download(cloudflare_uid, download_type) ⇒ Object

Enable specific type of video download for a video

Parameters:

  • cloudflare_uid (String)

    the Cloudflare video UID

  • download_type (String)

    either 'default' or 'audio'



36
37
38
39
40
41
42
43
44
45
46
# File 'app/services/cloudflare_stream_api.rb', line 36

def enable_mp4_download(cloudflare_uid, download_type)
  raise ArgumentError, "Invalid download type: #{download_type}" unless %w[default audio].include?(download_type)

  endpoint = "#{STREAM_BASE_URL}/#{cloudflare_uid}/downloads/#{download_type}"

  response = make_request(:post, endpoint)
  response&.success?
rescue StandardError => e
  Rails.logger.error "Failed to enable #{download_type} download for video #{cloudflare_uid}: #{e.message}"
  false
end

#enable_mp4_downloads(cloudflare_uid) ⇒ Object

Enable video downloads for a video (legacy method - creates default download)



29
30
31
# File 'app/services/cloudflare_stream_api.rb', line 29

def enable_mp4_downloads(cloudflare_uid)
  enable_mp4_download(cloudflare_uid, 'default')
end

#get_downloads(cloudflare_uid) ⇒ Object

Get downloads information for a video



64
65
66
67
68
69
70
71
72
73
74
75
# File 'app/services/cloudflare_stream_api.rb', line 64

def get_downloads(cloudflare_uid)
  endpoint = "#{STREAM_BASE_URL}/#{cloudflare_uid}/downloads"

  response = make_request(:get, endpoint)
  return nil unless response&.success?

  data = JSON.parse(response.body)
  data['success'] ? data['result'] : nil
rescue StandardError => e
  Rails.logger.error "Failed to get downloads for video #{cloudflare_uid}: #{e.message}"
  nil
end

#get_poster_url(cloudflare_uid) ⇒ Object

Get poster/thumbnail URL for a video



78
79
80
# File 'app/services/cloudflare_stream_api.rb', line 78

def get_poster_url(cloudflare_uid)
  "#{CF_STREAM_URL}/#{cloudflare_uid}/thumbnails/thumbnail.jpg"
end

#get_video(cloudflare_uid) ⇒ Object

Get video details from Cloudflare Stream



15
16
17
18
19
20
21
22
23
24
25
26
# File 'app/services/cloudflare_stream_api.rb', line 15

def get_video(cloudflare_uid)
  endpoint = "#{STREAM_BASE_URL}/#{cloudflare_uid}"

  response = make_request(:get, endpoint)
  return nil unless response&.success?

  data = JSON.parse(response.body)
  data['success'] ? data['result'] : nil
rescue StandardError => e
  Rails.logger.error "Failed to get Cloudflare video #{cloudflare_uid}: #{e.message}"
  nil
end

#get_vtt_captions(cloudflare_uid, language) ⇒ Object

Get VTT captions content



137
138
139
140
141
142
143
144
145
# File 'app/services/cloudflare_stream_api.rb', line 137

def get_vtt_captions(cloudflare_uid, language)
  endpoint = "#{STREAM_BASE_URL}/#{cloudflare_uid}/captions/#{language}/vtt"

  response = make_request(:get, endpoint)
  response&.success? ? response.body : nil
rescue StandardError => e
  Rails.logger.error "Failed to get VTT captions for video #{cloudflare_uid}: #{e.message}"
  nil
end

#initiate_tus_upload(upload_length:, upload_metadata:) ⇒ Hash

Initiate a TUS resumable upload via the Cloudflare Stream direct-user endpoint.

Returns the Location header URL that the client should resume uploading to.
Called by the controller's +cf_upload+ action so that no Cloudflare API
details leak into application controllers.

Parameters:

  • upload_length (String)

    value of the Upload-Length TUS header

  • upload_metadata (String)

    value of the Upload-Metadata TUS header

Returns:

  • (Hash)

    { success: true, location: url } or { success: false, error: "..." }



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'app/services/cloudflare_stream_api.rb', line 156

def initiate_tus_upload(upload_length:, upload_metadata:)
  conn = Faraday.new(url: "#{STREAM_BASE_URL}?direct_user=true") do |f|
    f.headers['Authorization']   = "bearer #{@token}"
    f.headers['Tus-Resumable']   = '1.0.0'
    f.headers['Upload-Length']   = upload_length.to_s
    f.headers['Upload-Metadata'] = .to_s
    f.headers['Content-Type']    = 'application/json'
    f.adapter Faraday.default_adapter
  end

  response = conn.post
  if response.success?
    { success: true, location: response.headers['Location'] }
  else
    { success: false, error: response.body }
  end
rescue Faraday::Error => e
  Rails.logger.error "CloudflareStreamApi#initiate_tus_upload failed: #{e.message}"
  { success: false, error: e.message }
end

#list_captions(cloudflare_uid) ⇒ Object

List captions for a video



112
113
114
115
116
117
118
119
120
121
122
123
# File 'app/services/cloudflare_stream_api.rb', line 112

def list_captions(cloudflare_uid)
  endpoint = "#{STREAM_BASE_URL}/#{cloudflare_uid}/captions"

  response = make_request(:get, endpoint)
  return [] unless response&.success?

  data = JSON.parse(response.body)
  data['success'] ? data['result'] : []
rescue StandardError => e
  Rails.logger.error "Failed to list captions for video #{cloudflare_uid}: #{e.message}"
  []
end

#upload_captions(cloudflare_uid, language, vtt_file_path) ⇒ Object

Upload VTT captions to a video



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'app/services/cloudflare_stream_api.rb', line 83

def upload_captions(cloudflare_uid, language, vtt_file_path)
  endpoint = "#{STREAM_BASE_URL}/#{cloudflare_uid}/captions/#{language}"

  # Create multipart form data using proper multipart handling
  conn = Faraday.new(url: endpoint) do |faraday|
    faraday.headers['Authorization'] = "Bearer #{@token}"
    faraday.request :multipart
    faraday.request :url_encoded
    faraday.adapter Faraday.default_adapter
  end

  payload = {
    file: Faraday::UploadIO.new(vtt_file_path, 'text/vtt')
  }

  response = conn.put do |req|
    req.body = payload
  end

  return nil unless response&.success?

  data = JSON.parse(response.body)
  data['success'] ? data['result'] : nil
rescue StandardError => e
  Rails.logger.error "Failed to upload captions for video #{cloudflare_uid}: #{e.message}"
  nil
end