encoder-tools 0.0.2 → 1.0.0

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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -23
  3. data/.rspec +3 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -10
  7. data/Gemfile.lock +37 -83
  8. data/LICENSE +1 -1
  9. data/README.md +14 -0
  10. data/Rakefile +3 -43
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/encoder-tools.gemspec +28 -0
  14. data/lib/encoder-tools.rb +1 -0
  15. data/lib/encoder-tools/cli.rb +23 -12
  16. data/lib/encoder-tools/cli/subtitles.rb +1 -0
  17. data/lib/encoder-tools/cli/subtitles/base.rb +26 -3
  18. data/lib/encoder-tools/cli/subtitles/fix_lengths.rb +35 -21
  19. data/lib/encoder-tools/cli/subtitles/offset.rb +33 -12
  20. data/lib/encoder-tools/cli/subtitles/renumber.rb +3 -1
  21. data/lib/encoder-tools/cli/subtitles/spell_check.rb +73 -0
  22. data/lib/encoder-tools/subtitles/list.rb +5 -0
  23. data/lib/encoder-tools/subtitles/parser.rb +1 -1
  24. data/lib/encoder-tools/subtitles/subtitle.rb +9 -2
  25. data/lib/encoder-tools/util.rb +5 -0
  26. data/lib/encoder-tools/util/text_reader.rb +26 -0
  27. data/lib/encoder-tools/version.rb +3 -0
  28. metadata +95 -96
  29. data/README.rdoc +0 -17
  30. data/VERSION +0 -1
  31. data/spec/cli/subtitles/fix_lengths_spec.rb +0 -51
  32. data/spec/cli/subtitles/offset_spec.rb +0 -40
  33. data/spec/cli/subtitles/renumber_spec.rb +0 -23
  34. data/spec/fixtures/subtitles/adjusted-long-subtitles.srt +0 -16
  35. data/spec/fixtures/subtitles/bad-numbering-corrected.srt +0 -16
  36. data/spec/fixtures/subtitles/bad-numbering.srt +0 -16
  37. data/spec/fixtures/subtitles/kill-bill-vol-2.srt +0 -345
  38. data/spec/fixtures/subtitles/no-long-subtitles.srt +0 -16
  39. data/spec/fixtures/subtitles/short-example-offset-2.srt +0 -16
  40. data/spec/fixtures/subtitles/short-example-offset-minus-2.srt +0 -16
  41. data/spec/fixtures/subtitles/short-example-offset-plus-2.srt +0 -16
  42. data/spec/fixtures/subtitles/short-example.srt +0 -16
  43. data/spec/fixtures/subtitles/some-long-subtitles.srt +0 -16
  44. data/spec/options/title_spec.rb +0 -41
  45. data/spec/spec.opts +0 -2
  46. data/spec/spec_helper.rb +0 -31
  47. data/spec/strategies/movie_spec.rb +0 -23
  48. data/spec/subtitles/list_spec.rb +0 -57
  49. data/spec/subtitles/subtitle_spec.rb +0 -31
@@ -5,6 +5,7 @@ module EncoderTools
5
5
  autoload :FixLengths, 'encoder-tools/cli/subtitles/fix_lengths'
6
6
  autoload :Offset, 'encoder-tools/cli/subtitles/offset'
7
7
  autoload :Renumber, 'encoder-tools/cli/subtitles/renumber'
8
+ autoload :SpellCheck, 'encoder-tools/cli/subtitles/spell_check'
8
9
  end
9
10
  end
10
11
  end
@@ -2,12 +2,35 @@ module EncoderTools
2
2
  class CLI
3
3
  module Subtitles
4
4
  class Base < CLI::Base
5
+ def self.parser(parser=nil)
6
+ case parser
7
+ when nil
8
+ @parser || EncoderTools::Subtitles::Parser
9
+ when :default
10
+ @parser = EncoderTools::Subtitles::Parser
11
+ when :relaxed
12
+ @parser = EncoderTools::Subtitles::RelaxedParser
13
+ when Class
14
+ @parser = parser
15
+ else
16
+ raise ArgumentError, "unexpected parser type: #{parser.inspect}"
17
+ end
18
+ end
19
+
20
+ def parser
21
+ self.class.parser
22
+ end
23
+
5
24
  def parse(text)
