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



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

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
# 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

  VideoPosterExtractionWorker.perform_async(video.id, timestamp_seconds, redirect_path)
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



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

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
  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



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

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 StandardError => 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



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

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



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

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 StandardError => 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 (currently unused)

Returns:

  • (Hash)

    Result with success status and message/image



22
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
# File 'app/services/video_poster_service.rb', line 22

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 StandardError => e
    {
      success: false,
      message: "Upload failed: #{e.message}"
    }
  end
end