m3u8 1.8.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 395231edf55669bc7358629d116d5f7486a74bf1e7c5f6f9287497b758178511
4
- data.tar.gz: f1592e2cd9cc9d175e3f06c7b330baffea7271ff1c153895865c35ed96e1c17e
3
+ metadata.gz: 00157be29743342bb6156539b9fca6a1e67ae9ae60e74cab04eebeda10a888ec
4
+ data.tar.gz: f82b069b8fef0ce72694b11640431b25ea04caa7616ee69ab6c56e9f9029fdc3
5
5
  SHA512:
6
- metadata.gz: 7e0c36b5d0afbd29e1d822c020e0302c052c1051c2637e90795772165f9ca5f2a452ad9e4da593d895f88dbf3527d831829dc741dfbb9d2b88b9f8223f02b3c0
7
- data.tar.gz: d7631c7d2cf13392f4a317c00f0cf7f90097695da58d20cdb5a76d66db01ce2e93643c48cfbf30d40f9b20260c793ffb9e430f0aea0f99c5c25d8c9c253fca93
6
+ metadata.gz: 659a5a703d0a00887b9dc724bb252aeb56fb44010756772f8667b62ebd77b34b6b3a4f647c0c6aaf46cdfbfafbdf5173b59db2f1a84efa34fd4c5b4aafbab732
7
+ data.tar.gz: 07a539694c4231895fd0f7eebfb18f860c0dbbcbdd0f63e31b50ce7485937dc84fcac198ad443f9f6591bed7c17d32839bae7aba3904e452781e98520f21153c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ **1.8.1**
2
+
3
+ * Merged pull request #59 from [marocchino](https://github.com/marocchino).
4
+ Fixes floating-point formatting issue (#58).
5
+
6
+ ***
7
+
1
8
  **1.8.0**
2
9
 
3
10
  * Standardized all item classes to use class-level `self.parse` methods,
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'bigdecimal'
4
+
3
5
  module M3u8
4
6
  # Shared helpers for formatting HLS tag attributes
5
7
  module AttributeFormatter
@@ -26,5 +28,20 @@ module M3u8
26
28
  def boolean_format(key, value)
27
29
  "#{key}=#{value == true ? 'YES' : 'NO'}" unless value.nil?
28
30
  end
31
+
32
+ # Format a decimal attribute, ensuring it formatted as a floating-point
33
+ # number or integer
34
+ # @param number [Float, Integer, nil] the number to format
35
+ # @return [String, nil] formatted string or nil when value is nil
36
+ def decimal_format(number)
37
+ case number
38
+ when nil
39
+ nil
40
+ when Float
41
+ BigDecimal(number).to_s('F')
42
+ else
43
+ number.to_s
44
+ end
45
+ end
29
46
  end
30
47
  end
@@ -131,8 +131,8 @@ module M3u8
131
131
  quoted_format('CLASS', class_name),
132
132
  %(START-DATE="#{start_date}"),
133
133
  quoted_format('END-DATE', end_date),
134
- unquoted_format('DURATION', duration),
135
- unquoted_format('PLANNED-DURATION', planned_duration),
134
+ unquoted_format('DURATION', decimal_format(duration)),
135
+ unquoted_format('PLANNED-DURATION', decimal_format(planned_duration)),
136
136
  client_attributes_format,
137
137
  interstitial_formats,
138
138
  unquoted_format('SCTE35-CMD', scte35_cmd),
@@ -147,7 +147,7 @@ module M3u8
147
147
 
148
148
  client_attributes.map do |attribute|
149
149
  value = attribute.last
150
- fmt = decimal?(value) ? value : %("#{value}")
150
+ fmt = decimal?(value) ? decimal_format(value) : %("#{value}")
151
151
  "#{attribute.first}=#{fmt}"
152
152
  end
153
153
  end
@@ -166,8 +166,8 @@ module M3u8
166
166
  def interstitial_formats
167
167
  [quoted_format('X-ASSET-URI', asset_uri),
168
168
  quoted_format('X-ASSET-LIST', asset_list),
169
- unquoted_format('X-RESUME-OFFSET', resume_offset),
170
- unquoted_format('X-PLAYOUT-LIMIT', playout_limit),
169
+ unquoted_format('X-RESUME-OFFSET', decimal_format(resume_offset)),
170
+ unquoted_format('X-PLAYOUT-LIMIT', decimal_format(playout_limit)),
171
171
  quoted_format('X-RESTRICT', restrict),
172
172
  quoted_format('X-SNAP', snap),
173
173
  quoted_format('X-TIMELINE-OCCUPIES', timeline_occupies),
@@ -45,7 +45,7 @@ module M3u8
45
45
  private
46
46
 
47
47
  def formatted_attributes
48
- [unquoted_format('DURATION', duration),
48
+ [unquoted_format('DURATION', decimal_format(duration)),
49
49
  quoted_format('URI', uri),
50
50
  independent_format,
51
51
  quoted_format('BYTERANGE', byterange),
@@ -5,6 +5,7 @@ module M3u8
5
5
  # optionally allowing an EXT-X-BYTERANGE tag to be set.
6
6
  class SegmentItem
7
7
  include M3u8
8
+ include AttributeFormatter
8
9
 
9
10
  # @return [Float, nil] segment duration in seconds
10
11
  # @return [String, nil] segment URI
@@ -32,7 +33,7 @@ module M3u8
32
33
  # Render as an m3u8 EXTINF tag with segment URI.
33
34
  # @return [String]
34
35
  def to_s
35
- "#EXTINF:#{duration},#{comment}#{byterange_format}" \
36
+ "#EXTINF:#{decimal_format(duration)},#{comment}#{byterange_format}" \
36
37
  "\n#{date_format}#{segment}"
37
38
  end
38
39
 
data/lib/m3u8/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  # M3u8 provides parsing, generation, and validation of m3u8 playlists
4
4
  module M3u8
5
- VERSION = '1.8.0'
5
+ VERSION = '1.8.1'
6
6
  end
data/m3u8.gemspec CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.files = `git ls-files -z`.split("\x0")
23
23
  .grep_v(/\A(CLAUDE|AGENTS)\.md\z/)
24
24
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
25
+ spec.add_dependency 'bigdecimal'
25
26
  spec.require_paths = ['lib']
26
27
 
27
28
  end
@@ -172,6 +172,26 @@ describe M3u8::DateRangeItem do
172
172
  expect(item.to_s).to eq(expected)
173
173
  end
174
174
 
175
+ it 'should render small float values as floating-point number instead of scientific notation' do
176
+ options = { id: 'test_id', start_date: '2014-03-05T11:15:00Z',
177
+ duration: 0.00001,
178
+ planned_duration: 0.00002,
179
+ resume_offset: 0.00003,
180
+ playout_limit: 0.00004,
181
+ client_attributes: { 'X-CUSTOM' => 0.00005 } }
182
+ item = described_class.new(options)
183
+
184
+ expected = '#EXT-X-DATERANGE:ID="test_id",' \
185
+ 'START-DATE="2014-03-05T11:15:00Z",' \
186
+ 'DURATION=0.00001,' \
187
+ 'PLANNED-DURATION=0.00002,' \
188
+ 'X-CUSTOM=0.00005,' \
189
+ 'X-RESUME-OFFSET=0.00003,' \
190
+ 'X-PLAYOUT-LIMIT=0.00004'
191
+
192
+ expect(item.to_s).to eq(expected)
193
+ end
194
+
175
195
  it 'should ignore optional attributes' do
176
196
  options = { id: 'test_id', start_date: '2014-03-05T11:15:00Z' }
177
197
  item = described_class.new(options)
@@ -56,5 +56,12 @@ describe M3u8::PartItem do
56
56
  expected = '#EXT-X-PART:DURATION=1.5,URI="part1.ts"'
57
57
  expect(item.to_s).to eq(expected)
58
58
  end
59
+
60
+ it 'should render small float values as floating-point number instead of scientific notation' do
61
+ options = { duration: 0.00001, uri: 'part1.ts' }
62
+ item = described_class.new(options)
63
+ expected = '#EXT-X-PART:DURATION=0.00001,URI="part1.ts"'
64
+ expect(item.to_s).to eq(expected)
65
+ end
59
66
  end
60
67
  end
@@ -89,4 +89,15 @@ describe M3u8::SegmentItem do
89
89
  'segment.aac'
90
90
  expect(output).to eq expected
91
91
  end
92
+
93
+ it 'converts very small durations to floating point' do
94
+ time = Time.iso8601('2020-11-25T20:27:00Z')
95
+ hash = { duration: 0.000001, segment: 'test.ts', program_date_time: time }
96
+ item = M3u8::SegmentItem.new(hash)
97
+ output = item.to_s
98
+ expected = "#EXTINF:0.000001,\n" \
99
+ "#EXT-X-PROGRAM-DATE-TIME:2020-11-25T20:27:00Z\n" \
100
+ 'test.ts'
101
+ expect(output).to eq expected
102
+ end
92
103
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: m3u8
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 1.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seth Deckard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-07 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2026-03-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bigdecimal
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: Generate and parse m3u8 playlists for HTTP Live Streaming (HLS).
14
28
  email:
15
29
  - seth@deckard.me