Class: VideoProcessing::VideoTranslationService

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

Overview

Service to translate video captions and transcripts using AssemblyAI Speech Understanding API
Supports translation to Quebec French (fr-CA), Mexican Spanish (es-MX), and Polish (pl)

Uses native AssemblyAI translation for better quality and efficiency:

  • Single API call translates to all languages at once
  • Native utterance matching preserves timing
  • Falls back to LLM Gateway for individual caption translation if needed

Constant Summary collapse

SUPPORTED_LOCALES =

Supported target locales with AssemblyAI language codes

{
  'fr-CA' => { language: 'French', variant: 'Quebec French / Canadian French', name: 'Français', assemblyai_code: 'fr' },
  'es-MX' => { language: 'Spanish', variant: 'Mexican Spanish', name: 'Español', assemblyai_code: 'es' },
  'pl' => { language: 'Polish', variant: 'Standard Polish', name: 'Polski', assemblyai_code: 'pl' }
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(video) ⇒ VideoTranslationService

Returns a new instance of VideoTranslationService.



21
22
23
# File 'app/services/video_processing/video_translation_service.rb', line 21

def initialize(video)
  @video = video
end

Instance Attribute Details

#videoObject (readonly)

Returns the value of attribute video.



19
20
21
# File 'app/services/video_processing/video_translation_service.rb', line 19

def video
  @video
end

Class Method Details

.available_translations(video) ⇒ Object

Get available translations for a video



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'app/services/video_processing/video_translation_service.rb', line 76

def self.available_translations(video)
  return [] unless video.structured_transcript_json.present?

  translations = []
  SUPPORTED_LOCALES.each_key do |locale|
    vtt_key = "vtt_#{locale.underscore}"
    next unless video.structured_transcript_json[vtt_key].present?

    translations << {
      locale: locale,
      name: SUPPORTED_LOCALES[locale][:name],
      caption_count: video.structured_transcript_json[vtt_key].length
    }
  end

  translations
end

Instance Method Details

#translate_captions(locales = SUPPORTED_LOCALES.keys, &progress_callback) {|83, "Translating to #{valid_locales.length} languages..."| ... } ⇒ Hash

Translate VTT captions to specified locales using AssemblyAI LLM Gateway
Translates the POLISHED captions, preserving our terminology and grammatical fixes

Parameters:

  • locales (Array<String, Symbol>) (defaults to: SUPPORTED_LOCALES.keys)

    Target locales (e.g., ['fr-CA', 'es-MX', 'pl'])

  • progress_callback (Proc)

    Optional callback for progress updates

Yields:

  • (83, "Translating to #{valid_locales.length} languages...")

Returns:

  • (Hash)

    Results for each locale



30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'app/services/video_processing/video_translation_service.rb', line 30

def translate_captions(locales = SUPPORTED_LOCALES.keys, &progress_callback)
  Rails.logger.debug("Translating captions", video_id: @video.id, locale_count: locales&.size)

  return { success: false, error: 'No polished VTT available' } unless @video.has_polished_vtt?

  # Filter to valid locales
  valid_locales = locales.map(&:to_s).select { |l| SUPPORTED_LOCALES.key?(l) }

  yield(83, "Translating to #{valid_locales.length} languages...") if progress_callback

  # Translate polished captions using LLM Gateway
  translate_polished_captions(valid_locales, &progress_callback)
end

#translate_transcript(locales = SUPPORTED_LOCALES.keys) ⇒ Hash

Translate plain transcript text to specified locales

Parameters:

  • locales (Array<String, Symbol>) (defaults to: SUPPORTED_LOCALES.keys)

    Target locales

Returns:

  • (Hash)

    Results for each locale



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'app/services/video_processing/video_translation_service.rb', line 47

def translate_transcript(locales = SUPPORTED_LOCALES.keys)
  Rails.logger.debug("Translating transcript", video_id: @video.id, locale_count: locales&.size)

  return { success: false, error: 'No transcript available' } if @video.transcript.blank?

  results = {}
  locales.each do |locale|
    locale_str = locale.to_s
    next unless SUPPORTED_LOCALES.key?(locale_str)

    begin
      translated_text = translate_text(@video.transcript, locale_str)
      @video.send(:"transcript_#{locale_str.underscore}=", translated_text)
      results[locale_str] = { success: true, length: translated_text.length }
    rescue StandardError => e
      Rails.logger.error "Failed to translate transcript to #{locale_str}: #{e.message}"
      results[locale_str] = { success: false, error: e.message }
    end

    sleep(0.5) unless locale == locales.last
  end

  # Save the video with translated transcripts
  @video.save! if results.values.any? { |r| r[:success] }

  results
end