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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 334aef8d37ba538e00245a02dc3aefafd60b4b3c
4
- data.tar.gz: 4f8de891ef857224aced4f20d4ff9b7cb329ae7e
3
+ metadata.gz: d5c394dc6d5a00be3d75ff15ee0b842743cfb86b
4
+ data.tar.gz: cad902cd12157f5792f3e56b87f417e9a3522b8c
5
5
  SHA512:
6
- metadata.gz: 53186aae0f9fd1b468a9f2b1fdf8dbae02017e4a59fe7897acc3bd438074b4c6bf76fae29ae872936a9fb7d5465e4d1307b6b73e21942b1c80917019e1ed0da3
7
- data.tar.gz: 169efd96844d7d386f72834407ea8f5e4daee2708fdb939d1d2d873133981fe9eacb9c638211eded3828d5176ac7624349dc7ab30cbd980bb24632391979203a
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 series of name=value pairs.',
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 + 8}s, %-#{@name_width}s => Destination Path" % [
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
- "(~#{(@count/duration).to_i} songs/second))."
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 timed out"
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 = @copies.count - @count
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
- "%#{@width}d (%4.#{f ? 0 : 1}f%%) :: %-s\n source file => %-s\n " \
137
- "destination => %-s\n" % [
138
- left,
139
- left_perc,
140
- cur,
141
- cur_perc,
142
- @count,
143
- finished,
144
- (@transcode[mediafile.type].nil? ? '*copy*' : '*transcode*'),
145
- (mediafile.source + (err ? " **" : "") ),
146
- mediafile.out_path(transcode_table:@transcode)
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)
@@ -8,7 +8,7 @@ class MediaFile
8
8
 
9
9
  attr_reader :source, :type, :name, :base_dir
10
10
 
11
- def initialize(path,
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( path, File.extname( path ) )
20
- @source = path
21
- @type = path[/(\w+)$/].downcase.to_sym
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, transcode_table: transcode_table
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
- warn "File transfer is already in progress for #{@source} => #{temp_dest} => #{destination}"
44
- warn "This shouldn't happen! Check to make sure it was really copied."
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
- warn "File has already been transfered #{@source} => #{destination}" if @verbose
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
- really_copy(@source, temp_dest)
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
- safe_print "Attempting to transcode to the same format #{@source} from #{@type} to #{to}"
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
- safe_print "Timeout exceeded!\n" << tpids.map { |p|
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
- safe_print "###\nError transcoding #{@source}: #{err.map{ |it,stat|
200
+ error "###\nError transcoding #{@source}: #{err.map{ |it,stat|
198
201
  "#{it} EOT:#{stat.exitstatus} #{stat}" }.join(" and ") }\n###\n"
199
- exit 1
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
- #file = clean_string(
223
- case
224
- when (@disc_number && (@track > 0) && @title) && !(@disc_total && @disc_total == 1)
225
- "%1d_%02d-" % [@disc_number, @track] + clean_string(@title)
226
- when (@track > 0 && @title)
227
- "%02d-" % @track + clean_string(@title)
228
- when @title && @title != ""
229
- clean_string(@title)
230
- else
231
- clean_string(@name)
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, transcode_table: {})
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
- ) << ".#{transcode_table[@type] || @type}"
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
- case @type
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
- safe_print("##########\nNo tag returned for #{@name}: #{@source}\n#############\n\n")
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
- klass = (@type == :mp3) ? TagLib::MPEG::File : TagLib::FileRef
309
- method = (@type == :mp3) ? :id3v2_tag : :tag
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 (@type == :mp3)
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 (@type == :mp3)
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 from source: #{@source}\n"
442
+ @comment = "MediaFile source: #{@source}\n"
336
443
  TagLib::FileRef.open(@source) do |file|
337
444
  unless file.null?
338
445
  tag = file.tag
@@ -1,3 +1,3 @@
1
1
  module MediaFile
2
- VERSION = "0.1.9" unless defined?(::MediaFile::VERSION)
2
+ VERSION = "0.2.1" unless defined?(::MediaFile::VERSION)
3
3
  end
data/lib/mediafile.rb CHANGED
@@ -77,4 +77,9 @@ module MediaFile
77
77
  def info(msg = '')
78
78
  safe_print("INFO: #{caller_locations(1, 2)[0].label} >> #{msg}") if @verbose
79
79
  end
80
+
81
+ def error(msg = '')
82
+ safe_print("ERROR: #{caller_locations(1, 2)[0].label} >> #{msg}")
83
+ end
84
+
80
85
  end
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.9
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-16 00:00:00.000000000 Z
11
+ date: 2017-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: taglib-ruby