@omelhorsite/sdk 0.3.0 → 0.4.1
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.
- package/README.md +113 -131
- package/dist/index.js +1049 -1246
- package/dist/types/auth/device.d.ts +1 -1
- package/dist/types/auth/index.d.ts +2 -2
- package/dist/types/auth/tokens.d.ts +15 -15
- package/dist/types/client.d.ts +10 -10
- package/dist/types/errors.d.ts +12 -15
- package/dist/types/http.d.ts +74 -118
- package/dist/types/index.d.ts +2 -2
- package/dist/types/internal/attachments.d.ts +17 -0
- package/dist/types/internal/helpers.d.ts +9 -0
- package/dist/types/listing.d.ts +90 -0
- package/dist/types/local/qr.d.ts +1 -1
- package/dist/types/local/wordlist.d.ts +2 -3
- package/dist/types/resources/account.d.ts +19 -19
- package/dist/types/resources/admin/authorizedApplications.d.ts +133 -0
- package/dist/types/resources/admin/chests.d.ts +51 -0
- package/dist/types/resources/admin/eventAlerts.d.ts +60 -0
- package/dist/types/resources/admin/identities.d.ts +99 -0
- package/dist/types/resources/admin/index.d.ts +77 -0
- package/dist/types/resources/admin/jobs.d.ts +97 -0
- package/dist/types/resources/admin/myOauthApplications.d.ts +282 -0
- package/dist/types/resources/admin/notepads.d.ts +49 -0
- package/dist/types/resources/admin/oauthApplications.d.ts +309 -0
- package/dist/types/resources/admin/quotas.d.ts +114 -0
- package/dist/types/resources/admin/shortLinks.d.ts +201 -0
- package/dist/types/resources/admin/types.d.ts +222 -0
- package/dist/types/resources/admin/vocalSeparations.d.ts +98 -0
- package/dist/types/resources/admin.d.ts +17 -13
- package/dist/types/resources/auth/index.d.ts +1 -1
- package/dist/types/resources/auth/passkeys.d.ts +127 -163
- package/dist/types/resources/auth/sessions.d.ts +116 -161
- package/dist/types/resources/chests.d.ts +27 -31
- package/dist/types/resources/content/analysis.d.ts +62 -0
- package/dist/types/resources/content/blogs.d.ts +471 -0
- package/dist/types/resources/content/feedbacks.d.ts +263 -0
- package/dist/types/resources/content/index.d.ts +52 -0
- package/dist/types/resources/content/intel/articles.d.ts +230 -0
- package/dist/types/resources/content/intel/config.d.ts +135 -0
- package/dist/types/resources/content/intel/index.d.ts +53 -0
- package/dist/types/resources/content/intel/items.d.ts +91 -0
- package/dist/types/resources/content/intel/reports.d.ts +108 -0
- package/dist/types/resources/content/intel/scripts.d.ts +152 -0
- package/dist/types/resources/content/intel/sources.d.ts +236 -0
- package/dist/types/resources/content/intel/stats.d.ts +105 -0
- package/dist/types/resources/content/intel/types.d.ts +86 -0
- package/dist/types/resources/content/jokes.d.ts +93 -0
- package/dist/types/resources/content/notifications.d.ts +147 -0
- package/dist/types/resources/content/serviceUsages.d.ts +73 -0
- package/dist/types/resources/content/servicesStatus.d.ts +194 -0
- package/dist/types/resources/content/siteConfig.d.ts +43 -0
- package/dist/types/resources/content/spaceInvaders.d.ts +154 -0
- package/dist/types/resources/content.d.ts +41 -236
- package/dist/types/resources/dynamicQrs.d.ts +29 -45
- package/dist/types/resources/forms.d.ts +37 -58
- package/dist/types/resources/jobs.d.ts +35 -44
- package/dist/types/resources/library/annotations.d.ts +162 -0
- package/dist/types/resources/library/books.d.ts +534 -0
- package/dist/types/resources/library/chat.d.ts +259 -0
- package/dist/types/resources/library/index.d.ts +39 -0
- package/dist/types/resources/library/shelves.d.ts +237 -0
- package/dist/types/resources/library/types.d.ts +120 -0
- package/dist/types/resources/library.d.ts +20 -22
- package/dist/types/resources/media.d.ts +48 -61
- package/dist/types/resources/movies/addons.d.ts +388 -0
- package/dist/types/resources/movies/collections.d.ts +352 -0
- package/dist/types/resources/movies/index.d.ts +29 -0
- package/dist/types/resources/movies/types.d.ts +50 -0
- package/dist/types/resources/movies/watchProgress.d.ts +277 -0
- package/dist/types/resources/movies.d.ts +27 -53
- package/dist/types/resources/music/artists.d.ts +182 -246
- package/dist/types/resources/music/imports.d.ts +190 -218
- package/dist/types/resources/music/index.d.ts +8 -7
- package/dist/types/resources/music/playlists.d.ts +85 -117
- package/dist/types/resources/music/social.d.ts +153 -228
- package/dist/types/resources/music/songs.d.ts +165 -227
- package/dist/types/resources/realtime.d.ts +75 -88
- package/dist/types/resources/shortLinks.d.ts +38 -47
- package/dist/types/resources/social/groupChats.d.ts +458 -0
- package/dist/types/resources/social/index.d.ts +19 -0
- package/dist/types/resources/social/messages.d.ts +424 -0
- package/dist/types/resources/social/relationships.d.ts +322 -0
- package/dist/types/resources/social/types.d.ts +37 -0
- package/dist/types/resources/social.d.ts +21 -3
- package/dist/types/resources/storage/upload.d.ts +42 -56
- package/dist/types/resources/storage.d.ts +94 -109
- package/dist/types/resources/tickets.d.ts +19 -10
- package/dist/types/resources/tools/backgroundRemoval.d.ts +11 -13
- package/dist/types/resources/tools/captions.d.ts +107 -135
- package/dist/types/resources/tools/upscale.d.ts +12 -16
- package/dist/types/types.d.ts +29 -38
- package/package.json +1 -1
|
@@ -5,45 +5,36 @@
|
|
|
5
5
|
*
|
|
6
6
|
* ## An artist row belongs to ONE user
|
|
7
7
|
*
|
|
8
|
-
* There is no global artist table.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* hand one to another user's client.
|
|
8
|
+
* There is no global artist table. Every artist is scoped to the account that
|
|
9
|
+
* owns it, so two accounts that both own a Chico Buarque track own two
|
|
10
|
+
* different artist rows with two different integer ids. Never cache an artist
|
|
11
|
+
* id across identities, and never hand one to another user's client.
|
|
13
12
|
*
|
|
14
|
-
* Rows are created as a side effect of importing songs
|
|
15
|
-
*
|
|
16
|
-
* not routed - `resources :artists, only: [index, show, update, destroy]`.
|
|
13
|
+
* Rows are created as a side effect of importing songs; this namespace has no
|
|
14
|
+
* `create`, and `POST /artists` is not routed.
|
|
17
15
|
*
|
|
18
16
|
* ## Almost everything here shares ONE 60/min bucket
|
|
19
17
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
18
|
+
* `/lyrics*`, `/artists/*`, `/artist_metadata/*` and `/music_radios/*` share
|
|
19
|
+
* **60 requests a minute** - one bucket for all four families, keyed by the
|
|
20
|
+
* `Authorization` header (or by IP when the call is anonymous). A screen that
|
|
21
|
+
* opens an artist page while a lyrics panel polls is spending the same budget
|
|
22
|
+
* twice.
|
|
25
23
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* DO match. The `/artist_imports*` and `/artist_syncs*` routes do not (neither
|
|
32
|
-
* `artist_imports` nor `artist_syncs` is `artists/`) and are on the general
|
|
33
|
-
* ceiling as well.
|
|
34
|
-
*
|
|
35
|
-
* Every method below states which of the two it lands in.
|
|
24
|
+
* `GET /artists` itself (the roster index) is NOT in that bucket and falls
|
|
25
|
+
* under the general authenticated ceiling of 600/min. `/artists/overview`,
|
|
26
|
+
* `/artists/:id`, both uploads, `PATCH` and `DELETE` all are. `/artist_imports*`
|
|
27
|
+
* and `/artist_syncs*` are on the general ceiling as well. Every method below
|
|
28
|
+
* states which of the two it lands in.
|
|
36
29
|
*
|
|
37
30
|
* ## An OAuth access token cannot reach any of this
|
|
38
31
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* token gets `403 {"error":"insufficient_scope"}` on every route in this file,
|
|
43
|
-
* whatever scopes it was granted. Use a session token (`POST /sessions`) or,
|
|
44
|
-
* in the browser, the session cookie.
|
|
32
|
+
* Every route in this file answers `403 {"error":"insufficient_scope"}` to an
|
|
33
|
+
* OAuth access token, whatever scopes it was granted. Use a session token
|
|
34
|
+
* (`POST /sessions`) or, in the browser, the session cookie.
|
|
45
35
|
*/
|
|
46
36
|
import { ApiClient, Resource } from "../../http";
|
|
37
|
+
import type { ListParams } from "../../listing";
|
|
47
38
|
import type { FileInput, NativeFile, Paginated, RequestOptions, Timestamp } from "../../types";
|
|
48
39
|
/**
|
|
49
40
|
* Primary key of an artist. An INTEGER, unlike users, sessions and storage
|
|
@@ -53,25 +44,23 @@ export type ArtistId = number;
|
|
|
53
44
|
/** Primary key of an artist import. Also an integer. */
|
|
54
45
|
export type ArtistImportId = number;
|
|
55
46
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* three clients page identically and their caches line up.
|
|
47
|
+
* Default roster page size, 60. Nothing on the server requires it; it is here
|
|
48
|
+
* so every caller pages identically and caches line up.
|
|
59
49
|
*/
|
|
60
50
|
export declare const ARTIST_ROSTER_PAGE_SIZE = 60;
|
|
61
51
|
/** Largest artist image or banner the server will accept, in bytes. */
|
|
62
52
|
export declare const ARTIST_IMAGE_MAX_BYTES: number;
|
|
63
53
|
/**
|
|
64
|
-
* Content types
|
|
54
|
+
* Content types the server maps directly to a stored extension.
|
|
65
55
|
*
|
|
66
|
-
* A file whose type is not in this list is NOT necessarily refused: the
|
|
67
|
-
*
|
|
68
|
-
*
|
|
56
|
+
* A file whose type is not in this list is NOT necessarily refused: the server
|
|
57
|
+
* falls back to the extension of the filename and only gives up when that is
|
|
58
|
+
* empty too. So `application/octet-stream` + `cover.png` is accepted
|
|
69
59
|
* and `application/octet-stream` + `cover` is a 400.
|
|
70
60
|
*/
|
|
71
61
|
export declare const ARTIST_IMAGE_CONTENT_TYPES: readonly ["image/jpeg", "image/png", "image/webp", "image/gif"];
|
|
72
62
|
/**
|
|
73
|
-
* An artist, as `GET /artists` renders it
|
|
74
|
-
* two computed fields).
|
|
63
|
+
* An artist, as `GET /artists` renders it.
|
|
75
64
|
*
|
|
76
65
|
* ## Every image field, and which of them can be trusted
|
|
77
66
|
*
|
|
@@ -86,13 +75,12 @@ export declare const ARTIST_IMAGE_CONTENT_TYPES: readonly ["image/jpeg", "image/
|
|
|
86
75
|
* it - it is written only by `GET /songs/artist_pictures`, which lives in the
|
|
87
76
|
* songs namespace. See {@link Artist.pictures_fetched_at} for why a fresh
|
|
88
77
|
* timestamp there is not a promise.
|
|
89
|
-
* - `external_image_url` comes from Last.fm
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* rather than store.
|
|
78
|
+
* - `external_image_url` comes from Last.fm, and is usually null on purpose:
|
|
79
|
+
* Last.fm retired artist images and now answers with one grey-star
|
|
80
|
+
* placeholder for everybody, which is filtered out rather than stored.
|
|
93
81
|
* - `gallery_image_urls` (extended view only) is Wikipedia/Wikimedia.
|
|
94
82
|
*
|
|
95
|
-
* The resolution chain
|
|
83
|
+
* The resolution chain, in order:
|
|
96
84
|
* `compressed_image_media_id` -> `image_media_id` -> the size-appropriate
|
|
97
85
|
* `picture_*` (medium for an avatar, `picture_xl` for a hero) -> `picture` ->
|
|
98
86
|
* `gallery_image_urls[0]` -> `fallback_artwork_media_id` -> `external_image_url`
|
|
@@ -100,10 +88,8 @@ export declare const ARTIST_IMAGE_CONTENT_TYPES: readonly ["image/jpeg", "image/
|
|
|
100
88
|
*
|
|
101
89
|
* ## `*_media_id` and `*_fs_node_id` are the same value, twice
|
|
102
90
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* legacy `_fs_node_id` name. Read `_media_id`; the twin is scheduled to go.
|
|
106
|
-
* Both docs in `oms-music/docs` document only the legacy name.
|
|
91
|
+
* Both keys carry an identical value for every attachment. Read `_media_id`;
|
|
92
|
+
* the `_fs_node_id` twin is a legacy alias scheduled to go.
|
|
107
93
|
*/
|
|
108
94
|
export interface Artist {
|
|
109
95
|
/** Integer, and unique only within one user's library. */
|
|
@@ -130,9 +116,8 @@ export interface Artist {
|
|
|
130
116
|
/** Legacy twin of {@link Artist.image_media_id}. Same value. */
|
|
131
117
|
readonly image_fs_node_id: string | null;
|
|
132
118
|
/**
|
|
133
|
-
* Legacy compressed variant.
|
|
134
|
-
*
|
|
135
|
-
* companion, so on a modern row this is null.
|
|
119
|
+
* Legacy compressed variant. Nothing generates new ones, and an upload
|
|
120
|
+
* purges the stale companion, so on a modern row this is null.
|
|
136
121
|
*/
|
|
137
122
|
readonly compressed_image_media_id: string | null;
|
|
138
123
|
/** Legacy twin of {@link Artist.compressed_image_media_id}. */
|
|
@@ -164,21 +149,18 @@ export interface Artist {
|
|
|
164
149
|
* When the Deezer picture set was last WRITTEN - which is not the same as
|
|
165
150
|
* when it was last checked.
|
|
166
151
|
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* three days. That is the shape of the bug this behaviour exists to prevent,
|
|
173
|
-
* so do not read a stale timestamp as "Deezer has nothing for this artist".
|
|
152
|
+
* It is stamped only on a successful answer (a hit, or a genuine "not on
|
|
153
|
+
* Deezer" miss). A failure - a Deezer quota refusal included - stamps NOTHING
|
|
154
|
+
* and only pauses retries for 30 minutes, precisely so an over-quota sweep
|
|
155
|
+
* cannot mark a whole library as "fetched, no picture" for three days. So do
|
|
156
|
+
* not read a stale timestamp as "Deezer has nothing for this artist".
|
|
174
157
|
*/
|
|
175
158
|
readonly pictures_fetched_at: Timestamp | null;
|
|
176
159
|
/**
|
|
177
160
|
* When Last.fm's biography was last fetched, successfully OR NOT.
|
|
178
161
|
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* hammering an upstream that is down. The row therefore looks populated
|
|
162
|
+
* A failed fetch still stamps all three `*_fetched_at` fields, so an
|
|
163
|
+
* upstream that is down is not hammered. The row therefore looks populated
|
|
182
164
|
* while `bio_html` is still null. A null `bio_html` next to a recent
|
|
183
165
|
* `bio_fetched_at` means "we tried and got nothing", not "never tried".
|
|
184
166
|
*/
|
|
@@ -186,25 +168,21 @@ export interface Artist {
|
|
|
186
168
|
/** Same semantics as {@link Artist.bio_fetched_at}, for the similar list. */
|
|
187
169
|
readonly similar_fetched_at: Timestamp | null;
|
|
188
170
|
/**
|
|
189
|
-
* Number of
|
|
190
|
-
* featured and with), not distinct songs.
|
|
171
|
+
* Number of credits (primary, featured and with), not distinct songs.
|
|
191
172
|
*
|
|
192
|
-
* Computed per response, never stored.
|
|
193
|
-
* grouped `COUNT` for the whole page; everywhere else the blueprint runs its
|
|
194
|
-
* own `COUNT(*)`. Always present.
|
|
173
|
+
* Computed per response, never stored. Always present.
|
|
195
174
|
*/
|
|
196
175
|
readonly songs_count: number;
|
|
197
176
|
/**
|
|
198
177
|
* Artwork of one of this artist's own lead songs, for a card that has no
|
|
199
|
-
* picture at all. A media id, resolvable through `oms.media`
|
|
200
|
-
* `/media/:id/data`); `/fs_nodes/:id/data` is
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
* NON-NULL ONLY ON `GET /artists` AND `GET /artists/:id
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
* response loses its fallback. Keep the value you already had.
|
|
178
|
+
* picture at all. A media id, resolvable through `oms.media`
|
|
179
|
+
* (`/media/:id/data`); `/fs_nodes/:id/data` is a legacy alias that reaches
|
|
180
|
+
* the same bytes.
|
|
181
|
+
*
|
|
182
|
+
* NON-NULL ONLY ON `GET /artists` AND `GET /artists/:id`. `PATCH` and both
|
|
183
|
+
* uploads answer null here even for an artist that has one - so a card
|
|
184
|
+
* redrawn from an upload response loses its fallback. Keep the value you
|
|
185
|
+
* already had.
|
|
208
186
|
*/
|
|
209
187
|
readonly fallback_artwork_media_id: string | null;
|
|
210
188
|
/** Legacy twin of {@link Artist.fallback_artwork_media_id}. */
|
|
@@ -213,28 +191,19 @@ export interface Artist {
|
|
|
213
191
|
/** One entry of Last.fm's similar-artists list. */
|
|
214
192
|
export interface ArtistSimilarEntry {
|
|
215
193
|
readonly name: string;
|
|
216
|
-
/**
|
|
217
|
-
* Last.fm's similarity score, `0..1`. A NUMBER: the resolver runs `to_f`
|
|
218
|
-
* before storing it. The web frontend types the whole `similar` field as an
|
|
219
|
-
* object with an `artists` array, which is the DATABASE column's shape and
|
|
220
|
-
* not the payload's - the blueprint flattens it to this array.
|
|
221
|
-
*/
|
|
194
|
+
/** Last.fm's similarity score, `0..1`. A number. */
|
|
222
195
|
readonly match: number | null;
|
|
223
196
|
readonly mbid: string | null;
|
|
224
197
|
}
|
|
225
198
|
/**
|
|
226
|
-
* The
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
* subset. Returned by `GET /artists/:id`, `PATCH /artists/:id` and both upload
|
|
230
|
-
* endpoints. `oms-music/docs/api-music.md` describes `:compact` and `:card` as
|
|
231
|
-
* narrow subsets; they are not, for the same inheritance reason, and
|
|
232
|
-
* `docs/API.md` says so correctly.
|
|
199
|
+
* The extended artist: everything in {@link Artist} plus three fields, a
|
|
200
|
+
* superset and never a subset. Returned by `GET /artists/:id`,
|
|
201
|
+
* `PATCH /artists/:id` and both upload endpoints.
|
|
233
202
|
*/
|
|
234
203
|
export interface ArtistExtended extends Artist {
|
|
235
204
|
/**
|
|
236
205
|
* Last.fm biography summary. Contains HTML (and a trailing "Read more"
|
|
237
|
-
* anchor); sanitised on
|
|
206
|
+
* anchor); sanitised on `/artist_metadata/:name` but NOT here, so treat it
|
|
238
207
|
* as untrusted markup and sanitise before injecting it.
|
|
239
208
|
*/
|
|
240
209
|
readonly bio_html: string | null;
|
|
@@ -242,15 +211,15 @@ export interface ArtistExtended extends Artist {
|
|
|
242
211
|
* Wikipedia/Wikimedia photos of the artist.
|
|
243
212
|
*
|
|
244
213
|
* Writable through {@link MusicArtistsNamespace.update}, and OVERWRITTEN
|
|
245
|
-
* without warning by the next metadata refresh
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
214
|
+
* without warning by the next metadata refresh, which always replaces it
|
|
215
|
+
* with what Wikimedia returns. A hand-curated list survives only until the
|
|
216
|
+
* artist's gallery TTL (30 days, jittered by up to 7) expires and someone
|
|
217
|
+
* opens the artist page. There is
|
|
249
218
|
* no "pinned" flag. If a client offers gallery editing, it has to be
|
|
250
219
|
* prepared to re-apply it.
|
|
251
220
|
*/
|
|
252
221
|
readonly gallery_image_urls: string[];
|
|
253
|
-
/**
|
|
222
|
+
/** Empty array, never null. */
|
|
254
223
|
readonly similar: ArtistSimilarEntry[];
|
|
255
224
|
}
|
|
256
225
|
/**
|
|
@@ -258,23 +227,23 @@ export interface ArtistExtended extends Artist {
|
|
|
258
227
|
*
|
|
259
228
|
* The server's allowlist for this index is exactly `id`, `name`, `slug`,
|
|
260
229
|
* `canonical_name`, `created_at` and `updated_at`. Any other key inside
|
|
261
|
-
* `search` / `exact_search` is a **400**, not a silently ignored filter
|
|
262
|
-
*
|
|
263
|
-
*
|
|
230
|
+
* `search` / `exact_search` is a **400**, not a silently ignored filter: the
|
|
231
|
+
* allowlist fails closed on purpose, because the alternative (dropping the
|
|
232
|
+
* key) would answer with the UNFILTERED list.
|
|
264
233
|
*/
|
|
265
|
-
export
|
|
234
|
+
export declare const ARTIST_FILTER_COLUMNS: readonly ["id", "name", "slug", "canonical_name", "created_at"];
|
|
235
|
+
export interface ListArtistsParams extends ListParams<(typeof ARTIST_FILTER_COLUMNS)[number]> {
|
|
266
236
|
/**
|
|
267
237
|
* Substring match on the display name - `search[name]`. This is the roster
|
|
268
238
|
* search box.
|
|
269
239
|
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
* `"
|
|
276
|
-
*
|
|
277
|
-
* `TRANSLATE` map does not list survives as a hyphen, which still matches.
|
|
240
|
+
* The server slugifies BOTH sides before comparing: lowercase, accents
|
|
241
|
+
* transliterated, every run of anything outside `[a-z0-9-]` replaced by a
|
|
242
|
+
* hyphen, then a substring match. Three consequences follow: `"Beyoncé"`
|
|
243
|
+
* finds `"Beyonce"` and the reverse; `"chico buarque"` and `"chico-buarque"`
|
|
244
|
+
* are the SAME query; and punctuation is erased on both sides, so `"P!nk"`
|
|
245
|
+
* matches `"P nk"` too. An accent the transliteration does not know survives
|
|
246
|
+
* as a hyphen, which still matches.
|
|
278
247
|
*/
|
|
279
248
|
readonly name?: string;
|
|
280
249
|
/** Exact slug. Cheaper and less surprising than a `name` search. */
|
|
@@ -292,12 +261,11 @@ export interface ListArtistsParams {
|
|
|
292
261
|
* `"column:asc"` / `"column:desc"`.
|
|
293
262
|
*
|
|
294
263
|
* PASS ONE. The index has no default order, so paging without it is paging
|
|
295
|
-
* an unordered
|
|
296
|
-
* and never hand back another. `name:asc` and `created_at:desc` are
|
|
297
|
-
*
|
|
264
|
+
* an unordered set: the server may hand back the same row on two pages
|
|
265
|
+
* and never hand back another. `name:asc` and `created_at:desc` are the
|
|
266
|
+
* usual choices.
|
|
298
267
|
*
|
|
299
|
-
*
|
|
300
|
-
* `model.column_names` and SILENTLY IGNORES anything else, so
|
|
268
|
+
* A column the record does not have is SILENTLY IGNORED, so
|
|
301
269
|
* `songs_count:desc` is not an error and is not an ordering either - that
|
|
302
270
|
* field is computed per response and cannot be sorted on. Sort client-side,
|
|
303
271
|
* or read {@link MusicArtistsNamespace.overview}, which ranks for you.
|
|
@@ -366,11 +334,8 @@ export interface ArtistNeglectedEntry {
|
|
|
366
334
|
}
|
|
367
335
|
/**
|
|
368
336
|
* The editorial header of the Artists page: five aggregate queries answered as
|
|
369
|
-
* one document.
|
|
370
|
-
*
|
|
371
|
-
* The artists inside are rendered with the blueprint's `:card` view, which
|
|
372
|
-
* inherits the base fields, so each one is a whole {@link Artist} - including
|
|
373
|
-
* `fallback_artwork_media_id`, which the controller precomputes here.
|
|
337
|
+
* one document. Each artist inside is a whole {@link Artist},
|
|
338
|
+
* `fallback_artwork_media_id` included.
|
|
374
339
|
*/
|
|
375
340
|
export interface ArtistOverview {
|
|
376
341
|
readonly stats: ArtistOverviewStats;
|
|
@@ -400,13 +365,13 @@ export interface ArtistOverview {
|
|
|
400
365
|
/** In the library, nothing played in 90 days. Up to 12. */
|
|
401
366
|
readonly neglected: ArtistNeglectedEntry[];
|
|
402
367
|
}
|
|
403
|
-
/**
|
|
368
|
+
/** Every state an artist import can be in. */
|
|
404
369
|
export declare const ARTIST_IMPORT_STATES: readonly ["queued", "running", "complete", "failed"];
|
|
405
370
|
/** State of a bulk artist import. */
|
|
406
371
|
export type ArtistImportState = (typeof ARTIST_IMPORT_STATES)[number];
|
|
407
372
|
/**
|
|
408
|
-
*
|
|
409
|
-
*
|
|
373
|
+
* Sensible cadence to poll an in-flight import at. Nothing pushes import
|
|
374
|
+
* progress over the realtime stream.
|
|
410
375
|
*/
|
|
411
376
|
export declare const ARTIST_IMPORT_POLL_INTERVAL_MS = 1500;
|
|
412
377
|
/**
|
|
@@ -420,10 +385,8 @@ export declare function isArtistImportTerminal(state: ArtistImportState | string
|
|
|
420
385
|
/**
|
|
421
386
|
* One bulk import of an artist's Spotify catalogue into the library.
|
|
422
387
|
*
|
|
423
|
-
*
|
|
424
|
-
*
|
|
425
|
-
* `processed_albums` of `total_albums` is progress, and
|
|
426
|
-
* `queued + skipped + failed` is what became of the tracks.
|
|
388
|
+
* Read the counters together: `processed_albums` of `total_albums` is
|
|
389
|
+
* progress, and `queued + skipped + failed` is what became of the tracks.
|
|
427
390
|
*/
|
|
428
391
|
export interface ArtistImport {
|
|
429
392
|
readonly id: ArtistImportId;
|
|
@@ -438,7 +401,7 @@ export interface ArtistImport {
|
|
|
438
401
|
readonly album_ids: string[];
|
|
439
402
|
readonly state: ArtistImportState;
|
|
440
403
|
readonly total_albums: number | null;
|
|
441
|
-
/** Null until the
|
|
404
|
+
/** Null until the albums have been expanded into tracks. */
|
|
442
405
|
readonly total_tracks: number | null;
|
|
443
406
|
readonly processed_albums: number;
|
|
444
407
|
/** Tracks handed to the song-import pipeline. */
|
|
@@ -460,11 +423,7 @@ export interface ArtistImportRosterMatch {
|
|
|
460
423
|
readonly id: ArtistId;
|
|
461
424
|
readonly name: string;
|
|
462
425
|
readonly slug: string;
|
|
463
|
-
/**
|
|
464
|
-
* A real URL (`picture_medium`, then `external_image_url`), or null. An
|
|
465
|
-
* earlier version put a raw storage node id in this field, which no client
|
|
466
|
-
* could load as an image.
|
|
467
|
-
*/
|
|
426
|
+
/** A real URL (`picture_medium`, then `external_image_url`), or null. */
|
|
468
427
|
readonly image_url: string | null;
|
|
469
428
|
}
|
|
470
429
|
/** A Spotify search hit offered by the import picker. */
|
|
@@ -533,9 +492,9 @@ export interface ListArtistImportsParams {
|
|
|
533
492
|
*
|
|
534
493
|
* ## Every route here needs a LINKED SPOTIFY IDENTITY, not a flag
|
|
535
494
|
*
|
|
536
|
-
*
|
|
537
|
-
*
|
|
538
|
-
*
|
|
495
|
+
* Without one the answer is `400 "Connect Spotify first."`, and when the
|
|
496
|
+
* stored refresh token no longer works it is
|
|
497
|
+
* `400 "Spotify connection needs to be relinked."`
|
|
539
498
|
* Both are plain JSON strings, both arrive as an {@link OmsApiError} with
|
|
540
499
|
* status 400, and the only way to tell them apart is the message - so match on
|
|
541
500
|
* it if the UI needs to distinguish "connect" from "reconnect".
|
|
@@ -544,11 +503,10 @@ export interface ListArtistImportsParams {
|
|
|
544
503
|
*
|
|
545
504
|
* ## These calls are SLOW and they are on the general ceiling
|
|
546
505
|
*
|
|
547
|
-
* `/artist_imports*`
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
* app allows 60 seconds for each, and so does this namespace.
|
|
506
|
+
* `/artist_imports*` sits under the general authenticated ceiling (600/min).
|
|
507
|
+
* What bounds it in practice is Spotify: {@link search} and {@link albums}
|
|
508
|
+
* both call out synchronously, and {@link albums} pages through an entire
|
|
509
|
+
* discography. This namespace allows 60 seconds for each.
|
|
552
510
|
*/
|
|
553
511
|
export declare class MusicArtistImportsNamespace extends Resource {
|
|
554
512
|
/**
|
|
@@ -577,12 +535,9 @@ export declare class MusicArtistImportsNamespace extends Resource {
|
|
|
577
535
|
*
|
|
578
536
|
* The server wraps the array in `{ items: [...] }`; this returns the array.
|
|
579
537
|
*
|
|
580
|
-
* `spotifyArtistId` is validated here rather than sent empty
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
* convention - it is a framework 400 with a framework body rather than a
|
|
584
|
-
* bare JSON string - and it trips `ErrorReporting`, which pages the owner on
|
|
585
|
-
* Discord for what is really a client bug.
|
|
538
|
+
* `spotifyArtistId` is validated here rather than sent empty: a missing key
|
|
539
|
+
* answers a generic 400 whose body is not one of the API's bare JSON
|
|
540
|
+
* strings.
|
|
586
541
|
*
|
|
587
542
|
* @throws {OmsError} `invalid_request` when `spotifyArtistId` is blank.
|
|
588
543
|
* @throws {OmsApiError} 400 `"Connect Spotify first."` with no linked
|
|
@@ -595,21 +550,21 @@ export declare class MusicArtistImportsNamespace extends Resource {
|
|
|
595
550
|
/**
|
|
596
551
|
* `POST /artist_imports` - queues every track of the chosen albums.
|
|
597
552
|
*
|
|
598
|
-
* Answers `201` immediately with a `queued` record; the work happens in
|
|
599
|
-
*
|
|
553
|
+
* Answers `201` immediately with a `queued` record; the work happens in the
|
|
554
|
+
* background. Watch it with {@link list} at
|
|
600
555
|
* {@link ARTIST_IMPORT_POLL_INTERVAL_MS} - THERE IS NO `GET
|
|
601
556
|
* /artist_imports/:id`, so polling means re-reading the recent list and
|
|
602
557
|
* finding your id in it.
|
|
603
558
|
*
|
|
604
559
|
* Not retried, and this one matters more than most: a replay does not
|
|
605
|
-
* deduplicate, it creates a second
|
|
606
|
-
*
|
|
560
|
+
* deduplicate, it creates a second import and runs the whole catalogue
|
|
561
|
+
* through the pipeline again. Pass `retry: {}` only if you are
|
|
607
562
|
* prepared to explain the duplicate.
|
|
608
563
|
*
|
|
609
564
|
* @throws {OmsError} `invalid_request` when `spotifyArtistId` is blank or
|
|
610
|
-
* `albumIds` is empty. The first would
|
|
611
|
-
*
|
|
612
|
-
*
|
|
565
|
+
* `albumIds` is empty. The first would answer a generic 400 (see
|
|
566
|
+
* {@link albums}); the second is a clean `400 "album_ids required"`,
|
|
567
|
+
* checked here only so the round trip is saved.
|
|
613
568
|
* @throws {OmsApiError} 400 for the two Spotify-identity messages.
|
|
614
569
|
*/
|
|
615
570
|
create(input: CreateArtistImportInput, options?: RequestOptions): Promise<ArtistImport>;
|
|
@@ -622,32 +577,29 @@ export declare class MusicArtistImportsNamespace extends Resource {
|
|
|
622
577
|
*
|
|
623
578
|
* This is also the poll: filter for a `state` that
|
|
624
579
|
* {@link isArtistImportTerminal} rejects to know whether anything is still
|
|
625
|
-
* running. Nothing pushes import progress over the
|
|
580
|
+
* running. Nothing pushes import progress over the realtime stream.
|
|
626
581
|
*/
|
|
627
582
|
list(params?: ListArtistImportsParams, options?: RequestOptions): Promise<ArtistImport[]>;
|
|
628
583
|
}
|
|
629
584
|
/**
|
|
630
585
|
* Primary key of an artist sync. An integer, like {@link ArtistId} and
|
|
631
586
|
* {@link ArtistImportId}, and interchangeable with NEITHER: a sync is keyed on
|
|
632
|
-
* a SPOTIFY artist id and never references a local
|
|
587
|
+
* a SPOTIFY artist id and never references a local artist at all. Three
|
|
633
588
|
* integer id spaces meet in this file and only the field name tells them apart.
|
|
634
589
|
*/
|
|
635
590
|
export type ArtistSyncId = number;
|
|
636
591
|
/**
|
|
637
592
|
* A followed artist, as `/artist_syncs` renders it.
|
|
638
593
|
*
|
|
639
|
-
* ##
|
|
594
|
+
* ## The base fields you expect are MISSING
|
|
640
595
|
*
|
|
641
|
-
*
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
* them holds everywhere a Blueprinter view is involved and stops here. Do not
|
|
645
|
-
* sort a list of these by `created_at` client-side; the server already returns
|
|
646
|
-
* them newest-first and that ordering is the only one available.
|
|
596
|
+
* This is the one music record with no `created_at` and no `updated_at`. Do
|
|
597
|
+
* not sort a list of these by `created_at` client-side; the server already
|
|
598
|
+
* returns them newest-first and that ordering is the only one available.
|
|
647
599
|
*
|
|
648
|
-
*
|
|
649
|
-
*
|
|
650
|
-
*
|
|
600
|
+
* The known album ids themselves are not exposed either: only their COUNT
|
|
601
|
+
* crosses the wire, so the SDK cannot tell you WHICH albums are already known
|
|
602
|
+
* - only how many.
|
|
651
603
|
*/
|
|
652
604
|
export interface ArtistSync {
|
|
653
605
|
readonly id: ArtistSyncId;
|
|
@@ -663,12 +615,11 @@ export interface ArtistSync {
|
|
|
663
615
|
*/
|
|
664
616
|
readonly artist_name: string | null;
|
|
665
617
|
/**
|
|
666
|
-
* Whether
|
|
667
|
-
* `where(enabled: true)`).
|
|
618
|
+
* Whether the daily check will pick this row up.
|
|
668
619
|
*
|
|
669
620
|
* Always `true` on anything this SDK can produce: `create` sets it, and there
|
|
670
621
|
* is no update route to turn it off. A `false` here can only have been
|
|
671
|
-
* written by
|
|
622
|
+
* written by an administrator, and the only way a client can stop a sync is
|
|
672
623
|
* {@link MusicArtistSyncsNamespace.delete}. Render it, do not offer a toggle.
|
|
673
624
|
*/
|
|
674
625
|
readonly enabled: boolean;
|
|
@@ -676,21 +627,20 @@ export interface ArtistSync {
|
|
|
676
627
|
* When the daily check last ran, ISO-8601, or `null` in the vanishingly
|
|
677
628
|
* short window before `create` saves the row.
|
|
678
629
|
*
|
|
679
|
-
* A TOUCH, not a success marker.
|
|
680
|
-
*
|
|
681
|
-
*
|
|
682
|
-
*
|
|
683
|
-
* `Rails.logger.warn` and nothing else - so a UI cannot honestly say "last
|
|
630
|
+
* A TOUCH, not a success marker. It is written whether the check succeeded
|
|
631
|
+
* or failed (dead refresh token, Spotify upstream error), so a fresh
|
|
632
|
+
* timestamp proves the check ran, never that Spotify answered. There is no
|
|
633
|
+
* field that records the last failure, so a UI cannot honestly say "last
|
|
684
634
|
* checked, all good".
|
|
685
635
|
*/
|
|
686
636
|
readonly last_checked_at: Timestamp | null;
|
|
687
637
|
/**
|
|
688
|
-
* How many Spotify album ids the snapshot holds.
|
|
638
|
+
* How many Spotify album ids the snapshot holds.
|
|
689
639
|
*
|
|
690
640
|
* This is the baseline the daily diff runs against, not a count of songs
|
|
691
|
-
* imported. It GROWS and never shrinks, because
|
|
692
|
-
*
|
|
693
|
-
* again cannot re-import as new.
|
|
641
|
+
* imported. It GROWS and never shrinks, because each check stores the union
|
|
642
|
+
* of what it knew and what it saw, precisely so an album Spotify hides and
|
|
643
|
+
* later shows again cannot re-import as new.
|
|
694
644
|
*/
|
|
695
645
|
readonly known_album_count: number;
|
|
696
646
|
}
|
|
@@ -723,7 +673,7 @@ export interface CreateArtistSyncInput {
|
|
|
723
673
|
* | --- | --- | --- |
|
|
724
674
|
* | what it does | imports the albums you CHOSE, now | watches for albums released LATER |
|
|
725
675
|
* | back catalogue | yes, that is the point | never |
|
|
726
|
-
* | when work happens | immediately
|
|
676
|
+
* | when work happens | immediately | daily, 05:00 server time |
|
|
727
677
|
* | you pick albums | yes, `albumIds` is required | no, there is no album argument |
|
|
728
678
|
* | repeating the call | duplicates the whole import | idempotent, one row per artist |
|
|
729
679
|
*
|
|
@@ -735,40 +685,35 @@ export interface CreateArtistSyncInput {
|
|
|
735
685
|
*
|
|
736
686
|
* ## What the sync produces is an ArtistImport, so watch it there
|
|
737
687
|
*
|
|
738
|
-
*
|
|
739
|
-
*
|
|
740
|
-
*
|
|
741
|
-
*
|
|
742
|
-
*
|
|
743
|
-
*
|
|
744
|
-
* diário…"`, which is the marker that tells an automatic import from one a
|
|
745
|
-
* person asked for.
|
|
688
|
+
* The daily check runs at 05:00 server time, at a random offset inside a
|
|
689
|
+
* 30-minute window. It re-walks the artist's catalogue, diffs it against the
|
|
690
|
+
* snapshot and, when something is new, creates an ordinary
|
|
691
|
+
* {@link ArtistImport} holding ONLY the new album ids - `last_message` starts
|
|
692
|
+
* as `"Novo lançamento detectado pelo sync diário…"`, which is the marker that
|
|
693
|
+
* tells an automatic import from one a person asked for.
|
|
746
694
|
*
|
|
747
695
|
* So there is no progress on this namespace and nothing to poll here. Progress
|
|
748
696
|
* lives in {@link MusicArtistImportsNamespace.list}, mixed in with manual
|
|
749
|
-
* imports. Nothing pushes either over the
|
|
697
|
+
* imports. Nothing pushes either over the realtime stream.
|
|
750
698
|
*
|
|
751
699
|
* ## A LINKED SPOTIFY IDENTITY is required to write, not to read
|
|
752
700
|
*
|
|
753
|
-
* {@link create}
|
|
754
|
-
* "spotify")` and answers `400 "Connect Spotify first."` without one. {@link
|
|
701
|
+
* {@link create} answers `400 "Connect Spotify first."` without one. {@link
|
|
755
702
|
* list} and {@link delete} are not gated, which matters after an unlink: the
|
|
756
|
-
* rows survive, they still list, they can still be deleted, and the daily
|
|
757
|
-
* quietly skips them
|
|
703
|
+
* rows survive, they still list, they can still be deleted, and the daily
|
|
704
|
+
* check quietly skips them without recording that it did.
|
|
758
705
|
*
|
|
759
706
|
* ## Ceilings and cost
|
|
760
707
|
*
|
|
761
708
|
* Every route here is on the GENERAL authenticated ceiling of 600/min.
|
|
762
|
-
* `/artist_syncs`
|
|
763
|
-
*
|
|
764
|
-
*
|
|
765
|
-
* slowest call in this file.
|
|
709
|
+
* `/artist_syncs` is not in the 60/min `/artists/*` bucket and has no budget
|
|
710
|
+
* of its own, even though {@link create} is by far the slowest call in this
|
|
711
|
+
* file.
|
|
766
712
|
*
|
|
767
713
|
* ## An OAuth access token cannot reach any of this
|
|
768
714
|
*
|
|
769
|
-
*
|
|
770
|
-
*
|
|
771
|
-
* here as it does everywhere else in `music`. Session token or cookie only.
|
|
715
|
+
* An OAuth access token gets `403 {"error":"insufficient_scope"}` here as it
|
|
716
|
+
* does everywhere else in `music`. Session token or cookie only.
|
|
772
717
|
*/
|
|
773
718
|
export declare class MusicArtistSyncsNamespace extends Resource {
|
|
774
719
|
/**
|
|
@@ -784,8 +729,8 @@ export declare class MusicArtistSyncsNamespace extends Resource {
|
|
|
784
729
|
* `/artist_imports` uses and the opposite of the bare arrays the rest of the
|
|
785
730
|
* API returns - and is unwrapped here.
|
|
786
731
|
*
|
|
787
|
-
* Safe to retry, and scoped to the caller:
|
|
788
|
-
*
|
|
732
|
+
* Safe to retry, and scoped to the caller: there is no way to read anyone
|
|
733
|
+
* else's follows.
|
|
789
734
|
*/
|
|
790
735
|
list(options?: RequestOptions): Promise<ArtistSync[]>;
|
|
791
736
|
/**
|
|
@@ -798,35 +743,31 @@ export declare class MusicArtistSyncsNamespace extends Resource {
|
|
|
798
743
|
*
|
|
799
744
|
* ## Idempotent, unlike its neighbour
|
|
800
745
|
*
|
|
801
|
-
*
|
|
802
|
-
*
|
|
803
|
-
*
|
|
804
|
-
*
|
|
805
|
-
*
|
|
806
|
-
*
|
|
807
|
-
* releases that arrived in between.
|
|
746
|
+
* One row per artist per account, so calling this twice for one artist
|
|
747
|
+
* updates a single row instead of creating a second - it re-enables the
|
|
748
|
+
* sync, overwrites the name if one was sent, and touches `last_checked_at`.
|
|
749
|
+
* It does NOT re-snapshot when a snapshot already exists, so a second create
|
|
750
|
+
* is cheap and, more importantly, cannot silently widen the baseline and
|
|
751
|
+
* swallow releases that arrived in between.
|
|
808
752
|
*
|
|
809
753
|
* That is why this one opts INTO retries (`retry: {}`) while
|
|
810
754
|
* {@link MusicArtistImportsNamespace.create} refuses them: a replayed follow
|
|
811
755
|
* converges on the same row, a replayed import runs a whole discography
|
|
812
756
|
* twice. Pass `retry: false` to opt back out.
|
|
813
757
|
*
|
|
814
|
-
* ## It is slow, because it walks the discography
|
|
758
|
+
* ## It is slow, because it walks the discography before answering
|
|
815
759
|
*
|
|
816
|
-
*
|
|
817
|
-
* response is written, exactly like
|
|
760
|
+
* Every album is paged through before the response is written, exactly like
|
|
818
761
|
* {@link MusicArtistImportsNamespace.albums}. Sixty seconds by default. The
|
|
819
762
|
* one case that is instant is a re-create over a row that already has a
|
|
820
763
|
* snapshot.
|
|
821
764
|
*
|
|
822
765
|
* An artist with a genuinely EMPTY catalogue never stops paying that cost:
|
|
823
|
-
*
|
|
766
|
+
* an empty snapshot counts as no snapshot, so every create for it walks
|
|
767
|
+
* Spotify again.
|
|
824
768
|
*
|
|
825
769
|
* @throws {OmsError} `invalid_request` when `spotifyArtistId` is blank. The
|
|
826
|
-
* server would answer `400` for it too
|
|
827
|
-
* `ParameterMissing` and this controller, unlike the import one, RESCUES it
|
|
828
|
-
* into a normal bad request, so it does not page the owner - but the round
|
|
829
|
-
* trip buys nothing.
|
|
770
|
+
* server would answer `400` for it too, but the round trip buys nothing.
|
|
830
771
|
* @throws {OmsApiError} 400 `"Connect Spotify first."` with no linked
|
|
831
772
|
* identity, `"Spotify connection needs to be relinked."` on a dead refresh
|
|
832
773
|
* token, or `"Spotify upstream error: ..."` (truncated to 200 characters).
|
|
@@ -854,8 +795,8 @@ export declare class MusicArtistSyncsNamespace extends Resource {
|
|
|
854
795
|
* would find nothing and report `404` for a row it had just removed.
|
|
855
796
|
*
|
|
856
797
|
* @throws {OmsApiError} 404 `"Artist sync not found"` for an id that is not
|
|
857
|
-
* yours or no longer exists. A non-numeric id lands here too
|
|
858
|
-
*
|
|
798
|
+
* yours or no longer exists. A non-numeric id lands here too rather than
|
|
799
|
+
* being rejected.
|
|
859
800
|
*/
|
|
860
801
|
delete(id: ArtistSyncId, options?: RequestOptions): Promise<void>;
|
|
861
802
|
}
|
|
@@ -869,14 +810,12 @@ export declare class MusicArtistsNamespace extends Resource {
|
|
|
869
810
|
/**
|
|
870
811
|
* `GET /artists` - the roster.
|
|
871
812
|
*
|
|
872
|
-
* On the GENERAL ceiling (600/min), not the 60/min artist bucket
|
|
873
|
-
*
|
|
874
|
-
* is normalised to `/artists`. It is the only route in this namespace with
|
|
875
|
-
* that luxury.
|
|
813
|
+
* On the GENERAL ceiling (600/min), not the 60/min artist bucket. It is the
|
|
814
|
+
* only route in this namespace with that luxury.
|
|
876
815
|
*
|
|
877
|
-
* Rows are the
|
|
878
|
-
*
|
|
879
|
-
*
|
|
816
|
+
* Rows are the base {@link Artist}: no `bio_html`, no `gallery_image_urls`,
|
|
817
|
+
* no `similar`. `songs_count` and `fallback_artwork_media_id` ARE filled in
|
|
818
|
+
* here.
|
|
880
819
|
*
|
|
881
820
|
* ALWAYS PASS `order`. See {@link ListArtistsParams.order} - the relation
|
|
882
821
|
* has no order of its own, and paging an unordered relation loses rows.
|
|
@@ -900,17 +839,16 @@ export declare class MusicArtistsNamespace extends Resource {
|
|
|
900
839
|
* Three lookups in order: a purely numeric segment is an id, otherwise a
|
|
901
840
|
* slug, otherwise a canonical name. So `get(42)`, `get("chico-buarque")` and
|
|
902
841
|
* `get("Chico Buarque")` all work, and an artist whose slug is all digits
|
|
903
|
-
* would be unreachable by slug - a case the
|
|
842
|
+
* would be unreachable by slug - a case the server does not handle and
|
|
904
843
|
* nothing in practice produces.
|
|
905
844
|
*
|
|
906
845
|
* ## This call can be slow the FIRST time, and it is not the network
|
|
907
846
|
*
|
|
908
|
-
*
|
|
909
|
-
*
|
|
910
|
-
*
|
|
911
|
-
*
|
|
912
|
-
*
|
|
913
|
-
* by a background job, deduplicated to one job per artist per ten minutes.
|
|
847
|
+
* Metadata is refreshed stale-while-revalidate, with one exception. A row
|
|
848
|
+
* that has never been populated is filled in INLINE, before the response: a
|
|
849
|
+
* MusicBrainz search, two Last.fm calls and a Wikimedia gallery fetch. A row
|
|
850
|
+
* that merely went stale is served immediately and refreshed in the
|
|
851
|
+
* background, at most once per artist per ten minutes.
|
|
914
852
|
*
|
|
915
853
|
* So budget a generous `timeoutMs` for a cold artist, and do not read a
|
|
916
854
|
* slow first load as a broken server. What you must NOT do is retry it fast:
|
|
@@ -918,7 +856,7 @@ export declare class MusicArtistsNamespace extends Resource {
|
|
|
918
856
|
*
|
|
919
857
|
* ## What null metadata means
|
|
920
858
|
*
|
|
921
|
-
* If every external call fails, the
|
|
859
|
+
* If every external call fails, the server still stamps `bio_fetched_at`,
|
|
922
860
|
* `similar_fetched_at` and the gallery timestamp, so the upstream is not
|
|
923
861
|
* hammered on every page view. The response is a normal `200` with
|
|
924
862
|
* `bio_html: null` and `similar: []`, and it will stay that way until the TTL
|
|
@@ -938,8 +876,8 @@ export declare class MusicArtistsNamespace extends Resource {
|
|
|
938
876
|
/**
|
|
939
877
|
* `GET /artists/overview` - the whole Artists page header in one request.
|
|
940
878
|
*
|
|
941
|
-
* **Cached server-side for one hour, per user
|
|
942
|
-
*
|
|
879
|
+
* **Cached server-side for one hour, per user.** Two consequences worth
|
|
880
|
+
* designing around:
|
|
943
881
|
* polling it is pointless, and an artist you just renamed or gave a new
|
|
944
882
|
* picture keeps its old card here for up to an hour while
|
|
945
883
|
* {@link MusicArtistsNamespace.get} already shows the new one. A client that
|
|
@@ -954,12 +892,10 @@ export declare class MusicArtistsNamespace extends Resource {
|
|
|
954
892
|
*
|
|
955
893
|
* ## The body is FLAT
|
|
956
894
|
*
|
|
957
|
-
* `{ name, gallery_image_urls }` at the top level.
|
|
958
|
-
* `
|
|
959
|
-
*
|
|
960
|
-
*
|
|
961
|
-
* not an error, it is a silent no-op, and it is the reason this method takes
|
|
962
|
-
* the fields rather than a body.
|
|
895
|
+
* `{ name, gallery_image_urls }` at the top level. A body nested under an
|
|
896
|
+
* `artist` key is not an error, it is a silent no-op: the server answers
|
|
897
|
+
* `200` with the record UNCHANGED. That is the reason this method takes the
|
|
898
|
+
* fields rather than a body.
|
|
963
899
|
*
|
|
964
900
|
* Fields outside those two are dropped in silence as well (the fail-closed
|
|
965
901
|
* 400 applies to filter buckets, not to update params), so read the returned
|
|
@@ -967,8 +903,8 @@ export declare class MusicArtistsNamespace extends Resource {
|
|
|
967
903
|
*
|
|
968
904
|
* ## Addressed by NUMERIC ID ONLY
|
|
969
905
|
*
|
|
970
|
-
* Unlike {@link get}, this
|
|
971
|
-
*
|
|
906
|
+
* Unlike {@link get}, this is addressed by id only, so a slug 404s - and
|
|
907
|
+
* with a different message,
|
|
972
908
|
* `"Resource not found"` rather than `"Artist not found"`. Resolve the slug
|
|
973
909
|
* with {@link get} first.
|
|
974
910
|
*
|
|
@@ -986,8 +922,8 @@ export declare class MusicArtistsNamespace extends Resource {
|
|
|
986
922
|
*
|
|
987
923
|
* ## A refusal arrives as 401, not 400
|
|
988
924
|
*
|
|
989
|
-
*
|
|
990
|
-
*
|
|
925
|
+
* An artist can be destroyed only by its owner and only when it has no
|
|
926
|
+
* credits left; anything else is
|
|
991
927
|
* `401 "You are not authorized to destroy this resource"`. So "this artist
|
|
992
928
|
* still has songs" and "this artist is not yours" are the SAME response, and
|
|
993
929
|
* the SDK surfaces both as an {@link OmsAuthError}.
|
|
@@ -1023,8 +959,8 @@ export declare class MusicArtistsNamespace extends Resource {
|
|
|
1023
959
|
/**
|
|
1024
960
|
* `POST /artists/:id/upload_banner` - multipart, field name **`banner`**.
|
|
1025
961
|
*
|
|
1026
|
-
* The field is `banner`, NOT `image
|
|
1027
|
-
*
|
|
962
|
+
* The field is `banner`, NOT `image`; sending `image` here answers
|
|
963
|
+
* `400 "banner required"`.
|
|
1028
964
|
*
|
|
1029
965
|
* ## Shared rules for both uploads
|
|
1030
966
|
*
|
|
@@ -1044,13 +980,13 @@ export declare class MusicArtistsNamespace extends Resource {
|
|
|
1044
980
|
* - Uploading purges the legacy `compressed_*` companion, because nothing
|
|
1045
981
|
* regenerates it for artists and a leftover copy would keep rendering the
|
|
1046
982
|
* OLD picture. Expect `compressed_banner_media_id` to be null afterwards.
|
|
1047
|
-
* - The response is
|
|
1048
|
-
* `fallback_artwork_media_id`
|
|
1049
|
-
*
|
|
983
|
+
* - The response is an {@link ArtistExtended} whose
|
|
984
|
+
* `fallback_artwork_media_id` is null even for an artist that has one.
|
|
985
|
+
* Merge the response into what you already had; do not replace it.
|
|
1050
986
|
* - Addressed like {@link get}, not like {@link update}: id, slug or
|
|
1051
987
|
* canonical name all resolve, and the 404 message is `"Artist not found"`.
|
|
1052
988
|
*
|
|
1053
|
-
* ## The three
|
|
989
|
+
* ## The three runtimes
|
|
1054
990
|
*
|
|
1055
991
|
* React Native passes the picked `{ uri, name, type }` straight through; the
|
|
1056
992
|
* transport appends it verbatim, which is the only thing that works there.
|