spotted 0.32.0 → 0.33.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: 5a543070c1c0ef74514cbd6cde809153c4e1df8088291e9213c99f04e6fdb63e
4
- data.tar.gz: 9f1f5aa4c514419418dd55aef926b1a164c9edefd0249acb8ed77d126de32e6f
3
+ metadata.gz: 6b3243cac7dcf0803cbd0bff07b62b0394ac29dfd62c143bc7c1334157f7f9a9
4
+ data.tar.gz: b59e7369c5fe8604894b0995a0dbdd2ff26d7392d04ca8373ab6b4c1d73e6a4c
5
5
  SHA512:
6
- metadata.gz: b48973410e9ee24cda90e014db519cf64ede913c62ec32f62ee8f8e283749253c9eb1a054bb3a401c85d3a39fbe0118c859595448b6d64c7430bd04c81e9d5e7
7
- data.tar.gz: '089e889095747aa0d54fd06b9b2d5e973b81b5e157b7de9ff142f967b61359c71d57e5ca962778b1bd2fa9d224d7733fdfd4b2bb3100c9b5b2e925061c23757f'
6
+ metadata.gz: bf009a7418ffbe4bbcbc04eb49c2fc60916b0d0d45013e134673a146852b79d77aad84e79e9a500c8383e3d6ccae1cd5f0ff35a650d39573871854067c07ff18
7
+ data.tar.gz: 38c5fe4312c8b96e2125aa365064f18580fd144874c0c19b7114d8cea47b1cde3ed501046c963042f89bdd627a6c423646b9b2a4fc879857cfa380fc8160107f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.33.0 (2026-01-15)
4
+
5
+ Full Changelog: [v0.32.0...v0.33.0](https://github.com/cjavdev/spotted/compare/v0.32.0...v0.33.0)
6
+
7
+ ### Features
8
+
9
+ * **api:** manual updates ([4a0240e](https://github.com/cjavdev/spotted/commit/4a0240e10435cc9af7425885c72fa9e361d29613))
10
+ * **api:** manual updates ([c937e8f](https://github.com/cjavdev/spotted/commit/c937e8f8f5ac33270ae32920640f28706df98965))
11
+ * **api:** turn off oauth ([88abcd6](https://github.com/cjavdev/spotted/commit/88abcd62763cadfd6036026af2efd7ee0f2de678))
12
+
3
13
  ## 0.32.0 (2026-01-14)
4
14
 
5
15
  Full Changelog: [v0.31.1...v0.32.0](https://github.com/cjavdev/spotted/compare/v0.31.1...v0.32.0)
data/README.md CHANGED
@@ -17,7 +17,7 @@ Use the Spotted MCP Server to enable AI assistants to interact with this API, al
17
17
 
18
18
  Documentation for releases of this gem can be found [on RubyDoc](https://gemdocs.org/gems/spotted).
19
19
 
20
- The REST API documentation can be found on [spotted.stldocs.com](https://spotted.stldocs.com?docs).
20
+ The REST API documentation can be found on [spotted.cjav.dev](https://spotted.cjav.dev).
21
21
 
22
22
  ## Installation
23
23
 
@@ -26,7 +26,7 @@ To use this gem, install via Bundler by adding the following to your application
26
26
  <!-- x-release-please-start-version -->
27
27
 
28
28
  ```ruby
29
- gem "spotted", "~> 0.32.0"
29
+ gem "spotted", "~> 0.33.0"
30
30
  ```
31
31
 
32
32
  <!-- x-release-please-end -->
@@ -38,8 +38,7 @@ require "bundler/setup"
38
38
  require "spotted"
39
39
 
40
40
  spotted = Spotted::Client.new(
41
- client_id: ENV["SPOTIFY_CLIENT_ID"], # This is the default and can be omitted
42
- client_secret: ENV["SPOTIFY_CLIENT_SECRET"] # This is the default and can be omitted
41
+ access_token: ENV["SPOTIFY_ACCESS_TOKEN"] # This is the default and can be omitted
43
42
  )
44
43
 
45
44
  album = spotted.albums.retrieve("4aawyAB9vmqN3uQ7FjRGTy")
@@ -15,13 +15,7 @@ module Spotted
15
15
  # Default max retry delay in seconds.
16
16
  DEFAULT_MAX_RETRY_DELAY = 8.0
17
17
 
18
- # @return [String, nil]
19
- attr_reader :client_id
20
-
21
- # @return [String, nil]
22
- attr_reader :client_secret
23
-
24
- # @return [String, nil]
18
+ # @return [String]
25
19
  attr_reader :access_token
26
20
 
27
21
  # @return [Spotted::Resources::Albums]
@@ -76,63 +70,13 @@ module Spotted
76
70
  #
77
71
  # @return [Hash{String=>String}]
78
72
  private def auth_headers
79
- unless @access_token.nil?
80
- return {**bearer_auth}
81
- end
82
-
83
- if @client_id && @client_secret
84
- return {**oauth_2_0}
85
- end
86
-
87
- {}
88
- end
89
-
90
- # @api private
91
- #
92
- # @return [Hash{String=>String}]
93
- private def bearer_auth
94
73
  return {} if @access_token.nil?
95
74
 
96
75
  {"authorization" => "Bearer #{@access_token}"}
97
76
  end
98
77
 
99
- # @api private
100
- # @return [Spotted::Internal::OAuth2ClientCredentials]
101
- attr_reader :oauth_2_0_state
102
-
103
- # @api private
104
- #
105
- # @return [Hash{String=>String}]
106
- private def oauth_2_0
107
- return @oauth_2_0_state.auth_headers if @oauth_2_0_state
108
-
109
- return {} unless @client_id && @client_secret
110
-
111
- path = Spotted::Internal::Util.interpolate_path("https://accounts.spotify.com/api/token")
112
- token_url = Spotted::Internal::Util.join_parsed_uri(
113
- @base_url_components,
114
- {
115
- path: path,
116
- query: {grant_type: "client_credentials"}
117
- }
118
- )
119
-
120
- @oauth_2_0_state = Spotted::Internal::OAuth2ClientCredentials.new(
121
- token_url: token_url.to_s,
122
- client_id: @client_id,
123
- client_secret: @client_secret,
124
- timeout: @timeout,
125
- client: self
126
- )
127
- @oauth_2_0_state.auth_headers
128
- end
129
-
130
78
  # Creates and returns a new client for interacting with the API.
131
79
  #
132
- # @param client_id [String, nil] Defaults to `ENV["SPOTIFY_CLIENT_ID"]`
133
- #
134
- # @param client_secret [String, nil] Defaults to `ENV["SPOTIFY_CLIENT_SECRET"]`
135
- #
136
80
  # @param access_token [String, nil] Defaults to `ENV["SPOTIFY_ACCESS_TOKEN"]`
137
81
  #
138
82
  # @param base_url [String, nil] Override the default base URL for the API, e.g.,
@@ -146,8 +90,6 @@ module Spotted
146
90
  #
147
91
  # @param max_retry_delay [Float]
148
92
  def initialize(
149
- client_id: ENV["SPOTIFY_CLIENT_ID"],
150
- client_secret: ENV["SPOTIFY_CLIENT_SECRET"],
151
93
  access_token: ENV["SPOTIFY_ACCESS_TOKEN"],
152
94
  base_url: ENV["SPOTTED_BASE_URL"],
153
95
  max_retries: self.class::DEFAULT_MAX_RETRIES,
@@ -157,9 +99,11 @@ module Spotted
157
99
  )
158
100
  base_url ||= "https://api.spotify.com/v1"
159
101
 
160
- @client_id = client_id&.to_s
161
- @client_secret = client_secret&.to_s
162
- @access_token = access_token&.to_s
102
+ if access_token.nil?
103
+ raise ArgumentError.new("access_token is required, and can be set via environ: \"SPOTIFY_ACCESS_TOKEN\"")
104
+ end
105
+
106
+ @access_token = access_token.to_s
163
107
 
164
108
  super(
165
109
  base_url: base_url,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Spotted
4
- VERSION = "0.32.0"
4
+ VERSION = "0.33.0"
5
5
  end
data/lib/spotted.rb CHANGED
@@ -16,7 +16,6 @@ require "rbconfig"
16
16
  require "securerandom"
17
17
  require "set"
18
18
  require "stringio"
19
- require "thread"
20
19
  require "time"
21
20
  require "uri"
22
21
  # rubocop:enable Lint/RedundantRequireStatement
@@ -53,7 +52,6 @@ require_relative "spotted/errors"
53
52
  require_relative "spotted/internal/transport/base_client"
54
53
  require_relative "spotted/internal/transport/pooled_net_requester"
55
54
  require_relative "spotted/client"
56
- require_relative "spotted/internal/oauth2"
57
55
  require_relative "spotted/internal/cursor_url_page"
58
56
  require_relative "spotted/models/audiobook_base"
59
57
  require_relative "spotted/models/playlist_user_object"
@@ -10,13 +10,7 @@ module Spotted
10
10
 
11
11
  DEFAULT_MAX_RETRY_DELAY = T.let(8.0, Float)
12
12
 
13
- sig { returns(T.nilable(String)) }
14
- attr_reader :client_id
15
-
16
- sig { returns(T.nilable(String)) }
17
- attr_reader :client_secret
18
-
19
- sig { returns(T.nilable(String)) }
13
+ sig { returns(String) }
20
14
  attr_reader :access_token
21
15
 
22
16
  sig { returns(Spotted::Resources::Albums) }
@@ -72,25 +66,9 @@ module Spotted
72
66
  private def auth_headers
73
67
  end
74
68
 
75
- # @api private
76
- sig { returns(T::Hash[String, String]) }
77
- private def bearer_auth
78
- end
79
-
80
- # @api private
81
- sig { returns(Spotted::Internal::OAuth2ClientCredentials) }
82
- attr_reader :oauth_2_0_state
83
-
84
- # @api private
85
- sig { returns(T::Hash[String, String]) }
86
- private def oauth_2_0
87
- end
88
-
89
69
  # Creates and returns a new client for interacting with the API.
90
70
  sig do
91
71
  params(
92
- client_id: T.nilable(String),
93
- client_secret: T.nilable(String),
94
72
  access_token: T.nilable(String),
95
73
  base_url: T.nilable(String),
96
74
  max_retries: Integer,
@@ -100,10 +78,6 @@ module Spotted
100
78
  ).returns(T.attached_class)
101
79
  end
102
80
  def self.new(
103
- # Defaults to `ENV["SPOTIFY_CLIENT_ID"]`
104
- client_id: ENV["SPOTIFY_CLIENT_ID"],
105
- # Defaults to `ENV["SPOTIFY_CLIENT_SECRET"]`
106
- client_secret: ENV["SPOTIFY_CLIENT_SECRET"],
107
81
  # Defaults to `ENV["SPOTIFY_ACCESS_TOKEN"]`
108
82
  access_token: ENV["SPOTIFY_ACCESS_TOKEN"],
109
83
  # Override the default base URL for the API, e.g.,
@@ -8,11 +8,7 @@ module Spotted
8
8
 
9
9
  DEFAULT_MAX_RETRY_DELAY: Float
10
10
 
11
- attr_reader client_id: String?
12
-
13
- attr_reader client_secret: String?
14
-
15
- attr_reader access_token: String?
11
+ attr_reader access_token: String
16
12
 
17
13
  attr_reader albums: Spotted::Resources::Albums
18
14
 
@@ -48,16 +44,7 @@ module Spotted
48
44
 
49
45
  private def auth_headers: -> ::Hash[String, String]
50
46
 
51
- private def bearer_auth: -> ::Hash[String, String]
52
-
53
- # @api private
54
- attr_reader oauth_2_0_state: Spotted::Internal::OAuth2ClientCredentials
55
-
56
- private def oauth_2_0: -> ::Hash[String, String]
57
-
58
47
  def initialize: (
59
- ?client_id: String?,
60
- ?client_secret: String?,
61
48
  ?access_token: String?,
62
49
  ?base_url: String?,
63
50
  ?max_retries: Integer,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spotted
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.32.0
4
+ version: 0.33.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Spotted
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-01-14 00:00:00.000000000 Z
11
+ date: 2026-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cgi
@@ -55,7 +55,6 @@ files:
55
55
  - lib/spotted/file_part.rb
56
56
  - lib/spotted/internal.rb
57
57
  - lib/spotted/internal/cursor_url_page.rb
58
- - lib/spotted/internal/oauth2.rb
59
58
  - lib/spotted/internal/transport/base_client.rb
60
59
  - lib/spotted/internal/transport/pooled_net_requester.rb
61
60
  - lib/spotted/internal/type/array_of.rb
@@ -281,7 +280,6 @@ files:
281
280
  - rbi/spotted/file_part.rbi
282
281
  - rbi/spotted/internal.rbi
283
282
  - rbi/spotted/internal/cursor_url_page.rbi
284
- - rbi/spotted/internal/oauth2.rbi
285
283
  - rbi/spotted/internal/transport/base_client.rbi
286
284
  - rbi/spotted/internal/transport/pooled_net_requester.rbi
287
285
  - rbi/spotted/internal/type/array_of.rbi
@@ -506,7 +504,6 @@ files:
506
504
  - sig/spotted/file_part.rbs
507
505
  - sig/spotted/internal.rbs
508
506
  - sig/spotted/internal/cursor_url_page.rbs
509
- - sig/spotted/internal/oauth2.rbs
510
507
  - sig/spotted/internal/transport/base_client.rbs
511
508
  - sig/spotted/internal/transport/pooled_net_requester.rbs
512
509
  - sig/spotted/internal/type/array_of.rbs
@@ -1,87 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Spotted
4
- module Internal
5
- class OAuth2ClientCredentials
6
- # @param token_url [String]
7
- # @param client_id [String]
8
- # @param client_secret [String]
9
- # @param timeout [Integer]
10
- # @param client [Object]
11
- def initialize(token_url:, client_id:, client_secret:, timeout:, client:)
12
- @token_url = token_url
13
- @client_id = client_id
14
- @client_secret = client_secret
15
- @client = client
16
- @timeout = timeout
17
- @token = nil
18
- @token_expires_at = nil
19
- @mutex = Thread::Mutex.new
20
- end
21
-
22
- # @api private
23
- #
24
- # @return [Hash{String=>String}]
25
- def auth_headers
26
- @mutex.synchronize do
27
- if @token && !token_expired?
28
- return {"Authorization" => "Bearer #{@token}"}
29
- end
30
-
31
- @token = nil
32
- @token_expires_at = nil
33
-
34
- token_response = fetch_token
35
- if token_response
36
- @token = token_response[:access_token]
37
- @token_expires_at = Time.now + token_response[:expires_in]
38
-
39
- return {"Authorization" => "Bearer #{@token}"}
40
- end
41
-
42
- {}
43
- end
44
- end
45
-
46
- # @api private
47
- #
48
- # @return [Boolean]
49
- private def token_expired?
50
- return true if @token_expires_at.nil? || @token.nil?
51
-
52
- # Consider token expired if it expires within 10 seconds
53
- Time.now > (@token_expires_at - 10)
54
- end
55
-
56
- # @api private
57
- #
58
- # @return [Object]
59
- private def fetch_token
60
- body = URI.encode_www_form(
61
- {
62
- grant_type: "client_credentials",
63
- client_id: @client_id,
64
- client_secret: @client_secret
65
- }
66
- )
67
- request = {
68
- method: :post,
69
- url: URI(@token_url),
70
- headers: {
71
- "Content-Type" => "application/x-www-form-urlencoded"
72
- },
73
- body: body,
74
- max_retries: @client.max_retries,
75
- timeout: @timeout
76
- }
77
- _status, response, stream = @client.send_request(
78
- request,
79
- redirect_count: 0,
80
- retry_count: 0,
81
- send_retry_header: true
82
- )
83
- Spotted::Internal::Util.decode_content(response, stream: stream)
84
- end
85
- end
86
- end
87
- end
@@ -1,34 +0,0 @@
1
- # typed: strong
2
-
3
- module Spotted
4
- module Internal
5
- class OAuth2ClientCredentials
6
- sig do
7
- params(
8
- token_url: String,
9
- client_id: String,
10
- client_secret: String,
11
- timeout: Integer,
12
- client: T.anything
13
- ).void
14
- end
15
- def initialize(token_url:, client_id:, client_secret:, timeout:, client:)
16
- end
17
-
18
- # @api private
19
- sig { returns(T::Hash[String, String]) }
20
- def auth_headers
21
- end
22
-
23
- # @api private
24
- sig { returns(T::Boolean) }
25
- private def token_expired?
26
- end
27
-
28
- # @api private
29
- sig { returns(T.anything) }
30
- private def fetch_token
31
- end
32
- end
33
- end
34
- end
@@ -1,19 +0,0 @@
1
- module Spotted
2
- module Internal
3
- class OAuth2ClientCredentials
4
- def initialize: (
5
- token_url: String,
6
- client_id: String,
7
- client_secret: String,
8
- timeout: Integer,
9
- client: top
10
- ) -> void
11
-
12
- def auth_headers: -> ::Hash[String, String]
13
-
14
- private def token_expired?: -> bool
15
-
16
- private def fetch_token: -> top
17
- end
18
- end
19
- end