kagu 3.0.9 → 4.0.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: c2b1c6ebb0b79f29eba4fe24a7dd3d79296ae91f0bbea0fb0fc7983bf835e281
4
- data.tar.gz: ca3eccc89f387079b124934601b42d55189caeb21655dcc2ba10b4925229cf44
3
+ metadata.gz: d1cedd40180e7fdaaafb0a589b9497904dc1fea117988356a1b9825720d906eb
4
+ data.tar.gz: 6bd45283ddd23b1151928c3d5fe01fa57591df612c90604a1994271500dcf380
5
5
  SHA512:
6
- metadata.gz: 9342fa71cbb60dc112d72a1578b1f0e08efe82bf2fcbafe90a3bf5bf1fb481bb5cc46df116227a5fea2076f68697dcc6bcc4eb695011acdab9b17a52ff0b7e58
7
- data.tar.gz: bf93af997250a6721bce474f3004a15721245d21ca0d8efd8d47144fc3a374b3eb46695a7b249c4c0546daad8f95be8163db80db19a9e770726d470518ed2264
6
+ metadata.gz: 5c58699f791c9f6451aa0e2c40b98bbd8cf7749655e27b96856ed73a5273e323948891025df00d6f84f510b079b4fab00ab629e54c7d4cb6ed3a1f3268438b53
7
+ data.tar.gz: cb1ceb390210ae2ce2408cd9c801dd9853aa8712767e178eebac86864bb2d670a4042c190be832b55714c9570b33993ac27a314ae404f107bbd8b53f61158320
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.0.9
1
+ 4.0.0
data/kagu.gemspec CHANGED
@@ -9,16 +9,20 @@ Gem::Specification.new do |s|
9
9
  s.description = 'API to manage macOS Music tracks and playlists'
10
10
  s.license = 'MIT'
11
11
 
