mkvtoolhelper 0.0.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1574e21575f6fef442a04b91cf93b02af84017a8
4
- data.tar.gz: 0550f9b6591a0e23dbed0313f17e31ddfc51771e
3
+ metadata.gz: 5d18de65cbc9d137ceb2a8595da11b4e67fbba8d
4
+ data.tar.gz: bea84ad47e72d198c955d9a0b76bbf1604d257d6
5
5
  SHA512:
6
- metadata.gz: 1dabef5cc0bd5149301e47a02ec3942a599825922bfd3a3cc25557df7367180da87a5ce7f553214c0e4aec6edee494f64cab8bc80de106189b36e9c62c96982e
7
- data.tar.gz: 1e373dc39dea5fa628a31b64001f52253fc97f804aa899774f25d0115c0ecbe7903265a13eac3aed1f503b7673a536f469aaffb6e3fcd81ecfc0f26b0a3aeda3
6
+ metadata.gz: 14a989c73a186e83cf87049efeb7c5e1b0a13ea27dddd0eb60220bc1ec011504704e7f8e05ff66363522af514600cfc84d4c3ce2f122534a2112ba7edc450740
7
+ data.tar.gz: 62c6fc1a6f8d60b8964276d183b841c05732e14ca25f86a69baa2bce43a3cc970722db19654c41b4ba679ed59474e4686db858f11785e51bf71a78002fe25f52
data/bin/mkvtoolhelper CHANGED
@@ -1,13 +1,277 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+ # TODO: remove pry
4
+ require 'pry'
4
5
 
