Skip to content

Instantly share code, notes, and snippets.

@msg7086
Last active August 29, 2015 14:20
Show Gist options
  • Save msg7086/7cb9f08f5c9ffeced507 to your computer and use it in GitHub Desktop.
Save msg7086/7cb9f08f5c9ffeced507 to your computer and use it in GitHub Desktop.
require 'rake'
require 'time'
Encoding.default_internal = Encoding::UTF_8
Encoding.default_external = Encoding::UTF_8
class Timestamp
attr_reader :from, :to
def initialize from, to
@from = from.to_i
@to = to.to_i
end
def to_inc
@to + 1
end
def duration
to_inc - from
end
end
class Fixnum
def to_time fps:, delay: 0
(self / fps - delay / 1000.0).round(3)
end
end
class SubtitleLine
attr_reader :dialog
def initialize line
@text = line
@dialog = line['Dialogue'] ? true : false
if @dialog
@timepart = line[/([0-9:.]{10,10}),([0-9:.]{10,10})/]
@start = Time.parse("1/1/1970 #{$1} UTC").to_f
@end = Time.parse("1/1/1970 #{$2} UTC").to_f
end
end
def offset ms
@start += ms
@end += ms
@start = 0 if @start < 0
@end = 0 if @end < 0
start_text = Time.at(@start).utc.strftime('%-H:%M:%S.%2N')
end_text = Time.at(@end).utc.strftime('%-H:%M:%S.%2N')
@text[@timepart] = "#{start_text},#{end_text}"
end
def disable!
return if !@dialog
@dialog = false
@text = '; ' + @text
end
def between? from:, to:
@start > from && @start < to
end
def to_s
@text
end
end
class TSProcessor
include FileUtils
def initialize time: 'time.txt', fps: 24000.0/1001
@time = time
@fps = fps
@timecode = []
end
def map &block
parse_timecode
@tc.map &block
end
def parse_timecode
return if [email protected]?
raise IOError.new("Timecode file not exists") if !File.exists? @time
@timecode = File.readlines(@time).map do |line|
m = line.match /(\d+)\D+(\d+)/
m.nil? ? nil : Timestamp.new(m[1], m[2])
end.compact
end
def aac source:, target: nil, delay: 0
parse_timecode
raise IOError if !File.exists? source
ext = File.extname(source)
base = File.basename(source, ext)
target = base + '.new.m4a' if target.nil?
if ext['.aac']
base = '_' + base + '.aacmk'
ext = '.m4a'
mp4 = base + ext
sh "muxer -i #{source} -o #{mp4}" if !File.exists? mp4
source = mp4
end
# Produce slices
diff = 0
name = ''
mp4_files = @timecode.map do |t|
from = t.from.to_time(fps: @fps) + diff
to = t.to_inc.to_time(fps: @fps)
duration = to - from
sh "mp4box -splitx #{from}:#{to} #{source}"
name = "#{base}_#{from.truncate}_#{to.truncate}#{ext}"
`mediainfo -f #{name} | grep Duration | head -n 1`[/ (\d+)\Z/]
actual_duration = $1.to_f / 1000.0
diff = actual_duration - duration
puts 'Ideal: %.3f, Actual: %.3f, Diff: %.3f' % [duration, actual_duration, diff]
" -cat #{name} "
end
# Concatinating
mv target, target+'.bak' if File.exists? target
if mp4_files.size == 1
mv name, target
else
sh "mp4box #{mp4_files.join} #{target}"
end
end
def avs source: 'main.avs', target: nil
parse_timecode
ext = File.extname(source)
base = File.basename(source, ext)
target = base + '.trim.avs' if target.nil?
File.write target, @timecode.map{ |t| "trim(#{t.from},#{t.to})" }.join(' + ')
end
def delogo source: 'main.avs', target: nil, fade: 0, offset1: 0, offset2: 0
parse_timecode
ext = File.extname(source)
base = File.basename(source, ext)
target = base + '.delogo.avs' if target.nil?
current = 0
File.open target, 'w' do |f|
@timecode.each do |tc|
length = tc.duration
f << 'EraseLOGO(logoFile'
f << ', fadein=%d, fadeout=%d' % [fade, fade] if fade != 0
f << ', start=%d, end=%d)' % [current + offset1, current + length - offset2]
f << "\n"
current += length
end
end
end
def sub source: 'main.kingyubi.ass', target: nil, delay: 0
parse_timecode
raise IOError if !File.exists? source
target = source.ext('.new.ass') if target.nil?
# Convert to Time Range
start_time = 0.0
timerange = @timecode.map do |t|
s = t.from.to_time fps: @fps
e = t.to_inc.to_time fps: @fps
offset = start_time - s
start_time += e - s
{:s => s, :e => e, :o => offset}
end
subs = File.readlines(source).map { |l| SubtitleLine.new l }
subs.each do |s|
next if !s.dialog
catch :offset do
timerange.each do |t|
if s.between? from: t[:s], to: t[:e]
s.offset t[:o]
throw :offset
end
end
s.disable!
end
end
File.open(target, 'w') do |f|
subs.each { |s| f << s.to_s }
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment