Class: VideoProcessing::TranscriptProcessor

Inherits:
Object
  • Object
show all
Defined in:
app/services/video_processing/transcript_processor.rb

Overview

Service object for writing transcript and SEO data back to a Video record
from an AssemblyAI transcription result, and for managing the audio
extraction upload lifecycle.

Extracted from Video to move the flog-158 update_transcript_data
orchestration (conditional field updates across transcript, SEO metadata,
structured JSON, and duration) out of the model. Pure read accessors that
read transcript / structured_transcript_json stay on Video.

All methods return a Result struct following the service-architecture
convention — never a bare boolean, hash, or record.

Examples:

result = VideoProcessing::TranscriptProcessor.new(video).update_transcript_data(result)
result.success? # => true
result.updated_attributes # => [:transcript, :meta_title]

See Also:

Defined Under Namespace

Classes: Result

Instance Method Summary collapse

Constructor Details

#initialize(video) ⇒ TranscriptProcessor

Initialize the transcript processor.

Parameters:

  • video (Video)

    the Video record this service will operate on



47
48
49
# File 'app/services/video_processing/transcript_processor.rb', line 47

def initialize(video)
  @video = video
end

Instance Method Details

#create_audio_extraction_upload(audio_file_path) ⇒ Result

Create an audio extraction upload for the video, replacing any existing
one. Used by the transcription pipeline to store an MP3 extract.
Creates the new upload first so a failure doesn't leave the video
with no audio extraction.

Parameters:

  • audio_file_path (String)

    path to the audio file.

Returns:

  • (Result)

    with upload set to the newly created Upload.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'app/services/video_processing/transcript_processor.rb', line 98

def create_audio_extraction_upload(audio_file_path)
  old_upload = @video.audio_extraction_upload

  new_upload = Upload.uploadify(
    audio_file_path,
    'audio_extraction',
    @video,
    "#{@video.title.parameterize}-audio.mp3"
  )

  old_upload&.destroy
  Result.new(success: true, upload: new_upload)
rescue StandardError => e
  Result.new(success: false, upload: nil, error: e.message)
end

#update_transcript_data(result) ⇒ Result

Update the video's transcript, SEO metadata, structured transcript JSON,
and duration from an AssemblyAI result hash. Only fills in fields that
are blank on the video — existing values are preserved.

Parameters:

  • result (Hash)

    the transcription result, with optional keys:
    :transcript, :assemblyai_transcript_id, :structured_transcript_json,
    :seo_content (a hash with 'meta_title', 'meta_description',
    'expanded_description'), and :duration_in_seconds.

Returns:



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'app/services/video_processing/transcript_processor.rb', line 60

def update_transcript_data(result)
  Rails.logger.info "Updating transcript data with: #{result.keys}"

  update_hash = {}
  update_hash[:transcript] = result[:transcript] if result[:transcript].present?
  update_hash[:assemblyai_transcript_id] = result[:assemblyai_transcript_id] if result[:assemblyai_transcript_id].present?
  update_hash[:structured_transcript_json] = result[:structured_transcript_json] if result[:structured_transcript_json].present? && !@video.has_structured_transcript_json?

  if result[:seo_content].present?
    seo_content = result[:seo_content]
    update_hash[:meta_title] = seo_content['meta_title'] if @video.meta_title.blank? && seo_content['meta_title'].present?
    update_hash[:meta_description] = seo_content['meta_description'] if @video.meta_description.blank? && seo_content['meta_description'].present?
    update_hash[:expanded_description] = seo_content['expanded_description'] if @video.expanded_description.blank? && seo_content['expanded_description'].present?
  else
    Rails.logger.warn 'No SEO content found in result'
  end

  update_hash[:duration_in_seconds] = result[:duration_in_seconds] if @video.duration_in_seconds.blank? && result[:duration_in_seconds].present?

  if update_hash.any?
    @video.update!(update_hash)
    Rails.logger.info "Video updated successfully with: #{update_hash.keys}"
    Result.new(success: true, updated_attributes: update_hash.keys)
  else
    Rails.logger.info 'No updates needed'
    Result.new(success: true, updated_attributes: [])
  end
rescue StandardError => e
  Result.new(success: false, updated_attributes: [], error: e.message)
end