unitf-tag 0.1.25 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7289107ae3745345d0485a0cd9dde05aec68cfe8a22ce366a45fe047467ad55f
4
- data.tar.gz: 11f751c7a07a90b39589a03088589c48514734819e9318de36a4371bc7263049
3
+ metadata.gz: 0e8a3abd8e593009ee730ed4ee98c85f9f0b5665cdccf7ac1ea68b0f29e793a6
4
+ data.tar.gz: 2b62f8700524ba32f9961b14c129869a98b4e0fbf304c05173f9e059820d8bb3
5
5
  SHA512:
6
- metadata.gz: e51cf3871b7fbe25c2f9adcc23389430cb02d1551cd61efcad6817209dc18723aa8f17282554e1af752db6af2e2adfdd043eabb4d54d3f1ced29651c0863146f
7
- data.tar.gz: e397e4fb24ed3e2cce546c08a1d47b07dec9b1adaefd9df3b0f1ff7163591eb2485a7065603c2316169da240ab161edf5fe7e330f872b0de11d7bc6febb9704a
6
+ metadata.gz: d5a1dd21d9cddb72642a99e8d0f1274f6e95a3fc76e80d9bb71838c6addc201814ac3dd0ff1d38482af6464ab37eddd5cd643d70240bf7fdf5fba80866d9c156
7
+ data.tar.gz: 6e7d036234644964322c41dd0dcc4d8e9982c76c6abc4dd430c28a88431f635194da76826c153dcd249b6c0e4a0d26c2e2892b74f75f8063d7ece32fdb99cca7
data/.gitignore CHANGED
@@ -8,8 +8,6 @@
8
8
  /tmp/
9
9
  Gemfile.lock
10
10
  *.gem
11
- *.mp3
12
- test-data/
13
11
 
14
12
  # rspec failure tracking
15
13
  .rspec_status
data/.vscode/tasks.json CHANGED
@@ -16,10 +16,18 @@
16
16
  "presentation": {
17
17
  "clear": true
18
18
  },
19
- "command": "bundle exec exe/tag --album FOO --year 2022 test-data"
19
+ "command": "bundle exec ruby bin/test.rb"
20
20
  },
21
21
  {
22
- "label": "test-project",
22
+ "label": "tag-test",
23
+ "type": "shell",
24
+ "options": {
25
+ "cwd": "${workspaceFolder}",
26
+ },
27
+ "command": "bundle exec ruby exe/tag --auto_tag '/Users/mbaron/music2/Dinosaur Jr/Little Fury Things'"
28
+ },
29
+ {
30
+ "label": "rspec",
23
31
  "type": "shell",
24
32
  "presentation": {
25
33
  "clear": true
data/CHANGELOG.md CHANGED
@@ -1,4 +1,11 @@
1
1
  # UnitF::Tag
2
2
 
3
+ ## v0.2.0
4
+ - `UnitF::Tag::File` no longer inherits anything]
5
+ - Spec Tests
6
+
7
+ ## v0.1.26
8
+ - Completely re-do auto-tagging
9
+
3
10
  ## v0.1.22
4
11
  - Look for covers in current directory and parent directory
data/bin/test.rb CHANGED
@@ -4,23 +4,7 @@ require 'unitf/tag'
4
4
 
5
5
  UnitF::Log.to_console
6
6
 
7
- UnitF::Tag.update('test-data/foo.mp3') do |file|
8
- file.tag.artist = 'bar123'
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
13
- file.auto_cover!
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!'
7
+ UnitF::Tag.update('test-data/song.mp3') do |file|
8
+ puts "FILE: #{file}"
9
+ file.tag.artist = "Update #{Time.now.to_s}"
26
10
  end
data/docs/taglib.md ADDED
@@ -0,0 +1,47 @@
1
+ # taglib Notes
2
+ Notes on `taglib` and `taglib-ruby` issues
3
+
4
+ **GitHub**
5
+ - https://github.com/taglib/taglib
6
+ - https://github.com/robinst/taglib-ruby
7
+
8
+ ## Custom TagLib 1.13.1 Build on MacOS
9
+ ```
10
+ curl -O https://taglib.org/releases/taglib-1.13.1.tar.gz
11
+ tar xvf taglib-1.13.1.tar.gz
12
+ cd taglib-1.13.1
13
+ cmake -DCMAKE_INSTALL_PREFIX=/usr/local/taglib1 -DCMAKE_BUILD_TYPE=Release .
14
+ make
15
+ sudo make install
16
+ ```
17
+
18
+ ```
19
+ TAGLIB_DIR=/usr/local/taglib1 gem install taglib-ruby --version '< 2'
20
+ ```
21
+
22
+ ## Custom Build on Linux
23
+ ```
24
+ git clone https://github.com/taglib/taglib.git
25
+ cd taglib/
26
+ git submodule update --init
27
+ cmake -DCMAKE_INSTALL_PREFIX=/usr/local/taglib -DCMAKE_BUILD_TYPE=Release .
28
+ make
29
+ make install
30
+ ```
31
+
32
+ ```
33
+ TAGLIB_DIR=/usr/local/taglib gem install taglib-ruby --version '>= 2'
34
+ ```
35
+
36
+ ### Taglib error w/ taglib 2.0.2
37
+ ```
38
+ root@homer:/nas/music2/WFMU# gem install /tmp/unitf-tag-0.1.27.gem
39
+ Successfully installed unitf-tag-0.1.27
40
+ Parsing documentation for unitf-tag-0.1.27
41
+ Installing ri documentation for unitf-tag-0.1.27
42
+ Done installing documentation for unitf-tag after 0 seconds
43
+ 1 gem installed
44
+ root@homer:/nas/music2/WFMU# tag
45
+ <internal:/usr/share/rubygems/rubygems/core_ext/kernel_require.rb>:136:in `require': /usr/local/lib64/gems/ruby/taglib-ruby-2.0.0/taglib_base.so: undefined symbol: _ZN6TagLib4File11removeBlockElm - /usr/local/lib64/gems/ruby/taglib-ruby-2.0.0/taglib_base.so (LoadError)
46
+ from <internal:/usr/share/rubygems/rubygems/core_ext/kernel_require.rb>:136:in `require'
47
+ ```
data/exe/tag CHANGED
@@ -54,6 +54,10 @@ targets = OptionParser.new do |opts|
54
54
  actions << :auto_tag
55
55
  end
56
56
 
57
+ opts.on('--auto_tags', 'Auto Tags') do
58
+ actions << :auto_tags
59
+ end
60
+
57
61
  opts.on('-f', '--force', 'Force') do
58
62
  opt[:force] = true
59
63
  end
@@ -96,19 +100,22 @@ end
96
100
 
97
101
  files.each do |file|
98
102
  UnitF::Log.info("Processing file: #{file}")
99
- file.update do |o|
100
- o.properties!(properties) unless properties.empty?
103
+
104
+ file.update do |f|
105
+ f.properties!(properties) unless properties.empty?
101
106
 
102
107
  actions.each do |action|
103
108
  case action
104
109
  when :cover
105
- o.cover!(opt[:cover])
110
+ f.cover!(opt[:cover])
106
111
  when :delete_cover
107
- o.delete_cover!
112
+ f.delete_cover!
108
113
  when :auto_cover
109
- o.auto_cover!
114
+ f.auto_cover!
110
115
  when :auto_tag
111
- o.auto_tag!
116
+ f.auto_tag!
117
+ when :auto_tags
118
+ puts "\tauto_tags: #{f.auto_tags}\n\n"
112
119
  end
113
120
  end
114
121
  end
@@ -0,0 +1,62 @@
1
+ module UnitF
2
+ module Tag
3
+ class AutoTags < Hash
4
+ def initialize(file_path)
5
+ super
6
+
7
+ @file_path = ::File.realpath(file_path.to_s)
8
+ @dirname = ::File.dirname(@file_path)
9
+
10
+ merge!(from_path)
11
+ merge!(from_file)
12
+ end
13
+
14
+ def auto_tag_path
15
+ return @auto_tag_path unless @auto_tag_path.nil?
16
+
17
+ ["#{@dirname}/.autotag", "#{@dirname}/../.autotag"].each do |path|
18
+ return @auto_tag_path = path if ::File.exist?(path)
19
+ end
20
+
21
+ nil
22
+ end
23
+
24
+ def from_path
25
+ tags = {}
26
+
27
+ tags[:title] = ::File.basename(@file_path)
28
+ tags[:track] = tags[:title].match(/^\s*\d+/).to_s.to_i
29
+
30
+ # Specific formatting for dated radio
31
+ if tags[:title].scan(/(\.|_|-)((\d\d\d\d)(\.|-)\d\d(\.|-)\d\d(\w*))\./).any?
32
+ tags[:title] = ::Regexp.last_match[2]
33
+ tags[:year] = ::Regexp.last_match[3].to_i
34
+ else
35
+ tags[:title].gsub!(/\.\w+$/, '')
36
+ tags[:title].gsub!(/^\d*\s*(-|\.)*\s*/, '')
37
+ end
38
+
39
+ path_parts = @dirname.to_s.split('/')
40
+ tags[:artist] = path_parts[-2]
41
+ tags[:album] = path_parts[-1]
42
+
43
+ tags
44
+ end
45
+
46
+ def from_file
47
+ tags = {}
48
+ return tags unless ::File.exist?(auto_tag_path)
49
+
50
+ ::File.read(auto_tag_path).each_line do |line|
51
+ line.chomp!
52
+ tag, value = line.split(/\s*=\s*/)
53
+ tags[tag.strip.to_sym] = value.strip
54
+ end
55
+
56
+ tags
57
+ rescue StandardError
58
+ {}
59
+ end
60
+ end
61
+ end
62
+ end
@@ -6,24 +6,22 @@ require 'json'
6
6
 
7
7
  module UnitF
8
8
  module Tag
9
- class File < Pathname
9
+ class File
10
+ attr_accessor :path, :realpath, :dirname, :extname
11
+
10
12
  def initialize(file_path)
11
- super(::File.absolute_path(file_path.to_s))
12
- end
13
+ raise Error, "Invalid file #{file_path}" unless ::File.exist?(file_path)
13
14
 
14
- def tag
15
- raise Error, "File is not open #{self.class.name}" if @file&.tag.nil?
15
+ @path = ::File.path(file_path.to_s)
16
+ @realpath = ::File.realpath(path)
17
+ @dirname = ::File.dirname(path)
18
+ @extname = ::File.extname(path)
16
19
 
17
- @file&.tag
20
+ raise Error, "Unknown file type: #{file_path}" unless mp3? || flac?
18
21
  end
19
22
 
20
- def save
21
- @file&.save
22
- end
23
-
24
- def close
25
- @file&.close
26
- @file = nil
23
+ def to_s
24
+ @path
27
25
  end
28
26
 
29
27
  def format_json
@@ -40,7 +38,7 @@ module UnitF
40
38
 
41
39
  def info
42
40
  {
43
- file: realpath.to_path,
41
+ file: realpath,
44
42
  artist: tag.artist,
45
43
  album: tag.album,
46
44
  title: tag.title,
@@ -68,12 +66,8 @@ module UnitF
68
66
  ["#{dirname}/cover.jpg", "#{dirname}/../cover.jpg"].each do |path|
69
67
  return ::File.realpath(path) if ::File.exist?(path)
70
68
  end
71
- end
72
69
 
73
- def auto_tag_path
74
- ["#{dirname}/.autotag", "#{dirname}/../.autotag"].each do |path|
75
- return ::File.realpath(path) if ::File.exist?(path)
76
- end
70
+ nil
77
71
  end
78
72
 
79
73
  def mp3?
@@ -85,104 +79,34 @@ module UnitF
85
79
  end
86
80
 
87
81
  def cover_available?
88
- cover_path != nil
82
+ !cover_path.nil?
89
83
  end
90
84
 
91
85
  def auto_cover!
92
86
  raise Error, "File is not open #{self.class.name}" if tag.nil?
93
87
 
94
- cover!(cover_path)
88
+ cover!(cover_path) if cover_available?
95
89
  true
96
90
  rescue StandardError => e
97
91
  UnitF::Log.error("Failed to auto-cover file #{e}")
92
+ puts e.backtrace
98
93
  false
99
94
  end
100
95
 
101
- def manual_auto_tags
102
- UnitF::Log.info(auto_tag_path)
103
- tags = {}
104
- return {} unless ::File.exist?(auto_tag_path)
105
- ::File.read(auto_tag_path).each_line do |line|
106
- line.chomp!
107
- UnitF::Log.info(line)
108
- tag, value = line.split(/\s*=\s*/)
109
- tags[tag.to_sym] = value
110
- end
111
- tags
112
- rescue StandardError
113
- {}
114
- end
115
-
116
- # def auto_tags
117
- # manual_tags = manual_auto_tags
118
- # tags = {}
119
-
120
- # tags[:title] = ::File.basename(realpath.to_path)
121
- # track = tags[:title].match(/^\s*\d+/).to_s.to_i
122
-
123
- # if tags[:title].scan(/(\.|_|-)(\d\d\d\d(\.|-)\d\d(\.|-)\d\d)/)
124
- # tags[:title] = ::Regexp::last_match
125
- # else
126
- # tags[:title].gsub!(/\.\w+$/, '')
127
- # tags[:title].gsub!(/^\d*\s*(-|\.)*\s*/, '')
128
- # end
129
-
130
- # path_parts = realpath.dirname.to_path.split('/')
131
- # tags[:album] = path_parts[-1]
132
- # tags[:artist] = path_parts[-2]
133
-
134
- # tags.merge(manual_auto_tags)
135
- # end
136
-
137
- def path_tags
138
- tags = {}
139
-
140
- tags[:title] = ::File.basename(realpath.to_path)
141
- tags[:track] = tags[:title].match(/^\s*\d+/).to_s.to_i
142
-
143
- # Specific formatting for dated radio
144
- if tags[:title].scan(/(\.|_|-)((\d\d\d\d)(\.|-)\d\d(\.|-)\d\d(\w*))\./).any?
145
- tags[:title] = ::Regexp.last_match[2]
146
- tags[:year] = ::Regexp.last_match[3].to_i
147
- else
148
- tags[:title].gsub!(/\.\w+$/, '')
149
- tags[:title].gsub!(/^\d*\s*(-|\.)*\s*/, '')
150
- end
151
-
152
- tags[:artist] = path_parts[-2]
153
- tags[:album] = tags[:album_artist] = path_parts[-1]
96
+ def auto_tags
97
+ @auto_tags ||= UnitF::Tag::AutoTags.new(path)
154
98
  end
155
99
 
156
100
  def auto_tag!
157
101
  UnitF::Log.info("Auto tagging #{self}")
102
+ UnitF::Log.info(auto_tags)
158
103
 
159
- title = ::File.basename(realpath.to_path)
160
- path_parts = realpath.dirname.to_path.split('/')
161
-
162
- year = nil
163
-
164
- # This must come before gsubbing the title
165
- track = title.match(/^\s*\d+/).to_s.to_i
166
-
167
- # Specific formatting for dated radio
168
- if title.scan(/(\.|_|-)((\d\d\d\d)(\.|-)\d\d(\.|-)\d\d(\w*))\./).any?
169
- title = ::Regexp.last_match[2]
170
- album = path_parts[-1]
171
- year = ::Regexp.last_match[3].to_i
172
- else
173
- title.gsub!(/\.\w+$/, '')
174
- title.gsub!(/^\d*\s*(-|\.)*\s*/, '')
175
- album = path_parts[-1]
176
- end
177
-
178
- artist = path_parts[-2]
179
-
180
- tag.year = year unless year.nil?
181
- tag.album = album
182
- tag.artist = artist
183
- tag.title = title
184
- tag.track = track if track.positive?
185
- self.album_artist = artist
104
+ tag.album = auto_tags[:album]
105
+ tag.artist = auto_tags[:artist]
106
+ tag.title = auto_tags[:title]
107
+ tag.track = auto_tags[:track] if auto_tags.key?(:track)
108
+ tag.year = auto_tags[:year] if auto_tags.key?(:year)
109
+ self.album_artist = auto_tags[:artist]
186
110
  end
187
111
 
188
112
  def properties!(properties)
@@ -193,21 +117,45 @@ module UnitF
193
117
  end
194
118
 
195
119
  def update
196
- open(auto_save: true) do |file|
197
- yield(file) if block_given?
120
+ open do |file|
121
+ if block_given?
122
+ yield(file)
123
+ file.save || (raise Error, "Failed to save file #{file}")
124
+ end
198
125
  end
199
126
  end
200
127
 
201
- def open(auto_save: false)
128
+ def open
202
129
  file = if flac?
203
- UnitF::Tag::FLAC.new(to_path)
204
- elsif mp3?
205
- UnitF::Tag::MP3.new(to_path)
206
- end
130
+ UnitF::Tag::FLAC.new(path)
131
+ elsif mp3?
132
+ UnitF::Tag::MP3.new(path)
133
+ end
207
134
  yield(file) if block_given?
208
- file.save if auto_save
209
135
  file.close
210
136
  end
137
+
138
+ #
139
+ # Methods that use @file, which comes from child classes
140
+ #
141
+ def tag
142
+ raise Error, "File is not open #{self.class.name}" if @file&.tag.nil?
143
+
144
+ @file&.tag
145
+ end
146
+
147
+ def save
148
+ raise Error, "File is not open #{self.class.name}" if @file&.tag.nil?
149
+
150
+ @file&.save
151
+ end
152
+
153
+ def close
154
+ raise Error, "File is not open #{self.class.name}" if @file&.tag.nil?
155
+
156
+ @file&.close
157
+ @file = nil
158
+ end
211
159
  end
212
160
  end
213
161
  end
data/lib/unitf/tag/mp3.rb CHANGED
@@ -23,6 +23,10 @@ module UnitF
23
23
  end
24
24
 
25
25
  def cover!(file_path)
26
+ return unless ::File.exist?(file_path.to_s)
27
+
28
+ delete_cover! if cover?
29
+
26
30
  UnitF::Log.info("Setting cover #{file_path}")
27
31
  apic = TagLib::ID3v2::AttachedPictureFrame.new
28
32
  apic.mime_type = 'image/jpeg'
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Unitf
4
4
  module Tag
5
- VERSION = '0.1.25'
5
+ VERSION = '0.2.0'
6
6
  end
7
7
  end
data/lib/unitf/tag.rb CHANGED
@@ -6,6 +6,7 @@ require_relative 'tag/file'
6
6
  require_relative 'tag/fileset'
7
7
  require_relative 'tag/flac'
8
8
  require_relative 'tag/mp3'
9
+ require_relative 'tag/auto_tags'
9
10
 
10
11
  require 'unitf/logging'
11
12
 
@@ -15,7 +16,6 @@ module UnitF
15
16
  def update(file_path)
16
17
  UnitF::Tag::File.new(file_path).update do |file|
17
18
  yield file
18
- file.save || (raise UnitF::Tag::Error, "Failed to save file #{file_path}")
19
19
  end
20
20
  end
21
21
 
@@ -0,0 +1,2 @@
1
+ artist=Custom Artist
2
+ album=Custom Album
Binary file
Binary file
Binary file
Binary file
data/unitf-tag.gemspec CHANGED
@@ -27,7 +27,8 @@ Gem::Specification.new do |spec|
27
27
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ['lib']
29
29
 
30
- #spec.add_dependency 'taglib-ruby', '1.1.2'
31
- spec.add_dependency 'taglib-ruby'
30
+ spec.add_dependency 'logger'
31
+ spec.add_dependency 'taglib-ruby', '1.1.3'
32
+ # spec.add_dependency 'taglib-ruby', '>= 2.0.0'
32
33
  spec.add_dependency 'unitf-logging'
33
34
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unitf-tag
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.25
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Baron
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-03-24 00:00:00.000000000 Z
10
+ date: 2025-04-04 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: taglib-ruby
13
+ name: logger
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: taglib-ruby
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - '='
31
+ - !ruby/object:Gem::Version
32
+ version: 1.1.3
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - '='
38
+ - !ruby/object:Gem::Version
39
+ version: 1.1.3
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: unitf-logging
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -57,9 +71,11 @@ files:
57
71
  - README.md
58
72
  - Rakefile
59
73
  - bin/test.rb
74
+ - docs/taglib.md
60
75
  - exe/tag
61
76
  - exe/unitf-autotags
62
77
  - lib/unitf/tag.rb
78
+ - lib/unitf/tag/auto_tags.rb
63
79
  - lib/unitf/tag/export/exporter.rb
64
80
  - lib/unitf/tag/file.rb
65
81
  - lib/unitf/tag/fileset.rb
@@ -67,6 +83,12 @@ files:
67
83
  - lib/unitf/tag/helpers.rb
68
84
  - lib/unitf/tag/mp3.rb
69
85
  - lib/unitf/tag/version.rb
86
+ - test-data/Foo/Bar/.autotag
87
+ - test-data/Foo/Bar/2025/qq250916.mp3
88
+ - test-data/Test Artist/Test Album/04 - Test Song.mp3
89
+ - test-data/WFMU/BJ/2025/bj250915.mp3
90
+ - test-data/cover.jpg
91
+ - test-data/song.mp3
70
92
  - unitf-tag.gemspec
71
93
  homepage: https://github.com/mattbaron/unitf-tag
72
94
  licenses: