subjuster 0.1.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.
- checksums.yaml +7 -0
- data/.travis.yml +5 -0
- data/LICENSE.txt +21 -0
- data/exe/subjuster +48 -0
- data/lib/subjuster.rb +23 -0
- data/lib/subjuster/adjuster.rb +43 -0
- data/lib/subjuster/generator.rb +26 -0
- data/lib/subjuster/parser.rb +64 -0
- data/lib/subjuster/user_input.rb +42 -0
- data/lib/subjuster/version.rb +3 -0
- data/subjuster.gemspec +32 -0
- metadata +100 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 191871bead5ef2eb2b837a2471bb1a33122fbd85
|
|
4
|
+
data.tar.gz: d28b97d62ff5a33ed541d56bbc88c6337f4fdec1
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c9c2fed49b6f742bcf33f0095b513a6ce6cd1f21c8e0dd5f1424a83e5d0e3d443fe6f1cd62a6d55d81a3a4b5b4ce532d6c7f30ecef058ef90e55c304a089996b
|
|
7
|
+
data.tar.gz: 8399acabe1456ea38fb26f4d17345e1677d440bfdc7defd8541c442697c9bf6434ab2fc55904127269007081aaca034a066bc73f515b664c6954bfc518704512
|
data/.travis.yml
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017 Shiva Bhusal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/exe/subjuster
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
# exe/hulaki
|
|
4
|
+
|
|
5
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
|
6
|
+
require 'subjuster'
|
|
7
|
+
require 'optparse'
|
|
8
|
+
|
|
9
|
+
options = {source: ARGV[0]}
|
|
10
|
+
|
|
11
|
+
OptionParser.new do |opts|
|
|
12
|
+
opts.banner = "Usage: subjuster [filename.srt] [options]\n"\
|
|
13
|
+
"\n"\
|
|
14
|
+
"Special Case:\n"\
|
|
15
|
+
"subjuster [fiename.srt] -a-12.23 # for -ve number i.e '-12.23'\n"\
|
|
16
|
+
"\n"\
|
|
17
|
+
"'+ve' number will add time while '-ve' will decrease. \n"\
|
|
18
|
+
"i.e. if subtitles appears 2 sec after the audio then use '-2' as adjustment\n"\
|
|
19
|
+
"---------------------------------------------------------------------------\n"\
|
|
20
|
+
"\n"\
|
|
21
|
+
|
|
22
|
+
opts.on("-a [Numeric]", "--adjustment [Numeric]", Float, "Time adjustment in sec") do |v|
|
|
23
|
+
options[:adjustment_in_sec] = v
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
opts.on("-t [Filename]", "--target [Filename]", String, "If Target file name not given then will be '[source_file].modified.srt'") do |v|
|
|
27
|
+
options[:target] = v
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
opts.on("-h", "--help", "Prints this help") do
|
|
31
|
+
puts opts
|
|
32
|
+
exit
|
|
33
|
+
end
|
|
34
|
+
end.parse!
|
|
35
|
+
|
|
36
|
+
$stdout.puts
|
|
37
|
+
|
|
38
|
+
options.each do |key, value|
|
|
39
|
+
$stdout.puts "#{key} #{' '*(20-key.length)} => #{value}" unless key == :target
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
$stdout.puts
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
Subjuster::Core.run(options)
|
|
46
|
+
rescue Subjuster::InputError => error
|
|
47
|
+
$stderr.puts error.message
|
|
48
|
+
end
|
data/lib/subjuster.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'subjuster/version'
|
|
2
|
+
require 'subjuster/user_input'
|
|
3
|
+
require 'subjuster/parser'
|
|
4
|
+
require 'subjuster/adjuster'
|
|
5
|
+
require 'subjuster/generator'
|
|
6
|
+
|
|
7
|
+
# lib/subjuster.rb
|
|
8
|
+
module Subjuster
|
|
9
|
+
class Core
|
|
10
|
+
class << self
|
|
11
|
+
def run(source:, target: nil, adjustment_in_sec: 0)
|
|
12
|
+
user_input = UserInput.new(source: source, target: target, adjustment_in_sec: adjustment_in_sec)
|
|
13
|
+
parsed_data = Parser.new(inputs: user_input).parse
|
|
14
|
+
adjusted_data = Adjuster.new(data: parsed_data, inputs: user_input).run
|
|
15
|
+
|
|
16
|
+
Generator.new(payload: adjusted_data, inputs: user_input).run
|
|
17
|
+
|
|
18
|
+
$stdout.puts "Yeah! successfully adjusted and compiled to file #{user_input.target_filepath}"
|
|
19
|
+
$stdout.puts
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Subjuster
|
|
2
|
+
class Adjuster
|
|
3
|
+
attr_reader :data, :inputs
|
|
4
|
+
|
|
5
|
+
def initialize(data:, inputs:)
|
|
6
|
+
@data = data
|
|
7
|
+
@inputs = inputs
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def run
|
|
11
|
+
# new_data = data.clone
|
|
12
|
+
data.map do |paragraph|
|
|
13
|
+
paragraph[:start_time] = process_on(paragraph[:start_time])
|
|
14
|
+
paragraph[:end_time] = process_on(paragraph[:end_time])
|
|
15
|
+
paragraph
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# TODO: Refactoring needed
|
|
22
|
+
def process_on(date)
|
|
23
|
+
_, hrs, min, sec, milli = /(..):(..):(..),(...)/.match(date).to_a.map(&:to_i)
|
|
24
|
+
timestamp_in_msec = milli + sec*1000 + min*60000 + hrs*3600000 + (inputs.adjustment_in_sec*1000).to_i
|
|
25
|
+
rim = ""
|
|
26
|
+
|
|
27
|
+
hr = timestamp_in_msec / 3600000
|
|
28
|
+
rim << ("%02i" % hr) << ":"
|
|
29
|
+
|
|
30
|
+
min = timestamp_in_msec / 60000
|
|
31
|
+
timestamp_in_msec -= min*60000
|
|
32
|
+
rim << ("%02i" % min) << ":"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
sec = timestamp_in_msec / 1000
|
|
36
|
+
timestamp_in_msec -= sec*1000
|
|
37
|
+
rim << ("%02i" % sec) << ":"
|
|
38
|
+
|
|
39
|
+
rim.slice!(-1)
|
|
40
|
+
rim << "," << "#{timestamp_in_msec}00"[0..2] << date[12..-1]
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Subjuster
|
|
2
|
+
class Generator
|
|
3
|
+
attr_reader :payload, :inputs
|
|
4
|
+
|
|
5
|
+
def initialize(payload:, inputs: nil)
|
|
6
|
+
@payload = payload
|
|
7
|
+
@inputs = inputs
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def run
|
|
11
|
+
file_content = _prepare_data
|
|
12
|
+
File.write(inputs.target_filepath, file_content)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def _prepare_data
|
|
16
|
+
@payload.map do |hash|
|
|
17
|
+
[
|
|
18
|
+
hash[:id],
|
|
19
|
+
"#{hash[:start_time]} --> #{hash[:end_time]}",
|
|
20
|
+
hash[:dialog]
|
|
21
|
+
].join("\n")
|
|
22
|
+
end.join("\n") + "\n\r\n"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Subjuster
|
|
2
|
+
class Parser
|
|
3
|
+
REGEXP = /\A[\d]+[\s]{,2}\Z/
|
|
4
|
+
# = Parser
|
|
5
|
+
#
|
|
6
|
+
# Subjuster::Parser parses the File you provide via UserInput object
|
|
7
|
+
#
|
|
8
|
+
# Example:
|
|
9
|
+
#
|
|
10
|
+
# inputs = Subjuster::UserInput.new(source: 'somefilename')
|
|
11
|
+
# Subjuster::Parser.new(inputs: inputs).parse
|
|
12
|
+
#
|
|
13
|
+
# # [{:id=>"1",
|
|
14
|
+
# # :start_time=>"00:00:57,918",
|
|
15
|
+
# # :end_time=>"00:01:02,514",
|
|
16
|
+
# # :dialog=>"\"In order to affect a timely halt\n" + "to deteriorating conditions\n"},..]
|
|
17
|
+
#
|
|
18
|
+
attr_reader :inputs
|
|
19
|
+
def initialize(inputs:)
|
|
20
|
+
@inputs = inputs
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def parse
|
|
24
|
+
inputs.validate! unless $test
|
|
25
|
+
_parse
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
# This will find next `dialog number` embedded line and
|
|
30
|
+
# return lines joined upto that line, along with the index of line of the `dialog num`
|
|
31
|
+
def find_dialog_from(list:, index:)
|
|
32
|
+
buffer = []
|
|
33
|
+
count = list.count
|
|
34
|
+
while !(list[index] =~ REGEXP) && index < count do
|
|
35
|
+
buffer << list[index]
|
|
36
|
+
index += 1
|
|
37
|
+
end
|
|
38
|
+
[buffer.join("\n"), index]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def _parse
|
|
42
|
+
items = []
|
|
43
|
+
file_content_array = File.read(inputs.source_filepath).split("\n")
|
|
44
|
+
count = file_content_array.count
|
|
45
|
+
index = 0
|
|
46
|
+
|
|
47
|
+
while index < count do
|
|
48
|
+
line = file_content_array[index]
|
|
49
|
+
|
|
50
|
+
if line =~ REGEXP
|
|
51
|
+
splitted_line = file_content_array[index+1].split(' --> ')
|
|
52
|
+
|
|
53
|
+
dialog, index = find_dialog_from(list: file_content_array, index: index + 2)
|
|
54
|
+
|
|
55
|
+
items << { id: line, start_time: splitted_line.first, end_time: splitted_line.last, dialog: dialog }
|
|
56
|
+
else
|
|
57
|
+
index += 1
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
items
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Subjuster
|
|
2
|
+
InputError = Class.new(StandardError)
|
|
3
|
+
# = Handle User Input
|
|
4
|
+
#
|
|
5
|
+
# Being a CLI tool, Subjuster is supposed to take input from +ARGV+
|
|
6
|
+
# This module will get those data via arguments to the constructor method.
|
|
7
|
+
#
|
|
8
|
+
# === For Example:
|
|
9
|
+
#
|
|
10
|
+
# inputs = UserInput.new(source: ARGV[0], target: ARGV[1], adjustment_in_sec: ARGV[2])
|
|
11
|
+
# inputs.valid? # => true / false
|
|
12
|
+
#
|
|
13
|
+
class UserInput
|
|
14
|
+
|
|
15
|
+
attr_reader :source_filepath, :target_filepath, :adjustment_in_sec
|
|
16
|
+
def initialize(source:, target: nil, adjustment_in_sec: 0)
|
|
17
|
+
@source_filepath = File.expand_path(source || '')
|
|
18
|
+
@target_filepath = target && File.expand_path(target) || "#{source_filepath}.modified.srt"
|
|
19
|
+
@adjustment_in_sec = adjustment_in_sec
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Validates the source file, if it exists
|
|
23
|
+
# === Valid condition
|
|
24
|
+
#
|
|
25
|
+
# inputs = UserInput.new(source: ARGV[0], target: ARGV[1], adjustment_in_sec: ARGV[2])
|
|
26
|
+
# inputs.valid? # => true
|
|
27
|
+
#
|
|
28
|
+
# === Invalid condition
|
|
29
|
+
#
|
|
30
|
+
# inputs = UserInput.new(source: ARGV[0], target: ARGV[1], adjustment_in_sec: ARGV[2])
|
|
31
|
+
# inputs.valid? # => false
|
|
32
|
+
#
|
|
33
|
+
#
|
|
34
|
+
def valid?
|
|
35
|
+
File.exist?(source_filepath)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def validate!
|
|
39
|
+
raise InputError, "Invalid file: #{source_filepath}" unless valid?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/subjuster.gemspec
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require "subjuster/version"
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "subjuster"
|
|
8
|
+
spec.version = Subjuster::VERSION
|
|
9
|
+
spec.authors = ["Shiva Bhusal"]
|
|
10
|
+
spec.email = ["hotline.shiva@gmail.com"]
|
|
11
|
+
|
|
12
|
+
spec.summary = %q{Subjuster | TDD guide for Software Engineers in OOP}
|
|
13
|
+
spec.description = %q{A command line tool to adjust your movie subtitle files while while playing audio and subtitle do not sync with each other. Normally it lags/gains by a few seconds/milliseconds. You will be able to adjust and generate a new subtitle file or update the existing one.}
|
|
14
|
+
spec.homepage = "http://subjuster.github.io"
|
|
15
|
+
spec.license = "MIT"
|
|
16
|
+
|
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
18
|
+
f.match(%r{^(test|spec|features|images|tmp|bin|doc)/}) || f =~ /[\w]{1,}.md\z/ || f =~ /\A.[\w]{1,}\z/
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Since we intend to make it a CLI tool, need to have executable script
|
|
22
|
+
# Create a new dir `exe` and put `subjuster`
|
|
23
|
+
spec.bindir = "exe"
|
|
24
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
25
|
+
spec.require_paths = ["lib"]
|
|
26
|
+
|
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
|
28
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
29
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
|
30
|
+
|
|
31
|
+
spec.post_install_message = 'Please run `$ subjuster -h` to learn how to use.'
|
|
32
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: subjuster
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Shiva Bhusal
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2017-07-01 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.15'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.15'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '10.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '10.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rspec
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '3.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '3.0'
|
|
55
|
+
description: A command line tool to adjust your movie subtitle files while while playing
|
|
56
|
+
audio and subtitle do not sync with each other. Normally it lags/gains by a few
|
|
57
|
+
seconds/milliseconds. You will be able to adjust and generate a new subtitle file
|
|
58
|
+
or update the existing one.
|
|
59
|
+
email:
|
|
60
|
+
- hotline.shiva@gmail.com
|
|
61
|
+
executables:
|
|
62
|
+
- subjuster
|
|
63
|
+
extensions: []
|
|
64
|
+
extra_rdoc_files: []
|
|
65
|
+
files:
|
|
66
|
+
- ".travis.yml"
|
|
67
|
+
- LICENSE.txt
|
|
68
|
+
- exe/subjuster
|
|
69
|
+
- lib/subjuster.rb
|
|
70
|
+
- lib/subjuster/adjuster.rb
|
|
71
|
+
- lib/subjuster/generator.rb
|
|
72
|
+
- lib/subjuster/parser.rb
|
|
73
|
+
- lib/subjuster/user_input.rb
|
|
74
|
+
- lib/subjuster/version.rb
|
|
75
|
+
- subjuster.gemspec
|
|
76
|
+
homepage: http://subjuster.github.io
|
|
77
|
+
licenses:
|
|
78
|
+
- MIT
|
|
79
|
+
metadata: {}
|
|
80
|
+
post_install_message: Please run `$ subjuster -h` to learn how to use.
|
|
81
|
+
rdoc_options: []
|
|
82
|
+
require_paths:
|
|
83
|
+
- lib
|
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
|
+
requirements:
|
|
91
|
+
- - ">="
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: '0'
|
|
94
|
+
requirements: []
|
|
95
|
+
rubyforge_project:
|
|
96
|
+
rubygems_version: 2.6.12
|
|
97
|
+
signing_key:
|
|
98
|
+
specification_version: 4
|
|
99
|
+
summary: Subjuster | TDD guide for Software Engineers in OOP
|
|
100
|
+
test_files: []
|