mediafile 0.1.9 → 0.2.1
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 +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
|