spotifyexporter 0.1.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 +7 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +108 -0
- data/LICENSE.txt +21 -0
- data/README.md +61 -0
- data/Rakefile +5 -0
- data/exe/spotifyexporter +6 -0
- data/lib/spotifyexporter/auth.rb +42 -0
- data/lib/spotifyexporter/cli.rb +37 -0
- data/lib/spotifyexporter/command/playlists.rb +67 -0
- data/lib/spotifyexporter/config.rb +132 -0
- data/lib/spotifyexporter/lib/injectable.rb +15 -0
- data/lib/spotifyexporter/lib/spotify.rb +47 -0
- data/lib/spotifyexporter/version.rb +5 -0
- data/lib/spotifyexporter.rb +3 -0
- data/sig/spotifyexporter.rbs +4 -0
- data/spotifyexporter.gemspec +38 -0
- metadata +135 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 90f4fca8c67b5e0521ccefa47c8745cf58779b50dcdf5a7ceac73b12a939e354
|
|
4
|
+
data.tar.gz: 53e47966eacae888046c5f84115b78c2e77759a343b5b48bd0db56a72698b4c8
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b616ff7bbabdc1eac578d9ae0707c405e42625a3b3b4b3ebb76413c8ff7101f9e95212cfac4f2571747ed40e219ed53ad01f6c7e5476e8342549ef0b79ae0e3e
|
|
7
|
+
data.tar.gz: ce35290bf8c7ec6fa6135100126893b3ae91112591dec12d58ba47c6e308439830af2e0328b2a17a451ebf2627753c60ab643e931c0060d9d27623615b6a370b
|
data/.rubocop.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
spotifyexporter (0.1.0)
|
|
5
|
+
addressable (~> 2.8.0)
|
|
6
|
+
rspotify (~> 2.1.1)
|
|
7
|
+
thor (~> 1.2.1)
|
|
8
|
+
tty-prompt (~> 0.23.1)
|
|
9
|
+
|
|
10
|
+
GEM
|
|
11
|
+
remote: https://rubygems.org/
|
|
12
|
+
specs:
|
|
13
|
+
addressable (2.8.0)
|
|
14
|
+
public_suffix (>= 2.0.2, < 5.0)
|
|
15
|
+
ast (2.4.2)
|
|
16
|
+
coderay (1.1.3)
|
|
17
|
+
domain_name (0.5.20190701)
|
|
18
|
+
unf (>= 0.0.5, < 1.0.0)
|
|
19
|
+
faraday (2.3.0)
|
|
20
|
+
faraday-net_http (~> 2.0)
|
|
21
|
+
ruby2_keywords (>= 0.0.4)
|
|
22
|
+
faraday-net_http (2.0.3)
|
|
23
|
+
hashie (5.0.0)
|
|
24
|
+
http-cookie (1.0.4)
|
|
25
|
+
domain_name (~> 0.5)
|
|
26
|
+
jwt (2.3.0)
|
|
27
|
+
method_source (1.0.0)
|
|
28
|
+
mime-types (3.4.1)
|
|
29
|
+
mime-types-data (~> 3.2015)
|
|
30
|
+
mime-types-data (3.2022.0105)
|
|
31
|
+
multi_json (1.15.0)
|
|
32
|
+
multi_xml (0.6.0)
|
|
33
|
+
netrc (0.11.0)
|
|
34
|
+
oauth2 (1.4.9)
|
|
35
|
+
faraday (>= 0.17.3, < 3.0)
|
|
36
|
+
jwt (>= 1.0, < 3.0)
|
|
37
|
+
multi_json (~> 1.3)
|
|
38
|
+
multi_xml (~> 0.5)
|
|
39
|
+
rack (>= 1.2, < 3)
|
|
40
|
+
omniauth (1.9.1)
|
|
41
|
+
hashie (>= 3.4.6)
|
|
42
|
+
rack (>= 1.6.2, < 3)
|
|
43
|
+
omniauth-oauth2 (1.3.1)
|
|
44
|
+
oauth2 (~> 1.0)
|
|
45
|
+
omniauth (~> 1.2)
|
|
46
|
+
parallel (1.22.1)
|
|
47
|
+
parser (3.1.2.0)
|
|
48
|
+
ast (~> 2.4.1)
|
|
49
|
+
pastel (0.8.0)
|
|
50
|
+
tty-color (~> 0.5)
|
|
51
|
+
pry (0.14.1)
|
|
52
|
+
coderay (~> 1.1)
|
|
53
|
+
method_source (~> 1.0)
|
|
54
|
+
public_suffix (4.0.7)
|
|
55
|
+
rack (2.2.3)
|
|
56
|
+
rainbow (3.1.1)
|
|
57
|
+
rake (13.0.6)
|
|
58
|
+
regexp_parser (2.4.0)
|
|
59
|
+
rest-client (2.0.2)
|
|
60
|
+
http-cookie (>= 1.0.2, < 2.0)
|
|
61
|
+
mime-types (>= 1.16, < 4.0)
|
|
62
|
+
netrc (~> 0.8)
|
|
63
|
+
rexml (3.2.5)
|
|
64
|
+
rspotify (2.1.1)
|
|
65
|
+
omniauth-oauth2 (~> 1.3.1)
|
|
66
|
+
rest-client (~> 2.0.2)
|
|
67
|
+
rubocop (1.29.1)
|
|
68
|
+
parallel (~> 1.10)
|
|
69
|
+
parser (>= 3.1.0.0)
|
|
70
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
71
|
+
regexp_parser (>= 1.8, < 3.0)
|
|
72
|
+
rexml (>= 3.2.5, < 4.0)
|
|
73
|
+
rubocop-ast (>= 1.17.0, < 2.0)
|
|
74
|
+
ruby-progressbar (~> 1.7)
|
|
75
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
|
76
|
+
rubocop-ast (1.18.0)
|
|
77
|
+
parser (>= 3.1.1.0)
|
|
78
|
+
ruby-progressbar (1.11.0)
|
|
79
|
+
ruby2_keywords (0.0.5)
|
|
80
|
+
thor (1.2.1)
|
|
81
|
+
tty-color (0.6.0)
|
|
82
|
+
tty-cursor (0.7.1)
|
|
83
|
+
tty-prompt (0.23.1)
|
|
84
|
+
pastel (~> 0.8)
|
|
85
|
+
tty-reader (~> 0.8)
|
|
86
|
+
tty-reader (0.9.0)
|
|
87
|
+
tty-cursor (~> 0.7)
|
|
88
|
+
tty-screen (~> 0.8)
|
|
89
|
+
wisper (~> 2.0)
|
|
90
|
+
tty-screen (0.8.1)
|
|
91
|
+
unf (0.1.4)
|
|
92
|
+
unf_ext
|
|
93
|
+
unf_ext (0.0.8.1)
|
|
94
|
+
unicode-display_width (2.1.0)
|
|
95
|
+
wisper (2.0.1)
|
|
96
|
+
|
|
97
|
+
PLATFORMS
|
|
98
|
+
arm64-darwin-21
|
|
99
|
+
ruby
|
|
100
|
+
|
|
101
|
+
DEPENDENCIES
|
|
102
|
+
pry
|
|
103
|
+
rake (~> 13.0)
|
|
104
|
+
rubocop (~> 1.21)
|
|
105
|
+
spotifyexporter!
|
|
106
|
+
|
|
107
|
+
BUNDLED WITH
|
|
108
|
+
2.3.7
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 aquaflamingo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# SpotifyExporter
|
|
2
|
+
|
|
3
|
+
Export Spotify Playlists
|
|
4
|
+
|
|
5
|
+
Currently only supports Ruby < 2.7
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add this line to your application's Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem 'spotifyexporter'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
And then execute:
|
|
16
|
+
|
|
17
|
+
$ bundle install
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
|
|
21
|
+
$ gem install spotifyexporter
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
You will need to authenticate the CLI before you can use it.
|
|
26
|
+
|
|
27
|
+
You can generate a Spotify API Key at: https://developer.spotify.com/dashboard/applications/6b96bdf7b9224f109aa65ebda1429522
|
|
28
|
+
|
|
29
|
+
After you have an API Key available, run the `auth` command and enter your credentials:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
|
|
33
|
+
spotifyexporter auth
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The CLI will persist these credentials on your file system within the `.config` directory.
|
|
38
|
+
|
|
39
|
+
Next, you can run the `export` command to get a full list of songs in each playlist for a particular user.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
|
|
43
|
+
# Create an output directory
|
|
44
|
+
mkdir output_dir
|
|
45
|
+
|
|
46
|
+
spotifyexporter export -u username -o output_dir
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Development
|
|
50
|
+
|
|
51
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
52
|
+
|
|
53
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
54
|
+
|
|
55
|
+
## Contributing
|
|
56
|
+
|
|
57
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/aquaflamingo/spotifyexporter.
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/exe/spotifyexporter
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tty-prompt"
|
|
4
|
+
require_relative "./config"
|
|
5
|
+
|
|
6
|
+
module SpotifyExporter
|
|
7
|
+
class AuthWorkflow
|
|
8
|
+
|
|
9
|
+
#
|
|
10
|
+
# Starts auth flow
|
|
11
|
+
#
|
|
12
|
+
def start
|
|
13
|
+
auth = prompt_for_auth
|
|
14
|
+
|
|
15
|
+
ConfigManager.instance.save_credentials(
|
|
16
|
+
client_id: auth[:spotify_client_id],
|
|
17
|
+
secret: auth[:spotify_client_secret]
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
# Creates an interactive prompt for user input
|
|
24
|
+
#
|
|
25
|
+
# @returns [Hash]
|
|
26
|
+
def prompt_for_auth
|
|
27
|
+
prompt = TTY::Prompt.new
|
|
28
|
+
|
|
29
|
+
prompt.collect do |_p|
|
|
30
|
+
# Project name
|
|
31
|
+
key(:spotify_client_id).ask("Enter Spotify Client ID: ") do |q|
|
|
32
|
+
q.required true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Project description
|
|
36
|
+
key(:spotify_client_secret).ask("Enter Spotify Client Secret: ") do |q|
|
|
37
|
+
q.required true
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
require_relative "version"
|
|
5
|
+
require_relative "command/playlists"
|
|
6
|
+
require_relative "auth"
|
|
7
|
+
|
|
8
|
+
module SpotifyExporter
|
|
9
|
+
#
|
|
10
|
+
# The CLI spotify exporter
|
|
11
|
+
#
|
|
12
|
+
class CLI < Thor
|
|
13
|
+
package_name "spotifyexporter"
|
|
14
|
+
|
|
15
|
+
# Returns exit with non zero status when an exception occurs
|
|
16
|
+
def self.exit_on_failure?
|
|
17
|
+
true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
#
|
|
21
|
+
# Returns version of the cli
|
|
22
|
+
#
|
|
23
|
+
desc "version", "Prints the current version"
|
|
24
|
+
def version
|
|
25
|
+
puts SpotifyExporter::VERSION
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc "auth", "Authorizes the CLI with Spotify API"
|
|
29
|
+
def auth
|
|
30
|
+
workflow = AuthWorkflow.new
|
|
31
|
+
workflow.start
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
desc "playlists", "List playlists"
|
|
35
|
+
subcommand 'playlists', Playlists
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require "thor"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
require_relative '../config.rb'
|
|
4
|
+
require_relative '../lib/spotify.rb'
|
|
5
|
+
|
|
6
|
+
module SpotifyExporter
|
|
7
|
+
class Playlists < Thor
|
|
8
|
+
include ConfigDependant
|
|
9
|
+
|
|
10
|
+
desc "ls", "Lists playlist"
|
|
11
|
+
option :user, required: true, aliases: "-u"
|
|
12
|
+
option :url, required: false, aliases: "-l"
|
|
13
|
+
def ls
|
|
14
|
+
pl = spotify.get_user_playlists(options.user)
|
|
15
|
+
|
|
16
|
+
results = pl.map do |p|
|
|
17
|
+
r = p.name
|
|
18
|
+
|
|
19
|
+
r << " | #{p.external_urls["spotify"]}" unless options.url.nil?
|
|
20
|
+
|
|
21
|
+
r
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
puts results
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
desc "export", "Exports playlists"
|
|
28
|
+
option :user, required: true, aliases: "-u"
|
|
29
|
+
option :output_directory, required: true, aliases: "-o"
|
|
30
|
+
option :all, required: false, aliases: "-a"
|
|
31
|
+
def export
|
|
32
|
+
FileUtils.mkdir_p(options.output_directory) unless Dir.exist? options.output_directory
|
|
33
|
+
|
|
34
|
+
pl = spotify.get_user_playlists(options.user)
|
|
35
|
+
|
|
36
|
+
# For each playlist
|
|
37
|
+
pl.each do |p|
|
|
38
|
+
# Remove non-alphanumeric characters
|
|
39
|
+
sanitized_name = p.name.gsub(/[^[:alnum:]]/, "")
|
|
40
|
+
playlist_file_name = File.join(options.output_directory, sanitized_name)
|
|
41
|
+
|
|
42
|
+
# Create a file for the playlist
|
|
43
|
+
File.open(playlist_file_name, "w") do |f|
|
|
44
|
+
|
|
45
|
+
# Write the name of each artist and song
|
|
46
|
+
p.tracks.each do |t|
|
|
47
|
+
artists = t.artists.map(&:name).join(", ")
|
|
48
|
+
|
|
49
|
+
f.puts "#{artists} - #{t.name}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
#
|
|
57
|
+
# SpotifyClient
|
|
58
|
+
#
|
|
59
|
+
def spotify
|
|
60
|
+
return @spotify unless @spotify.nil?
|
|
61
|
+
|
|
62
|
+
@spotify = SpotifyClient.new(config)
|
|
63
|
+
|
|
64
|
+
@spotify
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "yaml"
|
|
5
|
+
require "singleton"
|
|
6
|
+
require "pry"
|
|
7
|
+
require_relative './lib/injectable.rb'
|
|
8
|
+
|
|
9
|
+
module SpotifyExporter
|
|
10
|
+
module ConfigDependant
|
|
11
|
+
include Injectable
|
|
12
|
+
|
|
13
|
+
inject :config, -> { ConfigManager.instance.config }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class AppConfig
|
|
17
|
+
attr_accessor :spotify_client_id, :spotify_client_secret
|
|
18
|
+
|
|
19
|
+
def initialize(id, secret)
|
|
20
|
+
@spotify_client_id = id
|
|
21
|
+
@spotify_client_secret = secret
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_yaml
|
|
25
|
+
to_h.to_yaml
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def credentials?
|
|
29
|
+
!@spotify_client_secret.nil? && !@spotify_client_id.nil?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.new_empty
|
|
33
|
+
new("", "")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def to_h
|
|
39
|
+
{ spotify_client_id: @spotify_client_id, spotify_client_secret: @spotify_client_secret }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class ConfigManager
|
|
44
|
+
include Singleton
|
|
45
|
+
|
|
46
|
+
attr_reader :config_path
|
|
47
|
+
|
|
48
|
+
DEFAULT_CONFIG_DIR = ".config/spotifyexporter"
|
|
49
|
+
|
|
50
|
+
FILE_CLI_CONFIG = "config.yml"
|
|
51
|
+
|
|
52
|
+
#
|
|
53
|
+
# Loads and saves the credentials to the main configuration
|
|
54
|
+
#
|
|
55
|
+
# @return [Boolean]
|
|
56
|
+
#
|
|
57
|
+
def save_credentials(client_id:, secret:)
|
|
58
|
+
load_config_file unless config_loaded?
|
|
59
|
+
|
|
60
|
+
@app_config.spotify_client_id = client_id
|
|
61
|
+
@app_config.spotify_client_secret = secret
|
|
62
|
+
|
|
63
|
+
save_config!
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
#
|
|
67
|
+
# Retrieves the AppConfig
|
|
68
|
+
#
|
|
69
|
+
# @return [AppConfig]
|
|
70
|
+
#
|
|
71
|
+
def config
|
|
72
|
+
load_config_file unless config_loaded?
|
|
73
|
+
|
|
74
|
+
@app_config
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
#
|
|
80
|
+
# Saves the currently in-memory configuration
|
|
81
|
+
#
|
|
82
|
+
# @return success [Boolean]
|
|
83
|
+
#
|
|
84
|
+
def save_config!
|
|
85
|
+
File.open(config_file_path, "w") do |f|
|
|
86
|
+
f.puts @app_config.to_yaml
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
true
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def config_loaded?
|
|
93
|
+
!@app_config.nil?
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def config_dir_path
|
|
97
|
+
@config_dir_path ||= File.join Dir.home, DEFAULT_CONFIG_DIR
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def config_file_path
|
|
101
|
+
@config_file_path ||= File.join(config_dir_path, FILE_CLI_CONFIG)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
#
|
|
105
|
+
# Reads the user file system configurations into the in-memory AppConfig
|
|
106
|
+
#
|
|
107
|
+
def load_config_file
|
|
108
|
+
initialize_config_file unless config_file_exists?
|
|
109
|
+
|
|
110
|
+
config_yaml = YAML.load_file(config_file_path)
|
|
111
|
+
|
|
112
|
+
@app_config = if config_yaml.nil?
|
|
113
|
+
AppConfig.new_empty
|
|
114
|
+
else
|
|
115
|
+
AppConfig.new(config_yaml[:spotify_client_id], config_yaml[:spotify_client_secret])
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def config_file_exists?
|
|
120
|
+
return false unless File.exist? config_file_path
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
#
|
|
124
|
+
# Initializes the configuration directory if non exists
|
|
125
|
+
#
|
|
126
|
+
def initialize_config_file
|
|
127
|
+
FileUtils.mkdir_p(config_dir_path) unless Dir.exist? config_dir_path
|
|
128
|
+
|
|
129
|
+
FileUtils.touch(config_file_path) unless File.exist? config_file_path
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module SpotifyExporter
|
|
2
|
+
module Injectable
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.extend ClassMethods
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
module ClassMethods
|
|
8
|
+
# Dynamically defines a private method in the included class
|
|
9
|
+
def inject(name, source)
|
|
10
|
+
define_method(name.to_sym) { source.call }
|
|
11
|
+
private name.to_sym
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'rspotify'
|
|
2
|
+
require 'addressable'
|
|
3
|
+
|
|
4
|
+
class SpotifyClient
|
|
5
|
+
def initialize(conf)
|
|
6
|
+
RSpotify.authenticate(
|
|
7
|
+
conf.spotify_client_id,
|
|
8
|
+
conf.spotify_client_secret
|
|
9
|
+
)
|
|
10
|
+
end
|
|
11
|
+
#
|
|
12
|
+
# GETs a user
|
|
13
|
+
#
|
|
14
|
+
# @param uname [String]
|
|
15
|
+
#
|
|
16
|
+
def get_user(uname)
|
|
17
|
+
RSpotify::User.find(uname)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
#
|
|
21
|
+
# GETs all playlists for a user
|
|
22
|
+
#
|
|
23
|
+
# @param uname [String]
|
|
24
|
+
#
|
|
25
|
+
def get_user_playlists(uname)
|
|
26
|
+
u = get_user(uname)
|
|
27
|
+
|
|
28
|
+
playlists = []
|
|
29
|
+
offset = 0
|
|
30
|
+
last_pull_count = 0
|
|
31
|
+
|
|
32
|
+
# You can only pull from API in increments of 50
|
|
33
|
+
# if the last pull count is a multiple of 50
|
|
34
|
+
# we know we can keep pulling, if not, stop
|
|
35
|
+
while (last_pull_count % 50).zero? do
|
|
36
|
+
p = u.playlists(limit: 50, offset: offset)
|
|
37
|
+
|
|
38
|
+
playlists << p
|
|
39
|
+
|
|
40
|
+
offset += 50
|
|
41
|
+
|
|
42
|
+
last_pull_count = p.count
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
playlists.compact.flatten
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/spotifyexporter/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "spotifyexporter"
|
|
7
|
+
spec.version = SpotifyExporter::VERSION
|
|
8
|
+
spec.authors = ["aquaflamingo"]
|
|
9
|
+
spec.email = ["16901597+aquaflamingo@users.noreply.github.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Export your Spotify playlists"
|
|
12
|
+
spec.description = "Export your Spotify playlists"
|
|
13
|
+
spec.homepage = "https://github.com/aquaflamingo/spotify-exporter"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/aquaflamingo/spotify-exporter"
|
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/aquaflamingo/spotify-exporter/releases"
|
|
20
|
+
|
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
24
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
25
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
spec.bindir = "exe"
|
|
29
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
30
|
+
spec.require_paths = ["lib"]
|
|
31
|
+
|
|
32
|
+
spec.add_dependency "rspotify", "~> 2.1.1"
|
|
33
|
+
spec.add_dependency "addressable", "~> 2.8.0"
|
|
34
|
+
spec.add_dependency "thor", "~> 1.2.1"
|
|
35
|
+
spec.add_dependency "tty-prompt", "~> 0.23.1"
|
|
36
|
+
|
|
37
|
+
spec.add_development_dependency "pry"
|
|
38
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: spotifyexporter
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- aquaflamingo
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2022-05-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rspotify
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 2.1.1
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 2.1.1
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: addressable
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 2.8.0
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 2.8.0
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: thor
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 1.2.1
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 1.2.1
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: tty-prompt
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 0.23.1
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: 0.23.1
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: pry
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
description: Export your Spotify playlists
|
|
84
|
+
email:
|
|
85
|
+
- 16901597+aquaflamingo@users.noreply.github.com
|
|
86
|
+
executables:
|
|
87
|
+
- spotifyexporter
|
|
88
|
+
extensions: []
|
|
89
|
+
extra_rdoc_files: []
|
|
90
|
+
files:
|
|
91
|
+
- ".rubocop.yml"
|
|
92
|
+
- CHANGELOG.md
|
|
93
|
+
- Gemfile
|
|
94
|
+
- Gemfile.lock
|
|
95
|
+
- LICENSE.txt
|
|
96
|
+
- README.md
|
|
97
|
+
- Rakefile
|
|
98
|
+
- exe/spotifyexporter
|
|
99
|
+
- lib/spotifyexporter.rb
|
|
100
|
+
- lib/spotifyexporter/auth.rb
|
|
101
|
+
- lib/spotifyexporter/cli.rb
|
|
102
|
+
- lib/spotifyexporter/command/playlists.rb
|
|
103
|
+
- lib/spotifyexporter/config.rb
|
|
104
|
+
- lib/spotifyexporter/lib/injectable.rb
|
|
105
|
+
- lib/spotifyexporter/lib/spotify.rb
|
|
106
|
+
- lib/spotifyexporter/version.rb
|
|
107
|
+
- sig/spotifyexporter.rbs
|
|
108
|
+
- spotifyexporter.gemspec
|
|
109
|
+
homepage: https://github.com/aquaflamingo/spotify-exporter
|
|
110
|
+
licenses:
|
|
111
|
+
- MIT
|
|
112
|
+
metadata:
|
|
113
|
+
homepage_uri: https://github.com/aquaflamingo/spotify-exporter
|
|
114
|
+
source_code_uri: https://github.com/aquaflamingo/spotify-exporter
|
|
115
|
+
changelog_uri: https://github.com/aquaflamingo/spotify-exporter/releases
|
|
116
|
+
post_install_message:
|
|
117
|
+
rdoc_options: []
|
|
118
|
+
require_paths:
|
|
119
|
+
- lib
|
|
120
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: 2.6.0
|
|
125
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
|
+
requirements:
|
|
127
|
+
- - ">="
|
|
128
|
+
- !ruby/object:Gem::Version
|
|
129
|
+
version: '0'
|
|
130
|
+
requirements: []
|
|
131
|
+
rubygems_version: 3.3.7
|
|
132
|
+
signing_key:
|
|
133
|
+
specification_version: 4
|
|
134
|
+
summary: Export your Spotify playlists
|
|
135
|
+
test_files: []
|