Class: Crm::VideoPresenter
Overview
Presenter: video presenter.
Instance Attribute Summary
#current_account, #options, #url_helper
Delegated Instance Attributes
collapse
#created_at, #title
#can?, #capture, #concat, #content_tag, #fa_icon, #link_to, #number_to_currency, #simple_format
Instance Method Summary
collapse
#build_transcript_schema, #cf_poster_url, #chapter_clips, #cloudflare_embed_url, #cloudflare_mp4_url, #cloudflare_video_url, #format_duration, #format_duration_for_display, #format_paragraphs_as_text, #format_sentences_as_text, #format_structured_paragraphs_as_text, #format_timestamp, #has_assemblyai_transcript_id?, #has_audio_extraction?, #has_cloudflare_captions?, #has_html_transcript?, #has_structured_transcript_json?, #page_description, #published_on, #schema_dot_org_structure, #transcript_display, #transcript_duration, #transcript_language, #video_medias_url
#h, #present, presents, #r, #safe_present, #u
Constructor Details
#initialize(model, view = nil, options = nil) ⇒ VideoPresenter
Returns a new instance of VideoPresenter.
7
8
9
10
|
# File 'app/presenters/crm/video_presenter.rb', line 7
def initialize(model, view = nil, options = nil)
@url_helper = Crm::BasePresenter::Router.new
super
end
|
Instance Method Details
#assemblyai_transcript_status_icon ⇒ Object
260
261
262
|
# File 'app/presenters/crm/video_presenter.rb', line 260
def assemblyai_transcript_status_icon
video.has_assemblyai_transcript_id? ? seo_status_icon('good') : seo_status_icon('missing')
end
|
#cloudflare_audio_download_display ⇒ Object
212
213
214
215
216
217
218
219
220
|
# File 'app/presenters/crm/video_presenter.rb', line 212
def cloudflare_audio_download_display
if video.has_audio_download?
status = video.cloudflare_download_status('audio')
status_text = status ? " (#{status})" : ''
"✅ Enabled#{status_text}"
else
'❌ Not enabled'
end
end
|
#cloudflare_audio_download_status_icon ⇒ Object
General health check icon helpers
256
257
258
|
# File 'app/presenters/crm/video_presenter.rb', line 256
def cloudflare_audio_download_status_icon
video.audio_download_ready? ? seo_status_icon('good') : seo_status_icon('missing')
end
|
#cloudflare_captions_status_display ⇒ Object
Cloudflare-specific methods
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
|
# File 'app/presenters/crm/video_presenter.rb', line 113
def cloudflare_captions_status_display
return 'Not available' unless video.is_cloudflare?
missing = video.missing_cloudflare_captions
missing_text = if missing.any?
" | #{h.tag.span("⚠️ Missing: #{missing.pluck(:name).join(', ')}", class: 'text-warning')}"
else
''
end
if video.cloudflare_captions_status.present?
status = video.cloudflare_captions_status
if status
parts = []
parts << '✅ Available'
parts << "Language: #{status['language']}" if status['language']
parts << "Label: #{status['label']}" if status['label']
parts << "Status: #{status['status']}" if status['status']
parts << "Generated: #{status['generated'] ? 'Yes' : 'No'}" if status.key?('generated')
"#{parts.join(' | ')}#{missing_text}".html_safe
else
"✅ Available#{missing_text}".html_safe
end
else
'❌ Not uploaded'
end
end
|
#cloudflare_default_download_display ⇒ Object
202
203
204
205
206
207
208
209
210
|
# File 'app/presenters/crm/video_presenter.rb', line 202
def cloudflare_default_download_display
if video.has_default_download?
status = video.cloudflare_download_status('default')
status_text = status ? " (#{status})" : ''
"✅ Enabled#{status_text}"
else
'❌ Not enabled'
end
end
|
#cloudflare_dimensions_display ⇒ Object
166
167
168
169
170
171
172
173
|
# File 'app/presenters/crm/video_presenter.rb', line 166
def cloudflare_dimensions_display
return 'Not available' unless video.is_cloudflare?
dimensions = video.cloudflare_input_dimensions
return 'Not available' unless dimensions
"#{dimensions[:width]} × #{dimensions[:height]}"
end
|
#cloudflare_duration_display ⇒ Object
185
186
187
188
189
190
191
192
|
# File 'app/presenters/crm/video_presenter.rb', line 185
def cloudflare_duration_display
return 'Not available' unless video.is_cloudflare?
duration = video.cloudflare_duration
return 'Not available' unless duration
format_duration(duration)
end
|
#cloudflare_file_size_display ⇒ Object
155
156
157
158
159
160
161
162
163
164
|
# File 'app/presenters/crm/video_presenter.rb', line 155
def cloudflare_file_size_display
return 'Not available' unless video.is_cloudflare?
size = video.cloudflare_file_size
return 'Not available' unless size
size_mb = (size / 1024.0 / 1024.0).round(1)
"#{size_mb} MB"
end
|
#cloudflare_ready_status_display ⇒ Object
175
176
177
178
179
180
181
182
183
|
# File 'app/presenters/crm/video_presenter.rb', line 175
def cloudflare_ready_status_display
return 'Not available' unless video.is_cloudflare?
if video.cloudflare_ready_to_stream?
'✅ Ready to stream'
else
'⏳ Processing'
end
end
|
#cloudflare_status_display ⇒ Object
142
143
144
145
146
147
148
149
150
151
152
153
|
# File 'app/presenters/crm/video_presenter.rb', line 142
def cloudflare_status_display
return 'Not available' unless video.is_cloudflare?
status = video.cloudflare_status
return 'Not available' unless status
parts = []
parts << "State: #{status['state']}" if status['state']
parts << "#{status['pctComplete']}% complete" if status['pctComplete']
parts << status['errorReasonText'] if status['errorReasonText']
parts.join(' | ')
end
|
#cloudflare_uid_display ⇒ Object
28
29
30
31
32
33
34
35
36
37
|
# File 'app/presenters/crm/video_presenter.rb', line 28
def cloudflare_uid_display
return if video.cloudflare_uid.blank?
h.link_to_unless(
video.cloudflare_uid.blank?,
h.fa_icon('arrow-up-right-from-square', text: video.cloudflare_uid),
"https://dash.cloudflare.com/79b7f58cf035093b5ad11747df30369a/stream/videos/#{video.cloudflare_uid}",
target: '_blank', rel: 'noopener'
)
end
|
#cloudflare_vtt_upload_status_display ⇒ Object
194
195
196
197
198
199
200
|
# File 'app/presenters/crm/video_presenter.rb', line 194
def cloudflare_vtt_upload_status_display
if video.has_polished_vtt?
'✅ Available (generated from transcript)'
else
'❌ Not available'
end
end
|
#cms_url_display ⇒ Object
50
51
52
53
54
55
56
57
58
|
# File 'app/presenters/crm/video_presenter.rb', line 50
def cms_url_display
h.link_to(
cms_url,
cms_url,
target: '_blank',
rel: 'noopener'
)
end
|
#expanded_description_badge_class ⇒ Object
320
321
322
323
324
325
326
|
# File 'app/presenters/crm/video_presenter.rb', line 320
def expanded_description_badge_class
case seo_check.expanded_description_quality_status
when 'good' then 'badge bg-success'
when 'poor' then 'badge bg-warning'
else 'badge bg-danger'
end
end
|
#expanded_description_status_icon ⇒ Object
251
252
253
|
# File 'app/presenters/crm/video_presenter.rb', line 251
def expanded_description_status_icon
seo_status_icon(seo_check.expanded_description_quality_status)
end
|
304
305
306
307
308
309
310
|
# File 'app/presenters/crm/video_presenter.rb', line 304
def meta_description_badge_class
case seo_check.meta_description_quality_status
when 'good' then 'badge bg-success'
when 'poor' then 'badge bg-warning'
else 'badge bg-danger'
end
end
|
243
244
245
|
# File 'app/presenters/crm/video_presenter.rb', line 243
def meta_description_status_icon
seo_status_icon(seo_check.meta_description_quality_status)
end
|
296
297
298
299
300
301
302
|
# File 'app/presenters/crm/video_presenter.rb', line 296
def meta_title_badge_class
case seo_check.meta_title_quality_status
when 'good' then 'badge bg-success'
when 'poor' then 'badge bg-warning'
else 'badge bg-danger'
end
end
|
239
240
241
|
# File 'app/presenters/crm/video_presenter.rb', line 239
def meta_title_status_icon
seo_status_icon(seo_check.meta_title_quality_status)
end
|
#original_transcript_data ⇒ Object
Alias for Video#original_transcript_data
110
|
# File 'app/presenters/crm/video_presenter.rb', line 110
delegate :original_transcript_data, to: :video
|
60
61
62
63
64
65
66
67
|
# File 'app/presenters/crm/video_presenter.rb', line 60
def related_sources
Source.where(
'name LIKE ? OR name LIKE ? OR description LIKE ?',
video.title.present? ? "%#{video.title}%" : nil,
video.youtube_title.present? ? "%#{video.youtube_title}%" : nil,
video.youtube_id.present? ? "%#{video.youtube_id}%" : nil
)
end
|
#sentences_data ⇒ Object
Alias for Video#sentences_data
106
|
# File 'app/presenters/crm/video_presenter.rb', line 106
delegate :sentences_data, to: :video
|
#seo_check ⇒ Object
223
224
225
|
# File 'app/presenters/crm/video_presenter.rb', line 223
def seo_check
@seo_check ||= VideoSeoCheckService.new(video)
end
|
#seo_status_icon(status) ⇒ Object
228
229
230
231
232
233
234
235
236
237
|
# File 'app/presenters/crm/video_presenter.rb', line 228
def seo_status_icon(status)
case status
when 'good'
h.fa_icon('check-circle', class: 'text-success')
when 'poor'
h.fa_icon('exclamation-triangle', class: 'text-warning')
else
h.fa_icon('ban', class: 'text-danger')
end
end
|
#structured_transcript_json ⇒ Object
Alias for Video#structured_transcript_json
100
|
# File 'app/presenters/crm/video_presenter.rb', line 100
delegate :structured_transcript_json, to: :video
|
Structured transcript methods
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
# File 'app/presenters/crm/video_presenter.rb', line 71
def structured_transcript_metadata_display
return nil if video.structured_transcript_json.blank?
metadata = video.structured_transcript_metadata
return nil if metadata.empty?
parts = []
parts << "ID: #{metadata['id']}" if metadata['id'].present?
if metadata['confidence'].present?
confidence_percentage = (metadata['confidence'] * 100).round(1)
parts << "Confidence: #{confidence_percentage}%"
end
parts << "Duration: #{format_duration(metadata['audio_duration'])}" if metadata['audio_duration'].present?
parts.join(' | ')
end
|
#structured_transcript_paragraphs ⇒ Object
Alias for Video#structured_transcript_paragraphs
108
|
# File 'app/presenters/crm/video_presenter.rb', line 108
delegate :structured_transcript_paragraphs, to: :video
|
#structured_transcript_paragraphs_count ⇒ Object
94
95
96
97
98
|
# File 'app/presenters/crm/video_presenter.rb', line 94
def structured_transcript_paragraphs_count
return nil if video.structured_transcript_json.blank?
video.structured_transcript_paragraphs.length
end
|
#structured_transcript_status_icon ⇒ Object
264
265
266
|
# File 'app/presenters/crm/video_presenter.rb', line 264
def structured_transcript_status_icon
video.has_structured_transcript_json? ? seo_status_icon('good') : seo_status_icon('missing')
end
|
312
313
314
315
316
317
318
|
# File 'app/presenters/crm/video_presenter.rb', line 312
def
case seo_check.
when 'good' then 'badge bg-success'
when 'poor' then 'badge bg-warning'
else 'badge bg-danger'
end
end
|
247
248
249
|
# File 'app/presenters/crm/video_presenter.rb', line 247
def
seo_status_icon(seo_check.)
end
|
#transcript_badge_class ⇒ Object
273
274
275
276
277
278
279
280
281
|
# File 'app/presenters/crm/video_presenter.rb', line 273
def transcript_badge_class
return 'badge bg-secondary opacity-75' if video.video_has_no_spoken_words?
case transcript_quality_status
when 'good' then 'badge bg-success'
when 'poor' then 'badge bg-warning'
else 'badge bg-danger'
end
end
|
#transcript_quality_status ⇒ Object
328
329
330
331
332
333
|
# File 'app/presenters/crm/video_presenter.rb', line 328
def transcript_quality_status
return 'missing' if video.transcript.blank?
return 'poor' if video.transcript.length < 100
'good'
end
|
283
284
285
286
287
288
289
290
291
292
293
294
|
# File 'app/presenters/crm/video_presenter.rb', line 283
def transcript_tooltip
return 'No spoken words; transcript not required' if video.video_has_no_spoken_words?
case transcript_quality_status
when 'good'
'Transcript is present and has sufficient content (>100 characters)'
when 'poor'
'Transcript is present but too short (<100 characters)'
else
'Transcript is missing'
end
end
|
#video_tab_options ⇒ Object
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
# File 'app/presenters/crm/video_presenter.rb', line 12
def video_tab_options
tabs = {}
tabs[:video_preview] = { partial: '/crm/videos/video_preview', locals: { vp: self } }
tabs[:general] = { partial: '/crm/videos/general', locals: { vp: self } }
tabs[:web] = { partial: '/crm/videos/web', locals: { vp: self } }
tabs[:youtube] = { partial: '/crm/videos/youtube', locals: { vp: self } }
tabs[:youtube_chapters] = { partial: '/crm/videos/youtube_chapters', locals: { vp: self } }
tabs[:transcript] = { partial: '/crm/videos/transcript', locals: { vp: self } }
tabs[:schema] = { partial: '/crm/videos/schema', locals: { vp: self } }
tabs[:cloudflare] = { partial: '/crm/videos/cloudflare', locals: { vp: self } } if video.is_cloudflare?
tabs[:attachments] = { partial: '/crm/videos/attachments', locals: { vp: self } }
tabs[:associations] = { partial: '/crm/videos/associations', locals: { vp: self } }
tabs[:related] = { partial: '/crm/videos/related', locals: { vp: self } }
tabs
end
|
#vtt_captions_status_icon ⇒ Object
268
269
270
|
# File 'app/presenters/crm/video_presenter.rb', line 268
def vtt_captions_status_icon
video.has_polished_vtt? ? seo_status_icon('good') : seo_status_icon('missing')
end
|
#vtt_original_data ⇒ Object
Alias for Video#vtt_original_data
102
|
# File 'app/presenters/crm/video_presenter.rb', line 102
delegate :vtt_original_data, to: :video
|
#vtt_polished_data ⇒ Object
Alias for Video#vtt_polished_data
104
|
# File 'app/presenters/crm/video_presenter.rb', line 104
delegate :vtt_polished_data, to: :video
|
#youtube_id_display ⇒ Object
39
40
41
42
43
44
45
46
47
48
|
# File 'app/presenters/crm/video_presenter.rb', line 39
def youtube_id_display
return if video.youtube_id.blank?
h.link_to_unless(
video.youtube_id.blank?,
h.fa_icon('arrow-up-right-from-square', text: video.youtube_id),
"https://www.youtube.com/watch?v=#{video.youtube_id}",
target: '_blank', rel: 'noopener'
)
end
|