jagthedrummer-rvideo 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/lib/rvideo/errors.rb +24 -0
  2. data/lib/rvideo/float.rb +7 -0
  3. data/lib/rvideo/inspector.rb +528 -0
  4. data/lib/rvideo/reporter/views/index.html.erb +27 -0
  5. data/lib/rvideo/reporter/views/report.css +27 -0
  6. data/lib/rvideo/reporter/views/report.html.erb +81 -0
  7. data/lib/rvideo/reporter/views/report.js +9 -0
  8. data/lib/rvideo/reporter.rb +176 -0
  9. data/lib/rvideo/string.rb +5 -0
  10. data/lib/rvideo/tools/abstract_tool.rb +393 -0
  11. data/lib/rvideo/tools/ffmpeg.rb +273 -0
  12. data/lib/rvideo/tools/ffmpeg2theora.rb +42 -0
  13. data/lib/rvideo/tools/flvtool2.rb +50 -0
  14. data/lib/rvideo/tools/mencoder.rb +103 -0
  15. data/lib/rvideo/tools/mp4box.rb +21 -0
  16. data/lib/rvideo/tools/mp4creator.rb +35 -0
  17. data/lib/rvideo/tools/mplayer.rb +31 -0
  18. data/lib/rvideo/tools/yamdi.rb +44 -0
  19. data/lib/rvideo/transcoder.rb +120 -0
  20. data/lib/rvideo/version.rb +9 -0
  21. data/lib/rvideo.rb +42 -0
  22. data/spec/files/boat.avi +0 -0
  23. data/spec/files/kites.mp4 +0 -0
  24. data/spec/fixtures/ffmpeg_builds.yml +28 -0
  25. data/spec/fixtures/ffmpeg_results.yml +608 -0
  26. data/spec/fixtures/files.yml +398 -0
  27. data/spec/fixtures/recipes.yml +58 -0
  28. data/spec/integrations/formats_spec.rb +315 -0
  29. data/spec/integrations/inspection_spec.rb +112 -0
  30. data/spec/integrations/recipes_spec.rb +0 -0
  31. data/spec/integrations/rvideo_spec.rb +17 -0
  32. data/spec/integrations/transcoder_integration_spec.rb +53 -0
  33. data/spec/integrations/transcoding_spec.rb +9 -0
  34. data/spec/spec.opts +1 -0
  35. data/spec/spec_helper.rb +16 -0
  36. data/spec/support.rb +36 -0
  37. data/spec/units/abstract_tool_spec.rb +111 -0
  38. data/spec/units/ffmpeg_spec.rb +311 -0
  39. data/spec/units/flvtool2_spec.rb +324 -0
  40. data/spec/units/inspector_spec.rb +103 -0
  41. data/spec/units/mencoder_spec.rb +4994 -0
  42. data/spec/units/mp4box_spec.rb +34 -0
  43. data/spec/units/mp4creator_spec.rb +34 -0
  44. data/spec/units/mplayer_spec.rb +34 -0
  45. data/spec/units/string_spec.rb +8 -0
  46. data/spec/units/transcoder_spec.rb +156 -0
  47. metadata +110 -0
