mediafile 0.1.7 → 0.1.8

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: cac681a4c71a49d088590a7403cc02080c5bb36a
4
- data.tar.gz: 606bd21a86ca1213b885729710d60d7730cdee2f
3
+ metadata.gz: c0ac9d9cb6df74d620341c0410167100c8ab9bb9
4
+ data.tar.gz: 21b7b5e10a2dbffea4222758f48ce2176e36e117
5
5
  SHA512:
6
- metadata.gz: 94e1462a37a4535968baed779bfd274198c7d1032cea7e079d73bf087ef2f5d827ef257411ab0cd0de4aeba2a46c46c12c945be81d418545551932611b0d73fd
7
- data.tar.gz: 1bb6182f3f01199253c2e8485c7474e19c4ec835faab869d9485191764abbda761cc555cefa5e635184a5a48d988eb7b0a2a72a3c90dc9d847db0e82752b7fd0
6
+ metadata.gz: fea4af286c2c6ffe85078a0d8064742baf58229c3c179f177eaed5151f5fe3e8eccdda321b081525f3f0ddb7e9453740c2ca9a5676d3a1e292b5ae4c2d48ee63
7
+ data.tar.gz: 894d6b89e8b32ebd7e34a7dc336579e7e209fff6381d2ed5b34b5c3acede6b6dbf5baba06600620095990da235de2000113bb10545440f692077d901479a7af0
data/bin/music_cp CHANGED
@@ -19,12 +19,14 @@ opt_files = {
19
19
  }
20
20
  dest = "."
21
21
  verbose = false
22
+ debug = false
22
23
  progress = true
23
24
  count = `grep -c '^processor' /proc/cpuinfo`.strip.to_i/2|1
24
25
  transcode = { flac: :mp3, wav: :mp3 }
25
26
  exclude_patterns = []
26
27
  album_artist = nil
27
28
  file_types = "{flac,mp3,MP3,FLAC,wav,WAV,m4a,M4A}"
29
+ yes = nil
28
30
 
29
31
  opts = OptionParser.new do |opt|
30
32
 
@@ -58,6 +60,10 @@ opts = OptionParser.new do |opt|
58
60
  opt.on("-v", "--[no-]verbose", "Be verbose") do |v|
59
61
  verbose = v
60
62
  end
63
+ opt.on("--debug", "Show debug output. Also enables verbose.") do
64
+ debug = true
65
+ verbose = true
66
+ end
61
67
  opt.on("-t", "--threads NUM",
62
68
  "Number of threads to spawn, useful for transcoding. Default: #{count}" ) do |n|
63
69
  count = n.to_i
@@ -69,6 +75,9 @@ opts = OptionParser.new do |opt|
69
75
  puts MediaFile::VERSION
70
76
  exit 0
71
77
  end
78
+ opt.on("-y", "--yes", "Don't ask before running.") do
79
+ yes = "yes"
80
+ end
72
81
  opt.on_tail("-h", "--help", "Show this message") do
73
82
  warn opt
74
83
  exit
@@ -132,8 +141,8 @@ files.each { |l| puts "\t"+l }
132
141
  puts "#{files.count} files total"
133
142
  puts "The following transcode table will be used:"
134
143
  puts transcode.any? ? transcode : 'none'
135
- puts "Do you wish proceed? (Y/n)"
136
- y = gets
144
+ puts "Do you wish to proceed? (Y/n)"
145
+ y = yes || gets
137
146
  if /n/i.match(y)
138
147
  puts "User cancel."
139
148
  exit
@@ -143,6 +152,7 @@ copier = MediaFile::BulkMediaCopy.new(
143
152
  files,
144
153
  destination_root: dest,
145
154
  verbose: verbose,
155
+ debug: debug,
146
156
  transcode: transcode,
147
157
  progress: progress,
148
158
  album_artist: album_artist
@@ -1,10 +1,15 @@
1
+ # vim:et sw=2 ts=2
2
+
3
+ require 'mediafile'
1
4
  module MediaFile; class BulkMediaCopy
5
+ include ::MediaFile
2
6
  def initialize(source,
3
7
  album_artist: nil,
4
8
  destination_root: ".",
5
9
  progress: false,
6
10
  transcode: {},
7
- verbose: false)
11
+ verbose: false,
12
+ debug: false)
8
13
  source = case source
9
14
  when String
10
15
  [source]
@@ -18,14 +23,8 @@ module MediaFile; class BulkMediaCopy
18
23
  @destination_root = destination_root
19
24
  @verbose = verbose
20
25
  @progress = progress
21
- @work = source.map { |s|
22
- MediaFile.new(s,
23
- base_dir: @destination_root,
24
- force_album_artist: album_artist,
25
- verbose: @verbose,
26
- printer: proc{ |msg| self.safe_print( msg ) }
27
- )
28
- }
26
+ @album_artist = album_artist
27
+ @work = get_work(source)
29
28
  @width = [@work.count.to_s.size, 2].max
30
29
  @name_width = @work.max{ |a,b| a.name.size <=> b.name.size }.name.size
31
30
  @transcode = transcode
@@ -59,29 +58,24 @@ module MediaFile; class BulkMediaCopy
59
58
  end
60
59
  end
61
60
 
62
- def safe_print(message='')
63
- locked {
64
- print block_given? ? yield : message
65
- }
66
- end
67
-
68
61
  private
69
62
 
70
- def locked
71
- if @semaphore
72
- @semaphore.synchronize {
73
- yield
74
- }
75
- else
76
- yield
77
- end
63
+ def get_work(source)
64
+ source.map { |s|
65
+ MediaFile.new(
66
+ s,
67
+ base_dir: @destination_root,
68
+ force_album_artist: @album_artist,
69
+ verbose: @verbose,
70
+ debug: @debug,
71
+ )
72
+ }
78
73
  end
79
74
 
80
75
  def mcopy(max)
81
76
  raise "Argument must repond to :times" unless max.respond_to? :times
82
77
  raise "I haven't any work..." unless @work
83
- require 'thread'
84
- @semaphore = Mutex.new
78
+ initialize_threads(max)
85
79
  queue = Queue.new
86
80
  @work.each { |s| queue << s }
87
81
  threads = []
@@ -93,7 +87,7 @@ module MediaFile; class BulkMediaCopy
93
87
  end
94
88
  end
95
89
  threads.each { |t| t.join }
96
- @semaphore = nil
90
+ cleanup
97
91
  end
98
92
 
99
93
  def scopy
@@ -105,7 +99,7 @@ module MediaFile; class BulkMediaCopy
105
99
 
106
100
  def copy(mediafile)
107
101
  dest = mediafile.out_path transcode_table: @transcode
