Class: HLS::State
- Inherits:
-
Object
- Object
- HLS::State
- Defined in:
- lib/hls/state.rb
Overview
Sidecar state for an encoded bundle. Records the input digest, rendition list, and per-file upload status so a re-run can skip work that’s already done and a crashed run can resume from the last successful upload.
Lives at ‘<output>/.hls-state.json` next to the encoded bundle.
Defined Under Namespace
Classes: CorruptError
Constant Summary collapse
- FILENAME =
".hls-state.json"
Instance Attribute Summary collapse
-
#path ⇒ Object
readonly
Returns the value of attribute path.
Class Method Summary collapse
- .default_data ⇒ Object
- .load(output_dir) ⇒ Object
-
.read(path) ⇒ Object
Reads state JSON from disk.
Instance Method Summary collapse
- #config_digest ⇒ Object
-
#encoded?(input_digest:, config_digest:) ⇒ Boolean
Has this output already been encoded for the given input AND profile config? A change to either invalidates the encode — bumping ‘audio_bitrate` or adding a rendition has to re-run ffmpeg even if the input file is byte-identical.
- #encoded_at ⇒ Object
-
#initialize(path:, data:) ⇒ State
constructor
A new instance of State.
- #input_digest ⇒ Object
- #profile ⇒ Object
- #record_encode(input_digest:, config_digest:, profile:, renditions:) ⇒ Object
- #record_upload(relative_key:, digest:, etag: nil) ⇒ Object
- #renditions ⇒ Object
- #save ⇒ Object
- #to_h ⇒ Object
-
#uploaded?(relative_key:, digest:) ⇒ Boolean
Has the file at relative_key already been uploaded with the given digest?.
- #uploads ⇒ Object
Constructor Details
#initialize(path:, data:) ⇒ State
Returns a new instance of State.
51 52 53 54 |
# File 'lib/hls/state.rb', line 51 def initialize(path:, data:) @path = Pathname.new(path) @data = data end |
Instance Attribute Details
#path ⇒ Object (readonly)
Returns the value of attribute path.
49 50 51 |
# File 'lib/hls/state.rb', line 49 def path @path end |
Class Method Details
.default_data ⇒ Object
38 39 40 41 42 43 44 45 46 47 |
# File 'lib/hls/state.rb', line 38 def self.default_data { input_digest: nil, config_digest: nil, profile: nil, renditions: [], encoded_at: nil, uploads: {} } end |
.load(output_dir) ⇒ Object
19 20 21 22 |
# File 'lib/hls/state.rb', line 19 def self.load(output_dir) path = Pathname.new(output_dir).join(FILENAME) new(path: path, data: read(path)) end |
.read(path) ⇒ Object
Reads state JSON from disk. Returns ‘default_data` for a missing file (the common first-run case). Raises CorruptError when the file exists but isn’t parseable — silently re-encoding on corruption would be expensive and surprising; explicit failure lets the caller decide whether to delete the sidecar and retry.
29 30 31 32 33 34 35 36 |
# File 'lib/hls/state.rb', line 29 def self.read(path) return default_data unless path.exist? raw = JSON.parse(path.read, symbolize_names: true) default_data.merge(raw) rescue JSON::ParserError => e raise CorruptError, "state file at #{path} is not valid JSON: #{e.}" end |
Instance Method Details
#config_digest ⇒ Object
57 |
# File 'lib/hls/state.rb', line 57 def config_digest = @data[:config_digest] |
#encoded?(input_digest:, config_digest:) ⇒ Boolean
Has this output already been encoded for the given input AND profile config? A change to either invalidates the encode —bumping ‘audio_bitrate` or adding a rendition has to re-run ffmpeg even if the input file is byte-identical.
67 68 69 70 71 |
# File 'lib/hls/state.rb', line 67 def encoded?(input_digest:, config_digest:) !@data[:encoded_at].nil? && @data[:input_digest] == input_digest && @data[:config_digest] == config_digest end |
#encoded_at ⇒ Object
60 |
# File 'lib/hls/state.rb', line 60 def encoded_at = @data[:encoded_at] |
#input_digest ⇒ Object
56 |
# File 'lib/hls/state.rb', line 56 def input_digest = @data[:input_digest] |
#profile ⇒ Object
58 |
# File 'lib/hls/state.rb', line 58 def profile = @data[:profile] |
#record_encode(input_digest:, config_digest:, profile:, renditions:) ⇒ Object
79 80 81 82 83 84 85 86 87 |
# File 'lib/hls/state.rb', line 79 def record_encode(input_digest:, config_digest:, profile:, renditions:) @data[:input_digest] = input_digest @data[:config_digest] = config_digest @data[:profile] = profile @data[:renditions] = renditions @data[:encoded_at] = Time.now.utc.iso8601 # Content has changed; previous upload records are stale. @data[:uploads] = {} end |
#record_upload(relative_key:, digest:, etag: nil) ⇒ Object
89 90 91 92 93 94 95 |
# File 'lib/hls/state.rb', line 89 def record_upload(relative_key:, digest:, etag: nil) @data[:uploads][relative_key.to_sym] = { digest: digest, etag: etag, uploaded_at: Time.now.utc.iso8601 } end |
#renditions ⇒ Object
59 |
# File 'lib/hls/state.rb', line 59 def renditions = @data[:renditions] |
#save ⇒ Object
97 98 99 100 |
# File 'lib/hls/state.rb', line 97 def save path.parent.mkpath path.write(JSON.pretty_generate(@data)) end |
#to_h ⇒ Object
102 103 104 |
# File 'lib/hls/state.rb', line 102 def to_h @data.dup end |
#uploaded?(relative_key:, digest:) ⇒ Boolean
Has the file at relative_key already been uploaded with the given digest?
74 75 76 77 |
# File 'lib/hls/state.rb', line 74 def uploaded?(relative_key:, digest:) upload = @data[:uploads][relative_key.to_sym] !upload.nil? && upload[:digest] == digest end |
#uploads ⇒ Object
61 |
# File 'lib/hls/state.rb', line 61 def uploads = @data[:uploads] |