vollbremsung 0.0.21 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- YzVmYjQzOGM0N2ZhOGM3MzJhOTRkNzAzNjg4MmQ5ZjE2N2U0YzlmNw==
5
- data.tar.gz: !binary |-
6
- Y2VmZDhmYmVmYTU3YTgwZDFmNDYxM2FiZTk4ZGZkMzNlMjExNjQxYg==
2
+ SHA256:
3
+ metadata.gz: 595920e5ff20374b33dde46862c3f4471f2a235d824210cf4f4f589e467c2d77
4
+ data.tar.gz: ea371f735bf0436f77639c9c15ad732417d5e2fd6f16cb03d569475d329ad8da
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- NWY0MGM2MTE3MTJmNDg2MTgyOTA1ZWIyOTEyYmFiYzcwNmZlNzRhMTVkMjU4
10
- ZDU0ZWI2MWE2ZTM0ZGJmOWRjNGIyZDUzMDhiM2VlMjI3NjU5ZTNhNGNlOTBm
11
- MDBlMjU1MDc5YWYzYzRhNjJlZmJhYWM2ODFiNDI0MGYwZWI4MWU=
12
- data.tar.gz: !binary |-
13
- YmE4ZDdhZThkYTJlMjZlNDQ4NWNhZjI5NzliYzliNzM2MDYzZDAxMzI4ZTNk
14
- ZGFhYjhjNzI0MGI1NTVjOWI2NDYzNmE1NzAxYmFmMDFhZTZhZTQxZDNjNDdj
15
- MjJkYWZjYzFkNDE0ZmQ1NDg2MmFjYmM3MDg0NDE2OGZkZGFhMjY=
6
+ metadata.gz: bb67f20d7c6bc9c84ce3b8b7356850a5be5a0ffe6d07f676045fb2bc8a2c21bf5241b841a91c056c296c23ec0ac4aad8cc02a868335ef045b337c2cc436a7cc0
7
+ data.tar.gz: 88b6a687a1f5822fc298e89f5cf6f7570dd0ed99d20f4ebbbc63959b6dffddb75a8f70ec1fa2ca3776fa6a2f207f691ad8727e9671d71ecc3c156c9b87e2b1ad
data/.gitignore CHANGED
@@ -1,6 +1,10 @@
1
1
  *.gem
2
2
  *.rbc
3
3
  /.config
4
+ /.bundle/
5
+ /.yardoc
6
+ /Gemfile.lock
7
+ /_yardoc/
4
8
  /coverage/
5
9
  /InstalledFiles
6
10
  /pkg/
@@ -32,3 +36,6 @@ build/
32
36
 
33
37
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
38
  .rvmrc
