drum 0.1.12 → 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: ccee94d125b95501fcfc0411415495ebd89e1fb628e354300cfe61a2c849d174
4
- data.tar.gz: 223c183c3fcff3aac3e892da9ffb1b29c79036054d236470a3f98ea575994138
3
+ metadata.gz: 744d7de8c05fe7f7dc8f843b04b2ccd1a77a2688228369706fa4fca79ddf6c07
4
+ data.tar.gz: c72fc4a18d59cb12017692e1a6fad3cb73cbec24ea73a64adf68279331c3eafb
5
5
  SHA512:
6
- metadata.gz: 20f2bb96ed9ffc1e72a9b336c7f0a211565325e12bd93d13f5b61efafbabe0beee8dd552a524190ba6f9609031eb27dceea2a47de712a6d9eada9e24bc9ca7a4
7
- data.tar.gz: a10fb471ba36bde100efbd9d6001344223efce95d1da5cf0a21e2646f54562dce7e1f97cf1634259ccef2ba8de9b62140e1a0efc279ea89b1ffab4be941fa3e9
6
+ metadata.gz: 9cd77d6cf666ef8cd90709b357b59994da0eb20f2a7afaea68f5a60c3aae909d4acf49b407426dd21af4e56caf542e2906dabae73e9270e8221272d543a02e5a
7
+ data.tar.gz: 488848553a20609b7f72bbd11431c46ba03e8d20db4ad428cca5d34df434a061a1ae55a668ee3a304836f42eb84ba5af84bf84e3a5ab0168271f1b86fd3e8f71
@@ -26,7 +26,7 @@ jobs:
26
26
  touch ~/.gem/credentials
27
27
  chmod 600 ~/.gem/credentials
28
28
  echo ":github: Bearer ${{ secrets.GITHUB_TOKEN }}" >> ~/.gem/credentials
29
- echo ":rubygems: Bearer ${{ secrets.RUBYGEMS_TOKEN }}" >> ~/.gem/credentials
29
+ echo ":rubygems: ${{ secrets.RUBYGEMS_TOKEN }}" >> ~/.gem/credentials
30
30
  - name: Push gem to GitHub Packages
31
31
  run: bundle exec gem push --key github --host "https://rubygems.pkg.github.com/fwcd" pkg/drum-*.gem
32
32
  - name: Push gem to RubyGems.org
@@ -4,13 +4,18 @@ on: [push]
4
4
 
5
5
  jobs:
6
6
  test:
7
- runs-on: ubuntu-latest
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: 2.7.0
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,10 +3,21 @@ 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'
9
22
  gem 'rspec', '~> 3.10'
10
- gem 'ruby-debug-ide', '~> 0.7'
11
- gem 'debase', '~> 0.2' # seems to be required by ruby-debug-ide
12
23
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- drum (0.1.12)
4
+ drum (0.2.0)
5
5
  highline (~> 2.0)
6
6
  jwt (~> 2.2)
7
7
  launchy (~> 2.4)
@@ -9,74 +9,55 @@ PATH
9
9
  rest-client (~> 2.0)
10
10
  rspotify (~> 2.10)
11
11
  ruby-limiter (~> 2.1)
12
- thor (~> 0.20)
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.5.2)
19
- public_suffix (>= 2.0.2, < 4.0)
20
- debase (0.2.4.1)
21
- debase-ruby_core_source (>= 0.10.2)
22
- debase-ruby_core_source (0.10.12)
23
- diff-lcs (1.4.4)
18
+ addressable (2.8.4)
19
+ public_suffix (>= 2.0.2, < 6.0)
20
+ diff-lcs (1.5.0)
24
21
  domain_name (0.5.20190701)
25
22
  unf (>= 0.0.5, < 1.0.0)
26
- faraday (1.8.0)
27
- faraday-em_http (~> 1.0)
28
- faraday-em_synchrony (~> 1.0)
29
- faraday-excon (~> 1.1)
30
- faraday-httpclient (~> 1.0.1)
31
- faraday-net_http (~> 1.0)
32
- faraday-net_http_persistent (~> 1.1)
33
- faraday-patron (~> 1.0)
34
- faraday-rack (~> 1.0)
35
- multipart-post (>= 1.2, < 3)
23
+ faraday (2.7.4)
24
+ faraday-net_http (>= 2.0, < 3.1)
36
25
  ruby2_keywords (>= 0.0.4)
37
- faraday-em_http (1.0.0)
38
- faraday-em_synchrony (1.0.0)
39
- faraday-excon (1.1.0)
40
- faraday-httpclient (1.0.1)
41
- faraday-net_http (1.0.1)
42
- faraday-net_http_persistent (1.2.0)
43
- faraday-patron (1.0.0)
44
- faraday-rack (1.0.0)
45
- ffi (1.15.4-x64-mingw32)
46
- hashie (4.1.0)
47
- highline (2.0.3)
48
- http-cookie (1.0.4)
26
+ faraday-net_http (3.0.2)
27
+ ffi (1.15.5)
28
+ hashie (5.0.0)
29
+ highline (2.1.0)
30
+ http-cookie (1.0.5)
49
31
  domain_name (~> 0.5)
