kagu 3.0.9 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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