6
- EncoderTools::Subtitles::List.load(text)
25
+ EncoderTools::Subtitles::List.load(text, parser)
26
+ end
27
+
28
+ def read
29
+ parse(input.read)
7
30
  end
8
31
 
9
- def parse_relaxed(text)
10
- EncoderTools::Subtitles::List.load(text, EncoderTools::Subtitles::RelaxedParser)
32
+ def write(result)
33
+ output << result.to_s
11
34
  end
12
35
  end
13
36
  end
@@ -2,43 +2,57 @@ module EncoderTools
2
2
  class CLI
3
3
  module Subtitles
4
4
  class FixLengths < Base
5
- THRESHOLD = 5
5
+ DEFAULT_THRESHOLD = 5
6
6
 
7
7
  def run
8
- if long_subtitles.empty?
9
- shell.say "No subtitles found over #{THRESHOLD}s"
10
- return
11
- end
8
+ begin
9
+ if long_subtitles? && fix_subtitles?
10
+ write fix_subtitles
11
+ end
12
12
 
13
- if not shell.yes?("Found #{long_subtitles.size} long subtitles. Would you like to fix them?")
14
- return
13
+ return nil
14
+ rescue Interrupt
15
+ # just return on ^C
15
16
  end
17
+ end
16
18
 
17
- long_subtitles.each do |subtitle|
18
- lines = subtitle.to_s.to_a
19
- range = lines.shift.chomp
20
- range += " (#{subtitle.duration.to_i}s)\n"
21
- lines.unshift(range)
22
- lines << "\n" << "\n"
19
+ private
20
+ def long_subtitles?
21
+ return true if long_subtitles.any?
23
22
 
24
- shell.say(lines.join)
25
- subtitle.duration = shell.ask("How long should it be?").to_i
23
+ shell.say "No subtitles found over #{threshold}s"
24
+ return false
26
25
  end
27
26
 
28
- output << list.to_s
27
+ def fix_subtitles?
28
+ shell.yes?("Found #{long_subtitles.size} long subtitles. Would you like to fix them?")
29
+ end
29
30
 
30
- return nil
31
- end
31
+ def fix_subtitles
32
+ long_subtitles.each do |subtitle|
33
+ fix_subtitle(subtitle)
34
+ end
35
+ return list
36
+ end
37
+
38
+ def fix_subtitle(subtitle)
39
+ range = "%s (%s)" % [subtitle.range_string, subtitle.timestamp(subtitle.duration)]
40
+ shell.say([range, subtitle.text, '', ''].join("\n"))
41
+ subtitle.duration = BigDecimal(shell.ask("How long should it be?"))
42
+ end
32
43
 
33
- private
34
44
  def long_subtitles
35
45
  @long_subtitles ||= list.entries.select do |subtitle|
36
- subtitle.duration > THRESHOLD
46
+ subtitle.duration > threshold
37
47
  end
38
48
  end
39
49
 
40
50
  def list
41
- @list ||= parse(input.read)
51
+ @list ||= read
52
+ end
53
+
54
+ def threshold
55
+ options[:threshold] || DEFAULT_THRESHOLD
42
56
  end
43
57
  end
44
58
  end
@@ -2,26 +2,47 @@ module EncoderTools
2
2
  class CLI
3
3
  module Subtitles
4
4
  class Offset < Base