5
- require 'mkvtoolhelper'
6
+ require 'fileutils'
7
+ require 'open3'
8
+ require 'json'
9
+ require 'readline'
10
+
11
+ def rotate_backups f
12
+ i = 0
13
+ i += 1 while File.exist?(f.path + ".backup#{i}")
14
+ i.downto(1) { |i| FileUtils.mv "#{f.path}.backup#{i - 1}", "#{f.path}.backup#{i}" }
15
+ end
16
+
17
+ def delete_backups f
18
+ i = 0
19
+ while File.exist?(f.path + ".backup#{i}") do
20
+ FileUtils.rm "#{f.path}.backup#{i}"
21
+ i += 1
22
+ end
23
+ end
24
+
25
+ class Mkv_File
26
+
27
+ class Audio_Track
28
+
29
+ def initialize track_data
30
+ @id = track_data['id']
31
+ @codec = track_data['codec']
32
+ @default = track_data['properties']['default_track']
33
+ @name = track_data['properties']['track_name']
34
+ @language = track_data['properties']['language']
35
+ @codec_id = track_data['properties']['codec_id']
36
+ @uid = track_data['properties']['uid']
37
+ end
38
+
39
+ def ==(other)
40
+ @id == other.id &&
41
+ @codec == other.codec &&
42
+ @default == other.default &&
43
+ @name == other.name &&
44
+ @language == other.language &&
45
+ @codec_id == other.codec_id &&
46
+ @uid == other.uid
47
+ end
48
+
49
+ def hash
50
+ @id.hash &
51
+ @codec.hash &
52
+ @default.hash &
53
+ @name.hash &
54
+ @language.hash &
55
+ @codec_id.hash &
56
+ @uid.hash
57
+ end
58
+
59
+ def to_s
60
+ "#<Audio_Track::#{@name} (#{@language})"
61
+ end
62
+
63
+ def get_tracks file
64
+ file.audio
65
+ end
66
+
67
+ attr_reader :id, :default, :name, :language, :codec, :codec_id, :uid
68
+
69
+ end
70
+
71
+ class Subtitles_Track
72
+
73
+ def initialize track_data
74
+ @id = track_data['id']
75
+ @default = track_data['properties']['default_track']
76
+ @name = track_data['properties']['track_name']
77
+ @language = track_data['properties']['language']
78
+ @uid = track_data['properties']['uid']
79
+ end
80
+
81
+ def ==(other)
82
+ @id == other.id &&
83
+ @default == other.default &&
84
+ @name == other.name &&
85
+ @language == other.language &&
86
+ @uid == other.uid
87
+ end
88
+
89
+ def hash
90
+ @id.hash &
91
+ @default.hash &
92
+ @name.hash &
93
+ @language.hash &
94
+ @uid.hash
95
+ end
96
+
97
+ def to_s
98
+ "#<Subtitles_Track::#{@name} (#{@language})"
99
+ end
100
+
101
+ def get_tracks file
102
+ file.subtitles
103
+ end
104
+
105
+ attr_reader :id, :default, :name, :language, :uid
106
+ end
107
+
108
+ def initialize path
109
+ @path = path
110
+ identify!
111
+ end
112
+
113
+ def identify!
114
+ json = invoke_and_get_output %W{mkvmerge --identify-verbose -J #{@path}}
115
+ @data = JSON.parse json
116
+ get_audio_tracks!
117
+ get_subtitle_tracks!
118
+ end
119
+
120
+ def to_s
121
+ "#<Mkv_File::#{@path}>"
122
+ end
123
+
124
+ attr_reader :audio, :subtitles, :path
125
+
126
+ private
127
+ def invoke_and_get_output args
128
+ o, s = Open3.capture2e(*args)
129
+ raise "Invoke failed: #{o}" unless s.success?
130
+ o
131
+ end
132
+
133
+ def get_audio_tracks!
134
+ @audio = []
135
+ @data['tracks'].select { |t| t['type'] == 'audio' }.each do |t|
136
+ @audio << Audio_Track.new(t)
137
+ end
138
+ end
139
+
140
+ def get_subtitle_tracks!
141
+ @subtitles = []
142
+ @data['tracks'].select { |t| t['type'] == 'subtitles' }.each do |t|
143
+ @subtitles << Subtitles_Track.new(t)
144
+ end
145
+ end
146
+
147
+ end
148
+
149
+ class Command
150
+ def initialize accepts, desc
151
+ @accepts = accepts
152
+ @desc = desc
153
+ @action = Proc.new
154
+ end
155
+ def accepts line
156
+ @line = line
157
+ @match = line.match(@accepts)
158
+ end
159
+ def action!
160
+ @action.call @match
161
+ end
162
+ def to_s
163
+ "#{@accepts.inspect} - #{@desc}"
164
+ end
165
+ end
166
+ $commands = []
167
+ $commands << Command.new(/^h(elp)?$/, "print help") do
168
+ puts "Available commands:"
169
+ $temp_commands.each { |c| puts c }
170
+ end
171
+ $commands << Command.new(/^q(uit)?$/, "quit program") { exit }
172
+ $commands << Command.new(/^n(ext)?$/, "continue to next group") { throw :next }
173
+ $commands << Command.new(/^d(efault)? ?(?<id>\d+)$/, "set as default") do |m|
174
+ old_id = $kit[:get_tracks].call($group.first).select { |t| t.default }.first&.id
175
+ new_id = m[:id].to_i
176
+ if old_id == new_id
177
+ puts "Track #{new_id} is already default."
178
+ next
179
+ end
180
+ old_uid = $kit[:get_tracks].call($group.first).find { |t| t.id == old_id }.uid
181
+ new_uid = $kit[:get_tracks].call($group.first).find { |t| t.id == new_id }.uid
182
+ $group.each do |f|
183
+ rotate_backups f
184
+ raise "Cannot backup!" unless system(*%W{cp --reflink=alway #{f.path} #{f.path}.backup0})
185
+ cmd = %W{mkvpropedit #{f.path}}
186
+ if old_id
187
+ cmd += %W{--edit track:=#{old_uid} --set flag-default=0}
188
+ end
189
+ cmd += %W{--edit track:=#{new_uid} --set flag-default=1}
190
+ o, s = Open3.capture2e(*cmd)
191
+ raise "Failed!: #{o}" unless s.success?
192
+ f.identify!
193
+ puts "Done: #{File.basename f.path}"
194
+ end
195
+ print_group $group
196
+ $kit[:print].call($group)
197
+ end
198
+ $commands << Command.new(/^d(elete backups)?$/, "delete backups") do |m|
199
+ $group.each do |f|
200
+ delete_backups f
201
+ puts "Deleted backups for #{File.basename f.path}"
202
+ end
203
+ end
204
+
205
+ def print_group group
206
+ puts "Files in this group:"
207
+ group.each { |f| puts " #{f.path}" }
208
+ end
209
+
210
+ def print_audio group
211
+ file = group.first
212
+ puts "Audio tracks in this group:"
213
+ file.audio.each do |a|
214
+ puts " #{a.default ? ?* : ' '} #{a.id}: #{a.name} (#{a.language}, #{a.codec})"
215
+ end
216
+ end
217
+
218
+ audio_kit = {
219
+ print: method(:print_audio),
220
+ commands: [],
221
+ get_tracks: ->(f) { f.audio },
222
+ }
223
+
224
+ def print_subtitles group
225
+ file = group.first
226
+ puts "Subtitles tracks in this group:"
227
+ file.subtitles.each do |a|
228
+ puts " #{a.default ? ?* : ' '} #{a.id}: #{a.name} (#{a.language})"
229
+ end
230
+ end
231
+
232
+ subtitles_kit = {
233
+ print: method(:print_subtitles),
234
+ commands: [],
235
+ get_tracks: ->(f) { f.subtitles },
236
+ }
237
+
238
+ def process_groups groups, kit
239
+ begin
240
+ $kit = kit
241
+ commands = [*kit[:commands], *$commands]
242
+ $temp_commands = commands
243
+ groups.each do |group|
244
+ $group = group
245
+ print_group group
246
+ kit[:print].call group
247
+ catch (:next) do
248
+ while line = Readline.readline("> ", true)
249
+ accepting = commands.select { |c| c.accepts line }
250
+ # sanity check
251
+ raise "Multiple commands for '#{line}'" if accepting.size > 1
252
+ if accepting.empty?
253
+ puts "No matching command."
254
+ else
255
+ accepting.first.action!
256
+ end
257
+ end
258
+ end
259
+ end
260
+ ensure
261
+ $group = nil
262
+ $kit = nil
263
+ end
264
+ end
6
265
 
7
266
  begin
8
- raise "TODO"
267
+ files = Dir.glob('**/*.mkv').map { |f| Mkv_File.new f }
268
+ by_audio = files.group_by { |f| f.audio.hash }.collect { |k, v| v }
269
+ by_subtitles = files.group_by { |f| f.subtitles.hash }.collect { |k, v| v }
270
+
271
+ process_groups by_audio, audio_kit
272
+ process_groups by_subtitles, subtitles_kit
9
273
  rescue => e
10
- STDERR.puts e.message
274
+ STDERR.puts "Exception: #{e.message}"
11
275
  STDERR.puts
12
276
  STDERR.puts "Backtrace:"
13
277
  STDERR.puts e.backtrace
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mkvtoolhelper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gray Wolf
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-30 00:00:00.000000000 Z
11
+ date: 2017-08-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  The cli interface of mkvtool isn't the best for batch editing
@@ -21,8 +21,6 @@ extensions: []
21
21
  extra_rdoc_files: []
22
22
  files:
23
23
  - bin/mkvtoolhelper
24
- - lib/mkvtoolhelper.rb
25
- - lib/mkvtoolhelper/version.rb
26
24
  homepage: https://github.com/graywolf/mkvtoolhelper
27
25
  licenses:
28
26
  - MIT
data/lib/mkvtoolhelper.rb DELETED
@@ -1 +0,0 @@
1
- require 'mkvtoolhelper/version'
@@ -1,3 +0,0 @@
1
- module Mkvtool
2
- VERSION = '0.0.1'
3
- end