encoder-tools 0.0.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []