got_mp3 0.2.0 → 0.4.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/got_mp3.rb +235 -32
- data.tar.gz.sig +0 -0
- metadata +63 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a109d9369982eb251b2439256a7ab8ecb0494f8b693efeb869ed6e29329d23e4
|
4
|
+
data.tar.gz: 960ba30e1ab1178b77280f9f6068248cb82616040caa2fca2880dcccc78c1af0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8777628c7ec8d16460fe3ff59199b98bf34bf10494ffdb22afd432cb1f7110591bd48370998f4bc4757f9564d3b4a190ec03537e03d2f9c7cecd01b0ddafe744
|
7
|
+
data.tar.gz: 7c9b29dc4e6e889644c2cfef6464fcb09fb34ead8101670a7f08942344132994099444a144b5ee5a24e9f76d1c176f595837845accd9a6ef8efb1ff269994e80
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/got_mp3.rb
CHANGED
@@ -2,12 +2,17 @@
|
|
2
2
|
|
3
3
|
# file: got_mp3.rb
|
4
4
|
|
5
|
+
require 'dxlite'
|
5
6
|
require 'ostruct'
|
6
|
-
require
|
7
|
+
require 'mp3info'
|
8
|
+
require 'ogginfo'
|
9
|
+
require 'playlist_creator'
|
7
10
|
|
8
11
|
|
9
12
|
class GotMP3
|
10
13
|
|
14
|
+
attr_accessor :dir
|
15
|
+
|
11
16
|
def initialize(dir: '.', debug: false)
|
12
17
|
@dir, @debug = dir, debug
|
13
18
|
end
|
@@ -22,6 +27,8 @@ class GotMP3
|
|
22
27
|
def add_jpg()
|
23
28
|
|
24
29
|
find_by_ext('.jpg').each do |directory, img_filename|
|
30
|
+
|
31
|
+
puts 'add_jpg to directory: ' + directory.inspect if @debug
|
25
32
|
add_image directory, img_filename
|
26
33
|
end
|
27
34
|
|
@@ -41,12 +48,118 @@ class GotMP3
|
|
41
48
|
|
42
49
|
end
|
43
50
|
|
44
|
-
|
51
|
+
# Adds an XSPF playlist file to each file directory containing MP3s
|
52
|
+
#
|
53
|
+
def add_playlist(dir: @dir)
|
45
54
|
|
46
|
-
|
47
|
-
|
55
|
+
# .new('/home/james/Music2/Christmas)
|
56
|
+
#mp3s = Dir[File.join(@dir, '*.mp3')].sort_by {|x| File.mtime(x)}.reverse
|
57
|
+
a = Dir.glob(File.join(dir, '*')).select {|f| File.directory? f}
|
58
|
+
|
59
|
+
tracks = []
|
60
|
+
|
61
|
+
a.each do |dir|
|
62
|
+
|
63
|
+
tracks += add_playlist(dir: dir)
|
64
|
+
|
65
|
+
each_media_track(dir) do |media, trackno, filepath|
|
66
|
+
|
67
|
+
info = case File.extname(filepath)
|
68
|
+
when '.mp3'
|
69
|
+
mp3info(media)
|
70
|
+
when '.ogg'
|
71
|
+
ogginfo(media)
|
72
|
+
end
|
73
|
+
|
74
|
+
h = {
|
75
|
+
title: info[:title],
|
76
|
+
location: File.join(File.basename(dir), File.basename(filepath)),
|
77
|
+
album: info[:album]
|
78
|
+
}
|
79
|
+
|
80
|
+
tracks << h
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
pc = PlaylistCreator.new()
|
89
|
+
|
90
|
+
tracks.each do |x|
|
91
|
+
pc.add title: x[:title], location: x[:location], album: x[:album]
|
92
|
+
end
|
93
|
+
|
94
|
+
File.write File.join(dir,'playlist.xspf'), pc.to_xspf
|
95
|
+
|
96
|
+
return tracks
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
# Copies all MP3 directories through the category file-directory stucture.
|
101
|
+
# To accomplish this, it need a reference (source) directory containing the
|
102
|
+
# .txt for each album.
|
103
|
+
#
|
104
|
+
def compile(source_directory: '', target_directory: '')
|
105
|
+
|
106
|
+
raise 'target_directory cannot be empty' if target_directory.empty?
|
48
107
|
|
49
|
-
|
108
|
+
find_by_ext('.txt').each do |directory, _ |
|
109
|
+
|
110
|
+
Dir[File.join(directory, '*.txt')].each do |txt_filename|
|
111
|
+
|
112
|
+
album = File.join(source_directory,
|
113
|
+
File.basename(txt_filename).sub(/\.txt$/,''))
|
114
|
+
library_dir = File.join(target_directory, File.basename(directory))
|
115
|
+
FileUtils.mkdir_p library_dir
|
116
|
+
|
117
|
+
if @debug then
|
118
|
+
puts 'copying from:' + album.inspect
|
119
|
+
puts 'copying to: ' + library_dir.inspect
|
120
|
+
end
|
121
|
+
|
122
|
+
puts 'copying ' + album + ' ...'
|
123
|
+
FileUtils.cp_r album, library_dir, remove_destination: true
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
# copy the .txt file from each mp3 directory into a central file directory
|
131
|
+
#
|
132
|
+
def consolidate_txt(target_directory: '')
|
133
|
+
|
134
|
+
raise 'target_directory cannot be empty' if target_directory.empty?
|
135
|
+
|
136
|
+
find_by_ext('.mp3').each do |directory, _ |
|
137
|
+
|
138
|
+
txt_filename = Dir[File.join(directory, '*.txt')].first
|
139
|
+
|
140
|
+
next unless txt_filename
|
141
|
+
|
142
|
+
target_file = File.basename(directory)
|
143
|
+
FileUtils.cp txt_filename, File.join(target_directory,
|
144
|
+
target_file + '.txt')
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
def each_media_file(directory=@dir, &blk)
|
151
|
+
|
152
|
+
puts 'each_media_file - directory: ' + directory.inspect if @debug
|
153
|
+
found = Dir[File.join(directory, "*.{mp3,ogg}")].sort_by { |x| File.mtime(x) }
|
154
|
+
puts 'each_nedia_file - found: ' + found.inspect if @debug
|
155
|
+
|
156
|
+
found.each.with_index do |media_filepath, i|
|
157
|
+
|
158
|
+
puts 'each_medi... - media_filepath: ' + media_filepath.inspect if @debug
|
159
|
+
|
160
|
+
next unless File.exists? media_filepath
|
161
|
+
|
162
|
+
blk.call(media_filepath, i )
|
50
163
|
|
51
164
|
end
|
52
165
|
|
@@ -60,6 +173,7 @@ class GotMP3
|
|
60
173
|
|
61
174
|
# find the image file
|
62
175
|
img_filename = Dir[File.join(directory, '*.jpg')].first
|
176
|
+
puts 'img_filename: ' + img_filename.inspect if @debug
|
63
177
|
|
64
178
|
# find the text file
|
65
179
|
txt_filename = Dir[File.join(directory, '*.txt')].first
|
@@ -79,7 +193,7 @@ class GotMP3
|
|
79
193
|
#
|
80
194
|
def rename()
|
81
195
|
|
82
|
-
|
196
|
+
each_media_file do |mp3_filepath|
|
83
197
|
|
84
198
|
mp3_directory = File.dirname(mp3_filepath)
|
85
199
|
mp3_filename = File.basename(mp3_filepath)
|
@@ -91,43 +205,94 @@ class GotMP3
|
|
91
205
|
|
92
206
|
end
|
93
207
|
|
94
|
-
|
208
|
+
# sort MP3 or OGG by track number in the filename
|
209
|
+
#
|
210
|
+
def sort_by_tracknum!()
|
211
|
+
|
212
|
+
each_media_file do |filepath|
|
95
213
|
|
96
|
-
|
214
|
+
directory = File.dirname(filepath)
|
215
|
+
a = Dir[File.join(directory, '*.{mp3,ogg}')]
|
216
|
+
a.sort_by {|x| x[/^\d+/]}.each {|file| FileUtils.touch file}
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
# write either a track listing into the media filepath, either in
|
223
|
+
# plain text or XML format.
|
224
|
+
#
|
225
|
+
def write_titles(format: 'txt')
|
226
|
+
|
227
|
+
puts 'inside write_titles()' if @debug
|
228
|
+
|
229
|
+
find_by_ext('.{mp3,ogg}').each do |directory, _ |
|
230
|
+
|
231
|
+
puts 'write_titles() - directory: ' + directory.inspect if @debug
|
232
|
+
txt_filename = Dir[File.join(directory, '*.txt')].first
|
233
|
+
|
234
|
+
next if txt_filename and format.to_sym == :txt
|
97
235
|
|
98
236
|
tracks = []
|
99
237
|
|
100
|
-
|
238
|
+
each_media_track(directory) do |media, trackno, media_filepath|
|
239
|
+
|
240
|
+
info = case File.extname(media_filepath)
|
241
|
+
when '.mp3'
|
242
|
+
mp3info(media)
|
243
|
+
when '.ogg'
|
244
|
+
ogginfo(media)
|
245
|
+
end
|
101
246
|
|
102
|
-
|
103
|
-
|
104
|
-
artist: mp3.tag.artist,
|
105
|
-
album: mp3.tag.album,
|
106
|
-
album_artist: mp3.tag2['TPE2'],
|
107
|
-
disc: mp3.tag2['TPOS'],
|
108
|
-
tracknum: mp3.tag.tracknum,
|
109
|
-
filename: File.basename(mp3_filepath)
|
110
|
-
})
|
247
|
+
info[:filename] = File.basename(media_filepath)
|
248
|
+
tracks << OpenStruct.new(info)
|
111
249
|
|
112
250
|
end
|
113
251
|
|
252
|
+
puts 'tracks: ' + tracks.inspect if @debug
|
253
|
+
|
114
254
|
heading = tracks[0].album_artist + ' - ' + tracks[0].album
|
255
|
+
|
115
256
|
s = "# %s\n\n" % [heading]
|
116
|
-
h = tracks.group_by(&:disc)
|
117
257
|
|
118
|
-
body = if h.length == 1 then
|
119
258
|
|
120
|
-
|
259
|
+
if format.to_sym == :txt then
|
260
|
+
|
261
|
+
h = tracks.group_by(&:disc)
|
262
|
+
|
263
|
+
body = if h.length == 1 then
|
264
|
+
|
265
|
+
list(tracks)
|
266
|
+
|
267
|
+
else
|
268
|
+
|
269
|
+
"\n" + h.map do |disc, tracks2|
|
270
|
+
("## Disc %d\n\n" % disc) + list(tracks2) + "\n\n"
|
271
|
+
end.join("\n")
|
272
|
+
|
273
|
+
end
|
274
|
+
|
275
|
+
File.write File.join(directory, heading + '.txt'), s + body
|
121
276
|
|
122
277
|
else
|
123
278
|
|
124
|
-
|
125
|
-
|
126
|
-
end.join("\n")
|
279
|
+
# :xml
|
280
|
+
puts 'xml' if @debug
|
127
281
|
|
128
|
-
|
282
|
+
dx = DxLite.new('album[album_artist, album]/track(tracknum, title, ' +
|
283
|
+
'artist, disc, album_artist)')
|
284
|
+
|
285
|
+
#h.each {|_,x| puts x.inspect } if @debug
|
286
|
+
#h.each {|_,x| x.each {|track| dx.create(track.to_h) } }
|
287
|
+
tracks.each {|track| dx.create(track.to_h) }
|
129
288
|
|
130
|
-
|
289
|
+
dx.album_artist = dx.all[0].album_artist
|
290
|
+
dx.album = dx.all[0].album
|
291
|
+
dx.save File.join(directory, 'playlist.dx')
|
292
|
+
|
293
|
+
yield(directory) if block_given?
|
294
|
+
|
295
|
+
end
|
131
296
|
|
132
297
|
end
|
133
298
|
|
@@ -139,11 +304,12 @@ class GotMP3
|
|
139
304
|
#
|
140
305
|
def add_image(directory, img_filename)
|
141
306
|
|
307
|
+
puts 'inside add_image - directory: ' + directory.inspect if @debug
|
142
308
|
puts 'img_filename: ' + img_filename.inspect if @debug
|
143
309
|
image_file = File.new(File.join(directory, img_filename),'rb')
|
144
310
|
img = image_file.read
|
145
311
|
|
146
|
-
|
312
|
+
each_media_track(directory) do |mp3, _, _|
|
147
313
|
|
148
314
|
mp3.tag2.remove_pictures
|
149
315
|
mp3.tag2.add_picture img
|
@@ -152,14 +318,16 @@ class GotMP3
|
|
152
318
|
|
153
319
|
end
|
154
320
|
|
321
|
+
# Modifies the MP3s' tags using the track titles in the .txt file
|
322
|
+
#
|
155
323
|
def add_tracktitles(directory, txt_filename)
|
156
324
|
|
157
325
|
txt_file = File.join(directory, txt_filename)
|
158
326
|
track_titles = File.read(txt_file).lines[1..-1].map(&:strip)
|
159
327
|
|
160
|
-
|
328
|
+
each_media_track(directory) do |media, trackno, _|
|
161
329
|
|
162
|
-
|
330
|
+
media.tag.title = track_titles[trackno-1]
|
163
331
|
|
164
332
|
end
|
165
333
|
end
|
@@ -197,11 +365,16 @@ class GotMP3
|
|
197
365
|
|
198
366
|
end
|
199
367
|
|
200
|
-
def
|
368
|
+
def each_media_track(directory, &blk)
|
201
369
|
|
202
|
-
|
370
|
+
each_media_file(directory) do |filepath, i|
|
203
371
|
|
204
|
-
|
372
|
+
case File.extname(filepath)
|
373
|
+
when '.mp3'
|
374
|
+
Mp3Info.open(filepath) {|mp3| blk.call(mp3, i+1, filepath) }
|
375
|
+
when '.ogg'
|
376
|
+
OggInfo.open(filepath) {|ogg| blk.call(ogg, i+1, filepath) }
|
377
|
+
end
|
205
378
|
|
206
379
|
end
|
207
380
|
|
@@ -209,6 +382,7 @@ class GotMP3
|
|
209
382
|
|
210
383
|
def find_by_ext(extension)
|
211
384
|
|
385
|
+
puts 'find_by_ext() - @dir' + @dir.inspect if @debug
|
212
386
|
a = Dir[File.join(@dir, "**", "*" + extension)]
|
213
387
|
puts 'a: ' + a.inspect if @debug
|
214
388
|
|
@@ -220,6 +394,8 @@ class GotMP3
|
|
220
394
|
end
|
221
395
|
end
|
222
396
|
|
397
|
+
# used by public method *write_titles* to list the tracks in plain text
|
398
|
+
#
|
223
399
|
def list(tracks)
|
224
400
|
|
225
401
|
a = if tracks.map(&:artist).uniq.length < 2 then
|
@@ -232,6 +408,33 @@ class GotMP3
|
|
232
408
|
|
233
409
|
end
|
234
410
|
|
411
|
+
def mp3info(mp3)
|
412
|
+
{
|
413
|
+
title: mp3.tag.title.sub(/^\d+\. */,''),
|
414
|
+
artist: mp3.tag.artist,
|
415
|
+
album: mp3.tag.album,
|
416
|
+
album_artist: mp3.tag2['TPE2'] || mp3.tag.artist,
|
417
|
+
disc: mp3.tag2['TPOS'] || 1,
|
418
|
+
tracknum: mp3.tag.tracknum,
|
419
|
+
}
|
420
|
+
end
|
421
|
+
|
422
|
+
def ogginfo(ogg)
|
423
|
+
|
424
|
+
# options
|
425
|
+
# ["title", "artist", "tracknumber", "tracktotal", "album", "albumartist",
|
426
|
+
# "genre", "discid", "musicbrainz_discid"]
|
427
|
+
|
428
|
+
{
|
429
|
+
title: ogg.tag.title.sub(/^\d+\. */,''),
|
430
|
+
artist: ogg.tag.artist,
|
431
|
+
album: ogg.tag.album,
|
432
|
+
album_artist: ogg.tag.albumartist || ogg.tag.artist,
|
433
|
+
disc: ogg.tag.discid || 1,
|
434
|
+
tracknum: ogg.tag.tracknumber,
|
435
|
+
}
|
436
|
+
end
|
437
|
+
|
235
438
|
end
|
236
439
|
|
237
440
|
class Titles
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: got_mp3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Robertson
|
@@ -35,8 +35,28 @@ cert_chain:
|
|
35
35
|
RFrZsrDO1Cwk4v03qCUvuZ8H/nCojYYJGMrJE1JhBBJNJXCAULivh/zlOYcDhG69
|
36
36
|
uPtZOD7YKrG45OftX8aNxlNv
|
37
37
|
-----END CERTIFICATE-----
|
38
|
-
date: 2021-11-
|
38
|
+
date: 2021-11-28 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: dxlite
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.3'
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 0.3.5
|
50
|
+
type: :runtime
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - "~>"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0.3'
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 0.3.5
|
40
60
|
- !ruby/object:Gem::Dependency
|
41
61
|
name: ruby-mp3info
|
42
62
|
requirement: !ruby/object:Gem::Requirement
|
@@ -57,6 +77,46 @@ dependencies:
|
|
57
77
|
- - ">="
|
58
78
|
- !ruby/object:Gem::Version
|
59
79
|
version: 0.8.10
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: ruby-ogginfo
|
82
|
+
requirement: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - "~>"
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0.7'
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.7.2
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.7'
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: 0.7.2
|
100
|
+
- !ruby/object:Gem::Dependency
|
101
|
+
name: playlist_creator
|
102
|
+
requirement: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: 0.2.0
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0.2'
|
110
|
+
type: :runtime
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 0.2.0
|
117
|
+
- - "~>"
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0.2'
|
60
120
|
description:
|
61
121
|
email: digital.robertson@gmail.com
|
62
122
|
executables: []
|
@@ -88,5 +148,5 @@ rubygems_version: 2.7.10
|
|
88
148
|
signing_key:
|
89
149
|
specification_version: 4
|
90
150
|
summary: A ruby-mp3info wrapper to make it convenient to update the album art and
|
91
|
-
track titles of multiple MP3 files
|
151
|
+
track titles of multiple MP3 files in a batch process.
|
92
152
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|