Class: HLS::Manifest

Inherits:
Object
  • Object
show all
Defined in:
lib/hls/manifest.rb

Overview

Reads an HLS bundle out of an S3-compatible bucket and returns signed-URL playlists ready to hand to a video player.

The Manifest is the read-side counterpart to ApplicationVideo. A profile class hands one to its caller via ‘profile.manifest(path)`, but Manifest can also be used directly with any bucket.

Example:

manifest = CourseVideo.manifest("phlex/forms/overview")
manifest.master_playlist  # => M3u8::Playlist with rewritten URIs
manifest.poster_url       # => pre-signed URL string

‘master_playlist` rewrites variant URIs from ffmpeg’s flat ‘0/index.m3u8` form to a controller-routable `<path>/0.m3u8` shape so the host app’s controller can serve each variant by name.

Defined Under Namespace

Classes: Variant

Constant Summary collapse

DEFAULT_POSTER =
"poster"
MASTER_PLAYLIST =
"index.m3u8"
VARIANT_PLAYLIST =
"index.m3u8"
DEFAULT_VARIANT_URI =
->(path:, variant_index:) {
  "#{::File.basename(path)}/#{variant_index}.m3u8"
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(storage:, path:, segment_duration: 4, variant_uri: nil, cache: nil) ⇒ Manifest

storage

An HLS::Storage adapter (S3, Memory, …). Owns the bucket and the default signing TTL.

path

key prefix where the bundle lives

segment_duration: HLS segment length, drives Variant#duration math

variant_uri

callable taking (path:, variant_index:) and returning the URI string to put in the master playlist for that variant. Default produces ‘<basename(path)>/<index>.m3u8`, which matches a `/videos/*path/:id/:variant` Rails route shape.

cache

an HLS::Cache (or any object responding to ‘fetch(key, &block)`). When set, raw playlists are read through it instead of being fetched on every request. nil disables caching.



42
43
44
45
46
47
48
49
50
# File 'lib/hls/manifest.rb', line 42

def initialize(storage:, path:,
               segment_duration: 4, variant_uri: nil,
               cache: nil)
  @storage          = storage
  @path             = path
  @segment_duration = Integer(segment_duration)
  @variant_uri      = variant_uri || DEFAULT_VARIANT_URI
  @cache            = cache
end

Instance Attribute Details

#cacheObject (readonly)

Returns the value of attribute cache.



27
28
29
# File 'lib/hls/manifest.rb', line 27

def cache
  @cache
end

#pathObject (readonly)

Returns the value of attribute path.



27
28
29
# File 'lib/hls/manifest.rb', line 27

def path
  @path
end

#segment_durationObject (readonly)

Returns the value of attribute segment_duration.



27
28
29
# File 'lib/hls/manifest.rb', line 27

def segment_duration
  @segment_duration
end

#storageObject (readonly)

Returns the value of attribute storage.



27
28
29
# File 'lib/hls/manifest.rb', line 27

def storage
  @storage
end

Instance Method Details

#expires_inObject

Default presigned-URL TTL for this manifest. Reads from the storage adapter so signing config lives in one place.



54
55
56
# File 'lib/hls/manifest.rb', line 54

def expires_in
  storage.signing_ttl
end

#master_playlistObject

Master playlist with variant URIs rewritten from ffmpeg’s ‘<index>/index.m3u8` form to whatever the configured `variant_uri` callable returns. The default produces `<id>/<index>.m3u8` where `<id>` is the last segment of the manifest’s path — relative to the master playlist’s URL, so a player fetching ‘/videos/<path>/<id>.m3u8` resolves the variant URI to `/videos/<path>/<id>/<index>.m3u8`.

Returns a fresh M3u8::Playlist on each call (does not mutate the cached raw playlist).



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/hls/manifest.rb', line 79

def master_playlist
  list = M3u8::Playlist.new
  list.items = raw_master_playlist.items.map do |item|
    rewritten = item.clone
    rewritten.uri = @variant_uri.call(
      path: path,
      variant_index: ::File.dirname(item.uri)
    )
    rewritten
  end
  list
end

#poster_url(name = DEFAULT_POSTER) ⇒ Object

Pre-signed URL for a poster image. With no argument, returns ‘<path>/poster.jpg` (back-compat with the legacy `HLS::Poster` filename). With a name, returns `<path>/<name>.jpg`.



65
66
67
# File 'lib/hls/manifest.rb', line 65

def poster_url(name = DEFAULT_POSTER)
  presigned_url("#{name}.jpg")
end

#presigned_url(*parts, expires_in: self.expires_in) ⇒ Object

Pre-signs an arbitrary key under this manifest’s path.



108
109
110
# File 'lib/hls/manifest.rb', line 108

def presigned_url(*parts, expires_in: self.expires_in)
  object(*parts).presigned_url(:get, expires_in: Integer(expires_in))
end

#variant(variant_path) ⇒ Object

Look up a variant by ffmpeg’s path identifier (“0”, “1”, …).



103
104
105
# File 'lib/hls/manifest.rb', line 103

def variant(variant_path)
  variants.find { |v| v.variant_path == variant_path }
end

#variantsObject

All variants discovered in the master playlist, indexed by ffmpeg’s variant path (‘“0”`, `“1”`, …).



94
95
96
97
98
99
100
# File 'lib/hls/manifest.rb', line 94

def variants
  @variants ||= raw_master_playlist.items.map do |item|
    variant_path = ::File.dirname(item.uri)
    variant_playlist = read_playlist(variant_path, VARIANT_PLAYLIST)
    Variant.new(manifest: self, variant_path: variant_path, items: variant_playlist.items)
  end
end