unitf-tag 0.1.10 → 0.1.23
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/.gitignore +2 -0
- data/.rubocop.yml +16 -1
- data/.vscode/tasks.json +1 -1
- data/CHANGELOG.md +3 -4
- data/bin/test.rb +16 -1
- data/exe/tag +28 -45
- data/exe/unitf-autotags +17 -0
- data/lib/unitf/tag/file.rb +74 -32
- data/lib/unitf/tag/fileset.rb +7 -2
- data/lib/unitf/tag/flac.rb +6 -5
- data/lib/unitf/tag/helpers.rb +14 -0
- data/lib/unitf/tag/mp3.rb +7 -6
- data/lib/unitf/tag/version.rb +1 -1
- data/lib/unitf/tag.rb +23 -55
- data/unitf-tag.gemspec +16 -16
- metadata +11 -10
- data/bin/console +0 -15
- data/bin/setup +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88fd979281b215b41615a9675d426d0dec125ae746b4c824600dfad98da63004
|
4
|
+
data.tar.gz: 60cb6b8fa88cf88bf915ecc0241b1aac928ceeb845851acb48c0afe6caffaa63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b591396ce2b828640af59694a96b6e3344e8a2a1112c999f85045f4bf8d8edaf83117f343ea4c881ad58774d2b6b047b0f8235b52703a9281534be1ce542df95
|
7
|
+
data.tar.gz: abda1347dd22199932558cfc2ea06529e0286c7b6a2fa23c0f63faa2edcb1a0778cb4f7a1c25ad1622e7366f1377700c0dda0f6d9537f4f78e43405e8c44fa27
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
AllCops:
|
2
|
+
NewCops: enable
|
3
|
+
|
1
4
|
Layout/EmptyLineAfterGuardClause:
|
2
5
|
Enabled: false
|
3
6
|
|
@@ -10,5 +13,17 @@ Style/FrozenStringLiteralComment:
|
|
10
13
|
Metrics/MethodLength:
|
11
14
|
Enabled: false
|
12
15
|
|
13
|
-
Metrics/AbcSize:
|
16
|
+
Metrics/AbcSize:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Metrics/ClassLength:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Style/FormatStringToken:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Style/FormatString:
|
14
26
|
Enabled: false
|
27
|
+
|
28
|
+
Metrics/BlockLength:
|
29
|
+
Enabled: false
|
data/.vscode/tasks.json
CHANGED
data/CHANGELOG.md
CHANGED
data/bin/test.rb
CHANGED
@@ -4,8 +4,23 @@ require 'unitf/tag'
|
|
4
4
|
|
5
5
|
UnitF::Log.to_console
|
6
6
|
|
7
|
-
UnitF::Tag.update('/
|
7
|
+
UnitF::Tag.update('test-data/foo.mp3') do |file|
|
8
8
|
file.tag.artist = 'bar123'
|
9
9
|
file.tag.year = 1972
|
10
|
+
file.tag.album = 'This is the album'
|
11
|
+
file.tag.title = 'This is the title'
|
12
|
+
file.tag.track = 99
|
10
13
|
file.auto_cover!
|
11
14
|
end
|
15
|
+
|
16
|
+
file = UnitF::Tag::File.new('test-data/bar.mp3')
|
17
|
+
# file.tag
|
18
|
+
|
19
|
+
file.update do |f|
|
20
|
+
f.tag.title = 'Hello world2!'
|
21
|
+
f.tag.track = 100
|
22
|
+
end
|
23
|
+
|
24
|
+
file.open do |f|
|
25
|
+
f.tag.title = 'Hello world9!'
|
26
|
+
end
|
data/exe/tag
CHANGED
@@ -10,8 +10,10 @@ require 'unitf/logging'
|
|
10
10
|
UnitF::Log.to_console
|
11
11
|
|
12
12
|
actions = []
|
13
|
-
files = []
|
14
13
|
opt = {}
|
14
|
+
properties = {}
|
15
|
+
|
16
|
+
no_args = ARGV.empty?
|
15
17
|
|
16
18
|
targets = OptionParser.new do |opts|
|
17
19
|
opts.on('-r', '--recursive', 'Auto Cover') do
|
@@ -34,6 +36,12 @@ targets = OptionParser.new do |opts|
|
|
34
36
|
opt[:format] = :line
|
35
37
|
end
|
36
38
|
|
39
|
+
opts.on('--cover COVER', 'Cover') do |arg|
|
40
|
+
actions << :cover
|
41
|
+
raise "Invalid cover file #{arg}" unless File.exist?(arg) && arg.downcase.end_with?('.jpg')
|
42
|
+
opt[:cover] = arg
|
43
|
+
end
|
44
|
+
|
37
45
|
opts.on('--auto_cover', 'Auto Cover') do
|
38
46
|
actions << :auto_cover
|
39
47
|
end
|
@@ -51,82 +59,57 @@ targets = OptionParser.new do |opts|
|
|
51
59
|
end
|
52
60
|
|
53
61
|
opts.on('--artist ARTIST', 'Artist') do |arg|
|
54
|
-
|
55
|
-
opt[:artist] = arg
|
62
|
+
properties[:artist] = arg
|
56
63
|
end
|
57
64
|
|
58
65
|
opts.on('--album ALBUM', 'Album') do |arg|
|
59
|
-
|
60
|
-
opt[:album] = arg
|
66
|
+
properties[:album] = arg
|
61
67
|
end
|
62
68
|
|
63
69
|
opts.on('--title TITLE', 'Song Title') do |arg|
|
64
|
-
|
65
|
-
opt[:title] = arg
|
70
|
+
properties[:title] = arg
|
66
71
|
end
|
67
72
|
|
68
73
|
opts.on('--year YEAR', 'Song Year') do |arg|
|
69
|
-
|
70
|
-
opt[:year] = arg.to_i
|
74
|
+
properties[:year] = arg.to_i
|
71
75
|
end
|
72
76
|
|
73
77
|
opts.on('--genre GENRE', 'Genre') do |arg|
|
74
|
-
|
75
|
-
|
78
|
+
properties[:genre] = arg
|
79
|
+
end
|
80
|
+
|
81
|
+
opts.on('--track TRACK', 'Song Track') do |arg|
|
82
|
+
properties[:track] = arg.to_i
|
76
83
|
end
|
77
84
|
end.parse!
|
78
85
|
|
86
|
+
targets = Dir.glob('*') if no_args
|
87
|
+
|
79
88
|
files = UnitF::Tag::FileSet.new(targets)
|
80
89
|
|
81
|
-
if files.
|
82
|
-
UnitF::Log.error('Cannot find any files to operate on')
|
83
|
-
end
|
90
|
+
UnitF::Log.error('Cannot find any files to operate on') if files.empty?
|
84
91
|
|
85
|
-
if actions.
|
92
|
+
if actions.empty? && properties.empty?
|
86
93
|
UnitF::Tag.list(files, format: opt[:format])
|
87
94
|
exit 0
|
88
95
|
end
|
89
96
|
|
90
97
|
files.each do |file|
|
91
98
|
UnitF::Log.info("Processing file: #{file}")
|
92
|
-
file.
|
99
|
+
file.update do |o|
|
100
|
+
o.properties!(properties) unless properties.empty?
|
101
|
+
|
93
102
|
actions.each do |action|
|
94
103
|
case action
|
104
|
+
when :cover
|
105
|
+
o.cover!(opt[:cover])
|
95
106
|
when :delete_cover
|
96
|
-
UnitF::Log.info('Delete cover')
|
97
107
|
o.delete_cover!
|
98
108
|
when :auto_cover
|
99
|
-
|
100
|
-
UnitF::Log.info("Auto Cover #{file.cover_path}")
|
101
|
-
begin
|
102
|
-
o.auto_cover!
|
103
|
-
rescue => e
|
104
|
-
UnitF::Log.error("Failed to auto-cover file #{e}")
|
105
|
-
end
|
106
|
-
end
|
109
|
+
o.auto_cover!
|
107
110
|
when :auto_tag
|
108
|
-
UnitF::Log.info('Auto Tag')
|
109
111
|
o.auto_tag!
|
110
|
-
when :artist
|
111
|
-
UnitF::Log.info("Setting artist to #{opt[:artist]}")
|
112
|
-
o.tag.artist = opt[:artist]
|
113
|
-
when :album
|
114
|
-
UnitF::Log.info("Setting album to #{opt[:album]}")
|
115
|
-
o.tag.album = opt[:album]
|
116
|
-
when :title
|
117
|
-
UnitF::Log.info("Setting title to #{opt[:title]}")
|
118
|
-
o.tag.title = opt[:title]
|
119
|
-
when :genre
|
120
|
-
UnitF::Log.info("Setting genre to #{opt[:genre]}")
|
121
|
-
o.tag.genre = opt[:genre]
|
122
|
-
when :track
|
123
|
-
UnitF::Log.info("Setting track to #{opt[:track]}")
|
124
|
-
o.tag.genre = opt[:track]
|
125
|
-
when :year
|
126
|
-
UnitF::Log.info("Setting year to #{opt[:year]}")
|
127
|
-
o.tag.year = opt[:year]
|
128
112
|
end
|
129
113
|
end
|
130
|
-
o.save || UnitF::Log.error("Unable to save changes to #{file}")
|
131
114
|
end
|
132
115
|
end
|
data/exe/unitf-autotags
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift("#{__dir__}/../lib")
|
4
|
+
|
5
|
+
require 'logger'
|
6
|
+
require 'optparse'
|
7
|
+
require 'unitf/tag'
|
8
|
+
require 'unitf/logging'
|
9
|
+
|
10
|
+
UnitF::Log.to_console
|
11
|
+
|
12
|
+
files = UnitF::Tag::FileSet.new(ARGV)
|
13
|
+
|
14
|
+
files.each do |file|
|
15
|
+
puts "File: #{file}"
|
16
|
+
puts "Tags: #{file.auto_tags}"
|
17
|
+
end
|
data/lib/unitf/tag/file.rb
CHANGED
@@ -12,7 +12,18 @@ module UnitF
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def tag
|
15
|
-
@file.
|
15
|
+
raise Error, "File is not open #{self.class.name}" if @file&.tag.nil?
|
16
|
+
|
17
|
+
@file&.tag
|
18
|
+
end
|
19
|
+
|
20
|
+
def save
|
21
|
+
@file&.save
|
22
|
+
end
|
23
|
+
|
24
|
+
def close
|
25
|
+
@file&.close
|
26
|
+
@file = nil
|
16
27
|
end
|
17
28
|
|
18
29
|
def format_json
|
@@ -54,7 +65,9 @@ module UnitF
|
|
54
65
|
end
|
55
66
|
|
56
67
|
def cover_path
|
57
|
-
"#{dirname}/cover.jpg"
|
68
|
+
["#{dirname}/cover.jpg", "#{dirname}/../cover.jpg"].each do |path|
|
69
|
+
return ::File.realpath(path) if ::File.exist?(path)
|
70
|
+
end
|
58
71
|
end
|
59
72
|
|
60
73
|
def auto_tag_path
|
@@ -70,11 +83,17 @@ module UnitF
|
|
70
83
|
end
|
71
84
|
|
72
85
|
def cover_available?
|
73
|
-
|
86
|
+
cover_path != nil
|
74
87
|
end
|
75
88
|
|
76
89
|
def auto_cover!
|
77
|
-
|
90
|
+
raise Error, "File is not open #{self.class.name}" if tag.nil?
|
91
|
+
|
92
|
+
cover!(cover_path)
|
93
|
+
true
|
94
|
+
rescue StandardError => e
|
95
|
+
UnitF::Log.error("Failed to auto-cover file #{e}")
|
96
|
+
false
|
78
97
|
end
|
79
98
|
|
80
99
|
def manual_auto_tags
|
@@ -88,41 +107,59 @@ module UnitF
|
|
88
107
|
tags[tag.to_sym] = value
|
89
108
|
end
|
90
109
|
tags
|
91
|
-
rescue
|
110
|
+
rescue StandardError
|
92
111
|
{}
|
93
112
|
end
|
94
113
|
|
95
|
-
def auto_tags
|
96
|
-
|
97
|
-
|
114
|
+
# def auto_tags
|
115
|
+
# manual_tags = manual_auto_tags
|
116
|
+
# tags = {}
|
98
117
|
|
99
|
-
|
100
|
-
|
101
|
-
tags[:title].gsub!(/\.\w+$/, '')
|
102
|
-
tags[:title].gsub!(/^\d*\s*(-|\.)*\s*/, '')
|
103
|
-
path_parts = realpath.dirname.to_path.split('/')
|
104
|
-
tags[:album] = path_parts[-1]
|
105
|
-
tags[:artist] = path_parts[-2]
|
118
|
+
# tags[:title] = ::File.basename(realpath.to_path)
|
119
|
+
# track = tags[:title].match(/^\s*\d+/).to_s.to_i
|
106
120
|
|
107
|
-
|
108
|
-
|
121
|
+
# if tags[:title].scan(/(\.|_|-)(\d\d\d\d(\.|-)\d\d(\.|-)\d\d)/)
|
122
|
+
# tags[:title] = ::Regexp::last_match
|
123
|
+
# else
|
124
|
+
# tags[:title].gsub!(/\.\w+$/, '')
|
125
|
+
# tags[:title].gsub!(/^\d*\s*(-|\.)*\s*/, '')
|
126
|
+
# end
|
127
|
+
|
128
|
+
# path_parts = realpath.dirname.to_path.split('/')
|
129
|
+
# tags[:album] = path_parts[-1]
|
130
|
+
# tags[:artist] = path_parts[-2]
|
131
|
+
|
132
|
+
# tags.merge(manual_auto_tags)
|
133
|
+
# end
|
109
134
|
|
110
135
|
def auto_tag!
|
111
|
-
UnitF::Log.info("Auto tagging #{
|
112
|
-
manual_auto_tags
|
136
|
+
UnitF::Log.info("Auto tagging #{self}")
|
113
137
|
|
114
138
|
title = ::File.basename(realpath.to_path)
|
139
|
+
path_parts = realpath.dirname.to_path.split('/')
|
140
|
+
|
141
|
+
year = nil
|
115
142
|
|
116
143
|
# This must come before gsubbing the title
|
117
144
|
track = title.match(/^\s*\d+/).to_s.to_i
|
118
145
|
|
119
|
-
|
120
|
-
title.
|
146
|
+
# Specific formatting for dated radio
|
147
|
+
if title.scan(/(\.|_|-)((\d\d\d\d)(\.|-)\d\d(\.|-)\d\d(\w*))\./).any?
|
148
|
+
title = ::Regexp.last_match[2]
|
149
|
+
album = path_parts[-1]
|
150
|
+
year = ::Regexp.last_match[3].to_i
|
151
|
+
# elsif title.scan(/^(\w\w(\d\d)(\d\d)(\d\d))\./).any?
|
152
|
+
# title = ::Regexp.last_match[1]
|
153
|
+
# year = ::Regexp.last_match[1].to_i + 2000
|
154
|
+
else
|
155
|
+
title.gsub!(/\.\w+$/, '')
|
156
|
+
title.gsub!(/^\d*\s*(-|\.)*\s*/, '')
|
157
|
+
album = path_parts[-1]
|
158
|
+
end
|
121
159
|
|
122
|
-
path_parts = realpath.dirname.to_path.split('/')
|
123
|
-
album = path_parts[-1]
|
124
160
|
artist = path_parts[-2]
|
125
161
|
|
162
|
+
tag.year = year unless year.nil?
|
126
163
|
tag.album = album
|
127
164
|
tag.artist = artist
|
128
165
|
tag.title = title
|
@@ -130,23 +167,28 @@ module UnitF
|
|
130
167
|
self.album_artist = artist
|
131
168
|
end
|
132
169
|
|
133
|
-
def
|
134
|
-
|
170
|
+
def properties!(properties)
|
171
|
+
properties.each_pair do |property, value|
|
172
|
+
UnitF::Log.info("Setting #{property} to #{value}")
|
173
|
+
tag.send("#{property}=", value)
|
174
|
+
end
|
135
175
|
end
|
136
176
|
|
137
|
-
def
|
138
|
-
|
139
|
-
|
177
|
+
def update
|
178
|
+
open(auto_save: true) do |file|
|
179
|
+
yield(file) if block_given?
|
180
|
+
end
|
140
181
|
end
|
141
182
|
|
142
|
-
def open
|
143
|
-
|
183
|
+
def open(auto_save: false)
|
184
|
+
file = if flac?
|
144
185
|
UnitF::Tag::FLAC.new(to_path)
|
145
186
|
elsif mp3?
|
146
187
|
UnitF::Tag::MP3.new(to_path)
|
147
188
|
end
|
148
|
-
yield(
|
149
|
-
|
189
|
+
yield(file) if block_given?
|
190
|
+
file.save if auto_save
|
191
|
+
file.close
|
150
192
|
end
|
151
193
|
end
|
152
194
|
end
|
data/lib/unitf/tag/fileset.rb
CHANGED
@@ -1,16 +1,20 @@
|
|
1
1
|
module UnitF
|
2
2
|
module Tag
|
3
3
|
class FileSet < Array
|
4
|
+
include Helpers
|
5
|
+
|
4
6
|
def initialize(targets)
|
7
|
+
targets = [targets] if targets.is_a?(String)
|
5
8
|
targets.each do |target|
|
6
9
|
process_target(target)
|
7
10
|
end
|
8
11
|
end
|
9
12
|
|
10
13
|
def process_target(target)
|
14
|
+
UnitF::Log.debug("Processing target #{target}...")
|
11
15
|
if ::File.directory?(target)
|
12
16
|
find_files(target)
|
13
|
-
elsif
|
17
|
+
elsif valid_file?(target)
|
14
18
|
append(UnitF::Tag::File.new(target))
|
15
19
|
end
|
16
20
|
end
|
@@ -18,7 +22,8 @@ module UnitF
|
|
18
22
|
def find_files(root_path)
|
19
23
|
Find.find(root_path) do |file_path|
|
20
24
|
UnitF::Log.debug("Considering #{file_path}")
|
21
|
-
next unless
|
25
|
+
next unless valid_file?(file_path)
|
26
|
+
|
22
27
|
UnitF::Log.debug("Including #{file_path}")
|
23
28
|
append(UnitF::Tag::File.new(file_path))
|
24
29
|
end
|
data/lib/unitf/tag/flac.rb
CHANGED
@@ -22,11 +22,12 @@ module UnitF
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def cover!(file_path)
|
25
|
+
UnitF::Log.info("Setting cover #{file_path}")
|
25
26
|
pic = TagLib::FLAC::Picture.new
|
26
27
|
pic.type = TagLib::FLAC::Picture::FrontCover
|
27
|
-
pic.mime_type =
|
28
|
-
pic.description =
|
29
|
-
pic.data = ::File.
|
28
|
+
pic.mime_type = 'image/jpeg'
|
29
|
+
pic.description = 'Front Cover'
|
30
|
+
pic.data = ::File.binread(file_path)
|
30
31
|
@file.add_picture(pic)
|
31
32
|
end
|
32
33
|
|
@@ -40,7 +41,7 @@ module UnitF
|
|
40
41
|
|
41
42
|
def stats
|
42
43
|
stats = @file.audio_properties
|
43
|
-
sprintf(
|
44
|
+
sprintf('%.1fkHz/%d-bit %dkbps', stats.sample_rate / 1000.to_f, stats.bits_per_sample, stats.bitrate)
|
44
45
|
end
|
45
46
|
|
46
47
|
def dump
|
@@ -57,4 +58,4 @@ module UnitF
|
|
57
58
|
end
|
58
59
|
end
|
59
60
|
end
|
60
|
-
end
|
61
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'unitf/logging'
|
2
|
+
|
3
|
+
module UnitF
|
4
|
+
module Tag
|
5
|
+
module Helpers
|
6
|
+
def valid_file?(file_path)
|
7
|
+
::File.file?(file_path) && file_path.encode.match(/\.(flac|mp3)$/i)
|
8
|
+
rescue ArgumentError => e
|
9
|
+
UnitF::Log.error("Error processing #{file_path} - #{e.message}")
|
10
|
+
false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/unitf/tag/mp3.rb
CHANGED
@@ -7,7 +7,7 @@ module UnitF
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def stats
|
10
|
-
sprintf(
|
10
|
+
sprintf('%.1fkHz/%dkbps', @file.audio_properties.sample_rate / 1000.to_f, @file.audio_properties.bitrate)
|
11
11
|
end
|
12
12
|
|
13
13
|
def info
|
@@ -19,15 +19,16 @@ module UnitF
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def cover?
|
22
|
-
@file.id3v2_tag.frame_list('APIC').size
|
22
|
+
@file.id3v2_tag.frame_list('APIC').size.positive?
|
23
23
|
end
|
24
24
|
|
25
25
|
def cover!(file_path)
|
26
|
+
UnitF::Log.info("Setting cover #{file_path}")
|
26
27
|
apic = TagLib::ID3v2::AttachedPictureFrame.new
|
27
|
-
apic.mime_type =
|
28
|
-
apic.description =
|
28
|
+
apic.mime_type = 'image/jpeg'
|
29
|
+
apic.description = 'Cover'
|
29
30
|
apic.type = TagLib::ID3v2::AttachedPictureFrame::FrontCover
|
30
|
-
apic.picture = ::File.
|
31
|
+
apic.picture = ::File.binread(file_path)
|
31
32
|
@file.id3v2_tag.add_frame(apic)
|
32
33
|
end
|
33
34
|
|
@@ -37,7 +38,7 @@ module UnitF
|
|
37
38
|
|
38
39
|
def album_artist=(artist)
|
39
40
|
@file.id3v2_tag.remove_frames('TPE2')
|
40
|
-
frame = TagLib::ID3v2::TextIdentificationFrame.new(
|
41
|
+
frame = TagLib::ID3v2::TextIdentificationFrame.new('TPE2', TagLib::String::UTF8)
|
41
42
|
frame.text = artist
|
42
43
|
@file.id3v2_tag.add_frame(frame)
|
43
44
|
end
|
data/lib/unitf/tag/version.rb
CHANGED
data/lib/unitf/tag.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'tag/helpers'
|
3
4
|
require_relative 'tag/version'
|
4
5
|
require_relative 'tag/file'
|
5
6
|
require_relative 'tag/fileset'
|
@@ -10,66 +11,33 @@ require 'unitf/logging'
|
|
10
11
|
|
11
12
|
module UnitF
|
12
13
|
module Tag
|
13
|
-
class
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@logger.add_writer(UnitF::Logging::ConsoleWriter.new)
|
20
|
-
end
|
21
|
-
@logger
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.update(file_path)
|
25
|
-
UnitF::Tag::File.new(file_path).open do |file|
|
26
|
-
yield file
|
27
|
-
file.save || (raise UnitF::Tag::Error, "Failed to save file #{file_path}")
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.valid_file?(file_path)
|
32
|
-
::File.file?(file_path) && file_path.encode.match(/\.(flac|mp3)$/i)
|
33
|
-
rescue ArgumentError => e
|
34
|
-
logger.error("Error processing #{file_path} - #{e.message}")
|
35
|
-
false
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.process_target(target)
|
39
|
-
if ::File.directory?(target)
|
40
|
-
find_files(target)
|
41
|
-
elsif valid_file?(target)
|
42
|
-
[UnitF::Tag::File.new(target)]
|
43
|
-
else
|
44
|
-
[]
|
14
|
+
class << self
|
15
|
+
def update(file_path)
|
16
|
+
UnitF::Tag::File.new(file_path).update do |file|
|
17
|
+
yield file
|
18
|
+
file.save || (raise UnitF::Tag::Error, "Failed to save file #{file_path}")
|
19
|
+
end
|
45
20
|
end
|
46
|
-
end
|
47
21
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
files.each do |file|
|
61
|
-
file.open do |o|
|
62
|
-
case format
|
63
|
-
when :json
|
64
|
-
buff << o.info
|
65
|
-
when :line
|
66
|
-
puts o.format_line
|
67
|
-
else
|
68
|
-
o.print
|
22
|
+
def list(files, format: :json)
|
23
|
+
buff = []
|
24
|
+
files.each do |file|
|
25
|
+
file.open do |o|
|
26
|
+
case format
|
27
|
+
when :json
|
28
|
+
buff << o.info
|
29
|
+
when :line
|
30
|
+
puts o.format_line
|
31
|
+
else
|
32
|
+
o.print
|
33
|
+
end
|
69
34
|
end
|
70
35
|
end
|
36
|
+
puts JSON.pretty_generate(buff) if format == :json
|
71
37
|
end
|
72
|
-
puts JSON.pretty_generate(buff) if format == :json
|
73
38
|
end
|
39
|
+
|
40
|
+
class Error < StandardError; end
|
41
|
+
class MissingCover < Error; end
|
74
42
|
end
|
75
43
|
end
|
data/unitf-tag.gemspec
CHANGED
@@ -1,32 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative 'lib/unitf/tag/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name =
|
6
|
+
spec.name = 'unitf-tag'
|
7
7
|
spec.version = Unitf::Tag::VERSION
|
8
|
-
spec.authors = [
|
9
|
-
spec.email = [
|
8
|
+
spec.authors = ['Matt Baron']
|
9
|
+
spec.email = ['mwb@unitf.net']
|
10
10
|
|
11
|
-
spec.summary =
|
11
|
+
spec.summary = 'Audio File Tagging'
|
12
12
|
spec.description = spec.summary
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
15
|
-
spec.required_ruby_version =
|
13
|
+
spec.homepage = 'https://github.com/mattbaron/unitf-tag'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
spec.required_ruby_version = '>= 3.0.0'
|
16
16
|
|
17
|
-
spec.metadata[
|
17
|
+
spec.metadata['allowed_push_host'] = 'https://www.rubygems.org'
|
18
18
|
|
19
|
-
spec.metadata[
|
20
|
-
spec.metadata[
|
21
|
-
spec.metadata[
|
19
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
20
|
+
spec.metadata['source_code_uri'] = 'https://github.com/mattbaron/unitf-tag'
|
21
|
+
spec.metadata['changelog_uri'] = 'https://github.com/mattbaron/unitf-tag/CHANGELOG.md'
|
22
22
|
|
23
23
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
24
24
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
25
25
|
end
|
26
|
-
spec.bindir =
|
26
|
+
spec.bindir = 'exe'
|
27
27
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
28
|
-
spec.require_paths = [
|
28
|
+
spec.require_paths = ['lib']
|
29
29
|
|
30
|
-
spec.add_dependency
|
31
|
-
spec.add_dependency
|
30
|
+
spec.add_dependency 'taglib-ruby', '1.1.2'
|
31
|
+
spec.add_dependency 'unitf-logging'
|
32
32
|
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unitf-tag
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.23
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Baron
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-02-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: taglib-ruby
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 1.1.2
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 1.1.2
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: unitf-logging
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -43,6 +43,7 @@ email:
|
|
43
43
|
- mwb@unitf.net
|
44
44
|
executables:
|
45
45
|
- tag
|
46
|
+
- unitf-autotags
|
46
47
|
extensions: []
|
47
48
|
extra_rdoc_files: []
|
48
49
|
files:
|
@@ -56,15 +57,15 @@ files:
|
|
56
57
|
- LICENSE.txt
|
57
58
|
- README.md
|
58
59
|
- Rakefile
|
59
|
-
- bin/console
|
60
|
-
- bin/setup
|
61
60
|
- bin/test.rb
|
62
61
|
- exe/tag
|
62
|
+
- exe/unitf-autotags
|
63
63
|
- lib/unitf/tag.rb
|
64
64
|
- lib/unitf/tag/export/exporter.rb
|
65
65
|
- lib/unitf/tag/file.rb
|
66
66
|
- lib/unitf/tag/fileset.rb
|
67
67
|
- lib/unitf/tag/flac.rb
|
68
|
+
- lib/unitf/tag/helpers.rb
|
68
69
|
- lib/unitf/tag/mp3.rb
|
69
70
|
- lib/unitf/tag/version.rb
|
70
71
|
- unitf-tag.gemspec
|
@@ -84,14 +85,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
85
|
requirements:
|
85
86
|
- - ">="
|
86
87
|
- !ruby/object:Gem::Version
|
87
|
-
version:
|
88
|
+
version: 3.0.0
|
88
89
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
90
|
requirements:
|
90
91
|
- - ">="
|
91
92
|
- !ruby/object:Gem::Version
|
92
93
|
version: '0'
|
93
94
|
requirements: []
|
94
|
-
rubygems_version: 3.
|
95
|
+
rubygems_version: 3.5.14
|
95
96
|
signing_key:
|
96
97
|
specification_version: 4
|
97
98
|
summary: Audio File Tagging
|
data/bin/console
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "bundler/setup"
|
5
|
-
require "unitf/tag"
|
6
|
-
|
7
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
-
# with your gem easier. You can also use a different console, if you like.
|
9
|
-
|
10
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
-
# require "pry"
|
12
|
-
# Pry.start
|
13
|
-
|
14
|
-
require "irb"
|
15
|
-
IRB.start(__FILE__)
|