108
- locked {
102
+ lock {
109
103
  return unless copy_check? mediafile.source_md5, mediafile.source, dest
110
104
  }
111
105
 
@@ -117,7 +111,7 @@ module MediaFile; class BulkMediaCopy
117
111
  err = true
118
112
  end
119
113
 
120
- locked {
114
+ lock {
121
115
  @count += 1
122
116
  if @progress
123
117
  left = @work.count - @count
@@ -1,23 +1,24 @@
1
- #!/usr/bin/env ruby
2
1
  # vim:et sw=2 ts=2
3
2
 
3
+ require 'mediafile'
4
4
  module MediaFile; class MediaFile
5
+ include ::MediaFile
5
6
 
6
7
  attr_reader :source, :type, :name, :base_dir
7
8
 
8
9
  def initialize(path,
9
10
  base_dir: '.',
10
11
  force_album_artist: nil,
11
- printer: proc {|msg| puts msg},
12
- verbose: false)
12
+ verbose: false,
13
+ debug: false)
13
14
  @base_dir = base_dir
14
15
  @destinations = Hash.new{ |k,v| k[v] = {} }
15
16
  @force_album_artist = force_album_artist
16
17
  @name = File.basename( path, File.extname( path ) )
17
- @printer = printer
18
18
  @source = path
19
19
  @type = path[/(\w+)$/].downcase.to_sym
20
20
  @verbose = verbose
21
+ @debug = debug
21
22
  end
22
23
 
23
24
  def source_md5
@@ -35,22 +36,30 @@ module MediaFile; class MediaFile
35
36
  def copy(dest: @base_dir, transcode_table: {})
36
37
  destination = out_path base_dir: dest, transcode_table: transcode_table
37
38
  temp_dest = tmp_path base_dir: dest, transcode_table: transcode_table
38
- unless File.exists? destination
39
- FileUtils.mkdir_p File.dirname destination
40
- begin
41
- transcode_table.has_key?(@type) ?
42
- transcode(transcode_table, temp_dest) :
43
- really_copy(@source, temp_dest)
44
- FileUtils.mv temp_dest, destination
45
- rescue => e
46
- FileUtils.rm temp_dest if File.exists? temp_dest
47
- raise e
39
+ lock{
40
+ if File.exists?(destination)
41
+ warn "File has already been transfered #{@source} => #{destination}" if @verbose
42
+ return
43
+ end
44
+ if File.exists?(temp_dest)
45
+ warn "File transfer is already in progress for #{@source} => #{temp_dest} => #{destination}"
46
+ warn "This shouldn't happen! Check to make sure it was really copied."
47
+ return
48
48
  end
49
+ FileUtils.mkdir_p File.dirname destination
50
+ FileUtils.touch temp_dest
51
+ }
52
+ begin
53
+ transcode_table.has_key?(@type) ?
54
+ transcode(transcode_table, temp_dest) :
55
+ really_copy(@source, temp_dest)
56
+ FileUtils.mv temp_dest, destination
57
+ rescue => e
58
+ FileUtils.rm temp_dest if File.exists? temp_dest
59
+ raise e
49
60
  end
50
- end
51
-
52
- def printit(msg)
53
- @printer.call msg
61
+ ensure
62
+ FileUtils.rm temp_dest if File.exists? temp_dest
54
63
  end
55
64
 
56
65
  def to_s
@@ -75,6 +84,7 @@ module MediaFile; class MediaFile
75
84
  def really_copy(src,dest)
76
85
  FileUtils.cp(src, dest)
77
86
  set_album_artist(dest)
87
+ set_comment_and_title(dest)
78
88
  end
79
89
 
80
90
  def set_decoder()
@@ -131,7 +141,7 @@ module MediaFile; class MediaFile
131
141
  def transcode(trans , destination)
132
142
  to = trans[@type]
133
143
  if to == @type
134
- printit "Attempting to transcode to the same format #{@source} from #{@type} to #{to}"
144
+ safe_print "Attempting to transcode to the same format #{@source} from #{@type} to #{to}"
135
145
  end
136
146
  FileUtils.mkdir_p File.dirname destination
137
147
 
@@ -139,13 +149,13 @@ module MediaFile; class MediaFile
139
149
 
140
150
  encoder = set_encoder(to, destination)
141
151
 
142
- printit "Decoder: '#{decoder.join(' ')}'\nEncoder: '#{encoder.join(' ')}'" if @verbose
152
+ safe_print "Decoder: '#{decoder.join(' ')}'\nEncoder: '#{encoder.join(' ')}'" if @verbose
143
153
 
144
154
  pipes = Hash[[:encoder,:decoder].zip IO.pipe]
145
155
  #readable, writeable = IO.pipe
146
156
  pids = {
147
- spawn(*decoder, :out=>pipes[:decoder], :err=>"/dev/null") => :decoder,
148
- spawn(*encoder, :in =>pipes[:encoder], :err=>"/dev/null") => :encoder,
157
+ spawn(*decoder, :out=>pipes[:decoder], :err=>"/tmp/decoder.err") => :decoder,
158
+ spawn(*encoder, :in =>pipes[:encoder], :err=>"/tmp/encoder.err") => :encoder,
149
159
  }
150
160
  tpids = pids.keys
151
161
  err = []
@@ -169,7 +179,7 @@ module MediaFile; class MediaFile
169
179
  end
170
180
  }
171
181
  rescue Timeout::Error
172
- printit "Timeout exceeded!\n" << tpids.map { |p|
182
+ safe_print "Timeout exceeded!\n" << tpids.map { |p|
173
183
  Process.kill 15, p
174
184
  Process.kill 9, p
175
185
  "#{p} #{Process.wait2( p )[1]}"
@@ -178,7 +188,7 @@ module MediaFile; class MediaFile
178
188
  raise
179
189
  end
180
190
  if err.any?
181
- printit "###\nError transcoding #{@source}: #{err.map{ |it,stat|
191
+ safe_print "###\nError transcoding #{@source}: #{err.map{ |it,stat|
182
192
  "#{it} EOT:#{stat.exitstatus} #{stat}" }.join(" and ") }\n###\n"
183
193
  exit 1
184
194
  end
@@ -191,14 +201,10 @@ module MediaFile; class MediaFile
191
201
  @relpath ||= (
192
202
  read_tags
193
203
  dest = File.join(
194
- [(@album_artist||"UNKNOWN"), (@album||"UNKNOWN")].map { |word|
195
- word.gsub(/^\.+|\.+$/,"").gsub(/\//,"_")
204
+ [@album_artist, @album].map { |word|
205
+ clean_string(word)
196
206
  }
197
207
  )
198
- bool=true
199
- dest.gsub(/\s/,"_").gsub(/[,:)\]\[('"@$^*<>?!]/,"").gsub(/_[&]_/,"_and_").split('').map{ |c|
200
- b = bool; bool = c.match('/|_'); b ? c.capitalize : c
201
- }.join('').gsub(/__+/,'_')
202
208
  )
203
209
  end
204
210
 
@@ -207,33 +213,45 @@ module MediaFile; class MediaFile
207
213
  @newname ||= (
208
214
  read_tags
209
215
  bool = true
210
- file = (
216
+ file = clean_string(
211
217
  case
212
218
  when (@disc_number && (@track > 0) && @title) && !(@disc_total && @disc_total == 1)
213
219
  "%1d_%02d-" % [@disc_number, @track] + @title
214
220
  when (@track > 0 && @title)
215
221
  "%02d-" % @track + @title
216
- when @title
222
+ when @title && @title != ""
217
223
  @title
218
224
  else
219
225
  @name
220
226
  end
221
- ).gsub(
222
- /^\.+|\.+$/,""
223
- ).gsub(
224
- /\//,"_"
225
- ).gsub(
226
- /\s/,"_"
227
- ).gsub(
228
- /[,:)\]\[('"@$^*<>?!=]/,""
229
- ).gsub(
230
- /_[&]_/,"_and_"
231
- ).split('').map{ |c|
232
- b = bool; bool = c.match('/|_'); b ? c.capitalize : c
233
- }.join('').gsub(/__+/,'_')
227
+ )
234
228
  )
235
229
  end
236
230
 
231
+ def clean_string(my_string)
232
+ my_string ||= ""
233
+ t = my_string.gsub(
234
+ /^\.+|\.+$/,""
235
+ ).gsub(
236
+ /\//,"_"
237
+ ).gsub(
238
+ /\s/,"_"
239
+ ).gsub(
240
+ /[,:;)\]\[('"@$^*<>?!=]/,""
241
+ ).gsub(
242
+ /^[.]/,''
243
+ ).gsub(
244
+ /_?[&]_?/,"_and_"
245
+ ).split('_').map{ |c|
246
+ puts "DEBUG: capitalize: '#{c}'" if @debug
247
+ "_and_" == c ? c : c.capitalize
248
+ }.join('_').gsub(
249
+ /__+/,'_'
250
+ ).gsub(/^[.]/, '')
251
+ puts "DEBUG: clean_string: '#{my_string} => '#{t}'" if @debug
252
+ t == "" ? "UNKNOWN" : t
253
+ end
254
+
237
255
  def tmp_file_name
238
256
  "." + new_file_name
239
257
  end
@@ -271,27 +289,51 @@ module MediaFile; class MediaFile
271
289
  tag.add_frame(frame)
272
290
  f.save
273
291
  else
274
- printit("##########\nNo tag returned for #{@name}: #{@source}\n#############\n\n")
292
+ safe_print("##########\nNo tag returned for #{@name}: #{@source}\n#############\n\n")
275
293
  end
276
294
  end
277
295
  end
278
296
  end
279
297
 
298
+ def set_comment_and_title(file)
299
+ klass = (@type == :mp3) ? TagLib::MPEG::File : TagLib::FileRef
300
+ method = (@type == :mp3) ? :id3v2_tag : :tag
301
+
302
+ klass.send(:open, file) do |f|
303
+ tag = if (@type == :mp3)
304
+ f.send(method, true)
305
+ else
306
+ f.send(method)
307
+ end
308
+ tag.comment = "#{@comment}"
309
+ tag.title = (@title || @name.gsub('_',' ')) unless tag.title && tag.title != ""
310
+ if (@type == :mp3)
311
+ f.save(TagLib::MPEG::File::ID3v2)
312
+ else
313
+ f.save
314
+ end
315
+ end
316
+ end
317
+
280
318
  def read_tags
281
319
  return if @red
282
- @album = @artist= @title = @genre = @year = nil
320
+ @album = nil
321
+ @artist= nil
322
+ @title = nil
323
+ @genre = nil
324
+ @year = nil
283
325
  @track = 0
284
- @comment = ""
326
+ @comment = "MediaFile from source: #{@source}\n"
285
327
  TagLib::FileRef.open(@source) do |file|
286
328
  unless file.null?
287
329
  tag = file.tag
288
- @album = tag.album if tag.album
289
- @artist = tag.artist if tag.artist
290
- @title = tag.title if tag.title
291
- @genre = tag.genre if tag.genre
292
- @comment= tag.comment if tag.comment
293
- @track = tag.track if tag.track
294
- @year = tag.year if tag.year
330
+ @album = tag.album if tag.album && tag.album != ""
331
+ @artist = tag.artist if tag.artist && tag.artist != ""
332
+ @title = tag.title if tag.title && tag.title != ""
333
+ @genre = tag.genre if tag.genre && tag.genre != ""
334
+ @comment+= tag.comment if tag.comment && tag.comment != ""
335
+ @track = tag.track if tag.track && tag.track != ""
336
+ @year = tag.year if tag.year && tag.year != ""
295
337
  end
296
338
  end
297
339
  @album_artist = @artist
@@ -335,6 +377,8 @@ module MediaFile; class MediaFile
335
377
  else
336
378
  @album_artist ||= @artist
337
379
  end
380
+ puts "DEBUG: album:'#{@album}', artist:'#{@artist}'" +
381
+ " @title:'#{@title}' @genre:'#{@genre}' @year:'#{@year}'" if @debug
338
382
  @red = true
339
383
  end
340
384
  end; end
@@ -1,3 +1,3 @@
1
1
  module MediaFile
2
- VERSION = "0.1.7" unless defined?(::MediaFile::VERSION)
2
+ VERSION = "0.1.8" unless defined?(::MediaFile::VERSION)
3
3
  end
data/lib/mediafile.rb CHANGED
@@ -25,4 +25,41 @@ module MediaFile
25
25
  "Transcoding may not work in all cases."
26
26
  end
27
27
 
28
- end
28
+ private
29
+
30
+ @@thread_count = 1
31
+ @@semaphore = nil
32
+ @@initialized = false
33
+
34
+ def initialize_threads(count = 1)
35
+ return if @@initialized
36
+ @@initialized = 1
37
+ @@thread_count = count
38
+ if @@thread_count > 1
39
+ require 'thread'
40
+ @@semaphore = Mutex.new
41
+ end
42
+ end
43
+
44
+ def safe_print(message = '')
45
+ lock {
46
+ print block_given? ? yield : message
47
+ }
48
+ end
49
+
50
+ def cleanup
51
+ @@semaphore = nil
52
+ true
53
+ end
54
+
55
+ def lock
56
+ if @@semaphore
57
+ @@semaphore.synchronize {
58
+ yield
59
+ }
60
+ else
61
+ yield
62
+ end
63
+ end
64
+
65
+ 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.7
4
+ version: 0.1.8
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: 2016-11-11 00:00:00.000000000 Z
11
+ date: 2017-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: taglib-ruby