39
+
40
+ # rspec failure tracking
41
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ before_install: gem install bundler -v 1.15.1
@@ -0,0 +1,72 @@
1
+ # Changelog
2
+
3
+ **1.0.0**
4
+
5
+ + Total refactoring of the codebase, new project structure
6
+ + Support for transcoding to HEVC with the `--x265` flag
7
+ + Support for passing quality value to Handbrake, with the `-q` option
8
+
9
+ **0.0.21**
10
+
11
+ + Added the `--match` option.
12
+
13
+ **0.0.20**
14
+
15
+ + Added `webm` to the matching file extensions.
16
+
17
+ **0.0.19**
18
+
19
+ + Fixed a bug where temporary files were not deleted when their mp4 title could not be changed.
20
+
21
+ **0.0.18**
22
+
23
+ + Fixed a bug with multiple target directories where all but the first target were not probed correctly.
24
+
25
+ **0.0.17**
26
+
27
+ + Added support for multiple targets.
28
+ + Added option for `mp4` as file extension instead of `m4v`.
29
+ + Fixed a bug which made the `PRESET` argument of `--x264-preset` optional (and the option thereby useless).
30
+
31
+ **0.0.16**
32
+
33
+ + Fixed a bug when probing files with square braces in their path.
34
+ + Fixed a bug where file names where not correctly extracted from the files path and the files extension.
35
+
36
+ **0.0.15**
37
+
38
+ + Fixed another case of the bug of **0.0.13**.
39
+
40
+ **0.0.14**
41
+
42
+ + Fixed the bug that **0.0.13** should have fixed.
43
+
44
+ **0.0.13**
45
+
46
+ + Fixed a bug which did not output the correct relative path of a file based on the target path.
47
+
48
+ **0.0.12**
49
+
50
+ + Changed audio codec to AAC only. AC3, DTSHD, DTS and MP3 audio streams will no longer be passed through any more.
51
+ + Changed file extension of the output files to `m4v`.
52
+
53
+ **0.0.11**
54
+
55
+ + Improved help message.
56
+ + Fixed a bug where target paths ending with a / where not proved correctly.
57
+
58
+ **0.0.10**
59
+
60
+ + Added an option to show the program version number.
61
+ + Added a descent help message.
62
+
63
+ **0.0.9**
64
+
65
+ + Added `.ogm` to the matching file extensions.
66
+ + Added a specific handbrake gem version to the dependencies.
67
+ + Added `--list-only` option.
68
+ + Added `--x264-preset` option.
69
+
70
+ **0.0.8**
71
+
72
+ + Turned the simple ruby script to a RubyGem.
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2014-2015 Maximilian Irro
3
+ Copyright (c) 2014-2018 Maximilian Irro
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/vollbremsung.svg)](http://badge.fury.io/rb/vollbremsung)
4
4
 
5
- `vollbremsung` is a [Handbrake](https://handbrake.fr) bulk encoding tool, desigend to reencode a file structure to a DLNA enabled TV compatible format comfortably.
5
+ `vollbremsung` is a [Handbrake](https://handbrake.fr) bulk encoding tool, designed to reencode a file structure to a DLNA enabled TV compatible format comfortably.
6
6
 
7
7
  ## Installation
8
8
 
@@ -16,7 +16,7 @@ You need to have `ffmpeg`, `ffprobe` and `HandbrakeCLI` (on FreeBSD it's `HandBr
16
16
 
17
17
  vollbremsung [options] target [target [...]]
18
18
 
19
- It takes target paths and probes them for suited files. If a target path is a file, it is the only match, if it is a directory all containing files with a matching file type (basically all the non MP4 multimedia types like .avi, .flv, .mov, etc.) are taken. The `--recursive` option will extend the search scope to probe the sub filetree as well. It furthermore analyses each file for its structure utilising [ffmpegs](https://www.ffmpeg.org) `ffprobe` tool in order to extend Handbrakes default preset, processing *all* audio and subtitle tracks, not only the first ones.
19
+ It takes target paths and probes them for suited files. If a target path is a file, it is the only match, if it is a directory all containing files with a matching file type (basically all the non MP4 multimedia types like `avi`, `flv`, `mov`, etc.) are taken. The `--recursive` option will extend the search scope to probe the sub filetree as well. It furthermore analyses each file for its structure utilising [ffmpegs](https://www.ffmpeg.org) `ffprobe` tool in order to extend Handbrakes default preset, processing *all* audio and subtitle tracks, not only the first ones.
20
20
 
21
21
  The video streams will be converted to h.264 while audio streams will enjoy the AAC codec. Every DLNA enabled TV should be able to handle these two.
22
22
 
@@ -36,12 +36,14 @@ In order to only match a given range of file extensions, the `--match` option ac
36
36
 
37
37
  -d, --delete Delete source files after successful encoding
38
38
  --list-only List matching files only. Do not run processing
39
- --match ext1,ext2,ext3 Match only specific file extensions
39
+ --match ext1,ext2,ext3 Match only specific file extensions
40
40
  --mp4-ext Use 'mp4' as file extension instead of 'm4v'
41
41
  -m, --move Move source files to <FILENAME>.old after encoding
42
+ -q, --quality VALUE Set quality value to use (default is: 22)
42
43
  -r, --recursive Process subdirectories recursively as well
43
44
  -t, --title Set the MP4 metadata title tag to the filename
44
- --x264-preset PRESET Set the x264-preset. Default is: veryfast
45
+ --preset PRESET Set the x264/x265 preset. Default is: veryfast
46
+ --x265 Use x265 encoder for HEVC conversion
45
47
  --version Show program version information
46
48
  -h, --help Show this message
47
49
 
@@ -51,63 +53,4 @@ In order to only match a given range of file extensions, the `--match` option ac
51
53
 
52
54
  ## Changelog
53
55
 
54
- **0.0.21**
55
-
56
- + Added the `--match` option.
57
-
58
- **0.0.20**
59
-
60
- + Added `.webm` to the matching file extensions.
61
-
62
- **0.0.19**
63
-
64
- + Delete temporary file when mp4 title could not be changed.
65
-
66
- **0.0.18**
67
-
68
- + Fixed a bug with multiple target directories where all but the first target were not probed correctly.
69
-
70
- **0.0.17**
71
-
72
- + Added support for multiple targets.
73
- + Added option for `mp4` as file extension instead of `m4v`.
74
- + Fixed a bug which made the `PRESET` agrument of `--x264-preset` optional (and the option thereby useless).
75
-
76
- **0.0.16**
77
-
78
- + Fixed a bug when probing files with square braces in their path.
79
- + Fixed a bug where file names where not correctly extracted from the files path and the files extension.
80
-
81
- **0.0.14**
82
-
83
- + Fixed the bug that **0.0.13** should have fixed.
84
-
85
- **0.0.13**
86
-
87
- + Fixed a bug which did not output the correct relative path of a file based on the target path.
88
-
89
- **0.0.12**
90
-
91
- + Changed audio codec to AAC only. AC3, DTSHD, DTS and MP3 will no longer be carried on any more.
92
- + Changed file extension of the output files to `m4v`.
93
-
94
- **0.0.11**
95
-
96
- + Improved help message.
97
- + Fixed a bug where target paths ending with a / where not proved correctly.
98
-
99
- **0.0.10**
100
-
101
- + Added an option to show the program version number.
102
- + Added a descent help message.
103
-
104
- **0.0.9**
105
-
106
- + Added `.ogm` to the matching file extensions.
107
- + Added a specific handbrake gem version to the dependencies.
108
- + Added `--list-only` option.
109
- + Added `--x264-preset` option.
110
-
111
- **0.0.8**
112
-
113
- + Turned the simple ruby script to a RubyGem.
56
+ see [CHANGELOG.md](CHANGELOG.md)
data/Rakefile CHANGED
@@ -1,4 +1,6 @@
1
- task :pushall do
2
- sh 'git push --all bitbucket'
3
- sh 'git push --all github'
4
- end
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1 @@
1
+ theme: jekyll-theme-minimal
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "vollbremsung"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -5,33 +5,11 @@ require 'mkmf' # part of stdlib
5
5
  require 'open3'
6
6
  require 'json'
7
7
  require 'optparse'
8
- require 'handbrake'
9
8
  require 'vollbremsung'
10
9
 
11
- def log(msg)
12
- puts Time.new.strftime("%Y-%m-%d %H:%M:%S") + " #{msg}"
13
- end
14
-
15
- def ffprobe(file)
16
- stdout,stderr,status = Open3.capture3("ffprobe -v quiet -print_format json -show_format -show_streams \"#{file}\"")
17
- if status.success?
18
- return JSON.parse(stdout)
19
- else
20
- STDERR.puts stderr
21
- return nil
22
- end
23
- end
24
-
25
- # square brackets have a special meaning in the context of shell globbing
26
- # --> escape them in order to find files in directories with [, ], {, }
27
- # symbols in their path
28
- def escape_glob(s)
29
- s.gsub(/[\\\{\}\[\]\*\?]/) { |x| "\\"+x }
30
- end
31
-
32
10
  options = {
33
11
  :extension => "m4v",
34
- :match_list => Vollbremsung::CONVERT_TYPES
12
+ :match_extensions => Vollbremsung::CONVERT_TYPES
35
13
  }
36
14
 
37
15
  OptionParser.new do |opts|
@@ -57,7 +35,7 @@ OptionParser.new do |opts|
57
35
  end
58
36
 
59
37
  opts.on("--match ext1,ext2,ext3", String, "Match only specific file extensions") do |es|
60
- options[:match_list] = es.split(',')
38
+ options[:match_extensions] = es.split(',')
61
39
  end
62
40
 
63
41
  opts.on("--mp4-ext", "Use 'mp4' as file extension instead of 'm4v'") do |flag|
@@ -68,6 +46,16 @@ OptionParser.new do |opts|
68
46
  options[:move] = true
69
47
  end
70
48
 
49
+ opts.on("-q", "--quality VALUE", "Set quality value to use (default is: #{Vollbremsung::DEFAULT_QUALITY})") do |quality|
50
+ # TODO check if quality is an integer value in the range of acceptable values
51
+ begin
52
+ options[:quality] = quality.to_i
53
+ rescue
54
+ puts "Not a numer provided for option --quality value: #{quality}"
55
+ exit 6
56
+ end
57
+ end
58
+
71
59
  opts.on("-r", "--recursive", "Process subdirectories recursively as well") do |flag|
72
60
  options[:recursive] = true
73
61
  end
@@ -76,8 +64,12 @@ OptionParser.new do |opts|
76
64
  options[:title] = true
77
65
  end
78
66
 
79
- opts.on("--x264-preset PRESET", String, "Set the x264-preset. Default is: #{Vollbremsung::X264_DEFAULT_PRESET}") do |preset|
80
- options[:x264_preset] = preset
67
+ opts.on("--preset PRESET", String, "Set the x264/x265 preset. Default is: #{Vollbremsung::DEFAULT_PRESET}") do |preset|
68
+ options[:preset] = preset
69
+ end
70
+
71
+ opts.on("--x265", "Use x265 encoder for HEVC conversion") do |flag|
72
+ options[:encoder] = "x265".freeze
81
73
  end
82
74
 
83
75
  opts.on("--version", "Show program version information") do
@@ -96,8 +88,10 @@ if options[:version]
96
88
  exit
97
89
  end
98
90
 
99
-
100
- options[:x264_preset] = Vollbremsung::X264_DEFAULT_PRESET if options[:x264_preset].nil?
91
+ # TODO move this to the top to the options[] init
92
+ options[:encoder] = Vollbremsung::DEFAULT_QUALITY if options[:encoder].nil?
93
+ options[:quality] = Vollbremsung::DEFAULT_QUALITY if options[:quality].nil?
94
+ options[:preset] = Vollbremsung::DEFAULT_PRESET if options[:preset].nil?
101
95
 
102
96
 
103
97
  if ARGV[0].nil?
@@ -111,14 +105,14 @@ end
111
105
  ARGV.each do |target|
112
106
  unless File.exists?(target)
113
107
  puts "The target #{target} does not exist."
114
- exit 1
108
+ exit 2
115
109
  end
116
110
  end
117
111
 
118
112
  if options[:move] && options[:delete]
119
113
  puts "--delete (-d) and --move (-m) are mutually exclusive - choose one!"
120
114
  puts "It is not possible to delete and move the source files at the same time."
121
- exit 1
115
+ exit 3
122
116
  end
123
117
 
124
118
  # probe for required executable
@@ -129,7 +123,7 @@ unless options[:list_only]
129
123
  puts "You can get the executable from http://handbrake.fr/downloads.php"
130
124
 
131
125
  File.delete 'mkmf.log' if File.exists?('mkmf.log') # find_executable seems to create such file in case executable is not found
132
- exit 1
126
+ exit 4
133
127
  end
134
128
 
135
129
  unless find_executable 'ffprobe'
@@ -137,141 +131,10 @@ unless options[:list_only]
137
131
  puts "ffprobe is part of ffmpeg. Install it for your system and run again."
138
132
 
139
133
  File.delete 'mkmf.log' if File.exists?('mkmf.log') # find_executable seems to create such file in case executable is not found
140
- exit 1
134
+ exit 5
141
135
  end
142
136
 
143
137
  File.delete 'mkmf.log' if File.exists?('mkmf.log') # find_executable seems to create such file in case executable is not found
144
138
  end
145
139
 
146
-
147
- #HANDBRAKE_OPTIONS = "--encoder x264 --quality 20.0 --aencode faac -B 160 --mixdown dpl2 --arate Auto -D 0.0 --format mp4 --markers --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 --x264-preset veryfast --loose-anamorphic --modulus 2"
148
-
149
-
150
- target_files = []
151
- scope = options[:recursive] ? "/**/*" : "/*"
152
-
153
- ARGV.each do |target|
154
-
155
- if File.directory?(target)
156
-
157
- log "probing for target files in #{File.absolute_path(target) + scope}"
158
- log "files found:"
159
-
160
- Dir[escape_glob(File.absolute_path(target)) + scope].sort.each do |file|
161
- unless File.directory?(file)
162
- if options[:match_list].include?(File.extname(file).downcase[1..-1])
163
- puts "* " + File.absolute_path(file)[File.absolute_path(target).length+1..-1]
164
- target_files << [file,target] # file and provided target_dir
165
- end
166
- end
167
- end
168
-
169
- else
170
- puts "* " + target
171
- target_files << [File.absolute_path(target),File.absolute_path(target)]
172
- end
173
-
174
- end
175
-
176
- exit if options[:list_only]
177
-
178
- target_files.each do |infile, target_dir|
179
-
180
- metadata = ffprobe(infile)
181
- if metadata.nil?
182
- log "ERROR retrieving metadata -- skipping this file"
183
- next
184
- end
185
-
186
- astreams = Vollbremsung::StreamDescr.new
187
- sstreams = Vollbremsung::StreamDescr.new
188
-
189
- metadata['streams'].each do |stream|
190
- case stream['codec_type']
191
- when 'audio'
192
- astreams.count += 1
193
- astreams.names << stream['tags']['title'] unless stream['tags'].nil? || stream['tags']['title'].nil?
194
- when 'subtitle'
195
- sstreams.count += 1
196
- sstreams.names << stream['tags']['title'] unless stream['tags'].nil? || stream['tags']['title'].nil?
197
- else
198
- # this is attachment stuff, like typefonts --> ignore
199
- end
200
- end
201
-
202
-
203
- infile_basename = File.basename(infile)
204
- infile_basename_noext = File.basename(infile, File.extname(infile)) # without ext
205
- infile_dirname = File.dirname(infile)
206
- infile_path_noext = File.join(infile_dirname, infile_basename_noext)
207
- infile_relative_path = #File.directory?(TARGET_PATH) ? infile[TARGET_PATH.length+1..-1] : File.basename(TARGET_PATH)
208
- if File.directory?(target_dir)
209
- File.absolute_path(infile)[File.absolute_path(target_dir).length+1..-1]
210
- else
211
- File.basename(target_dir)
212
- end
213
-
214
- outfile = "#{infile_path_noext}.#{options[:extension]}"
215
-
216
- log "processing: #{infile_relative_path}"
217
-
218
- #%x( #{HANDBRAKE_CLI} #{HANDBRAKE_OPTIONS} --audio #{(1..astreams.count).to_a.join(',')} --aname #{astreams.names.join(',')} --subtitle #{(1..sstreams.count).to_a.join(',')} -i \"#{infile}\" -o \"#{outfile}\" 2>&1 )
219
-
220
- success = false
221
- begin
222
- HandBrake::CLI.new.input(infile).encoder('x264').quality('20.0').aencoder('faac').
223
- ab('160').mixdown('dpl2').arate('Auto').drc('0.0').format('mp4').markers.
224
- audio_copy_mask('aac').audio_fallback('ffac3').x264_preset(options[:x264_preset]).
225
- loose_anamorphic.modulus('2').audio((1..astreams.count).to_a.join(',')).aname(astreams.names.join(',')).
226
- subtitle((1..sstreams.count).to_a.join(',')).output(outfile)
227
-
228
- # if we make it here, encoding went well
229
- log "SUCCESS: encoding done"
230
- success = true
231
- rescue
232
- log "ERROR: Handbrake exited with an error"
233
- end # HandBrake::CLI
234
-
235
- if success
236
- infile_size = File.size(infile)
237
- outfile_size = File.size(outfile)
238
-
239
- log "compression ratio: %.2f" % (outfile_size.to_f / infile_size.to_f)
240
-
241
- if options[:title]
242
- log "setting MP4 title"
243
-
244
- infile_noext = File.join( File.dirname(infile), File.basename(infile,File.extname(infile)))
245
- tmpfile = infile_noext + ".tmp.mp4"
246
-
247
- %x( ffmpeg -i \"#{outfile}\" -metadata title=\"#{infile_basename_noext}\" #{Vollbremsung::FFMPEG_OPTIONS} \"#{tmpfile}\" 2>&1 )
248
-
249
- if $?.exitstatus == 0
250
- begin
251
- File.delete outfile
252
- File.rename tmpfile, outfile
253
- rescue
254
- log "ERROR: moving #{tmpfile} to #{outfile}"
255
- end
256
- else
257
- log "ERROR: MP4 title could not be changed"
258
- File.delete tmpfile
259
- end
260
- end # if options[:title]
261
-
262
- if options[:move]
263
- log "moving source file to *.old"
264
- File.rename(infile, "#{infile}.old") rescue log "ERROR: renaming source file"
265
- elsif options[:delete]
266
- log "deleting source file"
267
- File.delete(infile) rescue log "ERROR: deleting source file"
268
- end
269
-
270
- end # if success
271
- end # target_files.each
272
-
273
- if target_files.empty?
274
- log "nothing to do"
275
- else
276
- log "all items processed"
277
- end
140
+ Vollbremsung.process(ARGV,options)
@@ -1,10 +1,22 @@
1
+ require "vollbremsung/version"
2
+ require 'logger'
3
+
1
4
  module Vollbremsung
2
5
 
3
- USAGE = "Usage: vollbremsung [options] target [target [...]]"
4
- VERSION = '0.0.21'.freeze
5
- CONVERT_TYPES = ['avi','flv','mkv','mpg','mov','ogm','webm','wmv']
6
- FFMPEG_OPTIONS = "-map 0 -acodec copy -vcodec copy -scodec copy"
7
- X264_DEFAULT_PRESET = "veryfast".freeze
6
+ USAGE = "Usage: vollbremsung [options] target [target [...]]"
7
+ CONVERT_TYPES = ['avi','flv','mkv','mpg','mov','ogm','webm','wmv']
8
+ FFMPEG_OPTIONS = "-map 0 -acodec copy -vcodec copy -scodec copy"
9
+ FFPROBE_OPTIONS = "-v quiet -print_format json -show_format -show_streams"
10
+ DEFAULT_ENCODER = "x264".freeze
11
+ DEFAULT_PRESET = "veryfast".freeze
12
+ DEFAULT_QUALITY = 22
13
+
14
+ case RUBY_PLATFORM
15
+ when /darwin/ then
16
+ HANDBRAKE_CLI = "HandBrakeCLI".freeze
17
+ else
18
+ HANDBRAKE_CLI = "HandbrakeCLI".freeze
19
+ end
8
20
 
9
21
  class StreamDescr < Struct.new(:count,:names)
10
22
  def initialize
@@ -12,4 +24,223 @@ module Vollbremsung
12
24
  end
13
25
  end
14
26
 
15
- end
27
+ class Brake
28
+
29
+ def initialize
30
+ @logger = Logger.new STDOUT
31
+ @logger.level = Logger::INFO
32
+ #@logger.datetime_format = '%Y-%m-%d %H:%M:%S '
33
+ @logger.formatter = proc do |severity, datetime, progname, msg|
34
+ date_format = datetime.strftime("%Y-%m-%d %H:%M:%S")
35
+ if severity == "INFO" or severity == "WARN"
36
+ "[#{date_format}] #{severity} : #{msg}\n"
37
+ else
38
+ "[#{date_format}] #{severity} : #{msg}\n"
39
+ end
40
+ end # proc do
41
+ end # def initialize
42
+
43
+ # square brackets have a special meaning in the context of shell globbing
44
+ # --> escape them in order to find files in directories with [, ], {, }
45
+ # symbols in their path
46
+ private
47
+ def escape_glob(s)
48
+ s.gsub(/[\\\{\}\[\]\*\?]/) { |x| "\\"+x }
49
+ end
50
+
51
+ private
52
+ def ffprobe(file)
53
+ stdout,stderr,status = Open3.capture3("ffprobe #{Vollbremsung::FFPROBE_OPTIONS} \"#{file}\"")
54
+ if status.success?
55
+ return JSON.parse(stdout)
56
+ else
57
+ STDERR.puts stderr
58
+ return nil
59
+ end
60
+ end # def ffprobe
61
+
62
+ private
63
+ def match_files(targets, match_extensions, recursive)
64
+ scope = recursive ? "/**/*" : "/*"
65
+ matches = []
66
+ targets.each do |target|
67
+
68
+ if File.directory?(target)
69
+
70
+ @logger.info "probing for target files in #{File.absolute_path(target) + scope}"
71
+ @logger.info "files found:"
72
+
73
+ Dir[escape_glob(File.absolute_path(target)) + scope].sort.each do |file|
74
+ unless File.directory?(file)
75
+ if match_extensions.include?(File.extname(file).downcase[1..-1])
76
+ puts "* " + File.absolute_path(file)[File.absolute_path(target).length+1..-1]
77
+ matches << [file,target] # file and provided target_dir
78
+ end
79
+ end
80
+ end
81
+
82
+ else
83
+ puts "* " + target
84
+ matches << [File.absolute_path(target),File.absolute_path(target)]
85
+ end
86
+
87
+ end
88
+
89
+ return matches
90
+ end # def match_files
91
+
92
+ private
93
+ def parse_metadata(metadata)
94
+ astreams = Vollbremsung::StreamDescr.new
95
+ sstreams = Vollbremsung::StreamDescr.new
96
+
97
+ metadata['streams'].each do |stream|
98
+ case stream['codec_type']
99
+ when 'audio'
100
+ astreams.count += 1
101
+ astreams.names << stream['tags']['title'] unless stream['tags'].nil? || stream['tags']['title'].nil?
102
+ when 'subtitle'
103
+ sstreams.count += 1
104
+ sstreams.names << stream['tags']['title'] unless stream['tags'].nil? || stream['tags']['title'].nil?
105
+ else
106
+ # this is attachment stuff, like typefonts --> ignore
107
+ end
108
+ end
109
+
110
+ return astreams, sstreams
111
+ end # def parse_metadata
112
+
113
+ private
114
+ def full_outpath(infile, target_dir, extension)
115
+ infile_basename = File.basename(infile)
116
+ infile_basename_noext = File.basename(infile, File.extname(infile)) # without ext
117
+ infile_dirname = File.dirname(infile)
118
+ infile_path_noext = File.join(infile_dirname, infile_basename_noext)
119
+ infile_relative_path = #File.directory?(TARGET_PATH) ? infile[TARGET_PATH.length+1..-1] : File.basename(TARGET_PATH)
120
+ if File.directory?(target_dir)
121
+ File.absolute_path(infile)[File.absolute_path(target_dir).length+1..-1]
122
+ else
123
+ File.basename(target_dir)
124
+ end
125
+
126
+ outfile = "#{infile_path_noext}.#{extension}"
127
+
128
+ return outfile, infile_relative_path
129
+ end # def full_outpath
130
+
131
+ private
132
+ def run_handbrake(infile, outfile, astreams, sstreams, options)
133
+
134
+ handbrake_cmd =
135
+ %{ #{HANDBRAKE_CLI} \
136
+ --encoder #{options[:encoder]} \
137
+ --encoder-preset #{options[:preset]} \
138
+ --quality #{options[:quality]} \
139
+ --aencoder faac \
140
+ --audio-copy-mask aac \
141
+ --audio-fallback ffac3 \
142
+ --loose-anamorphic --modulus 2 \
143
+ --audio #{(1..astreams.count).to_a.join(',')} \
144
+ --aname #{astreams.names.join(',')} \
145
+ --subtitle #{(1..sstreams.count).to_a.join(',')} \
146
+ -i \"#{infile}\" -o \"#{outfile}\" 2>&1
147
+ }
148
+ #@logger.info "running handbrake cmd: #{handbrake_cmd}"
149
+ %x( #{handbrake_cmd} )
150
+
151
+ if $?.exitstatus == 0
152
+ @logger.info "encoding done"
153
+ return true
154
+ else
155
+ @logger.error "Handbrake exited with error code #{$?.exitstatus}"
156
+ return false
157
+ end
158
+ end # def run_handbrake
159
+
160
+ private
161
+ def write_mp4_title(infile, outfile)
162
+
163
+ @logger.info "setting MP4 title"
164
+
165
+ infile_noext = File.join( File.dirname(infile), File.basename(infile,File.extname(infile)))
166
+ tmpfile = infile_noext + ".tmp.mp4"
167
+
168
+ # TODO check if ffmpeg does exists (should if ffprobe is availabe, but just to make sure)
169
+
170
+ %x( ffmpeg -i \"#{outfile}\" -metadata title=\"#{infile_basename_noext}\" #{Vollbremsung::FFMPEG_OPTIONS} \"#{tmpfile}\" 2>&1 )
171
+
172
+ # if successful, either delete old file and replace with new, or delete the broken tempfile if it exists
173
+ if $?.exitstatus == 0
174
+ begin
175
+ File.delete outfile
176
+ File.rename tmpfile, outfile
177
+ rescue
178
+ @logger.error "moving #{tmpfile} to #{outfile}"
179
+ end
180
+ else
181
+ @logger.error "MP4 title could not be changed"
182
+ File.delete tmpfile
183
+ end
184
+ end # def write_mp4_title
185
+
186
+ public
187
+ def process(targets, options)
188
+
189
+ matches = match_files(ARGV, options[:match_extensions], options[:recursive])
190
+
191
+ if options[:list_only]
192
+ exit 0
193
+ end
194
+
195
+ if matches.empty?
196
+ @logger.info "no files found to process"
197
+ exit 0
198
+ else
199
+
200
+ matches.each do |infile, target_dir|
201
+
202
+ metadata = ffprobe(infile)
203
+ if metadata.nil?
204
+ @logger.error "could not retrieve metadata -- skipping file"
205
+ next
206
+ end
207
+
208
+ astreams, sstreams = parse_metadata(metadata)
209
+ outfile, infile_relative_path = full_outpath(infile, target_dir, options[:extension])
210
+
211
+ @logger.info "processing: #{infile_relative_path}"
212
+
213
+ success = run_handbrake(infile, outfile, astreams, sstreams, options)
214
+
215
+ if success
216
+ @logger.info "compression ratio: %.2f" % (File.size(outfile).to_f / File.size(infile).to_f)
217
+
218
+ if options[:title]
219
+ write_mp4_title(infile, outfile)
220
+ end # if options[:title]
221
+
222
+ if options[:move]
223
+ @logger.info "moving source file to *.old"
224
+ File.rename(infile, "#{infile}.old") rescue @logger.error "could not rename source file"
225
+ elsif options[:delete]
226
+ @logger.info "deleting source file"
227
+ File.delete(infile) rescue @logger.error "could not delete source file"
228
+ end
229
+
230
+ end # if success
231
+ end # target_files.each
232
+
233
+ @logger.info "🚗 finally came to a halt"
234
+ end # items.empty?
235
+ end # def process
236
+ end # class Brake
237
+
238
+ def process(targets, options)
239
+
240
+ brake = Vollbremsung::Brake.new
241
+ brake.process(targets, options)
242
+
243
+ end # def process
244
+ module_function :process
245
+
246
+ end # module Vollbremsung
@@ -0,0 +1,3 @@
1
+ module Vollbremsung
2
+ VERSION = "1.0.0"
3
+ end
@@ -1,24 +1,34 @@
1
1
  # encoding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'vollbremsung'
4
+ require 'vollbremsung/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = 'vollbremsung'
8
- spec.version = Vollbremsung::VERSION
9
- spec.date = '2014-09-21'
10
- spec.summary = "Handbrake bulk encoding tool"
11
- spec.description = "vollbremsung is a Handbrake bulk encoding tool, desigend to reencode a file structure to a DLNA enabled TV compatible format comfortably."
12
- spec.author = "Maximilian Irro"
13
- spec.email = 'max@disposia.org'
14
- spec.files = `git ls-files -z`.split("\x0")
15
- spec.executables = ['vollbremsung']
16
- spec.homepage = 'https://github.com/mpgirro/vollbremsung'
17
- spec.license = 'MIT'
18
-
19
- spec.require_paths = ['lib']
20
-
21
- spec.required_ruby_version = '>= 1.9.3'
22
- spec.add_dependency 'handbrake', '~> 0.4'
23
-
24
- end
7
+ spec.name = "vollbremsung"
8
+ spec.version = Vollbremsung::VERSION
9
+ spec.platform = Gem::Platform::RUBY
10
+ spec.date = "2018-02-25"
11
+ spec.author = "Maximilian Irro"
12
+ spec.email = "max@irro.at"
13
+
14
+ spec.summary = "Handbrake bulk encoding tool"
15
+ spec.description = "vollbremsung is a Handbrake bulk encoding tool, designed to re-encode a file structure to a DLNA enabled TV compatible format comfortably."
16
+ spec.homepage = "https://github.com/mpgirro/vollbremsung"
17
+ spec.license = "MIT"
18
+
19
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
20
+ f.match(%r{^(test|spec|features)/})
21
+ end
22
+ spec.executables = ["vollbremsung"]
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.required_ruby_version = ">= 2.0.0"
26
+ spec.add_dependency "logger"
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.15"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3.0"
31
+
32
+ spec.post_install_message = "🚗 Ready to pull the brake!"
33
+
34
+ end
metadata CHANGED
@@ -1,66 +1,115 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vollbremsung
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.21
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maximilian Irro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-21 00:00:00.000000000 Z
11
+ date: 2018-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: handbrake
14
+ name: logger
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0.4'
19
+ version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0.4'
27
- description: vollbremsung is a Handbrake bulk encoding tool, desigend to reencode
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.15'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.15'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: vollbremsung is a Handbrake bulk encoding tool, designed to re-encode
28
70
  a file structure to a DLNA enabled TV compatible format comfortably.
29
- email: max@disposia.org
71
+ email: max@irro.at
30
72
  executables:
31
73
  - vollbremsung
32
74
  extensions: []
33
75
  extra_rdoc_files: []
34
76
  files:
35
- - .gitignore
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
+ - CHANGELOG.md
36
81
  - Gemfile
37
- - LICENSE
82
+ - LICENSE.txt
38
83
  - README.md
39
84
  - Rakefile
85
+ - _config.yml
86
+ - bin/console
87
+ - bin/setup
40
88
  - bin/vollbremsung
41
89
  - lib/vollbremsung.rb
90
+ - lib/vollbremsung/version.rb
42
91
  - vollbremsung.gemspec
43
92
  homepage: https://github.com/mpgirro/vollbremsung
44
93
  licenses:
45
94
  - MIT
46
95
  metadata: {}
47
- post_install_message:
96
+ post_install_message: "\U0001F697 Ready to pull the brake!"
48
97
  rdoc_options: []
49
98
  require_paths:
50
99
  - lib
51
100
  required_ruby_version: !ruby/object:Gem::Requirement
52
101
  requirements:
53
- - - ! '>='
102
+ - - ">="
54
103
  - !ruby/object:Gem::Version
55
- version: 1.9.3
104
+ version: 2.0.0
56
105
  required_rubygems_version: !ruby/object:Gem::Requirement
57
106
  requirements:
58
- - - ! '>='
107
+ - - ">="
59
108
  - !ruby/object:Gem::Version
60
109
  version: '0'
61
110
  requirements: []
62
111
  rubyforge_project:
63
- rubygems_version: 2.4.2
112
+ rubygems_version: 2.7.6
64
113
  signing_key:
65
114
  specification_version: 4
66
115
  summary: Handbrake bulk encoding tool