vollbremsung 0.0.21 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -13
- data/.gitignore +7 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +72 -0
- data/{LICENSE → LICENSE.txt} +1 -1
- data/README.md +7 -64
- data/Rakefile +6 -4
- data/_config.yml +1 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/vollbremsung +27 -164
- data/lib/vollbremsung.rb +237 -6
- data/lib/vollbremsung/version.rb +3 -0
- data/vollbremsung.gemspec +29 -19
- metadata +65 -16
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
Y2VmZDhmYmVmYTU3YTgwZDFmNDYxM2FiZTk4ZGZkMzNlMjExNjQxYg==
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 595920e5ff20374b33dde46862c3f4471f2a235d824210cf4f4f589e467c2d77
|
4
|
+
data.tar.gz: ea371f735bf0436f77639c9c15ad732417d5e2fd6f16cb03d569475d329ad8da
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
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
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -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.
|
data/{LICENSE → LICENSE.txt}
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2014-
|
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
|
[](http://badge.fury.io/rb/vollbremsung)
|
4
4
|
|
5
|
-
`vollbremsung` is a [Handbrake](https://handbrake.fr) bulk encoding tool,
|
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
|
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
|
-
|
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
|
-
--
|
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
|
-
|
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
data/_config.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
theme: jekyll-theme-minimal
|
data/bin/console
ADDED
@@ -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__)
|
data/bin/setup
ADDED
data/bin/vollbremsung
CHANGED
@@ -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
|
-
:
|
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[:
|
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("--
|
80
|
-
options[:
|
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[:
|
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
|
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
|
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
|
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
|
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)
|
data/lib/vollbremsung.rb
CHANGED
@@ -1,10 +1,22 @@
|
|
1
|
+
require "vollbremsung/version"
|
2
|
+
require 'logger'
|
3
|
+
|
1
4
|
module Vollbremsung
|
2
5
|
|
3
|
-
USAGE
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
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
|
data/vollbremsung.gemspec
CHANGED
@@ -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
|
8
|
-
spec.version
|
9
|
-
spec.
|
10
|
-
spec.
|
11
|
-
spec.
|
12
|
-
spec.
|
13
|
-
|
14
|
-
spec.
|
15
|
-
spec.
|
16
|
-
spec.homepage
|
17
|
-
spec.license
|
18
|
-
|
19
|
-
spec.
|
20
|
-
|
21
|
-
|
22
|
-
spec.
|
23
|
-
|
24
|
-
|
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
|
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:
|
11
|
+
date: 2018-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: logger
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0
|
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
|
27
|
-
|
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@
|
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:
|
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.
|
112
|
+
rubygems_version: 2.7.6
|
64
113
|
signing_key:
|
65
114
|
specification_version: 4
|
66
115
|
summary: Handbrake bulk encoding tool
|