livelist 0.2.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 71f726fd7f7b086ff553ddcbfee959ef777185bbeab869f86426b0dbeb6e59d4
4
+ data.tar.gz: 54c1894d723b52a28ea85f77f631f852497edccf210f025aefe492e5bc583256
5
+ SHA512:
6
+ metadata.gz: 324b514aacc3ebe288319c56c7d89bb1f2a648af3cf907b42ec68c4bed140c1816762e084f32ce7862fb4b2f1c3356a0a453b25a5320dac6ac3e7db4c63fb880
7
+ data.tar.gz: ea1188459a10e9503edfb62995341ee399d29a264111c5f297cc781ee3125aba3d04c380a1c60fe5e30a52a77bcc86349de7daf18274c574248fb7e2b6497eb8
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Livelist
4
+ module Exceptions
5
+ # Livelist::Exceptions::InvalidFormat
6
+ # Throws an error if file has invalid format (if not .m3u8)
7
+ class InvalidFormat < StandardError
8
+ def initialize(message: 'That file has invalid format')
9
+ super(message)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Livelist
4
+ # Livelist::HLS
5
+ module HLS
6
+ def self.current_time(playlist, seconds = 2)
7
+ lines = File
8
+ .readlines(playlist)
9
+ .select { |line| line =~ /EXTINF:[0-9]/ }
10
+ lines.length * seconds
11
+ end
12
+
13
+ # Finish the playlist with a endlist tag
14
+ # @param String playlist
15
+ # @return void
16
+ def self.finish(playlist)
17
+ file = File.read(playlist)
18
+ content = file.gsub(/#EXT-X-PLAYLIST-TYPE:EVENT/, '#EXT-X-PLAYLIST-TYPE:VOD')
19
+ File.open(playlist, 'w') { |f| f.puts content}
20
+ File.open(playlist, 'a') do |file|
21
+ file.puts '#EXT-X-ENDLIST'
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Livelist
4
+ module HLS
5
+ # A pipeline for manipulate playlist
6
+ # using HTTP Livestreaming standards
7
+ class Pipeline
8
+ # @param [String] path
9
+ # @param [Hash] options
10
+ def initialize(path, options = {})
11
+ @path = path
12
+ @options = options
13
+ @playlist = Livelist::Playlist.new(@path, options)
14
+ end
15
+
16
+ # Run the pipeline to HTTP streaming
17
+ # @param [String] segment_name
18
+ # @return [void]
19
+ def run(segment_name)
20
+ create_playlist
21
+ append_segment(segment_name)
22
+ end
23
+
24
+ private
25
+
26
+ # Write the playlist file
27
+ def create_playlist
28
+ @playlist.write unless File.exist? @path
29
+ end
30
+
31
+ # Append a segment to playlist file
32
+ # the segment name can be a URL or a simple file
33
+ #
34
+ # @param [String] segment_name
35
+ def append_segment(segment_name)
36
+ Livelist::Segment
37
+ .new(segment_name, @options[:target_duration].to_f)
38
+ .append(@playlist)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Livelist
4
+ # A Playlist m3u8
5
+ class Playlist
6
+ # @param path [String] where will be the final m3u8
7
+ #
8
+ # @param options [Hash] all options allowed
9
+ # @option options [Integer] :version playlist version defaults 1
10
+ # @option options [Boolean] :allow playlist allow cache defaults false
11
+ # @option options [Integer] :target_duration playlist target duration in seconds defaults 10
12
+ #
13
+ # @raise [Exceptions::InvalidFormat] invalid format if file is not a .m3u8
14
+ def initialize(path, options = {})
15
+ @path = path
16
+ @options = options
17
+ @tag = "#EXTM3U"
18
+ raise Exceptions::InvalidFormat unless File.extname(path) == '.m3u8'
19
+ end
20
+
21
+ def path
22
+ @path
23
+ end
24
+
25
+ # Write the m3u8 file
26
+ def write
27
+ File.open(@path, 'w') do |file|
28
+ [
29
+ @tag,
30
+ type,
31
+ version,
32
+ media_sequence,
33
+ allow_cache?,
34
+ target_duration,
35
+ ].each { |tag| file.puts tag }
36
+ end
37
+ end
38
+
39
+ # Return a string version of m3u8 file
40
+ def to_s
41
+ [
42
+ @tag,
43
+ version,
44
+ media_sequence,
45
+ allow_cache?,
46
+ target_duration
47
+ ].join("\n")
48
+ end
49
+
50
+ private
51
+ def version
52
+ "#EXT-X-VERSION:#{@options[:version] ||= 1}"
53
+ end
54
+
55
+ def allow_cache?
56
+ "#EXT-X-ALLOW-CACHE:#{@options[:allow] ? 'YES' : 'NO'}"
57
+ end
58
+
59
+ def target_duration
60
+ "#EXT-X-TARGETDURATION:#{@options[:target_duration] ||= 10}"
61
+ end
62
+
63
+ def media_sequence
64
+ '#EXT-X-MEDIA-SEQUENCE:0'
65
+ end
66
+
67
+ def type
68
+ "#EXT-X-PLAYLIST-TYPE:#{@options[:type] ||= 'EVENT'}"
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Livelist
4
+ # A Segment to playlist file
5
+ class Segment
6
+ # @param src [String] src can be a URI or a file name
7
+ # @param duration [Float] duration in seconds
8
+ def initialize(src, duration)
9
+ @src = src
10
+ @duration = duration
11
+ end
12
+
13
+ # Append a new segment on playlist file
14
+ #
15
+ # @param [Livelist::Playlist] playlist
16
+ def append(playlist)
17
+ write(playlist) if File.exist? playlist.path
18
+ end
19
+
20
+ # Append a new segment but removing
21
+ # the previous segment from playlist
22
+ #
23
+ # @param [Livelist::Playlist] playlist
24
+ def hls_append(playlist)
25
+ hls_write(playlist) if File.exist? playlist.path
26
+ end
27
+
28
+ private
29
+
30
+ # @param [Livelist::Playlist] playlist
31
+ def write(playlist)
32
+ File.open(playlist.path, 'a') do |file|
33
+ [
34
+ "#EXTINF:#{@duration},",
35
+ @src
36
+ ].each { |line| file.puts line }
37
+ end
38
+ end
39
+
40
+ # @param [Livelist::Playlist] playlist
41
+ def hls_write(playlist)
42
+ append(playlist)
43
+ segment = File.open(playlist.path, 'r') do |file|
44
+ context = file.readlines.map(&:chomp)
45
+ remove_tag_and_segment(context)
46
+ end
47
+ File.open(playlist.path, 'w') { |f| f.puts segment }
48
+ end
49
+
50
+ # @param context [Array] an array containing tag and segment
51
+ # @return [Array] updated array without tag and segment
52
+ def remove_tag_and_segment(context)
53
+ if context.grep(/#EXTINF:[0-9]+/).length > 2
54
+ context.delete_at 5
55
+ context.delete_at 5
56
+ end
57
+ context
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Livelist
4
+ # A Media Sequence for playlist file
5
+ class Sequence
6
+ # @param playlist [Livelist::Playlist] Playlist instance
7
+ def initialize(playlist)
8
+ @playlist = playlist
9
+ @pattern = /#EXT-X-MEDIA-SEQUENCE:[0-9]+/
10
+ end
11
+
12
+ # Writes the new sequence on playlist file
13
+ def add
14
+ file = File.read(@playlist.path)
15
+ content = file.gsub(@pattern, new_sequence_line(sequence_line))
16
+ File.open(@playlist.path, 'w') { |f| f.puts content}
17
+ end
18
+
19
+ private
20
+
21
+ # Return the sequence line adding 1 to the previous value
22
+ # @param line [String]
23
+ # @return [String] the sequence tag with the updated value
24
+ def new_sequence_line(line)
25
+ tag, sequence = line.split(':')
26
+ seq = sequence.strip.to_i + 1
27
+ "#{tag}:#{seq}"
28
+ end
29
+
30
+ # Return the sequence tag based on regex pattern
31
+ # @return [String] sequence tag line
32
+ def sequence_line
33
+ File.open(@playlist.path, 'r') do |file|
34
+ file.find { |line| line =~ @pattern }
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Livelist
4
+ VERSION = '0.2.1'
5
+ end
data/lib/livelist.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'livelist/version'
2
+ Dir[File.dirname(__FILE__) + "/livelist/**/*.rb"].each { |file| require file }
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: livelist
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Vitor Roque
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-10-12 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A simple gem to manipulate m3u8 files
14
+ email:
15
+ - vitor.roquep@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/livelist.rb
21
+ - lib/livelist/exceptions/invalid_format.rb
22
+ - lib/livelist/hls/hls.rb
23
+ - lib/livelist/hls/pipeline.rb
24
+ - lib/livelist/playlist.rb
25
+ - lib/livelist/segment.rb
26
+ - lib/livelist/sequence.rb
27
+ - lib/livelist/version.rb
28
+ homepage: https://github.com/roqueando/livelist
29
+ licenses:
30
+ - MIT
31
+ metadata:
32
+ allowed_push_host: https://rubygems.org
33
+ homepage_uri: https://github.com/roqueando/livelist
34
+ source_code_uri: https://github.com/roqueando/livelist
35
+ changelog_uri: https://github.com/roqueando/livelist
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 2.7.0
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubygems_version: 3.2.15
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: A simple gem to manipulate m3u8 files
55
+ test_files: []