ts2mkv.py

changeset 14
b398ae388328
parent 13
cf5c5cec1b2b
child 15
82361ad7b3fe
equal deleted inserted replaced
13:cf5c5cec1b2b 14:b398ae388328
36 break 36 break
37 if output: 37 if output:
38 print output.strip() 38 print output.strip()
39 rc = process.poll() 39 rc = process.poll()
40 return rc 40 return rc
41
42 def ffmpeg_filename(filename):
43 """
44 Escape filename path contents for ffmpeg shell command
45 """
46 fn = "\\'".join(p for p in filename.split("'"))
47 fn = fn.replace(" ", "\\ ")
48 return fn
41 49
42 class ts2mkv(object): 50 class ts2mkv(object):
43 """ 51 """
44 Main worker class, contains all the magic & ffmpeg voodoo 52 Main worker class, contains all the magic & ffmpeg voodoo
45 """ 53 """
133 self.msg_prepare += "WARNING: cropdetect inconsistent over scan time, disabling autocrop\n" 141 self.msg_prepare += "WARNING: cropdetect inconsistent over scan time, disabling autocrop\n"
134 return None 142 return None
135 self.msg_prepare += "Crop detected: %s\n" % option 143 self.msg_prepare += "Crop detected: %s\n" % option
136 return option 144 return option
137 145
146 def __get_audiomap(self, info):
147 """
148 Select the wanted german and english audio streams from ffmpeg info
149 output: mapping list
150 """
151 audiomap = []
152 audioall = filter_lines(info, "Audio:")
153 audio = filter_lines(audioall, "(deu):")
154 aidx = self.get_stream_index(
155 filter_lines(audio, "ac3"))
156 if aidx == "":
157 print audioall
158 print "No AC3 german audio stream found"
159 # try to find the first german audio stream
160 aidx = self.get_stream_index(audio.split("\n")[0])
161 if aidx == "":
162 print "No other german audio streams, trying english..."
163 else:
164 print "Selecting first german stream."
165 audiomap.append(aidx)
166 else:
167 audiomap.append(aidx)
168
169 audio = filter_lines(audioall, "(eng):")
170 aidx = self.get_stream_index(
171 filter_lines(audio, "ac3"))
172 if aidx != "":
173 # append english audio too!
174 print "Selecting english ac3 stream."
175 audiomap.append(aidx)
176 return audiomap
177
138 def get_ffmpeg_command(self): 178 def get_ffmpeg_command(self):
139 """ 179 """
140 Too complex to describe, this does all the magic 180 Too complex to describe, this does all the magic
141 output: produces internal ffmpeg command list (empty command list on error) 181 output: produces internal ffmpeg command list (empty command list on error)
142 """ 182 """
143 if not self.filename: 183 if not self.filename:
144 return None 184 return None
145 185
146 186
147 commands = [] 187 commands = []
148 fn = "\\'".join(p for p in self.filename.split("'")) 188 fn = ffmpeg_filename(self.filename)
149 fn = fn.replace(" ", "\\ ")
150 outfn = self.outfilebase + ".mkv" 189 outfn = self.outfilebase + ".mkv"
151 # double-check: pull the kill switch and exit if outfile exists already! 190 # double-check: pull the kill switch and exit if outfile exists already!
152 # we do not want to overwrite files in accident (caused by automatic file naming) 191 # we do not want to overwrite files in accident (caused by automatic file naming)
153 if len(glob.glob(outfn)) > 0: 192 if len(glob.glob(outfn)) > 0:
154 print "Output file exists: %s" % outfn 193 print "Output file exists: %s" % outfn
155 print "NOT overwriting it!" 194 print "NOT overwriting it!"
156 return None 195 return None
157 outfn = "\\'".join(p for p in outfn.split("'")) 196 outfn = ffmpeg_filename(outfn)
158 outfn = outfn.replace(" ", "\\ ") 197
159 198 cmd = [
160 cmd = ["ffmpeg", 199 "ffmpeg", "-hide_banner",
161 "-ss 00:05:00", "-t 1", # search to 5 minutes, analyze 1 second 200 "-ss 00:05:00", "-t 1", # search to 5 minutes, analyze 1 second
162 "-i %s" % fn, 201 "-i %s" % fn,
163 "-vf \"cropdetect=24:2:0\"", # detect black bar crop on top and bottom 202 "-vf \"cropdetect=24:2:0\"", # detect black bar crop on top and bottom
164 "-f null", "-" # no output file 203 "-f null", "-" # no output file
165 ] 204 ]
166 print " ".join(cmd)
167 p = subprocess.Popen(shlex.split(" ".join(cmd)), \ 205 p = subprocess.Popen(shlex.split(" ".join(cmd)), \
168 stdout=subprocess.PIPE, stderr=subprocess.PIPE) 206 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
169 out, err = p.communicate() 207 out, err = p.communicate()
170 print "Command return code: ", p.poll()
171 self.msg_ffmpeg = out + "\n" + err 208 self.msg_ffmpeg = out + "\n" + err
172 self.msg_ffmpeg = self.msg_ffmpeg[self.msg_ffmpeg.find("Input #0"):] 209 self.msg_ffmpeg = self.msg_ffmpeg[self.msg_ffmpeg.find("Input #0"):]
173 210
174 # find "Stream #0:" lines 211 # find "Stream #0:" lines
175 info = filter_lines(self.msg_ffmpeg, "Stream #0:") 212 info = filter_lines(self.msg_ffmpeg, "Stream #0:")
176
177
178 # CRAP! ffmpeg cannot decode dvb_teletext streams to srt :(
179 # are there any subtitle streams?!
180 #s = filter_lines(info, "Subtitle:")
181 #s = filter_lines(s, "(deu):")
182 #if s != "":
183 # s = self.get_stream_index(s.split("\n")[0])
184 # commands.append([
185 # "ffmpeg",
186 # "-txt_format text",
187 # "-i %s" % fn,
188 # "-map %s" % s,
189 # "%s.srt" % os.path.splitext(fn)[0]
190 # ])
191 213
192 v = self.get_stream_index( 214 v = self.get_stream_index(
193 filter_lines(info, "Video:")) 215 filter_lines(info, "Video:"))
194 if v == "": 216 if v == "":
195 print "No video stream found" 217 print "No video stream found"
196 return None 218 return None
197 219
198 audiomap = [] 220
199 audioall = filter_lines(info, "Audio:") 221 # TODO: copy ALL subtitle streams if present!
200 audio = filter_lines(audioall, "(deu):") 222 # Stream #0:0[0x20](deu): Subtitle: dvb_teletext ([6][0][0][0] / 0x0006), 492x250
201 a = self.get_stream_index( 223 submap = []
202 filter_lines(audio, "ac3")) 224 for tmp in filter_lines(info, "Subtitle: dvb_teletext").split("\n"):
203 # TODO: wenn kein ac3 stream dann dts oder mpeg fallback 225 if self.get_stream_index(tmp):
204 if a == "": 226 submap.append(self.get_stream_index(tmp))
205 print audioall 227
206 print "No AC3 german audio stream found" 228 # select audio streams
207 # try to find the first german audio stream 229 audiomap = self.__get_audiomap(info)
208 a = self.get_stream_index(audio.split("\n")[0])
209 if a == "":
210 print "No other german audio streams, trying english ac3..."
211 else:
212 print "Selecting first german stream."
213 audiomap.append(a)
214 else:
215 audiomap.append(a)
216
217 audio = filter_lines(audioall, "(eng):")
218 a = self.get_stream_index(
219 filter_lines(audio, "ac3"))
220 if a != "":
221 # append english audio too!
222 print "Selecting english ac3 stream."
223 audiomap.append(a)
224
225 if len(audiomap) == 0: 230 if len(audiomap) == 0:
226 print "No suitable audio stream found, aborting." 231 print "No suitable audio stream found, aborting."
227 return None 232 return None
228 233
229 234 # Old dreambox images did a file split: .ts .ts.001 .ts.002 etc.
230 self.msg_prepare += "Video Stream selected: Stream #%s\n" % v
231
232 # TODO: Old dreambox images did a file split: .ts .ts.001 .ts.002 etc.
233 # Find all these files and join them! 235 # Find all these files and join them!
234 inputs = [fn] 236 inputs = [fn]
235 if os.path.splitext(fn)[1].lower() == '.ts': 237 if os.path.splitext(fn)[1].lower() == '.ts':
236 for fpart in glob.glob(self.filename + '.' + ('[0-9]' * 3)): 238 for fpart in glob.glob(self.filename + '.' + ('[0-9]' * 3)):
237 fn = "\\'".join(p for p in fpart.split("'")) 239 fn = "\\'".join(p for p in fpart.split("'"))
238 fn = fn.replace(" ", "\\ ") 240 fn = fn.replace(" ", "\\ ")
239 inputs.append(fn) 241 inputs.append(fn)
240 #inputs.append(shlex.split(fpart))
241 242
242 if len(inputs) > 1: 243 if len(inputs) > 1:
243 # use ffmpeg input concat function 244 # use ffmpeg input concat function
244 # attention, ffmpeg doesnt like escape sequences 245 # attention, ffmpeg doesnt like escape sequences
245 fn = "\"concat:" + \ 246 fn = "\"concat:" + \
253 self.msg_prepare += "Input file #%i: %s\n" % ( 254 self.msg_prepare += "Input file #%i: %s\n" % (
254 idx, os.path.basename(tmp)) 255 idx, os.path.basename(tmp))
255 idx += 1 256 idx += 1
256 257
257 cmd = [ 258 cmd = [
258 "ffmpeg", 259 "ffmpeg", "-hide_banner",
259 "-i %s" % fn, 260 "-i %s" % fn,
260 "-map %s" % v,
261 ] 261 ]
262
263 for tmp in submap:
264 self.msg_prepare += "Subtitle Stream selected: Stream #%s\n" % tmp
265 cmd.append("-map %s" % tmp)
266
267 cmd.append("-map %s" % v)
268 self.msg_prepare += "Video Stream selected: Stream #%s\n" % v
269
262 flt = [] 270 flt = []
263 crop = self.get_crop_option() 271 crop = self.get_crop_option()
264 if crop: 272 if crop:
265 flt.append(crop) 273 flt.append(crop)
266 if self.scaleto_720p: 274 if self.scaleto_720p:
267 # -2 ensures division by two for codec 275 # -2 ensures division by two for codec
268 flt.append("scale='min(1280,iw)':-2'") 276 flt.append("scale='min(1280,iw)':-2'")
269 self.msg_prepare += "Scaling cropped output stream to 720p\n" 277 self.msg_prepare += "Scaling output stream to 720p if width >1280\n"
270 if len(flt) > 0: 278 if len(flt) > 0:
271 # append video filters 279 # append video filters
272 cmd.append('-filter:v "%s"' % ",".join(flt)) 280 cmd.append('-filter:v "%s"' % ",".join(flt))
273 for a in audiomap: 281 for tmp in audiomap:
274 self.msg_prepare += "Audio Stream selected: Stream #%s\n" % a 282 self.msg_prepare += "Audio Stream selected: Stream #%s\n" % tmp
275 cmd.append("-map %s" % a) 283 cmd.append("-map %s" % tmp)
284 if len(submap) > 0:
285 cmd.append("-c:s dvdsub")
276 cmd.extend(self.video_options) 286 cmd.extend(self.video_options)
277 cmd.extend(self.audio_options) 287 cmd.extend(self.audio_options)
278 cmd.append(outfn) 288 cmd.append(outfn)
279 289
280 commands.append(" ".join(cmd)) 290 commands.append(" ".join(cmd))

mercurial