Class: VideoPosterService

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

Overview

Service class for managing video poster images

Class Method Summary collapse

Class Method Details

.available_poster_images(limit: 20) ⇒ ActiveRecord::Relation

Get available poster images from library

Parameters:

  • limit (Integer) (defaults to: 20)

    Maximum number of images to return

Returns:

  • (ActiveRecord::Relation)

    Collection of suitable poster images



98
99
100
101
102
103
104
# File 'app/services/video_poster_service.rb', line 98

def self.available_poster_images(limit: 20)
  Image.active
       .tagged_with('video-poster')
       .or(Image.active.where(category: 'poster'))
       .order(created_at: :desc)
       .limit(limit)
end

.extract_poster_from_timestamp(video, timestamp_seconds, redirect_path) ⇒ String?

Extract poster from video at specific timestamp

Parameters:

  • video (Video)

    The video to extract poster from

  • timestamp_seconds (Float)

    Timestamp in seconds

  • redirect_path (String)

    Path to redirect to after completion

Returns:

  • (String, nil)

    Job ID if successful, nil otherwise



10
11
12
13
14
15
16
# File 'app/services/video_poster_service.rb', line 10

def self.extract_poster_from_timestamp(video, timestamp_seconds, redirect_path)
  return nil if timestamp_seconds.nil? || timestamp_seconds.negative?
  return nil if timestamp_seconds > video.duration_in_seconds

  job_id = VideoPosterExtractionWorker.perform_async(video.id, timestamp_seconds, redirect_path)
  job_id
end

.poster_url(video) ⇒ String?

Get poster image URL for video

Parameters:

  • video (Video)

    The video to get poster URL for

Returns:

  • (String, nil)

    Poster image URL or nil if no poster



149
150
151
152
153
154
155
156
157
158
159
# File 'app/services/video_poster_service.rb', line 149

def self.poster_url(video)
  return nil unless video.poster_image

  if video.poster_image.respond_to?(:thumbnail_url) && video.poster_image.thumbnail_url.present?
    video.poster_image.thumbnail_url
  elsif video.poster_image.respond_to?(:url) && video.poster_image.url.present?
    video.poster_image.url
  else
    nil
  end
end

.remove_poster(video) ⇒ Hash

Remove current poster from video

Parameters:

  • video (Video)

    The video to remove poster from

Returns:

  • (Hash)

    Result with success status and message



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'app/services/video_poster_service.rb', line 124

def self.remove_poster(video)
  return { success: false, message: 'No poster image to remove' } unless video.poster_image

  begin
    poster_image = video.poster_image
    video.update!(poster_image: nil)
    
    # Purge cache
    video.purge_edge_cache
    
    {
      success: true,
      message: "Poster image '#{poster_image.title}' removed successfully"
    }
  rescue => e
    {
      success: false,
      message: "Failed to remove poster: #{e.message}"
    }
  end
end

.search_poster_images(query, limit: 20) ⇒ ActiveRecord::Relation

Search for images suitable as video posters

Parameters:

  • query (String)

    Search query

  • limit (Integer) (defaults to: 20)

    Maximum number of results

Returns:

  • (ActiveRecord::Relation)

    Search results



110
111
112
113
114
115
116
117
118
119
# File 'app/services/video_poster_service.rb', line 110

def self.search_poster_images(query, limit: 20)
  return available_poster_images(limit: limit) if query.blank?

  Image.active
       .where('title ILIKE ? OR meta_description ILIKE ?', "%#{query}%", "%#{query}%")
       .tagged_with('video-poster')
       .or(Image.active.where('title ILIKE ? OR meta_description ILIKE ?', "%#{query}%", "%#{query}%").where(category: 'poster'))
       .order(created_at: :desc)
       .limit(limit)
end

.select_existing_poster(video, image_id) ⇒ Hash

Select existing image from library as poster

Parameters:

  • video (Video)

    The video to set poster for

  • image_id (Integer)

    The ID of the image to use

Returns:

  • (Hash)

    Result with success status and message



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
90
91
92
93
# File 'app/services/video_poster_service.rb', line 65

def self.select_existing_poster(video, image_id)
  return { success: false, message: 'No image ID provided' } unless image_id

  begin
    image = Image.find(image_id)
    
    # Update video with selected poster
    video.update!(poster_image: image)
    
    # Purge cache
    video.purge_edge_cache
    
    {
      success: true,
      message: "Poster image '#{image.title}' selected successfully",
      image: image
    }
  rescue ActiveRecord::RecordNotFound
    {
      success: false,
      message: 'Selected image not found'
    }
  rescue => e
    {
      success: false,
      message: "Failed to set poster: #{e.message}"
    }
  end
end

.upload_new_poster(video, image_file, user) ⇒ Hash

Upload new poster image for video

Parameters:

  • video (Video)

    The video to set poster for

  • image_file (ActionDispatch::Http::UploadedFile)

    The uploaded image file

  • user (User)

    The user performing the upload

Returns:

  • (Hash)

    Result with success status and message/image



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
54
55
56
57
58
59
# File 'app/services/video_poster_service.rb', line 23

def self.upload_new_poster(video, image_file, user)
  return { success: false, message: 'No image file provided' } unless image_file

  begin
    # Create new image record
    image = Image.new(
      title: "#{video.title} - Poster",
      source: 'video-poster-upload',
      tags: ['video-poster'],
      new_image: image_file
    )

    if image.save
      # Associate with video
      video.update!(poster_image: image)
      
      # Purge cache
      video.purge_edge_cache
      
      {
        success: true,
        message: 'Poster image uploaded successfully',
        image: image
      }
    else
      {
        success: false,
        message: "Failed to save image: #{image.errors.full_messages.join(', ')}"
      }
    end
  rescue => e
    {
      success: false,
      message: "Upload failed: #{e.message}"
    }
  end
end