12
- s.files = `git ls-files | grep -vE '^(spec/|test/|\\.|Gemfile|Rakefile)'`.split("\n")
13
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ s.files = %x(git ls-files | grep -vE '^(spec/|test/|\\.|Gemfile|Rakefile)').split("\n")
13
+ s.executables = %x(git ls-files -- bin/*).split("\n").map { |f| File.basename(f) }
14
14
  s.require_paths = ['lib']
15
15
 
16
- s.required_ruby_version = '>= 2.0.0'
16
+ s.required_ruby_version = '>= 3.1.0'
17
17
 
18
- s.add_dependency 'activesupport', '>= 4.1.0', '< 7.0.0'
18
+ s.add_dependency 'activesupport', '>= 7.0.0', '< 8.0.0'
19
+ s.add_dependency 'addressable', '>= 2.7.0', '< 3.0.0'
19
20
  s.add_dependency 'applescript', '>= 1.0', '< 2.0'
20
21
 
21
22
  s.add_development_dependency 'byebug', '>= 3.2.0', '< 12.0.0'
22
23
  s.add_development_dependency 'rake', '>= 10.3.0', '< 14.0.0'
23
- s.add_development_dependency 'rspec', '>= 3.1.0', '< 3.10.0'
24
+ s.add_development_dependency 'rspec', '>= 3.1.0', '< 3.11.0'
25
+ s.add_development_dependency 'rubocop', '>= 1.25.0', '< 2.0.0'
26
+ s.add_development_dependency 'rubocop-rake', '>= 0.6.0', '< 1.0.0'
27
+ s.add_development_dependency 'rubocop-rspec', '>= 2.8.0', '< 3.0.0'
24
28
  end
data/lib/kagu/finder.rb CHANGED
@@ -2,7 +2,7 @@ module Kagu
2
2
 
3
3
  class Finder
4
4
 
5
- MANDATORY_ATTRIBUTES = []
5
+ MANDATORY_ATTRIBUTES = [].freeze
6
6
 
7
7
  delegate :replace, :transliterate, to: 'self.class'
8
8
 
@@ -29,7 +29,7 @@ module Kagu
29
29
  tracks_digests.each_with_index do |hash, digests_index|
30
30
  digests(attributes).each_with_index do |digest, digest_index|
31
31
  tracks = hash[digest].presence || next
32
- tracks = tracks.select { |track| !matches.any? { |match| match.try(:include?, track) } }
32
+ tracks = tracks.reject { |track| matches.any? { |match| match.try(:include?, track) } }
33
33
  next if tracks.empty?
34
34
  index = [digests_index, digest_index].max
35
35
  matches[index] ||= []
@@ -83,10 +83,11 @@ module Kagu
83
83
 
84
84
  def ignored=(values)
85
85
  @ignored = [values].flatten.map do |value|
86
- if value.is_a?(Hash)
86
+ case value
87
+ when Hash
87
88
  value = value.stringify_keys
88
89
  value = "#{value['artist']} #{value['title']}"
89
- elsif value.is_a?(Track)
90
+ when Track
90
91
  value = "#{value.artist} #{value.title}"
91
92
  else
92
93
  value = value.to_s
data/lib/kagu/playlist.rb CHANGED
@@ -2,12 +2,12 @@ module Kagu
2
2
 
3
3
  class Playlist
4
4
 
5
- MANDATORY_ATTRIBUTES = %w(name)
5
+ MANDATORY_ATTRIBUTES = %w(name).freeze
6
6
 
7
7
  include AttributesInitializer
8
8
  include Enumerable
9
9
 
10
- attr_reader :name, :tracks
10
+ attr_reader :name
11
11
 
12
12
  delegate :each, to: :tracks
13
13
 
@@ -31,7 +31,7 @@ module Kagu
31
31
  return if tracks.empty?
32
32
  Kagu.logger.info('Kagu') { "Adding #{tracks.size} track(s) to playlist #{name.inspect}" }
33
33
  tracks.map(&:id).each_slice(500) do |ids|
34
- AppleScript.execute(%Q{
34
+ AppleScript.execute("
35
35
  tell application #{Kagu::OSX_APP_NAME.inspect}
36
36
  set playlistToPush to user playlist #{name.inspect}
37
37
  set idsToAdd to {#{ids.map(&:inspect).join(',')}}
@@ -39,7 +39,7 @@ module Kagu
39
39
  duplicate (tracks of library playlist 1 whose persistent ID is idToAdd) to playlistToPush
40
40
  end repeat
41
41
  end tell
42
- })
42
+ ")
43
43
  end
44
44
  true
45
45
  rescue => e
@@ -48,11 +48,11 @@ module Kagu
48
48
 
49
49
  def clear
50
50
  Kagu.logger.info('Kagu') { "Removing all tracks from playlist #{name.inspect}" }
51
- AppleScript.execute(%Q{
51
+ AppleScript.execute("
52
52
  tell application #{Kagu::OSX_APP_NAME.inspect}
53
53
  delete tracks of playlist #{name.inspect}
54
54
  end tell
55
- })
55
+ ")
56
56
  true
57
57
  rescue => e
58
58
  raise Error.new(e)
@@ -60,13 +60,13 @@ module Kagu
60
60
 
61
61
  def create
62
62
  Kagu.logger.info('Kagu') { "Creating playlist #{name.inspect}" }
63
- AppleScript.execute(%Q{
63
+ AppleScript.execute("
64
64
  tell application #{Kagu::OSX_APP_NAME.inspect}
65
65
  if not (exists user playlist #{name.inspect}) then
66
66
  make new user playlist with properties { name: #{name.inspect} }
67
67
  end if
68
68
  end tell
69
- })
69
+ ")
70
70
  true
71
71
  rescue => e
72
72
  raise Error.new(e)
@@ -12,27 +12,27 @@ module Kagu
12
12
  build(attributes).tap(&:save)
13
13
  end
14
14
 
15
- def each(&block)
15
+ def each
16
16
  return unless block_given?
17
17
  Kagu.logger.debug('Kagu') { 'Loading library playlists' }
18
- tracks = {}.tap do |tracks|
19
- Tracks.new.each { |track| tracks[track.id] = track }
18
+ tracks = {}.tap do |items|
19
+ Tracks.new.each { |track| items[track.id] = track }
20
20
  end
21
21
  playlist_name = nil
22
22
  playlist_tracks = []
23
- SwiftHelper.execute(%Q{
23
+ SwiftHelper.execute("
24
24
  import iTunesLibrary
25
25
 
26
- let library = try! ITLibrary(apiVersion: "1")
26
+ let library = try! ITLibrary(apiVersion: \"1\")
27
27
  for playlist in library.allPlaylists.filter({ !$0.isMaster }) {
28
- print("BEGIN_PLAYLIST")
28
+ print(\"BEGIN_PLAYLIST\")
29
29
  print(playlist.name)
30
30
  for track in playlist.items.filter({ $0.mediaKind == ITLibMediaItemMediaKind.kindSong }) {
31
31
  print(String(track.persistentID.uint64Value, radix: 16).uppercased())
32
32
  }
33
- print("END_PLAYLIST")
33
+ print(\"END_PLAYLIST\")
34
34
  }
35
- }) do |line|
35
+ ").each do |line|
36
36
  if line == 'BEGIN_PLAYLIST'
37
37
  playlist_name = nil
38
38
  playlist_tracks = []
@@ -44,6 +44,7 @@ module Kagu
44
44
  playlist_tracks << tracks[line]
45
45
  end
46
46
  end
47
+ nil
47
48
  end
48
49
 
49
50
  end
@@ -2,7 +2,7 @@ module Kagu
2
2
 
3
3
  module SwiftHelper
4
4
 
5
- def self.execute(code, &block)
5
+ def self.execute(code)
6
6
  tempfile = Tempfile.new
7
7
  begin
8
8
  tempfile << code
@@ -12,12 +12,7 @@ module Kagu
12
12
  begin
13
13
  stdout, stderr, result = Open3.capture3("swift #{tempfile.path.inspect}")
14
14
  raise(stderr.presence || "Swift command returned with code: #{result.exitstatus}") unless result.success?
15
- if block_given?
16
- stdout.lines.each { |line| yield(line.chomp) }
17
- nil
18
- else
19
- stdout
20
- end
15
+ stdout.lines.map(&:chomp)
21
16
  ensure
22
17
  tempfile.unlink
23
18
  end
data/lib/kagu/track.rb CHANGED
@@ -5,7 +5,7 @@ module Kagu
5
5
  include AttributesInitializer
6
6
  include Comparable
7
7
 
8
- MANDATORY_ATTRIBUTES = %w(added_at id length path)
8
+ MANDATORY_ATTRIBUTES = %w(added_at id length).freeze
9
9
 
10
10
  attr_reader :added_at, :album, :artist, :bpm, :genre, :id, :length, :path, :title, :year
11
11
 
@@ -22,19 +22,14 @@ module Kagu
22
22
  super || self == other
23
23
  end
24
24
 
25
- def exists?
26
- path.file?
25
+ def exists_on_disk?
26
+ path.present? && path.file?
27
27
  end
28
28
 
29
29
  def hash
30
30
  [artist, title].hash
31
31
  end
32
32
 
33
- def relative_path(directory)
34
- directory = directory.to_s
35
- directory.present? ? Pathname.new(path.to_s.gsub(/\A#{Regexp.escape(directory)}\//, '')) : path
36
- end
37
-
38
33
  def to_s
39
34
  "#{artist} - #{title}"
40
35
  end
@@ -42,9 +37,10 @@ module Kagu
42
37
  private
43
38
 
44
39
  def added_at=(value)
45
- if value.is_a?(String)
40
+ case value
41
+ when String
46
42
  value = Time.parse(value)
47
- elsif value.is_a?(Integer)
43
+ when Integer
48
44
  value = Time.at(value)
49
45
  end
50
46
  @added_at = value.is_a?(Time) ? value.utc : nil
@@ -60,7 +56,7 @@ module Kagu
60
56
 
61
57
  def bpm=(value)
62
58
  value = value.to_s =~ /\A[0-9]+\z/ ? value.to_i : nil
63
- @bpm = (value && value > 0) ? value : nil
59
+ @bpm = value && value > 0 ? value : nil
64
60
  end
65
61
 
66
62
  def genre=(value)
@@ -77,15 +73,9 @@ module Kagu
77
73
 
78
74
  def path=(value)
79
75
  value = value.to_s.presence
80
- value = URI.unescape(URI.parse(value).path).presence if value.is_a?(String) && value.starts_with?('file://')
76
+ value = Addressable::URI.unescape(Addressable::URI.parse(value).path).presence if value.is_a?(String) && value.starts_with?('file://')
81
77
  value = value.encode('UTF-8', 'UTF-8-MAC') if value.present? && Kagu::IS_MAC_OS
82
- if value.present?
83
- @path = Pathname.new(value)
84
- raise Error.new("No such file: #{path.to_s.inspect}") if path.exist? && !exists?
85
- Kagu.logger.error('Kagu') { "No such track: #{path.inspect}" } unless exists?
86
- else
87
- @path = nil
88
- end
78
+ @path = value.present? ? Pathname.new(value) : nil
89
79
  end
90
80
 
91
81
  def title=(value)
data/lib/kagu/tracks.rb CHANGED
@@ -6,44 +6,45 @@ module Kagu
6
6
 
7
7
  EXTENSIONS = %w(.aac .flac .mp3 .wav).freeze
8
8
 
9
- def each(&block)
9
+ def each
10
10
  return unless block_given?
11
11
  Kagu.logger.debug('Kagu') { 'Loading library tracks' }
12
12
  attributes = {}
13
- SwiftHelper.execute(%Q{
13
+ SwiftHelper.execute("
14
14
  import iTunesLibrary
15
15
 
16
16
  func printObjectProperty<T: Encodable>(name: String, value: T?) {
17
17
  let jsonEncoder = JSONEncoder()
18
18
  let jsonData = try! jsonEncoder.encode(value)
19
19
  let json = String(data: jsonData, encoding: String.Encoding.utf8)
20
- print("\\(name)=\\(json!)")
20
+ print(\"\\(name)=\\(json!)\")
21
21
  }
22
22
 
23
- let library = try! ITLibrary(apiVersion: "1")
23
+ let library = try! ITLibrary(apiVersion: \"1\")
24
24
  for track in library.allMediaItems.filter({ $0.mediaKind == ITLibMediaItemMediaKind.kindSong }) {
25
- print("BEGIN_TRACK")
26
- printObjectProperty(name: "added_at", value: track.addedDate!.timeIntervalSince1970)
27
- printObjectProperty(name: "album", value: track.album.title)
28
- printObjectProperty(name: "artist", value: track.artist!.name)
29
- printObjectProperty(name: "bpm", value: track.beatsPerMinute)
30
- printObjectProperty(name: "genre", value: track.genre)
31
- printObjectProperty(name: "id", value: String(track.persistentID.uint64Value, radix: 16).uppercased())
32
- printObjectProperty(name: "length", value: track.totalTime)
33
- printObjectProperty(name: "path", value: track.location)
34
- printObjectProperty(name: "title", value: track.title)
35
- printObjectProperty(name: "year", value: track.year)
36
- print("END_TRACK")
25
+ print(\"BEGIN_TRACK\")
26
+ printObjectProperty(name: \"added_at\", value: track.addedDate!.timeIntervalSince1970)
27
+ printObjectProperty(name: \"album\", value: track.album.title)
28
+ printObjectProperty(name: \"artist\", value: track.artist!.name)
29
+ printObjectProperty(name: \"bpm\", value: track.beatsPerMinute)
30
+ printObjectProperty(name: \"genre\", value: track.genre)
31
+ printObjectProperty(name: \"id\", value: String(track.persistentID.uint64Value, radix: 16).uppercased())
32
+ printObjectProperty(name: \"length\", value: track.totalTime)
33
+ printObjectProperty(name: \"path\", value: track.location)
34
+ printObjectProperty(name: \"title\", value: track.title)
35
+ printObjectProperty(name: \"year\", value: track.year)
36
+ print(\"END_TRACK\")
37
37
  }
38
- }) do |line|
38
+ ").each do |line|
39
39
  if line == 'BEGIN_TRACK'
40
40
  attributes = {}
41
41
  elsif line == 'END_TRACK'
42
42
  yield(Track.new(attributes))
43
- elsif match = /(^\w+)=(.*)/.match(line)
43
+ elsif (match = /(^\w+)=(.*)/.match(line))
44
44
  attributes[match[1]] = JSON.parse(match[2])
45
45
  end
46
46
  end
47
+ nil
47
48
  end
48
49
 
49
50
  end
data/lib/kagu.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'active_support'
2
2
  require 'active_support/core_ext'
3
+ require 'addressable/uri'
3
4
  require 'applescript'
4
5
  require 'byebug' if ENV['DEBUGGER']
5
6
  require 'logger'
@@ -14,9 +15,7 @@ module Kagu
14
15
  IS_MAC_OS = RUBY_PLATFORM =~ /darwin/
15
16
  OSX_APP_NAME = begin
16
17
  if IS_MAC_OS
17
- `sw_vers -productVersion`.chomp.to_f >= 10.15 ? 'Music' : 'iTunes'
18
- else
19
- nil
18
+ %x(sw_vers -productVersion).chomp.to_f >= 10.15 ? 'Music' : 'iTunes'
20
19
  end
21
20
  end
22
21
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kagu
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.9
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Toulotte
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-30 00:00:00.000000000 Z
11
+ date: 2022-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,20 +16,40 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 4.1.0
19
+ version: 7.0.0
20
20
  - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 8.0.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
21
28
  - !ruby/object:Gem::Version
22
29
  version: 7.0.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 8.0.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: addressable
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 2.7.0
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: 3.0.0
23
43
  type: :runtime
24
44
  prerelease: false
25
45
  version_requirements: !ruby/object:Gem::Requirement
26
46
  requirements:
27
47
  - - ">="
28
48
  - !ruby/object:Gem::Version
29
- version: 4.1.0
49
+ version: 2.7.0
30
50
  - - "<"
31
51
  - !ruby/object:Gem::Version
32
- version: 7.0.0
52
+ version: 3.0.0
33
53
  - !ruby/object:Gem::Dependency
34
54
  name: applescript
35
55
  requirement: !ruby/object:Gem::Requirement
@@ -99,7 +119,7 @@ dependencies:
99
119
  version: 3.1.0
100
120
  - - "<"
101
121
  - !ruby/object:Gem::Version
102
- version: 3.10.0
122
+ version: 3.11.0
103
123
  type: :development
104
124
  prerelease: false
105
125
  version_requirements: !ruby/object:Gem::Requirement
@@ -109,7 +129,67 @@ dependencies:
109
129
  version: 3.1.0
110
130
  - - "<"
111
131
  - !ruby/object:Gem::Version
112
- version: 3.10.0
132
+ version: 3.11.0
133
+ - !ruby/object:Gem::Dependency
134
+ name: rubocop
135
+ requirement: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: 1.25.0
140
+ - - "<"
141
+ - !ruby/object:Gem::Version
142
+ version: 2.0.0
143
+ type: :development
144
+ prerelease: false
145
+ version_requirements: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: 1.25.0
150
+ - - "<"
151
+ - !ruby/object:Gem::Version
152
+ version: 2.0.0
153
+ - !ruby/object:Gem::Dependency
154
+ name: rubocop-rake
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: 0.6.0
160
+ - - "<"
161
+ - !ruby/object:Gem::Version
162
+ version: 1.0.0
163
+ type: :development
164
+ prerelease: false
165
+ version_requirements: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: 0.6.0
170
+ - - "<"
171
+ - !ruby/object:Gem::Version
172
+ version: 1.0.0
173
+ - !ruby/object:Gem::Dependency
174
+ name: rubocop-rspec
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: 2.8.0
180
+ - - "<"
181
+ - !ruby/object:Gem::Version
182
+ version: 3.0.0
183
+ type: :development
184
+ prerelease: false
185
+ version_requirements: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: 2.8.0
190
+ - - "<"
191
+ - !ruby/object:Gem::Version
192
+ version: 3.0.0
113
193
  description: API to manage macOS Music tracks and playlists
114
194
  email: al@alweb.org
115
195
  executables: []
@@ -142,14 +222,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
142
222
  requirements:
143
223
  - - ">="
144
224
  - !ruby/object:Gem::Version
145
- version: 2.0.0
225
+ version: 3.1.0
146
226
  required_rubygems_version: !ruby/object:Gem::Requirement
147
227
  requirements:
148
228
  - - ">="
149
229
  - !ruby/object:Gem::Version
150
230
  version: '0'
151
231
  requirements: []
152
- rubygems_version: 3.0.3
232
+ rubygems_version: 3.3.3
153
233
  signing_key:
154
234
  specification_version: 4
155
235
  summary: API for macOS Music