subshifter 0.0.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d13c057ce4824a8dbb9e009e257d9a0e8423a1c5859685a8f789d5802e339bdb
4
+ data.tar.gz: 5e256de24659debae5352673f36a2420e6d70f5aea02e6201366bcc1d5de4a2d
5
+ SHA512:
6
+ metadata.gz: f2f73225262802c3b7c14111361bb905a94a72179904c31afd7d40c9893baaf3719a62704c36d9d7c0671900657ce880ef57a19e75c627056fe35a5d1fca5a27
7
+ data.tar.gz: e2e2b34bcc44173232f56264332b0dad33a2499ac5b8a95f96014d35a168e8e78fa0d3d39c923ec6aa51f76ac0390a46e3f34595462a981f880d62ebd2c45f23
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift "#{File.dirname(__FILE__)}/lib"
4
+ require "subtitle"
5
+
6
+ begin
7
+ shift = Float(ARGV.shift)
8
+ rescue ArgumentError, TypeError
9
+ puts <<-HELP
10
+ Usage: subshift.rb <duration> [files]
11
+ <duration> is in seconds and can be decimal.
12
+ Positive number – subtitles will appear later.
13
+ Negative number – subtitles will appear sooner.
14
+
15
+ Example: ruby subshift.rb -10.7 subtitle.srt
16
+ HELP
17
+ exit
18
+ end
19
+
20
+ subtitle = Subtitle.new $<.read
21
+ subtitle.shift! shift
22
+
23
+ print subtitle
@@ -0,0 +1,39 @@
1
+ require 'error/negative_time_error'
2
+ require 'error/no_time_error'
3
+
4
+ class Duration
5
+ include Comparable
6
+
7
+ def initialize seconds
8
+ raise NoTimeError.new "Time must be set." if seconds.nil?
9
+
10
+ @seconds = seconds.to_f
11
+ raise NegativeTimeError.new "Duration cannot be negative." if @seconds < 0
12
+ end
13
+
14
+ def to_f
15
+ @seconds
16
+ end
17
+
18
+ def + add
19
+ shifted = @seconds + add
20
+ Duration.new(shifted)
21
+ end
22
+
23
+ def - subtract
24
+ self + (-subtract)
25
+ end
26
+
27
+ def <=> compare
28
+ to_f <=> compare.to_f
29
+ end
30
+
31
+ def to_s
32
+ seconds = @seconds % 60
33
+ total_minutes = (@seconds / 60).floor
34
+ minutes = total_minutes % 60
35
+ hours = (total_minutes / 60).floor
36
+ "#{'%02d' % hours}:#{'%02d' % minutes}:#{('%06.3f' % seconds).tr '.', ','}"
37
+ end
38
+
39
+ end
@@ -0,0 +1,2 @@
1
+ class EmptyBodyError < ArgumentError
2
+ end
@@ -0,0 +1,3 @@
1
+ class NegativeTimeError < ArgumentError
2
+
3
+ end
@@ -0,0 +1,2 @@
1
+ class NoTimeError < ArgumentError
2
+ end
@@ -0,0 +1,2 @@
1
+ class OverlapError < ArgumentError
2
+ end
@@ -0,0 +1,2 @@
1
+ class ParseError < ArgumentError
2
+ end
@@ -0,0 +1,50 @@
1
+ =begin
2
+ A whole subtitle stream consisting of chunks representing the
3
+ single dialogs. Accepts an IO object (e.g. a File) or a string
4
+ itself in the SRT format.
5
+ =end
6
+
7
+ require 'subtitle_chunk'
8
+
9
+ class Subtitle
10
+ attr_reader :chunks
11
+
12
+ def initialize input
13
+ if input.is_a? Array
14
+ @chunks = input
15
+ else
16
+ input = input.read if input.respond_to? :read
17
+
18
+ @chunks = []
19
+
20
+ chunks = input.split /(\r?\n){2}/
21
+ chunks.each do |chunk|
22
+ chunk.strip!
23
+ @chunks << SubtitleChunk.new(chunk) unless chunk.empty?
24
+ end
25
+ end
26
+ end
27
+
28
+ def initialize_copy source
29
+ super
30
+ @chunks = @chunks.dup
31
+ @chunks.map! { |chunk| chunk.dup }
32
+ end
33
+
34
+ def to_s
35
+ @chunks.map.with_index { |chunk, order| chunk.to_s(order + 1) }.join "\n"
36
+ end
37
+
38
+ def shift seconds
39
+ subtitle = self.dup
40
+ subtitle.shift! seconds
41
+ end
42
+
43
+ def shift! seconds
44
+ @chunks.each do |chunk|
45
+ chunk.shift! seconds
46
+ end
47
+ self
48
+ end
49
+
50
+ end
@@ -0,0 +1,80 @@
1
+ =begin
2
+ Single subtitle chunk. Dialog part with a begin and end timestamp.
3
+ Both timestamps can be manipulated at the same time using the
4
+ shift method or the +/- operators.
5
+ =end
6
+
7
+ require 'duration'
8
+ require 'error/empty_body_error'
9
+ require 'error/parse_error'
10
+ require 'error/no_time_error'
11
+ require 'error/overlap_error'
12
+ class SubtitleChunk
13
+ attr_reader :body, :begin, :end
14
+
15
+ # Can be initialized either from a string in the SRT format,
16
+ # or from a hash of the begin and end timestamps and the body.
17
+ def initialize chunk
18
+ if chunk.is_a? Hash
19
+ @body = chunk[:body]
20
+ @begin = Duration.new chunk[:begin]
21
+ @end = Duration.new chunk[:end]
22
+ else
23
+ chunk.match /^\d+(?<NEWLINE>\r?\n)(?<BEGIN>(?<TIME>\d{2}:\d{2}:\d{2},\d{3})) --> (?<END>\g<TIME>)\k<NEWLINE>(?<BODY>.+)$/m do |match|
24
+ @body = match['BODY']
25
+ @begin = duration match['BEGIN']
26
+ @end = duration match['END']
27
+ end or raise ParseError.new "Invalid subtitle chunk: #{chunk}"
28
+ end
29
+
30
+ @body.strip!
31
+ raise EmptyBodyError.new "Body cannot be empty." if @body.empty?
32
+ raise OverlapError.new "Duration cannot be negative." if @begin.to_f > @end.to_f
33
+ end
34
+
35
+ def initialize_copy source
36
+ super
37
+ @begin, @end = @begin.dup, @end.dup
38
+ end
39
+
40
+ def shift! seconds
41
+ shifted = self + seconds
42
+ @begin, @end = shifted.begin, shifted.end
43
+ self
44
+ end
45
+
46
+ def shift seconds
47
+ self + seconds
48
+ end
49
+
50
+ def + seconds
51
+ SubtitleChunk.new body: @body,
52
+ begin: @begin + seconds,
53
+ end: @end + seconds
54
+ end
55
+
56
+ def - seconds
57
+ self + (-seconds)
58
+ end
59
+
60
+ def to_s order = nil
61
+ output = ""
62
+ output << "#{order}\n" unless order.nil?
63
+ output << <<-EOS
64
+ #{@begin} --> #{@end}
65
+ #{@body}
66
+ EOS
67
+ end
68
+
69
+ protected
70
+ # Parse the SRT timestamp format HH:MM:SS,SSS into Duration.
71
+ def duration stamp
72
+ stamp.match /(?<HOURS>\d{2}):(?<MINUTES>\d{2}):(?<SECONDS>\d{2}),(?<MILLISECONDS>\d{3})/ do |match|
73
+ hours = match['HOURS'].to_f
74
+ minutes = hours * 60 + match['MINUTES'].to_f
75
+ seconds = minutes * 60 + "#{match['SECONDS']}.#{match['MILLISECONDS']}".to_f
76
+ Duration.new seconds
77
+ end or raise ArgumentError.new "Invalid duration stamp: #{stamp}"
78
+ end
79
+
80
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: subshifter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Glutexo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-04-13 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A simple application to shift time in SRT subtitle files
14
+ email: glutexo@icloud.com
15
+ executables:
16
+ - subshift
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - bin/subshift
21
+ - lib/duration.rb
22
+ - lib/error/empty_body_error.rb
23
+ - lib/error/negative_time_error.rb
24
+ - lib/error/no_time_error.rb
25
+ - lib/error/overlap_error.rb
26
+ - lib/error/parse_error.rb
27
+ - lib/subtitle.rb
28
+ - lib/subtitle_chunk.rb
29
+ homepage: https://github.com/Glutexo/subshifter
30
+ licenses:
31
+ - MIT
32
+ metadata: {}
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubygems_version: 3.0.3
49
+ signing_key:
50
+ specification_version: 4
51
+ summary: Subtitle shifter
52
+ test_files: []