50
- jwt (2.3.0)
51
- launchy (2.4.3)
52
- addressable (~> 2.3)
53
- mime-types (3.3.1)
32
+ jwt (2.7.0)
33
+ launchy (2.5.2)
34
+ addressable (~> 2.8)
35
+ mime-types (3.4.1)
54
36
  mime-types-data (~> 3.2015)
55
- mime-types-data (3.2021.0901)
56
- multi_json (1.15.0)
37
+ mime-types-data (3.2023.0218.1)
57
38
  multi_xml (0.6.0)
58
- multipart-post (2.1.1)
59
39
  netrc (0.11.0)
60
- oauth2 (1.4.7)
61
- faraday (>= 0.8, < 2.0)
40
+ oauth2 (2.0.9)
41
+ faraday (>= 0.17.3, < 3.0)
62
42
  jwt (>= 1.0, < 3.0)
63
- multi_json (~> 1.3)
64
43
  multi_xml (~> 0.5)
65
- rack (>= 1.2, < 3)
66
- omniauth (2.0.4)
44
+ rack (>= 1.2, < 4)
45
+ snaky_hash (~> 2.0)
46
+ version_gem (~> 1.1)
47
+ omniauth (2.1.1)
67
48
  hashie (>= 3.4.6)
68
- rack (>= 1.6.2, < 3)
49
+ rack (>= 2.2.3)
69
50
  rack-protection
70
- omniauth-oauth2 (1.7.1)
71
- oauth2 (~> 1.4)
72
- omniauth (>= 1.9, < 3)
51
+ omniauth-oauth2 (1.8.0)
52
+ oauth2 (>= 1.4, < 3)
53
+ omniauth (~> 2.0)
73
54
  options (2.3.2)
74
55
  progress_bar (1.3.3)
75
56
  highline (>= 1.6, < 3)
76
57
  options (~> 2.3.0)
77
- public_suffix (3.1.1)
78
- rack (2.2.3)
79
- rack-protection (2.1.0)
58
+ public_suffix (5.0.1)
59
+ rack (3.0.7)
60
+ rack-protection (3.0.6)
80
61
  rack
81
62
  rake (13.0.6)
82
63
  rest-client (2.0.2)
@@ -88,34 +69,35 @@ GEM
88
69
  http-cookie (>= 1.0.2, < 2.0)
89
70
  mime-types (>= 1.16, < 4.0)
90
71
  netrc (~> 0.8)
91
- rspec (3.10.0)
92
- rspec-core (~> 3.10.0)
93
- rspec-expectations (~> 3.10.0)
94
- rspec-mocks (~> 3.10.0)
95
- rspec-core (3.10.1)
96
- rspec-support (~> 3.10.0)
97
- rspec-expectations (3.10.1)
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)
98
79
  diff-lcs (>= 1.2.0, < 2.0)
99
- rspec-support (~> 3.10.0)
100
- rspec-mocks (3.10.2)
80
+ rspec-support (~> 3.12.0)
81
+ rspec-mocks (3.12.5)
101
82
  diff-lcs (>= 1.2.0, < 2.0)
102
- rspec-support (~> 3.10.0)
103
- rspec-support (3.10.2)
104
- rspotify (2.10.3)
105
- addressable (~> 2.5.2)
83
+ rspec-support (~> 3.12.0)
84
+ rspec-support (3.12.0)
85
+ rspotify (2.11.1)
86
+ addressable (~> 2.8.0)
106
87
  omniauth-oauth2 (>= 1.6)
107
88
  rest-client (~> 2.0.2)
108
- ruby-debug-ide (0.7.3)
109
- rake (>= 0.8.1)
110
- ruby-limiter (2.1.0)
89
+ ruby-limiter (2.2.2)
111
90
  ruby2_keywords (0.0.5)
112
- thor (0.20.3)
91
+ snaky_hash (2.0.1)
92
+ hashie
93
+ version_gem (~> 1.1, >= 1.1.1)
94
+ thor (1.2.1)
113
95
  unf (0.1.4)
114
96
  unf_ext
115
- unf_ext (0.0.8)
116
- unf_ext (0.0.8-x64-mingw32)
117
- webrick (1.7.0)
118
- yard (0.9.26)
97
+ unf_ext (0.0.8.2)
98
+ version_gem (1.1.2)
99
+ webrick (1.8.1)
100
+ yard (0.9.34)
119
101
 
120
102
  PLATFORMS
121
103
  ruby
@@ -123,12 +105,10 @@ PLATFORMS
123
105
  x86_64-linux
124
106
 
125
107
  DEPENDENCIES
126
- debase (~> 0.2)
127
108
  drum!
128
109
  rake (~> 13.0)
129
110
  rspec (~> 3.10)
130
- ruby-debug-ide (~> 0.7)
131
111
  yard (~> 0.9)
