drum 0.1.14 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/file.rb +5 -4
- data/lib/drum/service/music.rb +117 -0
- 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: 744d7de8c05fe7f7dc8f843b04b2ccd1a77a2688228369706fa4fca79ddf6c07
|
4
|
+
data.tar.gz: c72fc4a18d59cb12017692e1a6fad3cb73cbec24ea73a64adf68279331c3eafb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9cd77d6cf666ef8cd90709b357b59994da0eb20f2a7afaea68f5a60c3aae909d4acf49b407426dd21af4e56caf542e2906dabae73e9270e8221272d543a02e5a
|
7
|
+
data.tar.gz: 488848553a20609b7f72bbd11431c46ba03e8d20db4ad428cca5d34df434a061a1ae55a668ee3a304836f42eb84ba5af84bf84e3a5ab0168271f1b86fd3e8f71
|
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.
|
4
|
+
drum (0.2.0)
|
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
|
+
[![Test](https://github.com/fwcd/drum/actions/workflows/test.yml/badge.svg)](https://github.com/fwcd/drum/actions/workflows/test.yml)
|
4
|
+
[![Documentation](https://github.com/fwcd/drum/actions/workflows/documentation.yml/badge.svg)](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
|
![Icon](artwork/icon128.png)
|
@@ -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'
|
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/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.
|
4
|
+
version: 0.2.0
|
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
|