drum 0.1.14 → 0.2.1
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/.github/workflows/test.yml +7 -2
- data/Gemfile +13 -0
- data/Gemfile.lock +46 -42
- data/README.md +6 -0
- data/drum.gemspec +2 -2
- data/lib/drum/service/applemusic.rb +2 -2
- data/lib/drum/service/file.rb +5 -4
- data/lib/drum/service/music.rb +117 -0
- data/lib/drum/service/spotify.rb +2 -2
- data/lib/drum/service/stdio.rb +3 -2
- data/lib/drum/utils/persist.rb +48 -40
- data/lib/drum/utils/yaml.rb +11 -0
- data/lib/drum/version.rb +1 -1
- data/lib/drum.rb +4 -3
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0515ca290089497d8667ed64575eb60dc604ba793812b77437ced8d27f70e6bb
|
4
|
+
data.tar.gz: 1da960b8dc5e79ce85328e918c43d214898824e51d544b613b58ede726666f97
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db118818875e8c9d0d5281697e033bcaeebb3bc204355e41ab4436ed2da1738297c703ed8aa813f32456f968851729794f3e209c0510ca1ddbf218b93af75ab0
|
7
|
+
data.tar.gz: 24ce2b4be59b259617cc50ba66fc35c48aeeeb89faa716a5ad457a959364ccb39cb37b7fa86c4e4f4da6593b7c753bc9eebcde02226d24a6a65402f5d2e7eca8
|
data/.github/workflows/test.yml
CHANGED
@@ -4,13 +4,18 @@ on: [push]
|
|
4
4
|
|
5
5
|
jobs:
|
6
6
|
test:
|
7
|
-
|
7
|
+
strategy:
|
8
|
+
matrix:
|
9
|
+
os: ['ubuntu-latest', 'macos-latest']
|
10
|
+
ruby: ['2.7.0', '3.2.0']
|
11
|
+
|
12
|
+
runs-on: '${{ matrix.os }}'
|
8
13
|
steps:
|
9
14
|
- uses: actions/checkout@v2
|
10
15
|
- name: Set up Ruby
|
11
16
|
uses: ruby/setup-ruby@v1
|
12
17
|
with:
|
13
|
-
ruby-version:
|
18
|
+
ruby-version: '${{ matrix.ruby }}'
|
14
19
|
- name: Install dependencies
|
15
20
|
run: |
|
16
21
|
gem install bundler -v 2.2.0
|
data/Gemfile
CHANGED
@@ -3,6 +3,19 @@ source 'https://rubygems.org'
|
|
3
3
|
# Specify your gem's dependencies in drum.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
+
# Install AppleScript bridge on macOS
|
7
|
+
#
|
8
|
+
# Note: End-users installing drum via 'gem install'
|
9
|
+
# will need to install rb-scpt manually to use the integration.
|
10
|
+
#
|
11
|
+
# TODO: Investigate whether this could be done automatically
|
12
|
+
# See https://stackoverflow.com/a/10249133 for more info.
|
13
|
+
# Unfortunately checking the OS in the gemspec is insufficient,
|
14
|
+
# since the file is evaluated at packaging time.
|
15
|
+
if RUBY_PLATFORM =~ /\bdarwin/
|
16
|
+
gem 'rb-scpt', '~> 1.0'
|
17
|
+
end
|
18
|
+
|
6
19
|
group :development, :test do
|
7
20
|
gem 'rake', '~> 13.0'
|
8
21
|
gem 'yard', '~> 0.9'
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
drum (0.1
|
4
|
+
drum (0.2.1)
|
5
5
|
highline (~> 2.0)
|
6
6
|
jwt (~> 2.2)
|
7
7
|
launchy (~> 2.4)
|
@@ -9,55 +9,55 @@ PATH
|
|
9
9
|
rest-client (~> 2.0)
|
10
10
|
rspotify (~> 2.10)
|
11
11
|
ruby-limiter (~> 2.1)
|
12
|
-
thor (~>
|
12
|
+
thor (~> 1.2)
|
13
13
|
webrick (~> 1.7)
|
14
14
|
|
15
15
|
GEM
|
16
16
|
remote: https://rubygems.org/
|
17
17
|
specs:
|
18
|
-
addressable (2.8.
|
19
|
-
public_suffix (>= 2.0.2, <
|
20
|
-
diff-lcs (1.
|
18
|
+
addressable (2.8.4)
|
19
|
+
public_suffix (>= 2.0.2, < 6.0)
|
20
|
+
diff-lcs (1.5.0)
|
21
21
|
domain_name (0.5.20190701)
|
22
22
|
unf (>= 0.0.5, < 1.0.0)
|
23
|
-
faraday (2.
|
24
|
-
faraday-net_http (
|
23
|
+
faraday (2.7.4)
|
24
|
+
faraday-net_http (>= 2.0, < 3.1)
|
25
25
|
ruby2_keywords (>= 0.0.4)
|
26
|
-
faraday-net_http (
|
27
|
-
ffi (1.15.5
|
26
|
+
faraday-net_http (3.0.2)
|
27
|
+
ffi (1.15.5)
|
28
28
|
hashie (5.0.0)
|
29
|
-
highline (2.0
|
30
|
-
http-cookie (1.0.
|
29
|
+
highline (2.1.0)
|
30
|
+
http-cookie (1.0.5)
|
31
31
|
domain_name (~> 0.5)
|
32
|
-
jwt (2.
|
33
|
-
launchy (2.5.
|
34
|
-
addressable (~> 2.
|
32
|
+
jwt (2.7.0)
|
33
|
+
launchy (2.5.2)
|
34
|
+
addressable (~> 2.8)
|
35
35
|
mime-types (3.4.1)
|
36
36
|
mime-types-data (~> 3.2015)
|
37
|
-
mime-types-data (3.
|
38
|
-
multi_json (1.15.0)
|
37
|
+
mime-types-data (3.2023.0218.1)
|
39
38
|
multi_xml (0.6.0)
|
40
39
|
netrc (0.11.0)
|
41
|
-
oauth2 (
|
40
|
+
oauth2 (2.0.9)
|
42
41
|
faraday (>= 0.17.3, < 3.0)
|
43
42
|
jwt (>= 1.0, < 3.0)
|
44
|
-
multi_json (~> 1.3)
|
45
43
|
multi_xml (~> 0.5)
|
46
|
-
rack (>= 1.2, <
|
47
|
-
|
44
|
+
rack (>= 1.2, < 4)
|
45
|
+
snaky_hash (~> 2.0)
|
46
|
+
version_gem (~> 1.1)
|
47
|
+
omniauth (2.1.1)
|
48
48
|
hashie (>= 3.4.6)
|
49
49
|
rack (>= 2.2.3)
|
50
50
|
rack-protection
|
51
|
-
omniauth-oauth2 (1.
|
52
|
-
oauth2 (
|
53
|
-
omniauth (
|
51
|
+
omniauth-oauth2 (1.8.0)
|
52
|
+
oauth2 (>= 1.4, < 3)
|
53
|
+
omniauth (~> 2.0)
|
54
54
|
options (2.3.2)
|
55
55
|
progress_bar (1.3.3)
|
56
56
|
highline (>= 1.6, < 3)
|
57
57
|
options (~> 2.3.0)
|
58
|
-
public_suffix (
|
59
|
-
rack (
|
60
|
-
rack-protection (
|
58
|
+
public_suffix (5.0.1)
|
59
|
+
rack (3.0.7)
|
60
|
+
rack-protection (3.0.6)
|
61
61
|
rack
|
62
62
|
rake (13.0.6)
|
63
63
|
rest-client (2.0.2)
|
@@ -69,31 +69,35 @@ GEM
|
|
69
69
|
http-cookie (>= 1.0.2, < 2.0)
|
70
70
|
mime-types (>= 1.16, < 4.0)
|
71
71
|
netrc (~> 0.8)
|
72
|
-
rspec (3.
|
73
|
-
rspec-core (~> 3.
|
74
|
-
rspec-expectations (~> 3.
|
75
|
-
rspec-mocks (~> 3.
|
76
|
-
rspec-core (3.
|
77
|
-
rspec-support (~> 3.
|
78
|
-
rspec-expectations (3.
|
72
|
+
rspec (3.12.0)
|
73
|
+
rspec-core (~> 3.12.0)
|
74
|
+
rspec-expectations (~> 3.12.0)
|
75
|
+
rspec-mocks (~> 3.12.0)
|
76
|
+
rspec-core (3.12.2)
|
77
|
+
rspec-support (~> 3.12.0)
|
78
|
+
rspec-expectations (3.12.3)
|
79
79
|
diff-lcs (>= 1.2.0, < 2.0)
|
80
|
-
rspec-support (~> 3.
|
81
|
-
rspec-mocks (3.
|
80
|
+
rspec-support (~> 3.12.0)
|
81
|
+
rspec-mocks (3.12.5)
|
82
82
|
diff-lcs (>= 1.2.0, < 2.0)
|
83
|
-
rspec-support (~> 3.
|
84
|
-
rspec-support (3.
|
83
|
+
rspec-support (~> 3.12.0)
|
84
|
+
rspec-support (3.12.0)
|
85
85
|
rspotify (2.11.1)
|
86
86
|
addressable (~> 2.8.0)
|
87
87
|
omniauth-oauth2 (>= 1.6)
|
88
88
|
rest-client (~> 2.0.2)
|
89
89
|
ruby-limiter (2.2.2)
|
90
90
|
ruby2_keywords (0.0.5)
|
91
|
-
|
91
|
+
snaky_hash (2.0.1)
|
92
|
+
hashie
|
93
|
+
version_gem (~> 1.1, >= 1.1.1)
|
94
|
+
thor (1.2.1)
|
92
95
|
unf (0.1.4)
|
93
96
|
unf_ext
|
94
|
-
unf_ext (0.0.8.
|
95
|
-
|
96
|
-
|
97
|
+
unf_ext (0.0.8.2)
|
98
|
+
version_gem (1.1.2)
|
99
|
+
webrick (1.8.1)
|
100
|
+
yard (0.9.34)
|
97
101
|
|
98
102
|
PLATFORMS
|
99
103
|
ruby
|
@@ -107,4 +111,4 @@ DEPENDENCIES
|
|
107
111
|
yard (~> 0.9)
|
108
112
|
|
109
113
|
BUNDLED WITH
|
110
|
-
2.
|
114
|
+
2.4.12
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Drum
|
2
2
|
|
3
|
+
[](https://github.com/fwcd/drum/actions/workflows/test.yml)
|
4
|
+
[](https://fwcd.github.io/drum)
|
5
|
+
|
3
6
|
A small tool for copying your playlists across music streaming services. Think `rsync`, but for playlists.
|
4
7
|
|
5
8
|

|
@@ -21,6 +24,7 @@ The basic usage pattern is always `drum cp [source] [destination]` where `source
|
|
21
24
|
* `@spotify/playlists`
|
22
25
|
* `@spotify/tracks`
|
23
26
|
* `@applemusic/playlists`
|
27
|
+
* `@music/playlists` (referring to the local Apple Music (`Music.app`) library on macOS)
|
24
28
|
* `@stdin`
|
25
29
|
* `@stdout`
|
26
30
|
* A dash `-`, synonymous with `@stdin` and `@stdout`, depending on usage
|
@@ -67,6 +71,8 @@ To run the application, run `bundle exec bin/drum`. You can also run `bin/consol
|
|
67
71
|
|
68
72
|
To package the application into a gem, run `bundle exec rake build`. The built gem should then be located in `pkg`.
|
69
73
|
|
74
|
+
> Note: If you wish to install `drum` using `gem install`, you may need to install additional gems such as `rb-scpt` on macOS to use platform-specific integrations. See [the `Gemfile`](Gemfile) for more information.
|
75
|
+
|
70
76
|
To install the gem, run `bundle exec rake install`.
|
71
77
|
|
72
78
|
To generate the documentation, run `bundle exec rake yard`.
|
data/drum.gemspec
CHANGED
@@ -23,8 +23,8 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.bindir = 'bin'
|
24
24
|
spec.executables = ['drum']
|
25
25
|
spec.require_paths = ['lib']
|
26
|
-
|
27
|
-
spec.add_dependency 'thor', '~>
|
26
|
+
|
27
|
+
spec.add_dependency 'thor', '~> 1.2'
|
28
28
|
spec.add_dependency 'rspotify', '~> 2.10'
|
29
29
|
spec.add_dependency 'launchy', '~> 2.4'
|
30
30
|
spec.add_dependency 'ruby-limiter', '~> 2.1'
|
@@ -167,8 +167,6 @@ module Drum
|
|
167
167
|
server.shutdown
|
168
168
|
end
|
169
169
|
|
170
|
-
trap 'INT' do server.shutdown end
|
171
|
-
|
172
170
|
log.info "Launching callback HTTP server on port #{port}, waiting for auth code..."
|
173
171
|
server.start
|
174
172
|
|
@@ -186,6 +184,8 @@ module Drum
|
|
186
184
|
}
|
187
185
|
|
188
186
|
user_token
|
187
|
+
ensure
|
188
|
+
server&.shutdown
|
189
189
|
end
|
190
190
|
|
191
191
|
def authenticate
|
data/lib/drum/service/file.rb
CHANGED
@@ -2,6 +2,7 @@ require 'drum/model/playlist'
|
|
2
2
|
require 'drum/model/ref'
|
3
3
|
require 'drum/service/service'
|
4
4
|
require 'drum/utils/log'
|
5
|
+
require 'drum/utils/yaml'
|
5
6
|
require 'pathname'
|
6
7
|
require 'uri'
|
7
8
|
require 'yaml'
|
@@ -9,7 +10,7 @@ require 'yaml'
|
|
9
10
|
module Drum
|
10
11
|
# A service that reads/writes playlists to/from YAML files.
|
11
12
|
class FileService < Service
|
12
|
-
include Log
|
13
|
+
include Log, YAMLUtils
|
13
14
|
|
14
15
|
def name
|
15
16
|
'file'
|
@@ -45,12 +46,12 @@ module Drum
|
|
45
46
|
if base_path.directory?
|
46
47
|
Dir.glob("#{base_path}/**/*.{yaml,yml}").map do |p|
|
47
48
|
path = Pathname.new(p)
|
48
|
-
playlist = Playlist.deserialize(
|
49
|
+
playlist = Playlist.deserialize(from_yaml(path.read))
|
49
50
|
playlist.path = path.relative_path_from(base_path).parent.each_filename.to_a
|
50
51
|
playlist
|
51
52
|
end
|
52
53
|
else
|
53
|
-
[Playlist.deserialize(
|
54
|
+
[Playlist.deserialize(from_yaml(base_path.read))]
|
54
55
|
end
|
55
56
|
end
|
56
57
|
|
@@ -74,7 +75,7 @@ module Drum
|
|
74
75
|
end
|
75
76
|
|
76
77
|
length = 6
|
77
|
-
while playlist_path[length].exist? && Playlist.deserialize(
|
78
|
+
while playlist_path[length].exist? && Playlist.deserialize(from_yaml(playlist_path[length].read)).id != playlist.id
|
78
79
|
length += 1
|
79
80
|
end
|
80
81
|
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'drum/model/album'
|
2
|
+
require 'drum/model/artist'
|
3
|
+
require 'drum/model/ref'
|
4
|
+
require 'drum/model/playlist'
|
5
|
+
require 'drum/model/track'
|
6
|
+
require 'drum/service/service'
|
7
|
+
require 'drum/utils/log'
|
8
|
+
|
9
|
+
module Drum
|
10
|
+
# A service that uses AppleScript to interact with the local Apple Music (Music.app) library.
|
11
|
+
#
|
12
|
+
# Useful resources:
|
13
|
+
# - https://github.com/BrendanThompson/rb-scpt/tree/develop/sample
|
14
|
+
# - https://stackoverflow.com/questions/12964766/create-playlist-in-itunes-with-python-and-scripting-bridge
|
15
|
+
# - https://dougscripts.com/itunes/itinfo/info01.php
|
16
|
+
# - https://dougscripts.com/itunes/itinfo/info02.php
|
17
|
+
# - https://dougscripts.com/itunes/itinfo/info03.php
|
18
|
+
# - https://github.com/sorah/jockey/blob/master/add.rb
|
19
|
+
class MusicService < Service
|
20
|
+
include Log
|
21
|
+
|
22
|
+
def name
|
23
|
+
'music'
|
24
|
+
end
|
25
|
+
|
26
|
+
def require_rb_scpt
|
27
|
+
begin
|
28
|
+
require 'rb-scpt'
|
29
|
+
rescue LoadError
|
30
|
+
raise "Using the local Apple Music importer requires the 'rb-scpt' gem (which requires macOS)"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_library_proxy
|
35
|
+
self.require_rb_scpt
|
36
|
+
music = Appscript.app('Music')
|
37
|
+
source = music.sources[1]
|
38
|
+
unless source.kind.get == :library
|
39
|
+
raise 'Could not get music library source'
|
40
|
+
end
|
41
|
+
return source
|
42
|
+
end
|
43
|
+
|
44
|
+
# Ref parsing
|
45
|
+
|
46
|
+
def parse_ref(raw_ref)
|
47
|
+
if raw_ref.is_token
|
48
|
+
location = case raw_ref.text
|
49
|
+
when "#{self.name}/playlists" then :playlists
|
50
|
+
else return nil
|
51
|
+
end
|
52
|
+
Ref.new(self.name, :special, location)
|
53
|
+
else
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Upload helpers
|
59
|
+
|
60
|
+
def to_track_proxy(library_proxy, playlist, track)
|
61
|
+
# Get track metadata
|
62
|
+
name = track.name
|
63
|
+
artists = track.artist_ids.map { |id| playlist.artists[id].name }
|
64
|
+
|
65
|
+
# Match the track with a track in the local library
|
66
|
+
# TODO: Don't require exact 100% matches
|
67
|
+
name_query = Appscript.its.name.eq(name)
|
68
|
+
artists_query = artists
|
69
|
+
.map { |a| Appscript.its.artist.contains(a) }
|
70
|
+
.reduce { |q1, q2| q1.or(q2) }
|
71
|
+
query = name_query.and(artists_query)
|
72
|
+
|
73
|
+
# Find a matching track in the track
|
74
|
+
# TODO: Instead of choosing the first, prefer iTunes-matched/higher bitrate ones etc.
|
75
|
+
track_proxy = library_proxy.tracks[query].get.first
|
76
|
+
|
77
|
+
if track_proxy.nil?
|
78
|
+
log.warn "No match found for '#{track.name}' by '#{artists.first}'"
|
79
|
+
else
|
80
|
+
log.info "Matched '#{track.name}' with '#{track_proxy.name.get}' by '#{track_proxy.artist.get}'"
|
81
|
+
end
|
82
|
+
track_proxy
|
83
|
+
end
|
84
|
+
|
85
|
+
def upload_playlist(library_proxy, playlist)
|
86
|
+
playlist_proxy = library_proxy.make(new: :playlist, with_properties: {
|
87
|
+
name: playlist.name,
|
88
|
+
description: playlist.description,
|
89
|
+
}.compact)
|
90
|
+
|
91
|
+
playlist.tracks.each do |track|
|
92
|
+
track_proxy = self.to_track_proxy(library_proxy, playlist, track)
|
93
|
+
unless track_proxy.nil?
|
94
|
+
track_proxy.duplicate(to: playlist_proxy)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Service
|
100
|
+
|
101
|
+
def download(ref)
|
102
|
+
raise 'Downloading is not implemented yet'
|
103
|
+
end
|
104
|
+
|
105
|
+
def upload(ref, playlists)
|
106
|
+
library_proxy = self.get_library_proxy
|
107
|
+
|
108
|
+
unless ref.resource_type == :special && ref.resource_location == :playlists
|
109
|
+
raise "Cannot upload to anything other than @#{self.name}/playlists yet!"
|
110
|
+
end
|
111
|
+
|
112
|
+
playlists.each do |playlist|
|
113
|
+
self.upload_playlist(library_proxy, playlist)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
data/lib/drum/service/spotify.rb
CHANGED
@@ -137,8 +137,6 @@ module Drum
|
|
137
137
|
authorize_url = "https://accounts.spotify.com/authorize?client_id=#{client_id}&response_type=code&redirect_uri=http%3A%2F%2Flocalhost:#{port}%2Fcallback&scope=#{scopes.join('%20')}&state=#{csrf_state}"
|
138
138
|
Launchy.open(authorize_url)
|
139
139
|
|
140
|
-
trap 'INT' do server.shutdown end
|
141
|
-
|
142
140
|
log.info "Launching callback HTTP server on port #{port}, waiting for auth code..."
|
143
141
|
server.start
|
144
142
|
|
@@ -155,6 +153,8 @@ module Drum
|
|
155
153
|
})
|
156
154
|
|
157
155
|
self.consume_authentication_response(auth_response)
|
156
|
+
ensure
|
157
|
+
server&.shutdown
|
158
158
|
end
|
159
159
|
|
160
160
|
def authenticate_user_via_refresh(client_id, client_secret, refresh_token)
|
data/lib/drum/service/stdio.rb
CHANGED
@@ -2,12 +2,13 @@ require 'drum/model/playlist'
|
|
2
2
|
require 'drum/model/ref'
|
3
3
|
require 'drum/service/service'
|
4
4
|
require 'drum/utils/log'
|
5
|
+
require 'drum/utils/yaml'
|
5
6
|
require 'yaml'
|
6
7
|
|
7
8
|
module Drum
|
8
9
|
# A service that reads from stdin and writes to stdout.
|
9
10
|
class StdioService < Service
|
10
|
-
include Log
|
11
|
+
include Log, YAMLUtils
|
11
12
|
|
12
13
|
def name
|
13
14
|
'stdio'
|
@@ -31,7 +32,7 @@ module Drum
|
|
31
32
|
def download(playlist_ref)
|
32
33
|
if playlist_ref.resource_location.include?(:stdin)
|
33
34
|
# TODO: Support multiple, --- delimited playlists?
|
34
|
-
[Playlist.deserialize(
|
35
|
+
[Playlist.deserialize(from_yaml(STDIN.read))]
|
35
36
|
else
|
36
37
|
[]
|
37
38
|
end
|
data/lib/drum/utils/persist.rb
CHANGED
@@ -1,50 +1,58 @@
|
|
1
|
+
require 'drum/utils/log'
|
2
|
+
require 'drum/utils/yaml'
|
1
3
|
require 'yaml'
|
2
4
|
|
3
|
-
|
4
|
-
#
|
5
|
-
# @!attribute value
|
6
|
-
# @return [Hash] The wrapped hash
|
7
|
-
class Drum::PersistentHash
|
8
|
-
attr_reader :value
|
9
|
-
|
10
|
-
# Creates a new persistent hash.
|
5
|
+
module Drum
|
6
|
+
# A wrapper around a hash that stores values persistently in a YAML.
|
11
7
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
8
|
+
# @!attribute value
|
9
|
+
# @return [Hash] The wrapped hash
|
10
|
+
class PersistentHash
|
11
|
+
include Log, YAMLUtils
|
12
|
+
attr_reader :value
|
13
|
+
|
14
|
+
# Creates a new persistent hash.
|
15
|
+
#
|
16
|
+
# @param [String] file_path The path to the stored YAML file (may be non-existent).
|
17
|
+
# @param [Hash] value The initial default value, if the file doesn't exist yet or is malformed
|
18
|
+
def initialize(file_path, value={})
|
19
|
+
@file_path = file_path
|
20
|
+
begin
|
21
|
+
self.load
|
22
|
+
rescue StandardError => e
|
23
|
+
unless e.is_a?(Errno::ENOENT)
|
24
|
+
log.warn "Could not load #{file_path}: #{e.inspect}... creating from scratch"
|
25
|
+
end
|
26
|
+
@value = value
|
27
|
+
self.store
|
28
|
+
end
|
21
29
|
end
|
22
|
-
end
|
23
30
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
31
|
+
# Loads the hash from the file.
|
32
|
+
def load
|
33
|
+
@value = from_yaml(File.read(@file_path))
|
34
|
+
end
|
28
35
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
36
|
+
# Saves the hash to the file.
|
37
|
+
def store
|
38
|
+
File.write(@file_path, @value.to_yaml)
|
39
|
+
end
|
33
40
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
41
|
+
# Writes a mapping to the hash and stores it on disk.
|
42
|
+
#
|
43
|
+
# @param [Object] key The key to use.
|
44
|
+
# @param [Object] value The value to map the key to.
|
45
|
+
def []=(key, value)
|
46
|
+
@value[key] = value
|
47
|
+
store
|
48
|
+
end
|
42
49
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
50
|
+
# Reads a mapping from the hash.
|
51
|
+
#
|
52
|
+
# @param [Object] key The key to use.
|
53
|
+
# @return [Object] The value the key is mapped to.
|
54
|
+
def [](key)
|
55
|
+
@value[key]
|
56
|
+
end
|
49
57
|
end
|
50
58
|
end
|
data/lib/drum/version.rb
CHANGED
data/lib/drum.rb
CHANGED
@@ -2,6 +2,7 @@ require 'drum/model/raw_ref'
|
|
2
2
|
require 'drum/service/applemusic'
|
3
3
|
require 'drum/service/file'
|
4
4
|
require 'drum/service/mock'
|
5
|
+
require 'drum/service/music'
|
5
6
|
require 'drum/service/service'
|
6
7
|
require 'drum/service/spotify'
|
7
8
|
require 'drum/service/stdio'
|
@@ -25,8 +26,8 @@ module Drum
|
|
25
26
|
|
26
27
|
@hl = HighLine.new
|
27
28
|
|
28
|
-
# Set up
|
29
|
-
@dot_dir = Pathname.new(Dir.home) / '.drum'
|
29
|
+
# Set up directory for persisted state
|
30
|
+
@dot_dir = Pathname.new(Dir.home) / '.local' / 'state' / 'drum'
|
30
31
|
@dot_dir.mkdir unless @dot_dir.directory?
|
31
32
|
|
32
33
|
@cache_dir = @dot_dir / 'cache'
|
@@ -35,6 +36,7 @@ module Drum
|
|
35
36
|
# Declare services in descending order of parse priority
|
36
37
|
@services = [
|
37
38
|
MockService.new,
|
39
|
+
MusicService.new,
|
38
40
|
StdioService.new,
|
39
41
|
AppleMusicService.new(@cache_dir),
|
40
42
|
SpotifyService.new(@cache_dir),
|
@@ -130,7 +132,6 @@ module Drum
|
|
130
132
|
method_option :recase_paths,
|
131
133
|
type: :string,
|
132
134
|
enum: ['kebabcase', 'startcase', 'camelcase', 'pascalcase'],
|
133
|
-
default: false,
|
134
135
|
desc: "Convert each playlist's path segments to a specific case."
|
135
136
|
|
136
137
|
method_option :flatten,
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: drum
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fwcd
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '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.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspotify
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -171,12 +171,14 @@ files:
|
|
171
171
|
- lib/drum/service/applemusic.rb
|
172
172
|
- lib/drum/service/file.rb
|
173
173
|
- lib/drum/service/mock.rb
|
174
|
+
- lib/drum/service/music.rb
|
174
175
|
- lib/drum/service/service.rb
|
175
176
|
- lib/drum/service/spotify.rb
|
176
177
|
- lib/drum/service/stdio.rb
|
177
178
|
- lib/drum/utils/ext.rb
|
178
179
|
- lib/drum/utils/log.rb
|
179
180
|
- lib/drum/utils/persist.rb
|
181
|
+
- lib/drum/utils/yaml.rb
|
180
182
|
- lib/drum/version.rb
|
181
183
|
- userdoc/playlist-format.md
|
182
184
|
homepage: https://github.com/fwcd/drum.git
|