5
+ NUMBER = '(\d+|\d*\.\d+)'
6
+ HH_MM_SS_OFFSET = %r{\A(?:(\d\d?):)?(\d\d?):(\d\d?)\Z}
7
+ ABSOLUTE_OFFSET = %r{\A#{NUMBER}\Z}
8
+ NEGATIVE_OFFSET = %r{\A-#{NUMBER}\Z}
9
+ POSITIVE_OFFSET = %r{\A\+#{NUMBER}\Z}
10
+
11
+ MIN_PER_HOUR = 60
12
+ SEC_PER_MIN = 60
13
+
5
14
  def run
6
- output << offset(parse(input.read), options[:offset]).to_s
15
+ value = if options[:set]
16
+ options[:set]
17
+ elsif options[:add]
18
+ "+#{options[:add]}"
19
+ elsif options[:subtract]
20
+ "-#{options[:subtract]}"
21
+ else
22
+ raise ArgumentError, "Must provide a set, add, or subtract option to determine the offset"
23
+ end
24
+
25
+ write offset(read, value)
7
26
  end
8
27
 
9
28
  protected
10
- def offset(list, offset)
11
- list.offset = parse_offset(list, offset)
29
+ def offset(list, value)
30
+ list.offset = parse_offset(list, value)
12
31
  return list
13
32
  end
14
33
 
15
- def parse_offset(list, offset)
16
- case offset
34
+ def parse_offset(list, value)
35
+ case value
17
36
  when Fixnum
18
- offset
19
- when /^\+(\d+)$/
20
- list.offset + $1.to_i
21
- when /^-(\d+)$/
22
- list.offset - $1.to_i
23
- when /^\d+$/
24
- offset.to_i
37
+ value
38
+ when HH_MM_SS_OFFSET
39
+ ((BigDecimal($1 || '0') * MIN_PER_HOUR) + BigDecimal($2)) * SEC_PER_MIN + BigDecimal($3)
40
+ when POSITIVE_OFFSET
41
+ list.offset + BigDecimal($1)
42
+ when NEGATIVE_OFFSET
43
+ list.offset - BigDecimal($1)
44
+ when ABSOLUTE_OFFSET
45
+ BigDecimal(value)
25
46
  end
26
47
  end
27
48
  end
@@ -2,8 +2,10 @@ module EncoderTools
2
2
  class CLI
3
3
  module Subtitles
4
4
  class Renumber < Base
5
+ parser :relaxed
6
+
5
7
  def run
6
- output << parse_relaxed(input.read).to_s
8
+ write(read)
7
9
  end
8
10
  end
9
11
  end
@@ -0,0 +1,73 @@
1
+ require 'set'
2
+
3
+ module EncoderTools
4
+ class CLI
5
+ module Subtitles
6
+ class SpellCheck < Base
7
+ DEFAULT_DICT = '/usr/share/dict/words'.freeze
8
+
9
+ def run
10
+ write(read.each do |subtitle|
11
+ subtitle.text.gsub!(/\w+/) do |word|
12
+ fix_word(word)
13
+ end
14
+ end)
15
+ end
16
+
17
+ private
18
+
19
+ def fix_word(word)
20
+ return word if word?(word)
21
+ fix_ls_that_should_be_is(word)
22
+ end
23
+
24
+ def fix_ls_that_should_be_is(word)
25
+ # example: word == "bllllng"
26
+
27
+ ls = []
28
+
29
+ word.each_char.with_index do |char, i|
30
+ ls << i if char == 'l'
31
+ end
32
+
33
+ # ls == [1, 2, 3, 4]
34
+
35
+ ls.size.downto(1) do |n|
36
+ # substitute all 'l' with 'i' to start, then one less, etc
37
+ ls.combination(n) do |to_change|
38
+ # to_change == [1, 2, 3, 4], then [1, 2, 3], [1, 3, 4], etc
39
+ candidate = word.dup
40
+ to_change.each do |pos|
41
+ candidate[pos] = 'i'
42
+ end
43
+ # candidate == "biiiing", "biliing", "biiling", etc
44
+ return candidate if word?(candidate)
45
+ end
46
+ end
47
+
48
+ return word
49
+ end
50
+
51
+ def word?(word)
52
+ words.include?(word.downcase)
53
+ end
54
+
55
+ def words
56
+ @words ||= read_words
57
+ end
58
+
59
+ def read_words
60
+ if dict
61
+ Set.new(File.read(dict).split($/).map {|w| w.downcase })
62
+ else
63
+ raise ArgumentError, "Must provide a dictionary of valid words"
64
+ end
65
+ end
66
+
67
+ def dict
68
+ options.fetch(:dict, File.exist?(DEFAULT_DICT) ? DEFAULT_DICT : nil)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -24,6 +24,11 @@ module EncoderTools
24
24
  return str
25
25
  end
26
26
 
27
+ def each(&block)
28
+ entries.each(&block)
29
+ return self
30
+ end
31
+
27
32
  def self.load(input, parser_class=Parser)
28
33
  result = new
29
34
  parser = parser_class.new(input)
@@ -7,7 +7,7 @@ module EncoderTools
7
7
  class ParseError < RuntimeError; end
8
8
 
9
9
  def initialize(input)
10
- @scanner = StringScanner.new(input)
10
+ @scanner = StringScanner.new(Util::TextReader.read(input))
11
11
  @last_index = 0
12
12
  end
13
13
 
@@ -29,12 +29,19 @@ module EncoderTools
29
29
  other.text == self.text
30
30
  end
31
31
 
32
+ def range_string
33
+ "#{timestamp range.begin} --> #{timestamp range.end}"
34
+ end
35
+
32
36
  def to_s
33
- "#{timestamp range.begin} --> #{timestamp range.end}\n#{text}"
37
+ [range_string, text].join("\n")
34
38
  end
35
39
 
36
- private
37
40
  def timestamp(value)
41
+ self.class.timestamp(value)
42
+ end
43
+
44
+ def self.timestamp(value)
38
45
  seconds = value.to_i
39
46
  millis = ((value - seconds) * 1000).to_i
40
47
  minutes, seconds = seconds.divmod(60)
@@ -0,0 +1,5 @@
1
+ module EncoderTools
2
+ module Util
3
+ autoload :TextReader, 'encoder-tools/util/text_reader'
4
+ end
5
+ end
@@ -0,0 +1,26 @@
1
+ module EncoderTools
2
+ module Util
3
+ class TextReader
4
+ ENCODING_MARKER = "\xef\xbb\xbf".freeze
5
+
6
+ def initialize(input)
7
+ @input = input
8
+ end
9
+
10
+ def read
11
+ strip_encoding_marker(
12
+ @input.respond_to?(:read) ? @input.read : @input)
13
+ end
14
+
15
+ def self.read(input)
16
+ new(input).read
17
+ end
18
+
19
+ private
20
+ def strip_encoding_marker(string)
21
+ string[0, ENCODING_MARKER.size] == ENCODING_MARKER ?
22
+ string[ENCODING_MARKER.size..-1] : string
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module EncoderTools
2
+ VERSION = '1.0.0'.freeze
3
+ end
metadata CHANGED
@@ -1,66 +1,92 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: encoder-tools
3
- version: !ruby/object:Gem::Version
4
- version: 0.0.2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
5
  platform: ruby
6
- authors:
6
+ authors:
7
7
  - Brian Donovan
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
-
12
- date: 2010-02-14 00:00:00 -08:00
13
- default_executable: encoder-tools
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
11
+ date: 2017-12-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
16
14
  name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.18.1
17
20
  type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: "0"
24
- version:
25
- - !ruby/object:Gem::Dependency
26
- name: rspec
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.18.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.16'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
27
48
  type: :development
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 1.2.9
34
- version:
35
- - !ruby/object:Gem::Dependency
36
- name: yard
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
37
62
  type: :development
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- version: "0"
44
- version:
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
45
69
  description: Tools for ripping, encoding, and subtitling movies and TV shows
46
- email: brian.donovan@gmail.com
47
- executables:
48
- - encoder-tools
70
+ email:
71
+ - me@brian-donovan.com
72
+ executables: []
49
73
  extensions: []
50
-
51
- extra_rdoc_files:
52
- - LICENSE
53
- - README.rdoc
54
- files:
55
- - .document
56
- - .gitignore
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".document"
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
+ - CODE_OF_CONDUCT.md
57
81
  - Gemfile
58
82
  - Gemfile.lock
59
83
  - LICENSE
60
- - README.rdoc
84
+ - README.md
61
85
  - Rakefile
62
- - VERSION
86
+ - bin/console
63
87
  - bin/encoder-tools
88
+ - bin/setup
89
+ - encoder-tools.gemspec
64
90
  - lib/encoder-tools.rb
65
91
  - lib/encoder-tools/cli.rb
66
92
  - lib/encoder-tools/cli/base.rb
@@ -69,6 +95,7 @@ files:
69
95
  - lib/encoder-tools/cli/subtitles/fix_lengths.rb
70
96
  - lib/encoder-tools/cli/subtitles/offset.rb
71
97
  - lib/encoder-tools/cli/subtitles/renumber.rb
98
+ - lib/encoder-tools/cli/subtitles/spell_check.rb
72
99
  - lib/encoder-tools/options.rb
73
100
  - lib/encoder-tools/options/title.rb
74
101
  - lib/encoder-tools/strategies.rb
@@ -80,59 +107,31 @@ files:
80
107
  - lib/encoder-tools/subtitles/parser.rb
81
108
  - lib/encoder-tools/subtitles/relaxed_parser.rb
82
109
  - lib/encoder-tools/subtitles/subtitle.rb
83
- - spec/cli/subtitles/fix_lengths_spec.rb
84
- - spec/cli/subtitles/offset_spec.rb
85
- - spec/cli/subtitles/renumber_spec.rb
86
- - spec/fixtures/subtitles/adjusted-long-subtitles.srt
87
- - spec/fixtures/subtitles/bad-numbering-corrected.srt
88
- - spec/fixtures/subtitles/bad-numbering.srt
89
- - spec/fixtures/subtitles/kill-bill-vol-2.srt
90
- - spec/fixtures/subtitles/no-long-subtitles.srt
91
- - spec/fixtures/subtitles/short-example-offset-2.srt
92
- - spec/fixtures/subtitles/short-example-offset-minus-2.srt
93
- - spec/fixtures/subtitles/short-example-offset-plus-2.srt
94
- - spec/fixtures/subtitles/short-example.srt
95
- - spec/fixtures/subtitles/some-long-subtitles.srt
96
- - spec/options/title_spec.rb
97
- - spec/spec.opts
98
- - spec/spec_helper.rb
99
- - spec/strategies/movie_spec.rb
100
- - spec/subtitles/list_spec.rb
101
- - spec/subtitles/subtitle_spec.rb
102
- has_rdoc: true
103
- homepage: http://github.com/eventualbuddha/encoder-tools
104
- licenses: []
105
-
110
+ - lib/encoder-tools/util.rb
111
+ - lib/encoder-tools/util/text_reader.rb
112
+ - lib/encoder-tools/version.rb
113
+ homepage: https://github.com/eventualbuddha/encoder-tools
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
106
117
  post_install_message:
107
- rdoc_options:
108
- - --charset=UTF-8
109
- require_paths:
118
+ rdoc_options: []
119
+ require_paths:
110
120
  - lib
111
- required_ruby_version: !ruby/object:Gem::Requirement
112
- requirements:
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
113
123
  - - ">="
114
- - !ruby/object:Gem::Version
115
- version: "0"
116
- version:
117
- required_rubygems_version: !ruby/object:Gem::Requirement
118
- requirements:
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
119
128
  - - ">="
120
- - !ruby/object:Gem::Version
121
- version: "0"
122
- version:
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
123
131
  requirements: []
124
-
125
132
  rubyforge_project:
126
- rubygems_version: 1.3.5
133
+ rubygems_version: 2.5.2
127
134
  signing_key:
128
- specification_version: 3
129
- summary: Some tools to make encoding from DVDs easier
130
- test_files:
131
- - spec/cli/subtitles/fix_lengths_spec.rb
132
- - spec/cli/subtitles/offset_spec.rb
133
- - spec/cli/subtitles/renumber_spec.rb
134
- - spec/options/title_spec.rb
135
- - spec/spec_helper.rb
136
- - spec/strategies/movie_spec.rb
137
- - spec/subtitles/list_spec.rb
138
- - spec/subtitles/subtitle_spec.rb
135
+ specification_version: 4
136
+ summary: Tools for ripping, encoding, and subtitling movies and TV shows
137
+ test_files: []