@@ -0,0 +1,24 @@
1
+ module RVideo
2
+ class TranscoderError < RuntimeError
3
+ class InvalidCommand < TranscoderError
4
+ end
5
+
6
+ class InvalidFile < TranscoderError
7
+ end
8
+
9
+ class InputFileNotFound < TranscoderError
10
+ end
11
+
12
+ class UnexpectedResult < TranscoderError
13
+ end
14
+
15
+ class ParameterError < TranscoderError
16
+ end
17
+
18
+ class UnknownError < TranscoderError
19
+ end
20
+
21
+ class UnknownTool < TranscoderError
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ # Add a rounding method to the Float class.
2
+ class Float
3
+ def round_to(x)
4
+ (self * 10**x).round.to_f / 10**x
5
+ end
6
+ end
7
+
@@ -0,0 +1,528 @@
1
+ module RVideo # :nodoc:
2
+ # To inspect a video or audio file, initialize an Inspector object.
3
+ #
4
+ # file = RVideo::Inspector.new(options_hash)
5
+ #
6
+ # Inspector accepts three options: file, raw_response, and ffmpeg_binary.
7
+ # Either raw_response or file is required; ffmpeg binary is optional.
8
+ #
9
+ # :file is a path to a file to be inspected.
10
+ #
11
+ # :raw_response is the full output of "ffmpeg -i [file]". If the
12
+ # :raw_response option is used, RVideo will not actually inspect a file;
13
+ # it will simply parse the provided response. This is useful if your
14
+ # application has already collected the ffmpeg -i response, and you don't
15
+ # want to call it again.
16
+ #
17
+ # :ffmpeg_binary is an optional argument that specifies the path to the
18
+ # ffmpeg binary to be used. If a path is not explicitly declared, RVideo
19
+ # will assume that ffmpeg exists in the Unix path. Type "which ffmpeg" to
20
+ # check if ffmpeg is installed and exists in your operating system's path.
21
+ class Inspector
22
+ attr_reader :filename, :path, :full_filename, :raw_response, :raw_metadata
23
+
24
+ attr_accessor :ffmpeg_binary
25
+
26
+ def initialize(options = {})
27
+ if not (options[:raw_response] or options[:file])
28
+ raise ArgumentError, "Must supply either an input file or a pregenerated response"
29
+ end
30
+
31
+ if options[:raw_response]
32
+ initialize_with_raw_response(options[:raw_response])
33
+ elsif options[:file]
34
+ initialize_with_file(options[:file], options[:ffmpeg_binary])
35
+ end
36
+
37
+ metadata = /(Input \#.*)\n.+\n\Z/m.match(@raw_response)
38
+
39
+ if /Unknown format/i.match(@raw_response) || metadata.nil?
40
+ @unknown_format = true
41
+ elsif /Duration: N\/A/im.match(@raw_response)
42
+ # in this case, we can at least still get the container type
43
+ @unreadable_file = true
44
+ @raw_metadata = metadata[1]
45
+ else
46
+ @raw_metadata = metadata[1]
47
+ end
48
+ end
49
+
50
+ def initialize_with_raw_response(raw_response)
51
+ @raw_response = raw_response
52
+ end
53
+
54
+ def initialize_with_file(file, ffmpeg_binary = nil)
55
+ if ffmpeg_binary
56
+ @ffmpeg_binary = ffmpeg_binary
57
+ if not FileTest.exist?(@ffmpeg_binary)
58
+ raise "ffmpeg could not be found (trying #{@ffmpeg_binary})"
59
+ end
60
+ else
61
+ # assume it is in the unix path
62
+ if not FileTest.exist?(`which ffmpeg`.chomp)
63
+ raise "ffmpeg could not be found (expected ffmpeg to be found in the Unix path)"
64
+ end
65
+ @ffmpeg_binary = "ffmpeg"
66
+ end
67
+
68
+ if not FileTest.exist?(file.gsub('"',''))
69
+ raise TranscoderError::InputFileNotFound, "File not found (#{file})"
70
+ end
71
+
72
+ @full_filename = file
73
+ @filename = File.basename(@full_filename)
74
+ @path = File.dirname(@full_filename)
75
+
76
+ @raw_response = `#{@ffmpeg_binary} -i #{@full_filename.shell_quoted} 2>&1`
77
+ end
78
+
79
+ # Returns true if the file can be read successfully. Returns false otherwise.
80
+ def valid?
81
+ not (@unknown_format or @unreadable_file)
82
+ end
83
+
84
+ # Returns false if the file can be read successfully. Returns false otherwise.
85
+ def invalid?
86
+ not valid?
87
+ end
88
+
89
+ # True if the format is not understood ("Unknown Format")
90
+ def unknown_format?
91
+ @unknown_format ? true : false
92
+ end
93
+
94
+ # True if the file is not readable ("Duration: N/A, bitrate: N/A")
95
+ def unreadable_file?
96
+ @unreadable_file ? true : false
97
+ end
98
+
99
+ # Does the file have an audio stream?
100
+ def audio?
101
+ not audio_match.nil?
102
+ end
103
+
104
+ # Does the file have a video stream?
105
+ def video?
106
+ not video_match.nil?
107
+ end
108
+
109
+ # Take a screengrab of a movie. Requires an input file and a time parameter,
110
+ # and optionally takes an output filename. If no output filename is specfied,
111
+ # constructs one.
112
+ #
113
+ # Three types of time parameters are accepted - percentage (e.g. 3%), time in
114
+ # seconds (e.g. 60 seconds), and raw frame (e.g. 37). Will raise an exception if
115
+ # the time in seconds or the frame are out of the bounds of the input file.
116
+ #
117
+ # Types:
118
+ # 37s (37 seconds)
119
+ # 37f (frame 37)
120
+ # 37% (37 percent)
121
+ # 37 (default to seconds)
122
+ #
123
+ # If a time is outside of the duration of the file, it will choose a frame at the
124
+ # 99% mark.
125
+ #
126
+ # Example:
127
+ #
128
+ # t = RVideo::Transcoder.new('path/to/input_file.mp4')
129
+ # t.capture_frame('10%') # => '/path/to/screenshot/input-10p.jpg'
130
+ #
131
+ def capture_frame(timecode, output_file = nil)
132
+ t = calculate_time(timecode)
133
+ output_file ||= "#{TEMP_PATH}/#{File.basename(@full_filename, ".*")}-#{timecode.gsub("%","p")}.jpg"
134
+ command = "ffmpeg -i #{@full_filename.shell_quoted} -ss #{t} -t 00:00:01 -r 1 -vframes 1 -f image2 #{output_file.shell_quoted}"
135
+
136
+ RVideo.logger.info("\nCreating Screenshot: #{command}\n")
137
+ frame_result = `#{command} 2>&1`
138
+ RVideo.logger.info("\nScreenshot results: #{frame_result}")
139
+
140
+ output_file
141
+ end
142
+
143
+ def calculate_time(timecode)
144
+ m = /\A([0-9\.\,]*)(s|f|%)?\Z/.match(timecode)
145
+ if m.nil? or m[1].nil? or m[1].empty?
146
+ raise TranscoderError::ParameterError, "Invalid timecode for frame capture: #{timecode}. Must be a number, optionally followed by s, f, or %."
147
+ end
148
+
149
+ case m[2]
150
+ when "s", nil
151
+ t = m[1].to_f
152
+ when "f"
153
+ t = m[1].to_f / fps.to_f
154
+ when "%"
155
+ # milliseconds / 1000 * percent / 100
156
+ t = (duration.to_i / 1000.0) * (m[1].to_f / 100.0)
157
+ else
158
+ raise TranscoderError::ParameterError, "Invalid timecode for frame capture: #{timecode}. Must be a number, optionally followed by s, f, or p."
159
+ end
160
+
161
+ if (t * 1000) > duration
162
+ calculate_time("99%")
163
+ else
164
+ t
165
+ end
166
+ end
167
+
168
+ # Returns the version of ffmpeg used, In practice, this may or may not be
169
+ # useful.
170
+ #
171
+ # Examples:
172
+ #
173
+ # SVN-r6399
174
+ # CVS
175
+ #
176
+ def ffmpeg_version
177
+ @ffmpeg_version = @raw_response.split("\n").first.split("version").last.split(",").first.strip
178
+ end
179
+
180
+ # Returns the configuration options used to build ffmpeg.
181
+ #
182
+ # Example:
183
+ #
184
+ # --enable-mp3lame --enable-gpl --disable-ffplay --disable-ffserver
185
+ # --enable-a52 --enable-xvid
186
+ #
187
+ def ffmpeg_configuration
188
+ /(\s*configuration:)(.*)\n/.match(@raw_response)[2].strip
189
+ end
190
+
191
+ # Returns the versions of libavutil, libavcodec, and libavformat used by
192
+ # ffmpeg.
193
+ #
194
+ # Example:
195
+ #
196
+ # libavutil version: 49.0.0
197
+ # libavcodec version: 51.9.0
198
+ # libavformat version: 50.4.0
199
+ #
200
+ def ffmpeg_libav
201
+ /^(\s*lib.*\n)+/.match(@raw_response)[0].split("\n").each {|l| l.strip! }
202
+ end
203
+
204
+ # Returns the build description for ffmpeg.
205
+ #
206
+ # Example:
207
+ #
208
+ # built on Apr 15 2006 04:58:19, gcc: 4.0.1 (Apple Computer, Inc. build
209
+ # 5250)
210
+ #
211
+ def ffmpeg_build
212
+ /(\n\s*)(built on.*)(\n)/.match(@raw_response)[2]
213
+ end
214
+
215
+ # Returns the container format for the file. Instead of returning a single
216
+ # format, this may return a string of related formats.
217
+ #
218
+ # Examples:
219
+ #
220
+ # "avi"
221
+ #
222
+ # "mov,mp4,m4a,3gp,3g2,mj2"
223
+ #
224
+ def container
225
+ return nil if @unknown_format
226
+ /Input \#\d+\,\s*(\S+),\s*from/.match(@raw_metadata)[1]
227
+ end
228
+
229
+ # The duration of the movie, as a string.
230
+ #
231
+ # Example:
232
+ #
233
+ # "00:00:24.4" # 24.4 seconds
234
+ #
235
+ def raw_duration
236
+ return nil unless valid?
237
+ /Duration:\s*([0-9\:\.]+),/.match(@raw_metadata)[1]
238
+ end
239
+
240
+ # The duration of the movie in milliseconds, as an integer.
241
+ #
242
+ # Example:
243
+ #
244
+ # 24400 # 24.4 seconds
245
+ #
246
+ # Note that the precision of the duration is in tenths of a second, not
247
+ # thousandths, but milliseconds are a more standard unit of time than
248
+ # deciseconds.
249
+ #
250
+ def duration
251
+ return nil unless valid?
252
+
253
+ units = raw_duration.split(":")
254
+ (units[0].to_i * 60 * 60 * 1000) + (units[1].to_i * 60 * 1000) + (units[2].to_f * 1000).to_i
255
+ end
256
+
257
+ # The bitrate of the movie.
258
+ #
259
+ # Example:
260
+ #
261
+ # 3132
262
+ #
263
+ def bitrate
264
+ return nil unless valid?
265
+ bitrate_match[1].to_i
266
+ end
267
+
268
+ # The bitrate units used. In practice, this may always be kb/s.
269
+ #
270
+ # Example:
271
+ #
272
+ # "kb/s"
273
+ #
274
+ def bitrate_units
275
+ return nil unless valid?
276
+ bitrate_match[2]
277
+ end
278
+
279
+ def bitrate_with_units
280
+ "#{bitrate} #{bitrate_units}"
281
+ end
282
+
283
+ def audio_bit_rate
284
+ return nil unless audio?
285
+ audio_match[7].to_i
286
+ end
287
+
288
+ def audio_bit_rate_units
289
+ return nil unless audio?
290
+ audio_match[8]
291
+ end
292
+
293
+ def audio_bit_rate_with_units
294
+ "#{audio_bit_rate} #{audio_bit_rate_units}"
295
+ end
296
+
297
+ def audio_stream
298
+ return nil unless valid?
299
+
300
+ match = /\n\s*Stream.*Audio:.*\n/.match(@raw_response)
301
+ match[0].strip if match
302
+ end
303
+
304
+ # The audio codec used.
305
+ #
306
+ # Example:
307
+ #
308
+ # "aac"
309
+ #
310
+ def audio_codec
311
+ return nil unless audio?
312
+ audio_match[2]
313
+ end
314
+
315
+ # The sampling rate of the audio stream.
316
+ #
317
+ # Example:
318
+ #
319
+ # 44100
320
+ #
321
+ def audio_sample_rate
322
+ return nil unless audio?
323
+ audio_match[3].to_i
324
+ end
325
+
326
+ # The units used for the sampling rate. May always be Hz.
327
+ #
328
+ # Example:
329
+ #
330
+ # "Hz"
331
+ #
332
+ def audio_sample_rate_units
333
+ return nil unless audio?
334
+ audio_match[4]
335
+ end
336
+ alias_method :audio_sample_units, :audio_sample_rate_units
337
+
338
+ def audio_sample_rate_with_units
339
+ "#{audio_sample_rate} #{audio_sample_rate_units}"
340
+ end
341
+
342
+ # The channels used in the audio stream.
343
+ #
344
+ # Examples:
345
+ # "stereo"
346
+ # "mono"
347
+ # "5:1"
348
+ #
349
+ def audio_channels_string
350
+ return nil unless audio?
351
+ audio_match[5]
352
+ end
353
+
354
+ def audio_channels
355
+ return nil unless audio?
356
+
357
+ case audio_match[5]
358
+ when "mono" then 1
359
+ when "stereo" then 2
360
+ else
361
+ raise RuntimeError, "Unknown number of channels: #{audio_channels}"
362
+ end
363
+ end
364
+
365
+ # This should almost always return 16,
366
+ # as the vast majority of audio is 16 bit.
367
+ def audio_sample_bit_depth
368
+ return nil unless audio?
369
+ audio_match[6].to_i
370
+ end
371
+
372
+ # The ID of the audio stream (useful for troubleshooting).
373
+ #
374
+ # Example:
375
+ # #0.1
376
+ #
377
+ def audio_stream_id
378
+ return nil unless audio?
379
+ audio_match[1]
380
+ end
381
+
382
+ def video_stream
383
+ return nil unless valid?
384
+
385
+ match = /\n\s*Stream.*Video:.*\n/.match(@raw_response)
386
+ match[0].strip unless match.nil?
387
+ end
388
+
389
+ # The ID of the video stream (useful for troubleshooting).
390
+ #
391
+ # Example:
392
+ # #0.0
393
+ #
394
+ def video_stream_id
395
+ return nil unless video?
396
+ video_match[1]
397
+ end
398
+
399
+ # The video codec used.
400
+ #
401
+ # Example:
402
+ #
403
+ # "mpeg4"
404
+ #
405
+ def video_codec
406
+ return nil unless video?
407
+ video_match[3]
408
+ end
409
+
410
+ # The colorspace of the video stream.
411
+ #
412
+ # Example:
413
+ #
414
+ # "yuv420p"
415
+ #
416
+ def video_colorspace
417
+ return nil unless video?
418
+ video_match[4]
419
+ end
420
+
421
+ # The width of the video in pixels.
422
+ def width
423
+ return nil unless video?
424
+ video_match[5].to_i
425
+ end
426
+
427
+ # The height of the video in pixels.
428
+ def height
429
+ return nil unless video?
430
+ video_match[6].to_i
431
+ end
432
+
433
+ # width x height, as a string.
434
+ #
435
+ # Examples:
436
+ # 320x240
437
+ # 1280x720
438
+ #
439
+ def resolution
440
+ return nil unless video?
441
+ "#{width}x#{height}"
442
+ end
443
+
444
+ def pixel_aspect_ratio
445
+ return nil unless video?
446
+ video_match[7]
447
+ end
448
+
449
+ def display_aspect_ratio
450
+ return nil unless video?
451
+ video_match[8]
452
+ end
453
+
454
+ # The frame rate of the video in frames per second
455
+ #
456
+ # Example:
457
+ #
458
+ # "29.97"
459
+ #
460
+ def fps
461
+ return nil unless video?
462
+ video_match[2] or video_match[9]
463
+ end
464
+ alias_method :framerate, :fps
465
+
466
+ def time_base
467
+ return nil unless video?
468
+ video_match[10]
469
+ end
470
+
471
+ def codec_time_base
472
+ return nil unless video?
473
+ video_match[11]
474
+ end
475
+
476
+ private
477
+
478
+ def bitrate_match
479
+ /bitrate: ([0-9\.]+)\s*(.*)\s+/.match(@raw_metadata)
480
+ end
481
+
482
+ ###
483
+ # I am wondering how reliable it would be to simplify a lot
484
+ # of this regexp parsery by using split(/\s*,\s*/) - Seth
485
+
486
+ SEP = '(?:,\s*)'
487
+ VAL = '([^,]+)'
488
+
489
+ RATE = '([\d.]+)'
490
+
491
+ AUDIO_MATCH_PATTERN = /
492
+ Stream\s+(.*?)[,:\(\[].*?\s*
493
+ Audio:\s+
494
+ #{VAL}#{SEP} # codec
495
+ #{RATE}\s+(\w*)#{SEP}? # sample rate
496
+ ([a-zA-Z:]*)#{SEP}? # channels
497
+ (?:s(\d+)#{SEP}?)? # audio sample bit depth
498
+ (?:(\d+)\s+(\S+))? # audio bit rate
499
+ /x
500
+
501
+ def audio_match
502
+ return nil unless valid?
503
+ AUDIO_MATCH_PATTERN.match(audio_stream)
504
+ end
505
+
506
+ FPS = 'fps(?:\(r\))?'
507
+
508
+ VIDEO_MATCH_PATTERN = /
509
+ Stream\s*(\#[\d.]+)(?:[\(\[].+?[\)\]])?\s* # stream id
510
+ [,:]\s*
511
+ (?:#{RATE}\s*#{FPS}[,:]\s*)? # frame rate, older builds
512
+ Video:\s*
513
+ #{VAL}#{SEP} # codec
514
+ (?:#{VAL}#{SEP})? # color space
515
+ (\d+)x(\d+) # resolution
516
+ (?:\s*\[(?:PAR\s*(\d+:\d+))?\s*(?:DAR\s*(\d+:\d+))?\])? # pixel and display aspect ratios
517
+ #{SEP}?
518
+ (?:#{RATE}\s*(?:tbr|#{FPS})#{SEP}?)? # frame rate
519
+ (?:#{RATE}\s*tbn#{SEP}?)? # time base
520
+ (?:#{RATE}\s*tbc#{SEP}?)? # codec time base
521
+ /x
522
+
523
+ def video_match
524
+ return nil unless valid?
525
+ VIDEO_MATCH_PATTERN.match(video_stream)
526
+ end
527
+ end
528
+ end
@@ -0,0 +1,27 @@
1
+ <html>
2
+ <head>
3
+ <title>RVideo Reports Index</title>
4
+ <link rel="stylesheet" href="report.css" type="text/css" media="screen" />
5
+ <script type="text/javascript" src="report.js"></script>
6
+ </head>
7
+ <body>
8
+ <% @results.each do |input_file, recipes| %>
9
+ <h1>
10
+ <a href="<%= input_file %>"><%= File.basename(input_file) %> (launch file)</a>
11
+ </h1>
12
+ <ol>
13
+ <% recipes.each do |recipe_name, result| %>
14
+ <li>
15
+ <% css_class = 'warning' unless result['transcoder'].errors.empty? %>
16
+ <% css_class = 'critical' if result['errors'] %>
17
+ <% css_class = 'passed' if css_class.nil? %>
18
+ <div class="<%= css_class %>">
19
+ <% individual_report_url = "individual_reports/" + underscoreize_file_basename(input_file) + "_" + recipe_name + ".html" %>
20
+ <h2><a href="<%= result['output_file'] %>">Launch <%= recipe_name %></a> <a class="view-report" href="<%= individual_report_url %>" >view full report</a></h2>
21
+ </div>
22
+ </li>
23
+ <% end %>
24
+ </ol>
25
+ <% end %>
26
+ </body>
27
+ </html>
@@ -0,0 +1,27 @@
1
+ /* a { color: black; margin-left: 20px;}
2
+ a:visited { color: #111; }
3
+ .critical { background-color: #E6E6E6; border-left: 20px solid #F00; }
4
+ .warning { background-color: #E6E6E6; border-left: 20px solid orange; }
5
+ .passed { background-color: #E6E6E6; border-left: 20px solid #0F0; }
6
+
7
+ div { margin: 10px; border: 1px solid #ccc; padding: 5px; }
8
+ li { margin: 20px; padding: 5px; list-style-type: none; }
9
+ span { background-color: #ccc; padding: 5px; width: 500px;}
10
+ span:hover { cursor: pointer; text-decoration: underline; }
11
+ */
12
+
13
+ html { font-size: .75em;}
14
+ div { font-size: 1.2em;}
15
+ h1, h2, h3, h4, h5, ul { margin: .5em; }
16
+ a { color: black; margin-left: 2em; padding: 1em;}
17
+ a:visited { color: #111; }
18
+ .critical, .warning, .passed { background-color: #E6E6E6; border-left: 5em solid; }
19
+ .critical { border-left-color: #F00; }
20
+ .warning { border-left-color: orange; }
21
+ .passed { border-left-color: #0F0; }
22
+
23
+ div { margin: 1em; border: 1px solid #ccc; padding: .5em; }
24
+ ol li { margin: .01em; padding: 0; margin-left: 5em; }
25
+ ul li { margin: 1em; padding: 1em; list-style-type: none; }
26
+ span { background-color: #ccc; padding: 1em; width: 500px;}
27
+ span:hover { cursor: pointer; text-decoration: underline; }
@@ -0,0 +1,81 @@
1
+ <html>
2
+ <head>
3
+ <title>RVideo Reports</title>
4
+ <link rel="stylesheet" href="<%= report.css %>" type="text/css" media="screen" />
5
+ <script type="text/javascript" src="<%= report.js %>"></script>
6
+ </head>
7
+ <body>
8
+ <div>
9
+ <h2><a href="<%= @result['output_file'] %>">Launch output file</a></h2>
10
+ <ul>
11
+ <li>
12
+ <div>
13
+ <h2>Recipe: <%= @recipe_name %></h2>
14
+ <p>
15
+ <% @result['recipe'].each do |key, value|%>
16
+ <%= key %>: <%= value %><br />
17
+ <% end %>
18
+ </p>
19
+ </div>
20
+ </li>
21
+ <% unless @result['errors'].nil? %>
22
+ <li>
23
+ <span onclick="toggle('rescued-errors');">Hide/Show Rescued Errors</span>
24
+ <div id='rescued-errors' style="display: none;">
25
+ <h2>Rescued Error Backtrace</h2>
26
+ <h4><%= h(@result['errors'].class.name) %></h4>
27
+ <p><%= h(@result['errors'].message)%>
28
+ <p><%= h(@result['errors'].backtrace) %></p>
29
+ </div>
30
+ </li>
31
+ <% end %>
32
+ <li>
33
+ <span onclick="toggle('transcoder');">Hide/Show Transcoder</span>
34
+ <div id='transcoder' style="display: none;">
35
+ <h2>Transcoder</h2>
36
+ <% unless @result['transcoder'].errors.empty? %>
37
+ <h3>Transcoder Errors</h3>
38
+ <p><%= h(@result['transcoder'].errors.inspect) %></p>
39
+ <% end %>
40
+ <h3>Executed Commands</h3>
41
+ <p><%= h(@result['transcoder'].executed_commands.map(&:command)) %></p>
42
+ <h3>Raw Meta</h3>
43
+ <p><%= h(@result['transcoder'].metadata) %></p>
44
+ </div>
45
+ <li>
46
+ <span onclick="toggle('input-file');">Hide/Show Input File</span>
47
+ <div id='input-file' style="display: none;">
48
+ <h2>Original Input File</h2>
49
+ <h3>Raw Metadata</h3>
50
+ <p><%= h(@result['transcoder'].original.raw_metadata) %></p>
51
+ <h3>Raw Response</h3>
52
+ <p><%= h(@result['transcoder'].original.raw_response) %></p>
53
+ </div>
54
+ </li>
55
+ <% if @result['transcoder'].processed %>
56
+ <li>
57
+ <span onclick="toggle('output-file');">Hide/Show Output File</span>
58
+ <div id='output-file' style="display: none;">
59
+ <h2>Processed Output File</h2>
60
+ <h3>Raw Metadata</h3>
61
+ <p><%= h(@result['transcoder'].processed.raw_metadata) %></p>
62
+ <h3>Raw Response</h3>
63
+ <p><%= h(@result['transcoder'].processed.raw_response) %></p>
64
+ </div>
65
+ </li>
66
+ <% end %>
67
+ <li>
68
+ <span onclick="toggle('log');">Hide/Show Log</span>
69
+ <div id='log' style="display: none;">
70
+ <h2>Log</h2>
71
+ <p>
72
+ <% File.readlines(@result['log']).each do |line| %>
73
+ <%= line %> <br />
74
+ <% end %>
75
+ </p>
76
+ </div>
77
+ </li>
78
+ </ul>
79
+ </div>
80
+ </body>
81
+ </html>
@@ -0,0 +1,9 @@
1
+ function toggle(obj) {
2
+ var el = document.getElementById(obj);
3
+ if ( el.style.display != 'none' ) {
4
+ el.style.display = 'none';
5
+ }
6
+ else {
7
+ el.style.display = '';
8
+ }
9
+ }