mediafile 0.1.9 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/music_cp +2 -2
- data/lib/mediafile/bulkmediacopy.rb +27 -29
- data/lib/mediafile/mediafile.rb +147 -40
- data/lib/mediafile/version.rb +1 -1
- data/lib/mediafile.rb +5 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5c394dc6d5a00be3d75ff15ee0b842743cfb86b
|
4
|
+
data.tar.gz: cad902cd12157f5792f3e56b87f417e9a3522b8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71d866a921add9b4b39a05d8fa20fe537a4d79b063fa99be72cea9e40ebf468ede7090d66ee177e5a5fec96df798e96456a5b0d7a0fa264272263de7566b8d95
|
7
|
+
data.tar.gz: d2de722d5d6039b8d988626edefbe84b5f4e24464e50868ce1849afb476cc2e2cd666990048a9bd4d37cce058938b2197b7577927537eccbce499dfe82c8008d
|
data/bin/music_cp
CHANGED
@@ -44,7 +44,7 @@ opts = OptionParser.new do |opt|
|
|
44
44
|
dest = d
|
45
45
|
end
|
46
46
|
opt.on('--transcode <from=to[,from1=to1]>',
|
47
|
-
'A comma-seperated
|
47
|
+
'A comma-seperated list of name=value pairs.',
|
48
48
|
'Default is ' \
|
49
49
|
"#{transcode.to_a.map { |i| i.join('=') }.join(',')}}") do |fmt|
|
50
50
|
kill = true
|
@@ -64,7 +64,7 @@ opts = OptionParser.new do |opt|
|
|
64
64
|
end
|
65
65
|
opt.on('--exclude PATTERN', '-x PATTERN', String,
|
66
66
|
'Exclude files that match the given pattern.',
|
67
|
-
'Can specify more than once, file is excluded' \
|
67
|
+
'Can specify more than once, file is excluded ' \
|
68
68
|
'if any pattern matches') do |p|
|
69
69
|
exclude_patterns.concat p.split(',')
|
70
70
|
end
|
@@ -40,13 +40,12 @@ class BulkMediaCopy
|
|
40
40
|
def run(max=4)
|
41
41
|
start = Time.new
|
42
42
|
debug "Call run with max => '#{max}'"
|
43
|
-
puts "%#{@width + 8}s, %#{@width + 8}s,%#{@width +
|
43
|
+
puts "%#{@width + 8}s, %#{@width + 8}s,%#{@width + 9}s :: Mode" % [
|
44
44
|
"Remaining",
|
45
45
|
"Workers",
|
46
|
-
"Complete"
|
47
|
-
"File Name"
|
46
|
+
"Complete"
|
48
47
|
]
|
49
|
-
puts "%#{@width}d ( 100%%), %#{@width}d (%4.1f%%), %#{@width}d ( 0.0%%)" % [
|
48
|
+
puts "%#{@width}d ( 100%%), %#{@width}d (%4.1f%%), %#{@width}d ( 0.0%%) :: *wait*" % [
|
50
49
|
@work.count,
|
51
50
|
0,
|
52
51
|
0,
|
@@ -56,7 +55,7 @@ class BulkMediaCopy
|
|
56
55
|
stop = Time.new
|
57
56
|
duration = stop - start
|
58
57
|
puts "Copied #{@count} files in #{ "%d:%d:%d:%d" % duration.to_duration} " +
|
59
|
-
"(
|
58
|
+
"(~%.2f songs/second))." % [(@count/duration)]
|
60
59
|
dupes = @copies.select{ |_k,a| a.size > 1 }
|
61
60
|
if dupes.any?
|
62
61
|
puts "dupes"
|
@@ -64,7 +63,7 @@ class BulkMediaCopy
|
|
64
63
|
pp dupes
|
65
64
|
end
|
66
65
|
if @failed.any?
|
67
|
-
puts "Some files
|
66
|
+
puts "Some files failed to transfer."
|
68
67
|
@failed.each { |f| puts f.to_s }
|
69
68
|
end
|
70
69
|
end
|
@@ -112,43 +111,42 @@ class BulkMediaCopy
|
|
112
111
|
dest = mediafile.out_path transcode_table: @transcode
|
113
112
|
lock {
|
114
113
|
return unless copy_check? mediafile.source_md5, mediafile.source, dest
|
115
|
-
}
|
116
|
-
|
117
|
-
err = false
|
118
|
-
begin
|
119
|
-
mediafile.copy transcode_table: @transcode
|
120
|
-
rescue
|
121
|
-
@failed << mediafile
|
122
|
-
err = true
|
123
|
-
end
|
124
|
-
|
125
|
-
lock {
|
126
114
|
@count += 1
|
127
115
|
if @progress
|
128
116
|
left = @work.count - @count
|
129
117
|
left_perc = left == 0 ? left : left.to_f / @work.count * 100
|
130
|
-
cur
|
118
|
+
cur = @copies.count - @count
|
131
119
|
cur_perc = cur == 0 ? cur : cur.to_f / left * 100 # @work.count * 100
|
132
120
|
c = cur_perc == 100
|
133
121
|
finished = @count.to_f / @work.count * 100
|
134
122
|
f = finished == 100.0
|
135
123
|
print "%#{@width}d (%4.1f%%), %#{@width}d (%4.#{c ? 0 : 1}f%%), " \
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
124
|
+
"%#{@width}d (%4.#{f ? 0 : 1}f%%) :: %-s\n source file => %-s\n " \
|
125
|
+
"destination => %-s\n" % [
|
126
|
+
left,
|
127
|
+
left_perc,
|
128
|
+
cur,
|
129
|
+
cur_perc,
|
130
|
+
@count,
|
131
|
+
finished,
|
132
|
+
(@transcode[mediafile.type].nil? ? '*copy*' : '*transcode*'),
|
133
|
+
(mediafile.source),
|
134
|
+
mediafile.out_path(transcode_table:@transcode)
|
147
135
|
]
|
148
136
|
end
|
149
137
|
debug "#{mediafile.type} == #{@transcode[mediafile.type]}"
|
150
138
|
}
|
151
139
|
|
140
|
+
err = false
|
141
|
+
begin
|
142
|
+
mediafile.copy transcode_table: @transcode
|
143
|
+
rescue => e
|
144
|
+
debug("mediafile.copy failed. #{e}")
|
145
|
+
@failed << mediafile
|
146
|
+
err = true
|
147
|
+
raise
|
148
|
+
end
|
149
|
+
err
|
152
150
|
end
|
153
151
|
|
154
152
|
def copy_check?(md5,name,dest)
|
data/lib/mediafile/mediafile.rb
CHANGED
@@ -8,7 +8,7 @@ class MediaFile
|
|
8
8
|
|
9
9
|
attr_reader :source, :type, :name, :base_dir
|
10
10
|
|
11
|
-
def initialize(
|
11
|
+
def initialize(full_path,
|
12
12
|
base_dir: '.',
|
13
13
|
force_album_artist: nil,
|
14
14
|
verbose: false,
|
@@ -16,11 +16,15 @@ class MediaFile
|
|
16
16
|
@base_dir = base_dir
|
17
17
|
@destinations = Hash.new{ |k,v| k[v] = {} }
|
18
18
|
@force_album_artist = force_album_artist
|
19
|
-
@name = File.basename(
|
20
|
-
@source =
|
21
|
-
@type =
|
19
|
+
@name = File.basename( full_path, File.extname( full_path ) )
|
20
|
+
@source = full_path
|
21
|
+
@type = full_path[/(\w+)$/].downcase.to_sym
|
22
22
|
@verbose = verbose
|
23
23
|
@debug = debug
|
24
|
+
@cover = File.join(
|
25
|
+
File.dirname(full_path),
|
26
|
+
'cover.jpg')
|
27
|
+
@cover = nil unless File.exist?(@cover)
|
24
28
|
end
|
25
29
|
|
26
30
|
def source_md5
|
@@ -37,25 +41,30 @@ class MediaFile
|
|
37
41
|
|
38
42
|
def copy(dest: @base_dir, transcode_table: {})
|
39
43
|
destination = out_path base_dir: dest, transcode_table: transcode_table
|
40
|
-
temp_dest = tmp_path base_dir: dest,
|
44
|
+
temp_dest = tmp_path base_dir: dest, typ: transcode_table[@type]
|
45
|
+
debug "temp dest is '#{temp_dest}'"
|
41
46
|
lock{
|
42
47
|
if File.exist?(temp_dest)
|
43
|
-
|
44
|
-
|
48
|
+
error "File transfer is already in progress for #{@source} => #{temp_dest} => #{destination}"
|
49
|
+
error "This shouldn't happen! Check to make sure it was really copied."
|
45
50
|
raise
|
46
51
|
#return
|
47
52
|
end
|
48
53
|
if File.exist?(destination)
|
49
|
-
|
54
|
+
info("File has already been transfered #{@source} => #{destination}")
|
50
55
|
return
|
51
56
|
end
|
57
|
+
debug("Create parent directories at '#{File.dirname destination}'.")
|
52
58
|
FileUtils.mkdir_p File.dirname destination
|
53
59
|
FileUtils.touch temp_dest
|
54
60
|
}
|
55
61
|
begin
|
56
62
|
transcode_table.has_key?(@type) ?
|
57
63
|
transcode(transcode_table, temp_dest) :
|
58
|
-
|
64
|
+
FileUtils.cp(@source, temp_dest)
|
65
|
+
set_album_artist(temp_dest)
|
66
|
+
set_comment_and_title(temp_dest)
|
67
|
+
set_cover_art(temp_dest)
|
59
68
|
FileUtils.mv temp_dest, destination
|
60
69
|
rescue => e
|
61
70
|
FileUtils.rm temp_dest if File.exist? temp_dest
|
@@ -85,12 +94,6 @@ class MediaFile
|
|
85
94
|
:title, :genre, :year, :track,
|
86
95
|
:comment, :disc_number, :disc_total
|
87
96
|
|
88
|
-
def really_copy(src,dest)
|
89
|
-
FileUtils.cp(src, dest)
|
90
|
-
set_album_artist(dest)
|
91
|
-
set_comment_and_title(dest)
|
92
|
-
end
|
93
|
-
|
94
97
|
def set_decoder()
|
95
98
|
case @type
|
96
99
|
when :flac
|
@@ -147,7 +150,7 @@ class MediaFile
|
|
147
150
|
def transcode(trans , destination)
|
148
151
|
to = trans[@type]
|
149
152
|
if to == @type
|
150
|
-
|
153
|
+
error "Attempting to transcode to the same format #{@source} from #{@type} to #{to}"
|
151
154
|
end
|
152
155
|
FileUtils.mkdir_p File.dirname destination
|
153
156
|
|
@@ -185,7 +188,7 @@ class MediaFile
|
|
185
188
|
end
|
186
189
|
}
|
187
190
|
rescue Timeout::Error
|
188
|
-
|
191
|
+
error "Timeout exceeded!\n" << tpids.map { |p|
|
189
192
|
Process.kill 15, p
|
190
193
|
Process.kill 9, p
|
191
194
|
"#{p} #{Process.wait2( p )[1]}"
|
@@ -194,9 +197,9 @@ class MediaFile
|
|
194
197
|
raise
|
195
198
|
end
|
196
199
|
if err.any?
|
197
|
-
|
200
|
+
error "###\nError transcoding #{@source}: #{err.map{ |it,stat|
|
198
201
|
"#{it} EOT:#{stat.exitstatus} #{stat}" }.join(" and ") }\n###\n"
|
199
|
-
|
202
|
+
raise
|
200
203
|
end
|
201
204
|
end
|
202
205
|
|
@@ -219,18 +222,16 @@ class MediaFile
|
|
219
222
|
# this doesn't include the extension.
|
220
223
|
@newname ||= (
|
221
224
|
read_tags
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
end
|
233
|
-
#)
|
225
|
+
case
|
226
|
+
when (@disc_number && (@track > 0) && @title) && !(@disc_total && @disc_total == 1)
|
227
|
+
"%1d_%02d-" % [@disc_number, @track] + clean_string(@title)
|
228
|
+
when (@track > 0 && @title)
|
229
|
+
"%02d-" % @track + clean_string(@title)
|
230
|
+
when @title && @title != ""
|
231
|
+
clean_string(@title)
|
232
|
+
else
|
233
|
+
clean_string(@name)
|
234
|
+
end
|
234
235
|
)
|
235
236
|
end
|
236
237
|
|
@@ -264,17 +265,121 @@ class MediaFile
|
|
264
265
|
"." + new_file_name
|
265
266
|
end
|
266
267
|
|
267
|
-
def tmp_path(base_dir: @base_dir,
|
268
|
+
def tmp_path(base_dir: @base_dir, typ: nil)
|
269
|
+
typ ||= @type
|
268
270
|
File.join(
|
269
271
|
base_dir,
|
270
272
|
relative_path,
|
271
273
|
tmp_file_name,
|
272
|
-
) << ".#{
|
274
|
+
) << ".#{typ}"
|
275
|
+
end
|
276
|
+
|
277
|
+
def has_cover_art?(file)
|
278
|
+
typ = file[/(\w+)$/].downcase.to_sym
|
279
|
+
debug("Checking if #{file} has clover art. (#{typ})")
|
280
|
+
case typ
|
281
|
+
when :m4a
|
282
|
+
return true if file.tag.item_list_map['covr'].to_cover_art_list.find do |p|
|
283
|
+
p.format == TagLib::MP4::CoverArt::JPEG
|
284
|
+
end
|
285
|
+
when :flac
|
286
|
+
debug("It does.")
|
287
|
+
TagLib::FLAC::File.open(file) do |f|
|
288
|
+
return true if f.picture_list.find do |p|
|
289
|
+
p.type == TagLib::FLAC::Picture::FrontCover
|
290
|
+
end
|
291
|
+
end
|
292
|
+
when :mp3
|
293
|
+
TagLib::MPEG::File.open(file) do |f|
|
294
|
+
tag = f.id3v2_tag
|
295
|
+
# Don't overwrite an existing album cover.
|
296
|
+
debug("Checking if the target mp3 file already has a cover.")
|
297
|
+
return true if tag.frame_list('APIC').find do |p|
|
298
|
+
p.type == TagLib::ID3v2::AttachedPictureFrame::FrontCover
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
false
|
303
|
+
end
|
304
|
+
|
305
|
+
def set_cover_art(file)
|
306
|
+
debug("Checking for cover to apply to #{file}")
|
307
|
+
return if has_cover_art?(file)
|
308
|
+
write_cover_data(file, get_cover_data)
|
309
|
+
end
|
310
|
+
|
311
|
+
def get_cover_data
|
312
|
+
info("Getting cover art from #{@source}.")
|
313
|
+
# This is bad maybe
|
314
|
+
@cover ? File.open(@cover, 'rb') { |c| c.read } :
|
315
|
+
case @type
|
316
|
+
when :m4a
|
317
|
+
TagLib::MP4::File.open(@source) do
|
318
|
+
p = mp4.tag.item_list_map['covr'].to_cover_art_list.first
|
319
|
+
p.data if p
|
320
|
+
end
|
321
|
+
when :flac
|
322
|
+
TagLib::FLAC::File.open(@source) do |f|
|
323
|
+
info("Geting cover art from #{@source}.")
|
324
|
+
p = f.picture_list.find { |i| i.type == TagLib::FLAC::Picture::FrontCover }
|
325
|
+
p.data if p
|
326
|
+
end
|
327
|
+
when :mp3
|
328
|
+
TagLib::MPEG::File.open(@source) do |f|
|
329
|
+
tag = f.id3v2_tag
|
330
|
+
p = tag.frame_list('APIC').first
|
331
|
+
p.picture if p
|
332
|
+
end
|
333
|
+
else
|
334
|
+
error "Unsupported file type '#{@type}'. Not adding cover art from '#{@cover}'."
|
335
|
+
false
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
def write_cover_data(file, cover_art)
|
340
|
+
typ = file[/(\w+)$/].downcase.to_sym
|
341
|
+
case typ
|
342
|
+
when :m4a
|
343
|
+
TagLib::MP4::File.open(file) do
|
344
|
+
c = TagLib::MP4::CoverArt.new(TagLib::MP4::CoverArt::JPEG, cover_art)
|
345
|
+
item = TagLib::MP4::Item.from_cover_art_list([c])
|
346
|
+
file.tag.item_list_map.insert('covr', item)
|
347
|
+
file.save
|
348
|
+
end
|
349
|
+
when :flac
|
350
|
+
TagLib::FLAC::File.open(file) do |f|
|
351
|
+
pic = TagLib::FLAC::Picture.new
|
352
|
+
pic.type = TagLib::FLAC::Picture::FrontCover
|
353
|
+
pic.mime_type = 'image/jpeg'
|
354
|
+
pic.description = 'Cover'
|
355
|
+
pic.width = 90
|
356
|
+
pic.height = 90
|
357
|
+
pic.data = cover_art
|
358
|
+
info("Adding cover art tag to #{file}.")
|
359
|
+
f.add_picture(cover_art)
|
360
|
+
f.save
|
361
|
+
end
|
362
|
+
when :mp3
|
363
|
+
TagLib::MPEG::File.open(file) do |f|
|
364
|
+
tag = f.id3v2_tag
|
365
|
+
apic = TagLib::ID3v2::AttachedPictureFrame.new
|
366
|
+
apic.mime_type = 'image/jpeg'
|
367
|
+
apic.description = 'Cover'
|
368
|
+
apic.type = TagLib::ID3v2::AttachedPictureFrame::FrontCover
|
369
|
+
apic.picture = cover_art
|
370
|
+
tag.add_frame(apic)
|
371
|
+
f.save
|
372
|
+
end
|
373
|
+
else
|
374
|
+
error "Unsupported file type '#{typ}'. Not adding cover art from '#{@cover}'."
|
375
|
+
false
|
376
|
+
end
|
273
377
|
end
|
274
378
|
|
275
379
|
def set_album_artist(file)
|
276
380
|
return unless @force_album_artist
|
277
|
-
|
381
|
+
typ = file[/(\w+)$/].downcase.to_sym
|
382
|
+
case typ
|
278
383
|
when :m4a
|
279
384
|
TagLib::MP4::File.open(file) do |f|
|
280
385
|
f.tag.item_list_map.insert("aART",
|
@@ -298,25 +403,27 @@ class MediaFile
|
|
298
403
|
tag.add_frame(frame)
|
299
404
|
f.save
|
300
405
|
else
|
301
|
-
|
406
|
+
error("##########\nNo tag returned for #{@name}: #{@source}\n#############\n\n")
|
302
407
|
end
|
303
408
|
end
|
304
409
|
end
|
305
410
|
end
|
306
411
|
|
307
412
|
def set_comment_and_title(file)
|
308
|
-
|
309
|
-
|
413
|
+
debug "file is #{file}"
|
414
|
+
typ = file[/(\w+)$/].downcase.to_sym
|
415
|
+
klass = (typ == :mp3) ? TagLib::MPEG::File : TagLib::FileRef
|
416
|
+
method = (typ == :mp3) ? :id3v2_tag : :tag
|
310
417
|
|
311
418
|
klass.send(:open, file) do |f|
|
312
|
-
tag = if (
|
419
|
+
tag = if (typ == :mp3)
|
313
420
|
f.send(method, true)
|
314
421
|
else
|
315
422
|
f.send(method)
|
316
423
|
end
|
317
424
|
tag.comment = "#{@comment}"
|
318
425
|
tag.title = (@title || @name.tr('_',' ')) unless tag.title && tag.title != ""
|
319
|
-
if (
|
426
|
+
if (typ == :mp3)
|
320
427
|
f.save(TagLib::MPEG::File::ID3v2)
|
321
428
|
else
|
322
429
|
f.save
|
@@ -332,7 +439,7 @@ class MediaFile
|
|
332
439
|
@genre = nil
|
333
440
|
@year = nil
|
334
441
|
@track = 0
|
335
|
-
@comment = "MediaFile
|
442
|
+
@comment = "MediaFile source: #{@source}\n"
|
336
443
|
TagLib::FileRef.open(@source) do |file|
|
337
444
|
unless file.null?
|
338
445
|
tag = file.tag
|
data/lib/mediafile/version.rb
CHANGED
data/lib/mediafile.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mediafile
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Harvey-Smith
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: taglib-ruby
|