chronicle-spotify 0.1.0 → 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 +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +2 -44
- data/Gemfile +2 -4
- data/README.md +10 -17
- data/Rakefile +1 -3
- data/bin/console +3 -11
- data/chronicle-spotify.gemspec +29 -28
- data/lib/chronicle/spotify/authorizer.rb +13 -6
- data/lib/chronicle/spotify/builders.rb +53 -75
- data/lib/chronicle/spotify/like_transformer.rb +8 -23
- data/lib/chronicle/spotify/liked_tracks_extractor.rb +4 -3
- data/lib/chronicle/spotify/listen_transformer.rb +8 -17
- data/lib/chronicle/spotify/listens_extractor.rb +5 -4
- data/lib/chronicle/spotify/proxy.rb +11 -13
- data/lib/chronicle/spotify/saved_album_extractor.rb +18 -0
- data/lib/chronicle/spotify/saved_album_transformer.rb +20 -0
- data/lib/chronicle/spotify/spotify_extractor.rb +1 -1
- data/lib/chronicle/spotify/version.rb +1 -3
- data/lib/chronicle/spotify.rb +11 -12
- metadata +36 -21
- data/Gemfile.lock +0 -190
- data/lib/chronicle/spotify/saved_albums_extractor.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 05a2aa0279eb07b82642feb0b1a13b0e1d7e696325bc80316bef77ed5a44e6fd
|
4
|
+
data.tar.gz: 5dee1d598b949ca4c5d916005123b859d7bf00eb4d046fb40b0d2bfe74336357
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c2833de89d8ed0698ff984e724c1f47be7f714553811b6bd14247d2fb6e3962cce7745ba478ce4d6ddf8d992144bcd9db23dc4ab52d0c49d13376da80f59b00
|
7
|
+
data.tar.gz: faa9603a26db4d4a2ea0cdaed98d164cb3fec7fe6e13139b045fb7cfd492771b7b0956c6934d0327dd69a8140e93ccb10640f1580ce2f4caa1dbd4cfc0f8e8ba
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,44 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
TargetRubyVersion: 2.7
|
4
|
-
|
5
|
-
Style/FrozenStringLiteralComment:
|
6
|
-
SafeAutoCorrect: true
|
7
|
-
|
8
|
-
Style/StringLiterals:
|
9
|
-
Enabled: false
|
10
|
-
|
11
|
-
Layout/MultilineAssignmentLayout:
|
12
|
-
Enabled: false
|
13
|
-
|
14
|
-
Layout/MultilineMethodCallIndentation:
|
15
|
-
EnforcedStyle: indented
|
16
|
-
|
17
|
-
Layout/RedundantLineBreak:
|
18
|
-
Enabled: false
|
19
|
-
|
20
|
-
Style/MethodCallWithArgsParentheses:
|
21
|
-
Enabled: false
|
22
|
-
|
23
|
-
Style/MethodCalledOnDoEndBlock:
|
24
|
-
Exclude:
|
25
|
-
- 'spec/**/*'
|
26
|
-
|
27
|
-
Style/OpenStructUse:
|
28
|
-
Enabled: false
|
29
|
-
|
30
|
-
Style/Copyright:
|
31
|
-
Enabled: false
|
32
|
-
|
33
|
-
Style/MissingElse:
|
34
|
-
Enabled: false
|
35
|
-
|
36
|
-
Style/SymbolArray:
|
37
|
-
EnforcedStyle: brackets
|
38
|
-
|
39
|
-
Style/WordArray:
|
40
|
-
EnforcedStyle: brackets
|
41
|
-
|
42
|
-
Lint/ConstantResolution:
|
43
|
-
Enabled: false
|
44
|
-
|
1
|
+
inherit_gem:
|
2
|
+
chronicle-core: .rubocop-plugin.yml
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -36,15 +36,18 @@ This will open a browser window to authorize on spotify.com. When the flow is co
|
|
36
36
|
### 4. Use the the plugin
|
37
37
|
```sh
|
38
38
|
# Extract recent limits
|
39
|
-
$ chronicle-etl --extractor spotify:
|
39
|
+
$ chronicle-etl --extractor spotify:listen --limit 10
|
40
40
|
|
41
41
|
# Extract liked tracks from the last week
|
42
|
-
$ chronicle-etl --extractor spotify:
|
42
|
+
$ chronicle-etl --extractor spotify:like --since 1w
|
43
43
|
# Transform as Chronicle Schema
|
44
|
-
$ chronicle-etl --extractor spotify:
|
44
|
+
$ chronicle-etl --extractor spotify:like --since 1w --schema chronicle
|
45
45
|
|
46
46
|
# Extract saved albums
|
47
|
-
$ chronicle-etl --extractor spotify:saved-
|
47
|
+
$ chronicle-etl --extractor spotify:saved-album --limit 10
|
48
|
+
|
49
|
+
# Display a table of album names you've liked in last week
|
50
|
+
$ chronicle-etl --extractor spotify:saved-album --since 1w --schema chronicle --loader table --fields end_time object.name
|
48
51
|
```
|
49
52
|
|
50
53
|
## Available Connectors
|
@@ -52,26 +55,16 @@ $ chronicle-etl --extractor spotify:saved-albums --limit 10
|
|
52
55
|
|
53
56
|
All the extractors expect `uid`, `access_token` and `refresh_token` to be available in your Chronicle secrets. After doing the authorization flow, you can verify that they exist using: `$ chronicle-etl secrets:list spotify`
|
54
57
|
|
55
|
-
#### `
|
58
|
+
#### `like`
|
56
59
|
|
57
60
|
Extractor for your Spotify liked tracks
|
58
61
|
|
59
|
-
#### `saved-
|
62
|
+
#### `saved-album`
|
60
63
|
|
61
64
|
Extractor for your Spotify saved albums
|
62
|
-
#### `listens`
|
63
|
-
|
64
|
-
Extractor for your recent listens. Due to API limitations, only your 50 most recent
|
65
|
-
|
66
|
-
### Transformers
|
67
|
-
|
68
|
-
#### `like`
|
69
|
-
|
70
|
-
Transform a like (either from `saved-albums` or `liked-tracks`) into Chronicle Schema
|
71
|
-
|
72
65
|
#### `listen`
|
73
66
|
|
74
|
-
|
67
|
+
Extractor for your recent listens. Due to API limitations, only your 50 most recent
|
75
68
|
|
76
69
|
## Roadmap
|
77
70
|
- extractor for playlist activity ([#3](https://github.com/chronicle-app/chronicle-spotify/issues/3))
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
2
|
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'chronicle/spotify'
|
6
5
|
|
7
|
-
|
8
|
-
# with your gem easier. You can also use a different console, if you like.
|
9
|
-
|
10
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
-
# require "pry"
|
6
|
+
require 'pry'
|
12
7
|
Pry.start
|
13
|
-
|
14
|
-
# require "irb"
|
15
|
-
# IRB.start(__FILE__)
|
data/chronicle-spotify.gemspec
CHANGED
@@ -1,47 +1,48 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
lib = File.expand_path("../lib", __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
4
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require
|
3
|
+
require 'chronicle/spotify/version'
|
6
4
|
|
7
5
|
Gem::Specification.new do |spec|
|
8
|
-
spec.name =
|
6
|
+
spec.name = 'chronicle-spotify'
|
9
7
|
spec.version = Chronicle::Spotify::VERSION
|
10
|
-
spec.authors = [
|
11
|
-
spec.email = [
|
8
|
+
spec.authors = ['Andrew Louis']
|
9
|
+
spec.email = ['andrew@hyfen.net']
|
12
10
|
|
13
|
-
spec.summary =
|
14
|
-
spec.description =
|
15
|
-
spec.homepage =
|
11
|
+
spec.summary = 'Spotify importer for Chronicle'
|
12
|
+
spec.description = 'Spotify connectors'
|
13
|
+
spec.homepage = 'https://github.com/chronicle-app/chronicle-spotify'
|
16
14
|
|
17
15
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
16
|
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
17
|
if spec.respond_to?(:metadata)
|
20
|
-
spec.metadata['allowed_push_host'] =
|
18
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
21
19
|
|
22
|
-
spec.metadata[
|
23
|
-
spec.metadata[
|
24
|
-
spec.metadata[
|
20
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
21
|
+
spec.metadata['source_code_uri'] = 'https://github.com/chronicle-app/chronicle-spotify'
|
22
|
+
spec.metadata['changelog_uri'] = 'https://github.com/chronicle-app/chronicle-spotify'
|
25
23
|
else
|
26
|
-
raise
|
27
|
-
|
24
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
25
|
+
'public gem pushes.'
|
28
26
|
end
|
29
27
|
|
30
28
|
# Specify which files should be added to the gem when it is released.
|
31
29
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
32
|
-
spec.files
|
30
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
33
31
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
34
32
|
end
|
35
|
-
spec.bindir =
|
33
|
+
spec.bindir = 'exe'
|
36
34
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
37
|
-
spec.require_paths = [
|
38
|
-
|
39
|
-
spec.
|
40
|
-
|
41
|
-
spec.add_dependency
|
42
|
-
|
43
|
-
spec.
|
44
|
-
spec.
|
45
|
-
|
46
|
-
spec.add_development_dependency
|
35
|
+
spec.require_paths = ['lib']
|
36
|
+
spec.required_ruby_version = '>= 3.1'
|
37
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
38
|
+
|
39
|
+
spec.add_dependency 'chronicle-core', '~> 0.3'
|
40
|
+
spec.add_dependency 'chronicle-etl', '~> 0.6'
|
41
|
+
spec.add_dependency 'omniauth-spotify'
|
42
|
+
spec.add_dependency 'rspotify', '~> 2.11'
|
43
|
+
|
44
|
+
spec.add_development_dependency 'bundler', '~> 2.3'
|
45
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.10'
|
46
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
47
|
+
spec.add_development_dependency 'rubocop', '~> 1.63'
|
47
48
|
end
|
@@ -5,13 +5,20 @@ module Chronicle
|
|
5
5
|
class Authorizer < Chronicle::ETL::OauthAuthorizer
|
6
6
|
provider :spotify
|
7
7
|
omniauth_strategy :spotify
|
8
|
-
scope
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
scope %w[
|
9
|
+
user-read-recently-played
|
10
|
+
playlist-read-private
|
11
|
+
playlist-read-collaborative
|
12
|
+
user-read-private
|
13
|
+
user-read-email
|
14
|
+
user-library-read
|
15
|
+
].join(' ')
|
16
|
+
pluck_secrets({
|
17
|
+
access_token: %i[credentials token],
|
18
|
+
refresh_token: %i[credentials refresh_token],
|
12
19
|
uid: [:uid],
|
13
|
-
name: [
|
14
|
-
email: [
|
20
|
+
name: %i[info name],
|
21
|
+
email: %i[info email]
|
15
22
|
})
|
16
23
|
end
|
17
24
|
end
|
@@ -1,95 +1,73 @@
|
|
1
|
+
require 'chronicle/models'
|
2
|
+
|
1
3
|
module Chronicle
|
2
4
|
module Spotify
|
3
5
|
module Builders
|
4
|
-
def build_listen(
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
record.involved = build_track(track)
|
13
|
-
record
|
6
|
+
def build_listen(record:, agent:)
|
7
|
+
Chronicle::Models::ListenAction.new do |r|
|
8
|
+
r.end_time = record[:played_at]
|
9
|
+
r.object = build_track(record[:track])
|
10
|
+
r.agent = build_agent(agent)
|
11
|
+
r.source = 'spotify'
|
12
|
+
r.dedupe_on << %i[type source end_time]
|
13
|
+
end
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
provider: 'spotify',
|
20
|
-
end_at: timestamp
|
21
|
-
})
|
22
|
-
record.dedupe_on << [:provider, :verb, :end_at]
|
23
|
-
record.actor = build_actor(actor)
|
16
|
+
def build_like(record:, agent:)
|
17
|
+
Chronicle::Models::LikeAction.new do |r|
|
18
|
+
r.end_time = record[:added_at]
|
24
19
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
20
|
+
r.object = if record[:album]
|
21
|
+
build_album(record[:album])
|
22
|
+
elsif record[:track]
|
23
|
+
build_track(record[:track])
|
24
|
+
end
|
30
25
|
|
31
|
-
|
26
|
+
r.agent = build_agent(agent)
|
27
|
+
r.source = 'spotify'
|
28
|
+
r.dedupe_on << %i[type source end_time]
|
29
|
+
end
|
32
30
|
end
|
33
31
|
|
34
32
|
def build_track(track)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
record.dedupe_on << [:provider, :provider_id, :represents]
|
45
|
-
record
|
33
|
+
Chronicle::Models::MusicRecording.new do |r|
|
34
|
+
r.name = track[:name]
|
35
|
+
r.in_album = [build_album(track[:album])]
|
36
|
+
r.by_artist = track[:artists].map { |artist| build_artist(artist) }
|
37
|
+
r.duration = "PT#{track[:duration_ms] / 1000.0}S"
|
38
|
+
r.source = 'spotify'
|
39
|
+
r.source_id = track[:id]
|
40
|
+
r.dedupe_on = [[:url], %i[source_id source type]]
|
41
|
+
end
|
46
42
|
end
|
47
43
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
})
|
56
|
-
record.dedupe_on << [:provider_url]
|
57
|
-
record.dedupe_on << [:provider, :provider_id, :represents]
|
58
|
-
record
|
44
|
+
def build_artist(artist)
|
45
|
+
Chronicle::Models::MusicGroup.new do |r|
|
46
|
+
r.name = artist[:name]
|
47
|
+
r.url = artist[:external_urls][:spotify]
|
48
|
+
r.source = 'spotify'
|
49
|
+
r.source_id = artist[:id]
|
50
|
+
end
|
59
51
|
end
|
60
52
|
|
61
53
|
def build_album(album)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
record.attachments = build_image(album[:images].first[:url])
|
70
|
-
record.dedupe_on << [:provider_url]
|
71
|
-
record.dedupe_on << [:provider, :provider_id, :represents]
|
72
|
-
record
|
73
|
-
end
|
74
|
-
|
75
|
-
def build_image(image_url)
|
76
|
-
::Chronicle::ETL::Models::Attachment.new({
|
77
|
-
url_original: image_url
|
78
|
-
})
|
54
|
+
Chronicle::Models::MusicAlbum.new do |r|
|
55
|
+
r.name = album[:name]
|
56
|
+
r.source = 'spotify'
|
57
|
+
r.source_id = album[:id]
|
58
|
+
r.image = album[:images].first[:url]
|
59
|
+
r.url = album[:external_urls][:spotify]
|
60
|
+
end
|
79
61
|
end
|
80
62
|
|
81
|
-
def
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
})
|
90
|
-
record.dedupe_on << [:provider_url]
|
91
|
-
record.dedupe_on << [:provider, :provider_id, :represents]
|
92
|
-
record
|
63
|
+
def build_agent(agent)
|
64
|
+
Chronicle::Models::Person.new do |r|
|
65
|
+
r.name = agent[:display_name]
|
66
|
+
r.url = agent[:external_urls][:spotify]
|
67
|
+
r.source = 'spotify'
|
68
|
+
r.slug = agent[:id]
|
69
|
+
r.source_id = agent[:id]
|
70
|
+
end
|
93
71
|
end
|
94
72
|
end
|
95
73
|
end
|
@@ -4,31 +4,16 @@ module Chronicle
|
|
4
4
|
include Chronicle::Spotify::Builders
|
5
5
|
|
6
6
|
register_connector do |r|
|
7
|
-
r.
|
8
|
-
r.
|
9
|
-
r.
|
7
|
+
r.source = :spotify
|
8
|
+
r.type = :like
|
9
|
+
r.strategy = :api
|
10
|
+
r.description = 'a like'
|
11
|
+
r.from_schema = :extraction
|
12
|
+
r.to_schema = :chronicle
|
10
13
|
end
|
11
14
|
|
12
|
-
def transform
|
13
|
-
|
14
|
-
timestamp: timestamp,
|
15
|
-
object: object,
|
16
|
-
actor: @extraction.meta[:actor]
|
17
|
-
)
|
18
|
-
end
|
19
|
-
|
20
|
-
def id
|
21
|
-
object[:id]
|
22
|
-
end
|
23
|
-
|
24
|
-
def timestamp
|
25
|
-
Time.parse(@extraction.data[:added_at])
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def object
|
31
|
-
@object = @extraction.data[:track] || @extraction.data[:album]
|
15
|
+
def transform(record)
|
16
|
+
build_like(record: record.data, agent: record.extraction.meta[:agent])
|
32
17
|
end
|
33
18
|
end
|
34
19
|
end
|
@@ -2,14 +2,15 @@ module Chronicle
|
|
2
2
|
module Spotify
|
3
3
|
class SavedTracksExtractor < Chronicle::Spotify::SpotifyExtractor
|
4
4
|
register_connector do |r|
|
5
|
-
r.
|
5
|
+
r.source = :spotify
|
6
|
+
r.type = :like
|
7
|
+
r.strategy = :api
|
6
8
|
r.description = 'liked tracks'
|
7
|
-
r.identifier = 'liked-tracks'
|
8
9
|
end
|
9
10
|
|
10
11
|
def extract
|
11
12
|
@proxy.saved_tracks(after: @config.since, limit: @config.limit) do |item|
|
12
|
-
yield
|
13
|
+
yield build_extraction(data: item, meta: { agent: @agent })
|
13
14
|
end
|
14
15
|
end
|
15
16
|
end
|
@@ -4,25 +4,16 @@ module Chronicle
|
|
4
4
|
include Chronicle::Spotify::Builders
|
5
5
|
|
6
6
|
register_connector do |r|
|
7
|
-
r.
|
8
|
-
r.
|
9
|
-
r.
|
7
|
+
r.source = :spotify
|
8
|
+
r.type = :listen
|
9
|
+
r.strategy = :api
|
10
|
+
r.description = 'a listen'
|
11
|
+
r.from_schema = :extraction
|
12
|
+
r.to_schema = :chronicle
|
10
13
|
end
|
11
14
|
|
12
|
-
def transform
|
13
|
-
build_listen(
|
14
|
-
timestamp: timestamp,
|
15
|
-
track: @extraction.data[:track],
|
16
|
-
actor: @extraction.meta[:actor]
|
17
|
-
)
|
18
|
-
end
|
19
|
-
|
20
|
-
def id
|
21
|
-
@extraction.data[:track][:id]
|
22
|
-
end
|
23
|
-
|
24
|
-
def timestamp
|
25
|
-
Time.parse(@extraction.data[:played_at])
|
15
|
+
def transform(record)
|
16
|
+
build_listen(record: record.data, agent: record.extraction.meta[:agent])
|
26
17
|
end
|
27
18
|
end
|
28
19
|
end
|
@@ -2,14 +2,15 @@ module Chronicle
|
|
2
2
|
module Spotify
|
3
3
|
class ListenedExtractor < Chronicle::Spotify::SpotifyExtractor
|
4
4
|
register_connector do |r|
|
5
|
-
r.
|
6
|
-
r.
|
7
|
-
r.
|
5
|
+
r.source = :spotify
|
6
|
+
r.type = :listen
|
7
|
+
r.strategy = :api
|
8
|
+
r.description = 'listens'
|
8
9
|
end
|
9
10
|
|
10
11
|
def extract
|
11
12
|
@proxy.recently_played(after: @config.since, limit: @config.limit, before: @config.until) do |item|
|
12
|
-
yield
|
13
|
+
yield build_extraction(data: item, meta: { agent: @agent })
|
13
14
|
end
|
14
15
|
end
|
15
16
|
end
|
@@ -22,28 +22,28 @@ module Chronicle
|
|
22
22
|
JSON.parse(RSpotify::User.find(@authenticated_user.id), symbolize_names: true)
|
23
23
|
end
|
24
24
|
|
25
|
-
def saved_albums(after: nil,
|
26
|
-
retrieve_all(method: :saved_albums, after
|
25
|
+
def saved_albums(limit:, after: nil, &block)
|
26
|
+
retrieve_all(method: :saved_albums, after:, limit:, &block)
|
27
27
|
end
|
28
28
|
|
29
|
-
def saved_tracks(after: nil,
|
30
|
-
retrieve_all(method: :saved_tracks, after
|
29
|
+
def saved_tracks(limit:, after: nil, &block)
|
30
|
+
retrieve_all(method: :saved_tracks, after:, limit:, &block)
|
31
31
|
end
|
32
32
|
|
33
33
|
def recently_played(before: nil, after: nil, limit: nil, &block)
|
34
34
|
before = before&.to_i&.*(1000)
|
35
|
-
retrieve_all(method: :recently_played, before
|
35
|
+
retrieve_all(method: :recently_played, before:, after:, limit:, &block)
|
36
36
|
end
|
37
37
|
|
38
38
|
private
|
39
39
|
|
40
|
-
def retrieve_all(method:, before: nil, after: nil, limit: nil)
|
40
|
+
def retrieve_all(method:, before: nil, after: nil, limit: nil, &block)
|
41
41
|
has_more = true
|
42
42
|
count = 0
|
43
|
-
offset = 0 if method != :recently_played
|
43
|
+
offset = 0 if method != :recently_played # FIXME: hacky
|
44
44
|
|
45
45
|
while has_more
|
46
|
-
response = retrieve_page(method
|
46
|
+
response = retrieve_page(method:, before:, offset:)
|
47
47
|
|
48
48
|
records = response[:items]
|
49
49
|
records = records.first(limit - count) if limit
|
@@ -51,9 +51,7 @@ module Chronicle
|
|
51
51
|
|
52
52
|
break unless records.any?
|
53
53
|
|
54
|
-
records.each
|
55
|
-
yield track
|
56
|
-
end
|
54
|
+
records.each(&block)
|
57
55
|
|
58
56
|
count += records.count
|
59
57
|
has_more = response[:next]
|
@@ -65,9 +63,9 @@ module Chronicle
|
|
65
63
|
|
66
64
|
def retrieve_page(method:, before: nil, offset: nil)
|
67
65
|
options = {
|
68
|
-
offset
|
66
|
+
offset:,
|
69
67
|
limit: PER_PAGE,
|
70
|
-
before:
|
68
|
+
before:
|
71
69
|
}.compact
|
72
70
|
|
73
71
|
JSON.parse(@authenticated_user.send(method, **options), symbolize_names: true)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Chronicle
|
2
|
+
module Spotify
|
3
|
+
class SavedAlbumExtractor < Chronicle::Spotify::SpotifyExtractor
|
4
|
+
register_connector do |r|
|
5
|
+
r.source = :spotify
|
6
|
+
r.type = :saved_album
|
7
|
+
r.strategy = :api
|
8
|
+
r.description = 'saved_albums'
|
9
|
+
end
|
10
|
+
|
11
|
+
def extract
|
12
|
+
@proxy.saved_albums(after: @config.since, limit: @config.limit) do |item|
|
13
|
+
yield build_extraction(data: item, meta: { agent: @agent })
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Chronicle
|
2
|
+
module Spotify
|
3
|
+
class SavedAlbumTransformer < Chronicle::ETL::Transformer
|
4
|
+
include Chronicle::Spotify::Builders
|
5
|
+
|
6
|
+
register_connector do |r|
|
7
|
+
r.source = :spotify
|
8
|
+
r.type = :saved_album
|
9
|
+
r.strategy = :api
|
10
|
+
r.description = 'a saved album'
|
11
|
+
r.from_schema = :extraction
|
12
|
+
r.to_schema = :chronicle
|
13
|
+
end
|
14
|
+
|
15
|
+
def transform(record)
|
16
|
+
build_like(record: record.data, agent: record.extraction.meta[:agent])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/chronicle/spotify.rb
CHANGED
@@ -1,20 +1,19 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
require 'chronicle/etl'
|
4
2
|
|
5
|
-
require_relative
|
3
|
+
require_relative 'spotify/version'
|
6
4
|
|
7
|
-
require_relative
|
8
|
-
require_relative
|
9
|
-
require_relative
|
5
|
+
require_relative 'spotify/authorizer'
|
6
|
+
require_relative 'spotify/proxy'
|
7
|
+
require_relative 'spotify/builders'
|
10
8
|
|
11
|
-
require_relative
|
12
|
-
require_relative
|
13
|
-
require_relative
|
14
|
-
require_relative
|
9
|
+
require_relative 'spotify/spotify_extractor'
|
10
|
+
require_relative 'spotify/listens_extractor'
|
11
|
+
require_relative 'spotify/liked_tracks_extractor'
|
12
|
+
require_relative 'spotify/saved_album_extractor'
|
13
|
+
require_relative 'spotify/saved_album_transformer'
|
15
14
|
|
16
|
-
require_relative
|
17
|
-
require_relative
|
15
|
+
require_relative 'spotify/like_transformer'
|
16
|
+
require_relative 'spotify/listen_transformer'
|
18
17
|
module Chronicle
|
19
18
|
module Spotify
|
20
19
|
end
|
metadata
CHANGED
@@ -1,29 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chronicle-spotify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Louis
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-04-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: chronicle-core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.3'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: chronicle-etl
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - "~>"
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
33
|
+
version: '0.6'
|
20
34
|
type: :runtime
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
40
|
+
version: '0.6'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: omniauth-spotify
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,61 +67,61 @@ dependencies:
|
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '2.11'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
70
|
+
name: bundler
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
|
-
- - "
|
73
|
+
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
75
|
+
version: '2.3'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
|
-
- - "
|
80
|
+
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
82
|
+
version: '2.3'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
84
|
+
name: pry-byebug
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
87
|
- - "~>"
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
89
|
+
version: '3.10'
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
94
|
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
96
|
+
version: '3.10'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: rake
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
101
|
- - "~>"
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version: 13.0
|
103
|
+
version: '13.0'
|
90
104
|
type: :development
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
108
|
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
|
-
version: 13.0
|
110
|
+
version: '13.0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
112
|
+
name: rubocop
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
115
|
- - "~>"
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
117
|
+
version: '1.63'
|
104
118
|
type: :development
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
124
|
+
version: '1.63'
|
111
125
|
description: Spotify connectors
|
112
126
|
email:
|
113
127
|
- andrew@hyfen.net
|
@@ -119,7 +133,6 @@ files:
|
|
119
133
|
- ".rubocop.yml"
|
120
134
|
- CODE_OF_CONDUCT.md
|
121
135
|
- Gemfile
|
122
|
-
- Gemfile.lock
|
123
136
|
- README.md
|
124
137
|
- Rakefile
|
125
138
|
- bin/console
|
@@ -133,7 +146,8 @@ files:
|
|
133
146
|
- lib/chronicle/spotify/listen_transformer.rb
|
134
147
|
- lib/chronicle/spotify/listens_extractor.rb
|
135
148
|
- lib/chronicle/spotify/proxy.rb
|
136
|
-
- lib/chronicle/spotify/
|
149
|
+
- lib/chronicle/spotify/saved_album_extractor.rb
|
150
|
+
- lib/chronicle/spotify/saved_album_transformer.rb
|
137
151
|
- lib/chronicle/spotify/spotify_extractor.rb
|
138
152
|
- lib/chronicle/spotify/version.rb
|
139
153
|
homepage: https://github.com/chronicle-app/chronicle-spotify
|
@@ -143,6 +157,7 @@ metadata:
|
|
143
157
|
homepage_uri: https://github.com/chronicle-app/chronicle-spotify
|
144
158
|
source_code_uri: https://github.com/chronicle-app/chronicle-spotify
|
145
159
|
changelog_uri: https://github.com/chronicle-app/chronicle-spotify
|
160
|
+
rubygems_mfa_required: 'true'
|
146
161
|
post_install_message:
|
147
162
|
rdoc_options: []
|
148
163
|
require_paths:
|
@@ -151,14 +166,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
151
166
|
requirements:
|
152
167
|
- - ">="
|
153
168
|
- !ruby/object:Gem::Version
|
154
|
-
version: '
|
169
|
+
version: '3.1'
|
155
170
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
171
|
requirements:
|
157
172
|
- - ">="
|
158
173
|
- !ruby/object:Gem::Version
|
159
174
|
version: '0'
|
160
175
|
requirements: []
|
161
|
-
rubygems_version: 3.
|
176
|
+
rubygems_version: 3.4.10
|
162
177
|
signing_key:
|
163
178
|
specification_version: 4
|
164
179
|
summary: Spotify importer for Chronicle
|
data/Gemfile.lock
DELETED
@@ -1,190 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
chronicle-spotify (0.1.0)
|
5
|
-
chronicle-etl (~> 0.5)
|
6
|
-
omniauth-spotify
|
7
|
-
rspotify (~> 2.11)
|
8
|
-
|
9
|
-
GEM
|
10
|
-
remote: https://rubygems.org/
|
11
|
-
specs:
|
12
|
-
activesupport (7.0.2.3)
|
13
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
14
|
-
i18n (>= 1.6, < 2)
|
15
|
-
minitest (>= 5.1)
|
16
|
-
tzinfo (~> 2.0)
|
17
|
-
addressable (2.8.0)
|
18
|
-
public_suffix (>= 2.0.2, < 5.0)
|
19
|
-
ast (2.4.2)
|
20
|
-
byebug (11.1.3)
|
21
|
-
chronic_duration (0.10.6)
|
22
|
-
numerizer (~> 0.1.1)
|
23
|
-
chronicle-etl (0.5.3)
|
24
|
-
activesupport (~> 7.0)
|
25
|
-
chronic_duration (~> 0.10.6)
|
26
|
-
colorize (~> 0.8.1)
|
27
|
-
launchy
|
28
|
-
marcel (~> 1.0.2)
|
29
|
-
mini_exiftool (~> 2.10)
|
30
|
-
nokogiri (~> 1.13)
|
31
|
-
omniauth (~> 2)
|
32
|
-
sequel (~> 5.35)
|
33
|
-
sinatra (~> 2)
|
34
|
-
sqlite3 (~> 1.4)
|
35
|
-
thor (~> 1.2)
|
36
|
-
thor-hollaback (~> 0.2)
|
37
|
-
tty-progressbar (~> 0.17)
|
38
|
-
tty-prompt (~> 0.23)
|
39
|
-
tty-spinner
|
40
|
-
tty-table (~> 0.11)
|
41
|
-
xdg (>= 4.0)
|
42
|
-
coderay (1.1.3)
|
43
|
-
colorize (0.8.1)
|
44
|
-
concurrent-ruby (1.1.10)
|
45
|
-
domain_name (0.5.20190701)
|
46
|
-
unf (>= 0.0.5, < 1.0.0)
|
47
|
-
faraday (2.2.0)
|
48
|
-
faraday-net_http (~> 2.0)
|
49
|
-
ruby2_keywords (>= 0.0.4)
|
50
|
-
faraday-net_http (2.0.2)
|
51
|
-
hashie (5.0.0)
|
52
|
-
hollaback (0.1.1)
|
53
|
-
http-cookie (1.0.4)
|
54
|
-
domain_name (~> 0.5)
|
55
|
-
i18n (1.10.0)
|
56
|
-
concurrent-ruby (~> 1.0)
|
57
|
-
jwt (2.3.0)
|
58
|
-
launchy (2.5.0)
|
59
|
-
addressable (~> 2.7)
|
60
|
-
marcel (1.0.2)
|
61
|
-
method_source (1.0.0)
|
62
|
-
mime-types (3.4.1)
|
63
|
-
mime-types-data (~> 3.2015)
|
64
|
-
mime-types-data (3.2022.0105)
|
65
|
-
mini_exiftool (2.10.2)
|
66
|
-
minitest (5.15.0)
|
67
|
-
multi_json (1.15.0)
|
68
|
-
multi_xml (0.6.0)
|
69
|
-
mustermann (1.1.1)
|
70
|
-
ruby2_keywords (~> 0.0.1)
|
71
|
-
netrc (0.11.0)
|
72
|
-
nokogiri (1.13.4-arm64-darwin)
|
73
|
-
racc (~> 1.4)
|
74
|
-
numerizer (0.1.1)
|
75
|
-
oauth2 (1.4.9)
|
76
|
-
faraday (>= 0.17.3, < 3.0)
|
77
|
-
jwt (>= 1.0, < 3.0)
|
78
|
-
multi_json (~> 1.3)
|
79
|
-
multi_xml (~> 0.5)
|
80
|
-
rack (>= 1.2, < 3)
|
81
|
-
omniauth (2.1.0)
|
82
|
-
hashie (>= 3.4.6)
|
83
|
-
rack (>= 2.2.3)
|
84
|
-
rack-protection
|
85
|
-
omniauth-oauth2 (1.7.2)
|
86
|
-
oauth2 (~> 1.4)
|
87
|
-
omniauth (>= 1.9, < 3)
|
88
|
-
omniauth-spotify (0.0.13)
|
89
|
-
omniauth-oauth2 (~> 1.1)
|
90
|
-
parallel (1.22.1)
|
91
|
-
parser (3.1.2.0)
|
92
|
-
ast (~> 2.4.1)
|
93
|
-
pastel (0.8.0)
|
94
|
-
tty-color (~> 0.5)
|
95
|
-
pry (0.13.1)
|
96
|
-
coderay (~> 1.1)
|
97
|
-
method_source (~> 1.0)
|
98
|
-
pry-byebug (3.9.0)
|
99
|
-
byebug (~> 11.0)
|
100
|
-
pry (~> 0.13.0)
|
101
|
-
public_suffix (4.0.7)
|
102
|
-
racc (1.6.0)
|
103
|
-
rack (2.2.3)
|
104
|
-
rack-protection (2.2.0)
|
105
|
-
rack
|
106
|
-
rainbow (3.1.1)
|
107
|
-
rake (13.0.6)
|
108
|
-
regexp_parser (2.3.0)
|
109
|
-
rest-client (2.0.2)
|
110
|
-
http-cookie (>= 1.0.2, < 2.0)
|
111
|
-
mime-types (>= 1.16, < 4.0)
|
112
|
-
netrc (~> 0.8)
|
113
|
-
rexml (3.2.5)
|
114
|
-
rspotify (2.11.1)
|
115
|
-
addressable (~> 2.8.0)
|
116
|
-
omniauth-oauth2 (>= 1.6)
|
117
|
-
rest-client (~> 2.0.2)
|
118
|
-
rubocop (1.27.0)
|
119
|
-
parallel (~> 1.10)
|
120
|
-
parser (>= 3.1.0.0)
|
121
|
-
rainbow (>= 2.2.2, < 4.0)
|
122
|
-
regexp_parser (>= 1.8, < 3.0)
|
123
|
-
rexml
|
124
|
-
rubocop-ast (>= 1.16.0, < 2.0)
|
125
|
-
ruby-progressbar (~> 1.7)
|
126
|
-
unicode-display_width (>= 1.4.0, < 3.0)
|
127
|
-
rubocop-ast (1.17.0)
|
128
|
-
parser (>= 3.1.1.0)
|
129
|
-
ruby-progressbar (1.11.0)
|
130
|
-
ruby2_keywords (0.0.5)
|
131
|
-
sequel (5.55.0)
|
132
|
-
sinatra (2.2.0)
|
133
|
-
mustermann (~> 1.0)
|
134
|
-
rack (~> 2.2)
|
135
|
-
rack-protection (= 2.2.0)
|
136
|
-
tilt (~> 2.0)
|
137
|
-
sqlite3 (1.4.2)
|
138
|
-
strings (0.2.1)
|
139
|
-
strings-ansi (~> 0.2)
|
140
|
-
unicode-display_width (>= 1.5, < 3.0)
|
141
|
-
unicode_utils (~> 1.4)
|
142
|
-
strings-ansi (0.2.0)
|
143
|
-
thor (1.2.1)
|
144
|
-
thor-hollaback (0.2.1)
|
145
|
-
hollaback (~> 0.1)
|
146
|
-
thor (>= 0.19.1)
|
147
|
-
tilt (2.0.10)
|
148
|
-
tty-color (0.6.0)
|
149
|
-
tty-cursor (0.7.1)
|
150
|
-
tty-progressbar (0.18.2)
|
151
|
-
strings-ansi (~> 0.2)
|
152
|
-
tty-cursor (~> 0.7)
|
153
|
-
tty-screen (~> 0.8)
|
154
|
-
unicode-display_width (>= 1.6, < 3.0)
|
155
|
-
tty-prompt (0.23.1)
|
156
|
-
pastel (~> 0.8)
|
157
|
-
tty-reader (~> 0.8)
|
158
|
-
tty-reader (0.9.0)
|
159
|
-
tty-cursor (~> 0.7)
|
160
|
-
tty-screen (~> 0.8)
|
161
|
-
wisper (~> 2.0)
|
162
|
-
tty-screen (0.8.1)
|
163
|
-
tty-spinner (0.9.3)
|
164
|
-
tty-cursor (~> 0.7)
|
165
|
-
tty-table (0.12.0)
|
166
|
-
pastel (~> 0.8)
|
167
|
-
strings (~> 0.2.0)
|
168
|
-
tty-screen (~> 0.8)
|
169
|
-
tzinfo (2.0.4)
|
170
|
-
concurrent-ruby (~> 1.0)
|
171
|
-
unf (0.1.4)
|
172
|
-
unf_ext
|
173
|
-
unf_ext (0.0.8.1)
|
174
|
-
unicode-display_width (2.1.0)
|
175
|
-
unicode_utils (1.4.0)
|
176
|
-
wisper (2.0.1)
|
177
|
-
xdg (6.3.2)
|
178
|
-
|
179
|
-
PLATFORMS
|
180
|
-
arm64-darwin-20
|
181
|
-
|
182
|
-
DEPENDENCIES
|
183
|
-
bundler (~> 2.3)
|
184
|
-
chronicle-spotify!
|
185
|
-
pry-byebug (~> 3.9)
|
186
|
-
rake (~> 13.0)
|
187
|
-
rubocop
|
188
|
-
|
189
|
-
BUNDLED WITH
|
190
|
-
2.3.8
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Chronicle
|
2
|
-
module Spotify
|
3
|
-
class SavedAlbumsExtractor < Chronicle::Spotify::SpotifyExtractor
|
4
|
-
register_connector do |r|
|
5
|
-
r.provider = 'spotify'
|
6
|
-
r.description = 'saved albums'
|
7
|
-
r.identifier = 'saved-albums'
|
8
|
-
end
|
9
|
-
|
10
|
-
def extract
|
11
|
-
@proxy.saved_albums(after: @config.since, limit: @config.limit) do |item|
|
12
|
-
yield Chronicle::ETL::Extraction.new(data: item, meta: { actor: @actor })
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|