132
112
 
133
113
  BUNDLED WITH
134
- 2.2.29
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', '~> 0.20'
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'
@@ -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(YAML.load(path.read))
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(YAML.load(base_path.read))]
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(YAML.load(playlist_path[length].read)).id != playlist.id
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
@@ -555,8 +555,12 @@ module Drum
555
555
  log.info 'Fetching playlists...'
556
556
  Enumerator.new(sp_playlists.length) do |enum|
557
557
  sp_playlists.each do |sp_playlist|
558
- new_playlist = self.from_sp_playlist(sp_playlist)
559
- enum.yield new_playlist
558
+ begin
559
+ new_playlist = self.from_sp_playlist(sp_playlist)
560
+ enum.yield new_playlist
561
+ rescue StandardError => e
562
+ log.info "Could not download playlist '#{sp_playlist.name}': #{e}"
563
+ end
560
564
  end
561
565
  end
562
566
  when :tracks
@@ -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(YAML.load(STDIN.read))]
35
+ [Playlist.deserialize(from_yaml(STDIN.read))]
35
36
  else
36
37
  []
37
38
  end
@@ -1,50 +1,58 @@
1
+ require 'drum/utils/log'
2
+ require 'drum/utils/yaml'
1
3
  require 'yaml'
2
4
 
3
- # A wrapper around a hash that stores values persistently in a YAML.
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
- # @param [String] file_path The path to the stored YAML file (may be non-existent).
13
- # @param [Hash] value The initial default value, if the file doesn't exist yet or is malformed
14
- def initialize(file_path, value={})
15
- @file_path = file_path
16
- begin
17
- self.load
18
- rescue StandardError => e
19
- @value = value
20
- self.store
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
- # Loads the hash from the file.
25
- def load
26
- @value = YAML.load(File.read(@file_path))
27
- end
31
+ # Loads the hash from the file.
32
+ def load
33
+ @value = from_yaml(File.read(@file_path))
34
+ end
28
35
 
29
- # Saves the hash to the file.
30
- def store
31
- File.write(@file_path, @value.to_yaml)
32
- end
36
+ # Saves the hash to the file.
37
+ def store
38
+ File.write(@file_path, @value.to_yaml)
39
+ end
33
40
 
34
- # Writes a mapping to the hash and stores it on disk.
35
- #
36
- # @param [Object] key The key to use.
37
- # @param [Object] value The value to map the key to.
38
- def []=(key, value)
39
- @value[key] = value
40
- store
41
- end
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
- # Reads a mapping from the hash.
44
- #
45
- # @param [Object] key The key to use.
46
- # @return [Object] The value the key is mapped to.
47
- def [](key)
48
- @value[key]
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
@@ -0,0 +1,11 @@
1
+ require 'date'
2
+ require 'yaml'
3
+
4
+ module Drum
5
+ module YAMLUtils
6
+ # Deserializes YAML with a number of standard permitted classes.
7
+ def from_yaml(yaml)
8
+ YAML.load(yaml, permitted_classes: [Date, DateTime, Symbol, Time])
9
+ end
10
+ end
11
+ end
data/lib/drum/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Drum
2
- VERSION = '0.1.12'
2
+ VERSION = '0.2.0'
3
3
  end
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 .drum directory
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),
@@ -125,13 +127,17 @@ module Drum
125
127
  method_option :group_by_author,
126
128
  type: :boolean,
127
129
  default: false,
128
- desc: "Prepend the author name to each playlist's path"
130
+ desc: "Prepend the author name to each playlist's path."
129
131
 
130
132
  method_option :recase_paths,
131
133
  type: :string,
132
134
  enum: ['kebabcase', 'startcase', 'camelcase', 'pascalcase'],
135
+ desc: "Convert each playlist's path segments to a specific case."
136
+
137
+ method_option :flatten,
138
+ type: :boolean,
133
139
  default: false,
134
- desc: "Convert each playlist's path segments to a specific case"
140
+ desc: 'Clear all playlist paths. Note that flags such as --group-by-author will still be applied.'
135
141
 
136
142
  method_option :note_date,
137
143
  type: :boolean,
@@ -176,6 +182,10 @@ module Drum
176
182
  playlists = playlists.map do |playlist|
177
183
  bar&.increment!
178
184
 
185
+ if options[:flatten]
186
+ playlist.path = []
187
+ end
188
+
179
189
  if options[:group_by_author]
180
190
  author_name = playlist.author_id.try { |id| playlist.users[id] }&.display_name || 'Other'
181
191
  playlist.path.unshift(author_name)
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.12
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: 2021-10-15 00:00:00.000000000 Z
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: '0.20'
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: '0.20'
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
@@ -200,7 +202,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
200
202
  - !ruby/object:Gem::Version
201
203
  version: '0'
202
204
  requirements: []
203
- rubygems_version: 3.2.22
205
+ rubygems_version: 3.1.2
204
206
  signing_key:
205
207
  specification_version: 4
206
208
  summary: Playlist manager