Posted By

plexus on 12/22/07


Tagged

movie sync mplayer subtitles


Versions (?)

Who likes this?

2 people have marked this snippet as a favorite

willcodeforfood
webstic


Resync .srt subtitles


 / Published in: Ruby
 

URL: http://www.arnebrasseur.net

  1. #!/usr/bin/ruby
  2.  
  3. =begin doc
  4. Arne Brasseur, http://www.arnebrasseur.net
  5. Copyright 2007
  6. Freely available under the terms of the BSD licence
  7.  
  8. Suppose you have a .srt file with subtitles,
  9. it's a textfile that looks like this:
  10.  
  11.  
  12. 1
  13. 00:00:54,788 --> 00:00:57,222
  14. Besides the constant urge and inability to pee...
  15.  
  16. 2
  17. 00:00:57,290 --> 00:00:59,087
  18. Are there other symptoms?
  19.  
  20. 3
  21. 00:01:08,835 --> 00:01:10,700
  22. I'll just have a look.
  23.  
  24.  
  25. You also have a movie file, you try to play it with
  26. mplayer the subs using mplayer but they're badly out of sync.
  27.  
  28. Play the movie again with mplayer and pause it (space)
  29. at a point in the beginning where you know a certain
  30. subtitle should start, e.g. subtitle 3 : "I'll just have a look"
  31.  
  32. Now look at your terminal, the last line looks like this:
  33.  
  34. A:12.2 V:12.2 A-V: 0.000 ct: 0.048 30580/30580 4% 0% 1.1% 0 0
  35.  
  36. That first number 12.2 is the current position in seconds, take note of this.
  37.  
  38. Repeat this but this time for a subtitle somewhere at the end of the movie.
  39.  
  40. Now use subscale.rb to reposition the subtitles:
  41.  
  42. subscale.rb Filename.srt 3 12.2 1130 5357.4 > new_subtitles.srt
  43.  
  44. =end
  45.  
  46.  
  47. module SubScaler
  48. class <<self
  49. def scale(filename, percentage)
  50. puts( SubList.load(filename) * Float(percentage) )
  51. end
  52.  
  53. def reposition(filename, id1, time1, id2, time2)
  54. id1, id2, time1, time2 = Integer(id1), Integer(id2), Float(time1), Float(time2)
  55. subs = SubList.load(filename)
  56.  
  57. orig_diff = subs[id2].from.value - subs[id1].from.value
  58. new_diff = time2 - time1
  59. subs *= (new_diff / orig_diff)
  60. subs += time1 - subs[id1].from.value
  61.  
  62. puts subs
  63. end
  64. end
  65.  
  66. class Time
  67. REGEX = /(\d{2}):(\d{2}):(\d{2}),(\d{3})/
  68.  
  69. attr_reader :value
  70.  
  71. class <<self
  72. def parse(str)
  73. hh,mm,ss,ms = str.scan(REGEX).flatten.map{|i| Float(i)}
  74. value = ((((hh*60)+mm)*60)+ss) + ms/1000
  75. self.new(value)
  76. end
  77. end
  78.  
  79. def initialize(value)
  80. @value = value
  81. end
  82.  
  83. def *(factor)
  84. Time.new(@value * factor)
  85. end
  86.  
  87. def +(term)
  88. Time.new(@value + term)
  89. end
  90.  
  91. def to_s
  92. ss = @value.floor
  93. ms = ((@value - ss)*1000).to_i
  94.  
  95. mm = ss / 60
  96. ss = ss - mm * 60
  97.  
  98. hh = mm / 60
  99. mm = mm - hh * 60
  100.  
  101. "%02i:%02i:%02i,%03i" % [hh, mm, ss, ms]
  102. end
  103. end
  104.  
  105. class Sub < Struct.new(:index, :from, :to, :sub)
  106. def *(factor)
  107. Sub.new(self.index, self.from * factor, self.to * factor, self.sub)
  108. end
  109.  
  110. def +(term)
  111. Sub.new(self.index, self.from + term, self.to + term, self.sub)
  112. end
  113.  
  114. def to_s
  115. "#{self.index}
  116. #{self.from} --> #{self.to}
  117. #{self.sub}
  118.  
  119. "
  120. end
  121. end
  122.  
  123. class SubList
  124. TSREGEX = /\d{2}:\d{2}:\d{2},\d{3}/
  125. REGEX = /(\d+)
  126. (#{TSREGEX}) --> (#{TSREGEX})
  127. (.*?)
  128.  
  129. /m
  130.  
  131. attr_accessor :subs
  132.  
  133. class <<self
  134. def load(fn)
  135. subs = IO.read(fn).scan(REGEX).map do |r|
  136. Sub.new(r[0], Time.parse(r[1]), Time.parse(r[2]), r[3])
  137. end
  138. self.new subs
  139. end
  140. end
  141.  
  142. def initialize(subs)
  143. @subs = subs
  144. end
  145.  
  146. def *(factor)
  147. SubList.new(@subs.map {|s| s * factor})
  148. end
  149.  
  150. def +(term)
  151. SubList.new(@subs.map {|s| s + term})
  152. end
  153.  
  154. def [](i)
  155. @subs[i-1]
  156. end
  157.  
  158. def to_s
  159. @subs.map {|s| s.to_s}.join
  160. end
  161. end
  162. end
  163.  
  164. if __FILE__ == $0
  165. if ARGV.length == 2
  166. SubScaler.scale(ARGV[0], ARGV[1])
  167. elsif ARGV.length == 5
  168. SubScaler.reposition(*ARGV[0..5])
  169. else
  170. puts %{
  171. Usage:
  172. #{File.basename $0} filename percentage
  173. #{File.basename $0} filename index1 time1 index2 time2
  174. }
  175. end
  176. end

Report this snippet  

Comments

RSS Icon Subscribe to comments
Posted By: jhannes on February 23, 2008

I got this code to work, but not after some minor fixes. Two problems: First: Start and end time is both set to start time (whops), which means that subtitles will not show up. Second: Multiline subtitles are compressed to one line. Here is the fix:

In SubScaler::SubList<

Posted By: jhannes on February 23, 2008

My last comment didn't work. The fix again:

Sub.new(r[0], Time.parse(r[1]), Time.parse(r[1]), r[3].gsub(/\s+/, ' '))

should be

Sub.new(r[0], Time.parse(r[1]), Time.parse(r[2]), r[3])

Posted By: plexus on July 23, 2008

Thanks for your comment, I corrected the code.

Posted By: nilbus on April 2, 2012

Thanks for this! It should be noted that the file must have Unix (not DOS) line endings.

You need to login to post a comment.