thuss-shift_subtitle 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Todd Huss
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,37 @@
1
+ = shift_subtitle
2
+
3
+ My submission for Ruby Challenge #1: Shift Subtitle
4
+
5
+ http://rubylearning.com/blog/2009/09/24/rpcfn-shift-subtitle-1
6
+
7
+ == Features
8
+ * 100% RSpec[http://rspec.info/] test coverage of both lib/* and bin/*
9
+ * Bundled as a github gem (thanks to Jeweler[http://github.com/technicalpickles/jeweler])
10
+ * No external gem dependencies to use the gem
11
+ * Command line related code in shift_subtitle_cli.rb and SubRib/SRT processing code in shift_subtitle.rb
12
+ * shift_subtitle.rb uses IO streams (such as File and StringIO) to support processing large files
13
+ * Time shifting handles boundary cases
14
+ * Lower boundary shifting 00:00:00,001 down by a second shifts to 00:00:00,000
15
+ * Shifting above the 24 hour boundary works fine (25, 26, 27 hours) and doesn't rollover back to 00 hours
16
+
17
+ == Requirements
18
+
19
+ You must have Github in your gem sources
20
+
21
+ gem sources -a http://gems.github.com
22
+
23
+ == Install
24
+
25
+ sudo gem install thuss-shift_subtitle
26
+
27
+ == Usage
28
+ shift_subtitle --operation add --time 2,500 input output
29
+
30
+ == Todo
31
+
32
+ * Add a --version flag
33
+ * Perhaps throw a warning if a subtraction shift results in the floor of 00:00:00,000
34
+
35
+ == Copyright
36
+
37
+ Copyright (c) 2009 Todd Huss. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "thuss-shift_subtitle"
8
+ gem.summary = %Q{Ruby Challenge #1: Shift Subtitle}
9
+ gem.description = %Q{Submission for the Ruby Challenge to create a command line tool to shift SubRib formatted movie subtitles}
10
+ gem.email = "thuss@gabrito.com"
11
+ gem.homepage = "http://github.com/thuss/shift_subtitle"
12
+ gem.authors = ["Todd Huss"]
13
+ gem.add_development_dependency "rspec"
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler is not available. Install it with: sudo gem install jeweler"
18
+ end
19
+
20
+ require 'spec/rake/spectask'
21
+ Spec::Rake::SpecTask.new(:spec) do |spec|
22
+ spec.libs << 'lib' << 'spec'
23
+ spec.spec_files = FileList['spec/**/*_spec.rb']
24
+ end
25
+
26
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
27
+ spec.libs << 'lib' << 'spec'
28
+ spec.pattern = 'spec/**/*_spec.rb'
29
+ spec.rcov = true
30
+ spec.rcov_opts = ['--exclude', 'lib/spec,bin/spec,spec/*,rcov*']
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ if File.exist?('VERSION')
40
+ version = File.read('VERSION')
41
+ else
42
+ version = ""
43
+ end
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "shift_subtitle #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created on 2009-9-26.
4
+ # Copyright (c) 2009. All rights reserved.
5
+
6
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
7
+ require "shift_subtitle_cli"
8
+
9
+ ShiftSubtitleCli.new.execute(ARGV)
@@ -0,0 +1,33 @@
1
+ require 'time'
2
+
3
+ class ShiftSubtitle
4
+
5
+ # Takes an SRT IO stream (such as File) and shifts it by shift_seconds (e.g. -2.5)
6
+ def shift_srt(input_srt, output_srt, shift_seconds)
7
+ input_srt.each_line do | line |
8
+ srt_time_regex = /^(\d\d:\d\d:\d\d,\d\d\d)( --> )(\d\d:\d\d:\d\d,\d\d\d)/
9
+ if (line =~ srt_time_regex)
10
+ start_time = shift_timestamp($1, shift_seconds)
11
+ end_time = shift_timestamp($3, shift_seconds)
12
+ line.gsub!(srt_time_regex, start_time + $2 + end_time)
13
+ end
14
+ output_srt.write line
15
+ end
16
+ end
17
+
18
+ # Takes an SRT timestamp ('01:01:23,500') and shifts it by shift seconds (-2.5)
19
+ def shift_timestamp(srt_time_string, shift_seconds)
20
+ if (srt_time_string =~ /(\d\d):(\d\d):(\d\d),(\d\d\d)/)
21
+ time = Time.parse('2000-01-01 ' + srt_time_string + 'UTC') + shift_seconds
22
+ hours = time.hour
23
+ case
24
+ when time.day == 31 then hours = 0; time = Time.at(0).utc # Never go below 00:00:00,000
25
+ when time.day > 1 then hours += (time.day - 1) * 24 # Support times > 23 hours
26
+ end
27
+ "#{'%02d'%hours}:#{'%02d'%time.min}:#{'%02d'%time.sec},#{'%03d'%(time.usec/1000)}"
28
+ else
29
+ raise ArgumentError, "unexpected SRT timestamp format " + srt_time_string
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,59 @@
1
+ require 'optparse'
2
+ require 'shift_subtitle'
3
+
4
+ class ShiftSubtitleCli < ShiftSubtitle
5
+ def execute(arguments)
6
+ begin
7
+ options = parse_options arguments
8
+ input_file = File.new options[:input_file], 'r'
9
+ output_file = File.new options[:output_file], 'w'
10
+ shift_srt input_file, output_file, time_to_shift(options)
11
+ ensure
12
+ input_file.close unless input_file.nil?
13
+ output_file.close unless output_file.nil?
14
+ end
15
+ end
16
+
17
+ def parse_options(arguments)
18
+ options = {}
19
+ mandatory_options = [ :operation, :seconds, :milliseconds, :input_file, :output_file ]
20
+ parser = OptionParser.new do |opts|
21
+ opts.banner = <<-BANNER.gsub(/^ /,'')
22
+ Shift Subtitle Ruby Challenge
23
+
24
+ Usage: #{File.basename($0)} --operation [add|sub] --time [seconds,milliseconds] input_file output_file
25
+
26
+ Options are:
27
+ BANNER
28
+ opts.separator ""
29
+
30
+ opts.on("-o", "--operation add|sub", [:add, :sub], "Add or Subtract time") do |operation|
31
+ options[:operation] = operation
32
+ end
33
+
34
+ opts.on("-t", "--time seconds,milliseconds", "Seconds AND milliseconds to add or subtract") do |time|
35
+ if (time =~ /\d+,\d+/)
36
+ options[:seconds], options[:milliseconds] = time.split(/,/).map!{ |t| t.to_i }
37
+ else
38
+ raise OptionParser::InvalidArgument, "should be seconds,milliseconds"
39
+ end
40
+ end
41
+
42
+ opts.on("-h", "--help", "Show this help message.") { puts opts; exit 0 }
43
+
44
+ opts.parse!(arguments)
45
+ options[:input_file], options[:output_file] = arguments.pop(2)
46
+
47
+ if mandatory_options.find { |option| options[option].nil? }
48
+ $stderr.puts opts; exit 1
49
+ end
50
+ end
51
+ options
52
+ end
53
+
54
+ # Converts the time and operation into a float (:sub 2,500 returns -2.5)
55
+ def time_to_shift(options)
56
+ shift_time = options[:seconds].to_i + options[:milliseconds].to_f/1000
57
+ shift_time *= (options[:operation] == :sub)?-1:1
58
+ end
59
+ end
@@ -0,0 +1,59 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{shift_subtitle}
8
+ s.version = "0.2.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Todd Huss"]
12
+ s.date = %q{2009-10-03}
13
+ s.default_executable = %q{shift_subtitle}
14
+ s.description = %q{Submission for the Ruby Challenge to create a command line tool to shift SubRib formatted movie subtitles}
15
+ s.email = %q{thuss@gabrito.com}
16
+ s.executables = ["shift_subtitle"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".gitignore",
24
+ "LICENSE",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "bin/shift_subtitle",
29
+ "lib/shift_subtitle.rb",
30
+ "lib/shift_subtitle_cli.rb",
31
+ "shift_subtitle.gemspec",
32
+ "spec/shift_subtitle_cli_spec.rb",
33
+ "spec/shift_subtitle_spec.rb",
34
+ "spec/spec_helper.rb"
35
+ ]
36
+ s.homepage = %q{http://github.com/thuss/shift_subtitle}
37
+ s.rdoc_options = ["--charset=UTF-8"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.3.5}
40
+ s.summary = %q{Ruby Challenge #1: Shift Subtitle}
41
+ s.test_files = [
42
+ "spec/shift_subtitle_cli_spec.rb",
43
+ "spec/shift_subtitle_spec.rb",
44
+ "spec/spec_helper.rb"
45
+ ]
46
+
47
+ if s.respond_to? :specification_version then
48
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
49
+ s.specification_version = 3
50
+
51
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
52
+ s.add_development_dependency(%q<rspec>, [">= 0"])
53
+ else
54
+ s.add_dependency(%q<rspec>, [">= 0"])
55
+ end
56
+ else
57
+ s.add_dependency(%q<rspec>, [">= 0"])
58
+ end
59
+ end
@@ -0,0 +1,89 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'shift_subtitle_cli'
3
+
4
+ describe ShiftSubtitleCli do
5
+
6
+ before(:each) do
7
+ @cli = ShiftSubtitleCli.new
8
+ end
9
+
10
+ describe "running bin/shift_subtitle" do
11
+ before :each do
12
+ Object.send(:remove_const, :ARGV)
13
+ input = StringIO.new("1\n00:00:24,600 --> 00:00:27,800\nthis is a test...")
14
+ @output = StringIO.new
15
+ File.stub!(:new).and_return(input, @output)
16
+ end
17
+
18
+ it "should shift the time when an SRT file is provided" do
19
+ ARGV = ['--operation', 'sub', '--time', '2,500', 'infile', 'outfile']
20
+ eval File.read(File.join(File.dirname(__FILE__), '..', 'bin', 'shift_subtitle'))
21
+ @output.string.should eql("1\n00:00:22,100 --> 00:00:25,300\nthis is a test...")
22
+ end
23
+ end
24
+
25
+ it "should print help to stderr when no options are provided" do
26
+ output = capture_stderr do
27
+ lambda { @cli.parse_options([]) }.should raise_error(SystemExit)
28
+ end
29
+ output.should match(/Usage:/)
30
+ end
31
+
32
+ it "should set the input_file and output_file in the options hash" do
33
+ options = @cli.parse_options(['--operation', 'add', '--time', '2,500', 'infile', 'outfile'])
34
+ options[:input_file].should eql('infile')
35
+ options[:output_file].should eql('outfile')
36
+ end
37
+
38
+ it "should not allow a missing argument" do
39
+ args = ['--operation', 'add', '--time', '2,500', 'infile']
40
+ output = capture_stderr do
41
+ lambda { @cli.parse_options(args) }.should raise_error(SystemExit)
42
+ end
43
+ end
44
+
45
+ it "should convert the operation, seconds, and milliseconds into a float of seconds to shift" do
46
+ options = @cli.parse_options(['--operation', 'sub', '--time', '2,500', 'infile', 'outfile'])
47
+ @cli.time_to_shift(options).should eql(-2.5)
48
+ end
49
+
50
+ it "should convert unusually large millisecond values correctly" do
51
+ options = @cli.parse_options(['--operation', 'add', '--time', '2,5000', 'infile', 'outfile'])
52
+ @cli.time_to_shift(options).should eql(7.0)
53
+ end
54
+
55
+ describe "when the --operation argument is specified" do
56
+ it "should not allow an invalid operation" do
57
+ args = ['--operation', 'foo', '--time', '2,500', 'infile', 'outfile']
58
+ lambda { @cli.parse_options(args) }.should raise_error(OptionParser::InvalidArgument)
59
+ end
60
+
61
+ it "should allow the add operation and set the options hash" do
62
+ options = @cli.parse_options(['--operation', 'add', '--time', '2,500', 'infile', 'outfile'])
63
+ options[:operation].should eql(:add)
64
+ end
65
+ end
66
+
67
+ describe "when the --time argument is provided" do
68
+ it "should not allow an invalid time" do
69
+ args = ['--operation', 'add', '--time', 'XYZ', 'infile', 'outfile']
70
+ output = capture_stderr do
71
+ lambda { @cli.parse_options(args) }.should raise_error(OptionParser::InvalidArgument)
72
+ end
73
+ end
74
+
75
+ it "should parse a valid time and set the options hash" do
76
+ options = @cli.parse_options(['--operation', 'add', '--time', '2,500', 'infile', 'outfile'])
77
+ options[:seconds].should eql(2)
78
+ options[:milliseconds].should eql(500)
79
+ end
80
+ end
81
+
82
+ def capture_stderr
83
+ $stderr = StringIO.new
84
+ yield
85
+ $stderr.string
86
+ ensure
87
+ $stderr = STDERR
88
+ end
89
+ end
@@ -0,0 +1,66 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe ShiftSubtitle do
4
+ before(:each) do
5
+ @shift_subtitle = ShiftSubtitle.new
6
+ end
7
+
8
+ describe "when shifting time" do
9
+ it "should raise an exception on invalid input" do
10
+ lambda { @shift_subtitle.shift_timestamp('X1:31:51,210', 0) }.should raise_error(ArgumentError)
11
+ end
12
+
13
+ it "should leave time unchanged when shifting by zero" do
14
+ @shift_subtitle.shift_timestamp('01:31:51,210', 0).should eql('01:31:51,210')
15
+ end
16
+
17
+ it "should add time" do
18
+ @shift_subtitle.shift_timestamp('01:31:51,210', 1.5).should eql('01:31:52,710')
19
+ end
20
+
21
+ it "should add time beyond the 24 hour boundary" do
22
+ @shift_subtitle.shift_timestamp('23:59:59,500', 1.5).should eql('24:00:01,000')
23
+ end
24
+
25
+ it "should subtract time" do
26
+ @shift_subtitle.shift_timestamp('01:31:51,210', -1.5).should eql('01:31:49,710')
27
+ end
28
+
29
+ it "should not subtract below zero" do
30
+ @shift_subtitle.shift_timestamp('00:00:01,000', -1.5).should eql('00:00:00,000')
31
+ end
32
+ end
33
+
34
+ describe "when processing srt input" do
35
+ before(:each) do
36
+ @input_srt = StringIO.new(<<-SRT.gsub(/^ /,'')
37
+ 1
38
+ 00:00:20,000 --> 00:00:24,400
39
+ In connection with a dramatic increase
40
+ in crime in certain neighbourhoods,
41
+
42
+ 2
43
+ 00:00:24,600 --> 00:00:27,800
44
+ the government is implementing a new policy...
45
+ SRT
46
+ )
47
+ @output_srt = StringIO.new
48
+ end
49
+
50
+ it "should output the shifted srt" do
51
+ @shift_subtitle.shift_srt(@input_srt, @output_srt, 1.5)
52
+ @output_srt.string.should eql(<<-SRTOUT.gsub(/^ /,'')
53
+ 1
54
+ 00:00:21,500 --> 00:00:25,900
55
+ In connection with a dramatic increase
56
+ in crime in certain neighbourhoods,
57
+
58
+ 2
59
+ 00:00:26,100 --> 00:00:29,300
60
+ the government is implementing a new policy...
61
+ SRTOUT
62
+ )
63
+ end
64
+ end
65
+
66
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'shift_subtitle'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thuss-shift_subtitle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Todd Huss
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-03 00:00:00 -07:00
13
+ default_executable: shift_subtitle
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: Submission for the Ruby Challenge to create a command line tool to shift SubRib formatted movie subtitles
26
+ email: thuss@gabrito.com
27
+ executables:
28
+ - shift_subtitle
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.rdoc
39
+ - Rakefile
40
+ - VERSION
41
+ - bin/shift_subtitle
42
+ - lib/shift_subtitle.rb
43
+ - lib/shift_subtitle_cli.rb
44
+ - shift_subtitle.gemspec
45
+ - spec/shift_subtitle_cli_spec.rb
46
+ - spec/shift_subtitle_spec.rb
47
+ - spec/spec_helper.rb
48
+ has_rdoc: true
49
+ homepage: http://github.com/thuss/shift_subtitle
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options:
54
+ - --charset=UTF-8
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ version:
69
+ requirements: []
70
+
71
+ rubyforge_project:
72
+ rubygems_version: 1.3.5
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: "Ruby Challenge #1: Shift Subtitle"
76
+ test_files:
77
+ - spec/shift_subtitle_cli_spec.rb
78
+ - spec/shift_subtitle_spec.rb
79
+ - spec/spec_helper.rb