@omelhorsite/sdk 0.13.0 → 0.15.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/dist/index.js +27 -24
- package/dist/types/resources/music/social.d.ts +121 -81
- package/dist/types/resources/music/songs.d.ts +39 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13521,9 +13521,8 @@ var MUSIC_ASSISTANT_MAX_ACTIONS = 10;
|
|
|
13521
13521
|
var MUSIC_ASSISTANT_MAX_BODY_BYTES = 256 * 1024;
|
|
13522
13522
|
var MUSIC_ASSISTANT_READ_ONLY_AFTER_MS = 2 * 24 * 60 * 60 * 1000;
|
|
13523
13523
|
var MUSIC_ASSISTANT_TIMEOUT_MS = 90000;
|
|
13524
|
-
var MUSIC_DJ_HOURLY_CAP =
|
|
13524
|
+
var MUSIC_DJ_HOURLY_CAP = 90;
|
|
13525
13525
|
var MUSIC_DJ_TIMEOUT_MS = 120000;
|
|
13526
|
-
var MUSIC_DJ_BATCH_SIZE = 4;
|
|
13527
13526
|
|
|
13528
13527
|
class MusicJamsNamespace extends Resource {
|
|
13529
13528
|
async list(options = {}) {
|
|
@@ -13614,33 +13613,31 @@ class MusicAssistantNamespace extends Resource {
|
|
|
13614
13613
|
}
|
|
13615
13614
|
|
|
13616
13615
|
class MusicDjNamespace extends Resource {
|
|
13617
|
-
|
|
13618
|
-
const body = { next_song_id: input.nextSongId };
|
|
13619
|
-
if (input.previousSongId !== undefined && input.previousSongId !== null) {
|
|
13620
|
-
body["previous_song_id"] = input.previousSongId;
|
|
13621
|
-
}
|
|
13622
|
-
return this.http.post("/music_dj", body, {
|
|
13623
|
-
...options,
|
|
13624
|
-
timeoutMs: options.timeoutMs ?? MUSIC_DJ_TIMEOUT_MS,
|
|
13625
|
-
retry: options.retry ?? false
|
|
13626
|
-
});
|
|
13627
|
-
}
|
|
13628
|
-
batch(input = {}, options = {}) {
|
|
13616
|
+
next(input = {}, options = {}) {
|
|
13629
13617
|
const body = {};
|
|
13618
|
+
if (input.sessionId !== undefined)
|
|
13619
|
+
body["session_id"] = input.sessionId;
|
|
13630
13620
|
if (input.request !== undefined)
|
|
13631
13621
|
body["request"] = input.request;
|
|
13632
|
-
if (input.
|
|
13633
|
-
body["
|
|
13634
|
-
if (input.
|
|
13635
|
-
body["
|
|
13636
|
-
if (input.
|
|
13637
|
-
body["
|
|
13638
|
-
return this.http.post("/music_dj/
|
|
13622
|
+
if (input.skippedSongId !== undefined)
|
|
13623
|
+
body["skipped_song_id"] = input.skippedSongId;
|
|
13624
|
+
if (input.speak !== undefined)
|
|
13625
|
+
body["speak"] = input.speak;
|
|
13626
|
+
if (input.restart !== undefined)
|
|
13627
|
+
body["restart"] = input.restart;
|
|
13628
|
+
return this.http.post("/music_dj/next", body, {
|
|
13639
13629
|
...options,
|
|
13640
13630
|
timeoutMs: options.timeoutMs ?? MUSIC_DJ_TIMEOUT_MS,
|
|
13641
13631
|
retry: options.retry ?? false
|
|
13642
13632
|
});
|
|
13643
13633
|
}
|
|
13634
|
+
async session(options = {}) {
|
|
13635
|
+
const body = await this.http.get("/music_dj/session", options);
|
|
13636
|
+
return body.session ?? null;
|
|
13637
|
+
}
|
|
13638
|
+
async end(options = {}) {
|
|
13639
|
+
await this.http.delete("/music_dj/session", options);
|
|
13640
|
+
}
|
|
13644
13641
|
}
|
|
13645
13642
|
|
|
13646
13643
|
class MusicSocialNamespace extends Resource {
|
|
@@ -13743,7 +13740,8 @@ var SONG_FILTER_COLUMNS = Object.freeze([
|
|
|
13743
13740
|
"album",
|
|
13744
13741
|
"position",
|
|
13745
13742
|
"year",
|
|
13746
|
-
"artist"
|
|
13743
|
+
"artist",
|
|
13744
|
+
"language"
|
|
13747
13745
|
]);
|
|
13748
13746
|
var SONG_ORDER_COLUMNS = Object.freeze([
|
|
13749
13747
|
"id",
|
|
@@ -13938,7 +13936,13 @@ class MusicSongsNamespace extends Resource {
|
|
|
13938
13936
|
return listQuery(params, at, {
|
|
13939
13937
|
order: at === undefined ? undefined : "created_at:asc",
|
|
13940
13938
|
search: { title: params.title },
|
|
13941
|
-
exactSearch: {
|
|
13939
|
+
exactSearch: {
|
|
13940
|
+
album: params.album,
|
|
13941
|
+
year: params.year,
|
|
13942
|
+
id: params.ids,
|
|
13943
|
+
artist: params.artist,
|
|
13944
|
+
language: params.language
|
|
13945
|
+
},
|
|
13942
13946
|
top: params.artistRole === undefined ? {} : { artist_role: params.artistRole }
|
|
13943
13947
|
});
|
|
13944
13948
|
}
|
|
@@ -16283,7 +16287,6 @@ export {
|
|
|
16283
16287
|
MUSIC_PRESIGNED_URL_TTL_MS,
|
|
16284
16288
|
MUSIC_DJ_TIMEOUT_MS,
|
|
16285
16289
|
MUSIC_DJ_HOURLY_CAP,
|
|
16286
|
-
MUSIC_DJ_BATCH_SIZE,
|
|
16287
16290
|
MUSIC_ASSISTANT_TIMEOUT_MS,
|
|
16288
16291
|
MUSIC_ASSISTANT_READ_ONLY_AFTER_MS,
|
|
16289
16292
|
MUSIC_ASSISTANT_MAX_BODY_BYTES,
|
|
@@ -501,57 +501,99 @@ export interface SendMusicAssistantMessageInput {
|
|
|
501
501
|
readonly player?: MusicAssistantPlayerContext;
|
|
502
502
|
}
|
|
503
503
|
/**
|
|
504
|
-
*
|
|
504
|
+
* Turns per user per hour on `POST /music_dj/next`.
|
|
505
505
|
*
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
*
|
|
506
|
+
* A turn is one song, so an hour of listening spends about 17 of them and the
|
|
507
|
+
* rest of the budget absorbs skipping. The counter is incremented by EVERY
|
|
508
|
+
* request - including the ones it then refuses with `429` - so a retry loop
|
|
509
|
+
* drives the count further past the cap and can never recover inside the hour,
|
|
510
|
+
* which is why {@link MusicDjNamespace} passes `retry: false` unless you
|
|
511
|
+
* override it.
|
|
510
512
|
*/
|
|
511
|
-
export declare const MUSIC_DJ_HOURLY_CAP =
|
|
513
|
+
export declare const MUSIC_DJ_HOURLY_CAP = 90;
|
|
512
514
|
/**
|
|
513
|
-
* Default deadline for one DJ
|
|
514
|
-
*
|
|
515
|
+
* Default deadline for one DJ turn, in milliseconds. Choosing the song and
|
|
516
|
+
* speaking it takes well past the client's 60 s default.
|
|
515
517
|
*/
|
|
516
518
|
export declare const MUSIC_DJ_TIMEOUT_MS = 120000;
|
|
517
|
-
/**
|
|
518
|
-
export
|
|
519
|
-
/** `
|
|
520
|
-
|
|
521
|
-
/** The script as text, at most 320 characters. Worth showing while audio loads. */
|
|
522
|
-
readonly text: string;
|
|
519
|
+
/** One entry in a DJ session's transcript. */
|
|
520
|
+
export interface MusicDjTurn {
|
|
521
|
+
/** `"dj"` when he played or spoke, `"listener"` when they asked for something. */
|
|
522
|
+
readonly role: "dj" | "listener";
|
|
523
523
|
/**
|
|
524
|
-
*
|
|
525
|
-
*
|
|
524
|
+
* What was said. `null` on a DJ turn where he stayed quiet - he speaks on
|
|
525
|
+
* the first song, whenever the listener asks for something, and every third
|
|
526
|
+
* song after that, so most turns carry a song and no words.
|
|
526
527
|
*/
|
|
527
|
-
readonly
|
|
528
|
-
/**
|
|
529
|
-
readonly
|
|
528
|
+
readonly text: string | null;
|
|
529
|
+
/** The song that turn played, when it played one. */
|
|
530
|
+
readonly song_id: number | null;
|
|
531
|
+
readonly created_at: Timestamp;
|
|
530
532
|
}
|
|
531
|
-
/** `
|
|
532
|
-
export interface
|
|
533
|
+
/** `GET /music_dj/session`: the session still on air, if there is one. */
|
|
534
|
+
export interface MusicDjSession {
|
|
535
|
+
readonly id: number;
|
|
536
|
+
/** The listener's standing request. It holds until they ask for something else. */
|
|
537
|
+
readonly request: string | null;
|
|
538
|
+
readonly turns: MusicDjTurn[];
|
|
533
539
|
/**
|
|
534
|
-
*
|
|
535
|
-
*
|
|
536
|
-
* {@link
|
|
537
|
-
* empty set, so this is never `[]`.
|
|
540
|
+
* Every song the transcript refers to, in the full `GET /songs` shape, so a
|
|
541
|
+
* client can rebuild the session without a lookup per turn. Unordered: match
|
|
542
|
+
* them to {@link MusicDjTurn.song_id}.
|
|
538
543
|
*/
|
|
539
544
|
readonly songs: Song[];
|
|
540
545
|
}
|
|
541
|
-
/**
|
|
542
|
-
export interface
|
|
543
|
-
|
|
546
|
+
/** `POST /music_dj/next`: the next song, and the words for it when there are any. */
|
|
547
|
+
export interface MusicDjNext {
|
|
548
|
+
readonly session_id: number;
|
|
549
|
+
/** What to play now. Never one the session already played. */
|
|
550
|
+
readonly song: Song;
|
|
551
|
+
/** Two to five words naming this stretch of the show, for a screen. */
|
|
552
|
+
readonly theme: string | null;
|
|
553
|
+
/**
|
|
554
|
+
* The spoken link, present ONLY on the turns where he speaks. Its absence is
|
|
555
|
+
* the normal case, not an error: play the song straight away.
|
|
556
|
+
*/
|
|
557
|
+
readonly text?: string;
|
|
558
|
+
/**
|
|
559
|
+
* The same words spoken, base64, NOT a data URL. Decode with
|
|
560
|
+
* {@link musicDjAudioBytes} or wrap with {@link musicDjAudioDataUrl}.
|
|
561
|
+
*
|
|
562
|
+
* Absent when he stayed quiet AND when the voice service failed while he had
|
|
563
|
+
* something to say - in that second case `text` is there without it, and the
|
|
564
|
+
* song still plays. Never block playback on this field.
|
|
565
|
+
*/
|
|
566
|
+
readonly audio_base64?: string;
|
|
567
|
+
/** Container of the decoded bytes. `"wav"` today, and typed wide on purpose. */
|
|
568
|
+
readonly format?: string;
|
|
569
|
+
}
|
|
570
|
+
/** Everything `POST /music_dj/next` accepts. All of it optional. */
|
|
571
|
+
export interface MusicDjNextInput {
|
|
572
|
+
/**
|
|
573
|
+
* Continue this session. Omit to continue the caller's own session, which
|
|
574
|
+
* the server finds by itself - a session goes stale after two hours of
|
|
575
|
+
* silence and the next turn starts a fresh one.
|
|
576
|
+
*/
|
|
577
|
+
readonly sessionId?: number;
|
|
578
|
+
/**
|
|
579
|
+
* A steer ("something calmer", "fados"), truncated to 300 characters.
|
|
580
|
+
*
|
|
581
|
+
* STANDING ORDERS, not a one-off: it holds for every following turn until
|
|
582
|
+
* another request replaces it, and it makes him speak on this turn even if
|
|
583
|
+
* it was not his turn to.
|
|
584
|
+
*/
|
|
544
585
|
readonly request?: string;
|
|
586
|
+
/** The song the listener just skipped, as a direction to leave. */
|
|
587
|
+
readonly skippedSongId?: SongId;
|
|
545
588
|
/**
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
589
|
+
* Make him speak on this turn even if it was not his turn to.
|
|
590
|
+
*
|
|
591
|
+
* This is the DJ button: the listener wants to hear him now. It changes
|
|
592
|
+
* nothing about which song comes next.
|
|
549
593
|
*/
|
|
550
|
-
readonly
|
|
551
|
-
/**
|
|
552
|
-
readonly
|
|
553
|
-
/** Which set of the session this is, so the script can vary its opening. */
|
|
554
|
-
readonly batchIndex?: number;
|
|
594
|
+
readonly speak?: boolean;
|
|
595
|
+
/** Start a new session even though one is still on air. */
|
|
596
|
+
readonly restart?: boolean;
|
|
555
597
|
}
|
|
556
598
|
/**
|
|
557
599
|
* Jams over HTTP. The realtime half lives on the WebSocket stream and is not in
|
|
@@ -887,12 +929,16 @@ export declare class MusicAssistantNamespace extends Resource {
|
|
|
887
929
|
ask(messages: MusicAssistantMessage[], player?: MusicAssistantPlayerContext, options?: RequestOptions): Promise<MusicAssistantAnswer>;
|
|
888
930
|
}
|
|
889
931
|
/**
|
|
890
|
-
* "O Melhor DJ": a
|
|
891
|
-
*
|
|
932
|
+
* "O Melhor DJ": a radio session for one listener, one song at a time.
|
|
933
|
+
*
|
|
934
|
+
* The server holds the session - what played, what was skipped, what he said,
|
|
935
|
+
* what the listener asked for - so a client only ever says what just happened
|
|
936
|
+
* and gets the next song back. Ask for one when the current song ends, when
|
|
937
|
+
* the listener skips, or when they want something else.
|
|
892
938
|
*
|
|
893
|
-
*
|
|
939
|
+
* {@link next} passes `retry: false` by default. That is not caution about
|
|
894
940
|
* duplicates - the transport does not replay a `POST` anyway - it is about the
|
|
895
|
-
* one thing it DOES replay: a `429`. The hourly cap
|
|
941
|
+
* one thing it DOES replay: a `429`. The hourly cap counts every request
|
|
896
942
|
* including the refused ones, so waiting out a `Retry-After` and asking again
|
|
897
943
|
* pushes the count further past the cap and cannot succeed inside the hour.
|
|
898
944
|
* Pass `retry: {}` to opt back in if you are sure the `429` came from the
|
|
@@ -900,57 +946,46 @@ export declare class MusicAssistantNamespace extends Resource {
|
|
|
900
946
|
*/
|
|
901
947
|
export declare class MusicDjNamespace extends Resource {
|
|
902
948
|
/**
|
|
903
|
-
* `POST /music_dj` - the
|
|
949
|
+
* `POST /music_dj/next` - the next song, chosen with the whole session in
|
|
950
|
+
* view.
|
|
904
951
|
*
|
|
905
|
-
*
|
|
906
|
-
*
|
|
907
|
-
*
|
|
908
|
-
*
|
|
952
|
+
* `next({})` is a valid cold start: with no session on air the server opens
|
|
953
|
+
* one. The answer always carries a `song`; `text` and `audio_base64` come
|
|
954
|
+
* only on the turns where he speaks (the first song, after a request, and
|
|
955
|
+
* every third song), so treat their absence as normal and play the song.
|
|
909
956
|
*
|
|
910
|
-
*
|
|
911
|
-
*
|
|
912
|
-
* is what lets the script say goodbye to the outgoing track.
|
|
957
|
+
* A song the session already played is never returned, which is what lets a
|
|
958
|
+
* client keep the whole session in its queue and walk back through it.
|
|
913
959
|
*
|
|
914
|
-
*
|
|
915
|
-
* {@link MUSIC_DJ_TIMEOUT_MS}.
|
|
916
|
-
*
|
|
917
|
-
* playing and drop it on the boundary, ideally over the outgoing
|
|
918
|
-
* instrumental.
|
|
960
|
+
* Choosing takes seconds and speaking takes more; the deadline defaults to
|
|
961
|
+
* {@link MUSIC_DJ_TIMEOUT_MS}. Ask for the next song while the current one
|
|
962
|
+
* is still playing, or the listener hears the gap.
|
|
919
963
|
*
|
|
920
964
|
* @throws {OmsError} `401 "Session required"` when unauthenticated.
|
|
921
|
-
* @throws {OmsError} `404 "Song not found"` for either id.
|
|
922
965
|
* @throws {OmsError} `429 "DJ limit reached, try again later"` past
|
|
923
966
|
* {@link MUSIC_DJ_HOURLY_CAP}.
|
|
924
|
-
* @throws {OmsError} `502 "DJ is unavailable right now"` when
|
|
925
|
-
*
|
|
926
|
-
*
|
|
927
|
-
* say them.
|
|
967
|
+
* @throws {OmsError} `502 "DJ is unavailable right now"` when no song could
|
|
968
|
+
* be chosen - including the honest case where the library has nothing left
|
|
969
|
+
* that the session has not already played.
|
|
928
970
|
*/
|
|
929
|
-
|
|
930
|
-
readonly nextSongId: SongId;
|
|
931
|
-
readonly previousSongId?: SongId | null;
|
|
932
|
-
}, options?: RequestOptions): Promise<MusicDjInterstitial>;
|
|
971
|
+
next(input?: MusicDjNextInput, options?: RequestOptions): Promise<MusicDjNext>;
|
|
933
972
|
/**
|
|
934
|
-
* `
|
|
935
|
-
* introducing it, from one model call.
|
|
973
|
+
* `GET /music_dj/session` - the session still on air, or `null`.
|
|
936
974
|
*
|
|
937
|
-
*
|
|
938
|
-
*
|
|
939
|
-
*
|
|
940
|
-
*
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
*
|
|
945
|
-
* the last 60 are read anyway.
|
|
946
|
-
*
|
|
947
|
-
* `songs` comes back in play order, in the full `GET /songs` shape, already
|
|
948
|
-
* scoped to what the caller may play. It is never empty.
|
|
975
|
+
* `null` is the ordinary answer for someone who has not listened today: a
|
|
976
|
+
* session goes stale after two hours of silence and stops being served here.
|
|
977
|
+
* Use it to rebuild the transcript when a screen opens, not to decide
|
|
978
|
+
* whether {@link next} may be called - it always may.
|
|
979
|
+
*/
|
|
980
|
+
session(options?: RequestOptions): Promise<MusicDjSession | null>;
|
|
981
|
+
/**
|
|
982
|
+
* `DELETE /music_dj/session` - end the session on air.
|
|
949
983
|
*
|
|
950
|
-
*
|
|
951
|
-
*
|
|
984
|
+
* Idempotent: with nothing on air it still answers `204`. The next
|
|
985
|
+
* {@link next} starts a session with no memory of this one, which is the
|
|
986
|
+
* point - it is how a listener says "forget what I asked for".
|
|
952
987
|
*/
|
|
953
|
-
|
|
988
|
+
end(options?: RequestOptions): Promise<void>;
|
|
954
989
|
}
|
|
955
990
|
/**
|
|
956
991
|
* The `music.social` entry point, holding the five families as sub-namespaces.
|
|
@@ -1045,7 +1080,9 @@ export declare function musicStorageAffords(usage: MusicStorageUsage, bytes: num
|
|
|
1045
1080
|
* slightly short audio is a better failure than one that throws inside a
|
|
1046
1081
|
* playback callback.
|
|
1047
1082
|
*/
|
|
1048
|
-
export declare function musicDjAudioBytes(clip:
|
|
1083
|
+
export declare function musicDjAudioBytes(clip: {
|
|
1084
|
+
readonly audio_base64: string;
|
|
1085
|
+
}): Uint8Array;
|
|
1049
1086
|
/**
|
|
1050
1087
|
* Wraps a DJ clip as a `data:` URI, for a player that takes a URI rather than
|
|
1051
1088
|
* bytes - which is most of them.
|
|
@@ -1054,4 +1091,7 @@ export declare function musicDjAudioBytes(clip: Pick<MusicDjInterstitial, "audio
|
|
|
1054
1091
|
* in memory, so this is cheap in every sense that matters at this size. It is
|
|
1055
1092
|
* not a URL anything can fetch twice - it is the bytes, spelled differently.
|
|
1056
1093
|
*/
|
|
1057
|
-
export declare function musicDjAudioDataUrl(clip:
|
|
1094
|
+
export declare function musicDjAudioDataUrl(clip: {
|
|
1095
|
+
readonly audio_base64: string;
|
|
1096
|
+
readonly format?: string;
|
|
1097
|
+
}): string;
|
|
@@ -143,6 +143,35 @@ export interface Song extends Omit<BaseRecord, "id"> {
|
|
|
143
143
|
readonly source_id: string | null;
|
|
144
144
|
/** Recording identifier, when the importer resolved one. */
|
|
145
145
|
readonly isrc: string | null;
|
|
146
|
+
/**
|
|
147
|
+
* ISO 639-1 code of the language SUNG on the track (`"es"`, `"pt"`, `"ja"`),
|
|
148
|
+
* or `null`.
|
|
149
|
+
*
|
|
150
|
+
* Derived, not declared: the server runs a detector over the stored lyrics,
|
|
151
|
+
* and falls back to unambiguous Last.fm tags (`"j-pop"`, `"fado"`) when
|
|
152
|
+
* there are none. So `null` means "not known yet" - a song whose lyrics were
|
|
153
|
+
* never fetched, or an instrumental - and NEVER "no language". Treat it as
|
|
154
|
+
* unknown rather than quietly dropping it from a language view.
|
|
155
|
+
*/
|
|
156
|
+
readonly language: string | null;
|
|
157
|
+
/**
|
|
158
|
+
* Lowercased Last.fm tags for the track, most-voted first, at most six.
|
|
159
|
+
*
|
|
160
|
+
* Crowd tags, not a controlled vocabulary: expect `"rock"` and
|
|
161
|
+
* `"female vocalists"` in the same list, and expect `[]` for anything
|
|
162
|
+
* obscure. The server drops the junk ones ("seen live", "albums i own") and
|
|
163
|
+
* falls back to the artist's tags when the track has none of its own, which
|
|
164
|
+
* is why two songs by one artist often carry identical lists.
|
|
165
|
+
*/
|
|
166
|
+
readonly tags: string[];
|
|
167
|
+
/**
|
|
168
|
+
* Tempo in beats per minute, or `null` when it is not known.
|
|
169
|
+
*
|
|
170
|
+
* Comes from the recording's own registry entry, matched by ISRC, so a
|
|
171
|
+
* track with no ISRC never gets one and a remix is never given the
|
|
172
|
+
* original's tempo. `null` is common and means nothing about the music.
|
|
173
|
+
*/
|
|
174
|
+
readonly bpm: number | null;
|
|
146
175
|
readonly original_filename: string | null;
|
|
147
176
|
readonly audio_codec: string | null;
|
|
148
177
|
readonly audio_bitrate_kbps: number | null;
|
|
@@ -445,7 +474,7 @@ export interface MusicExternalSearchResult {
|
|
|
445
474
|
* `artist` is on the list but is not a column - see
|
|
446
475
|
* {@link ListSongsParams.artist}.
|
|
447
476
|
*/
|
|
448
|
-
export declare const SONG_FILTER_COLUMNS: readonly ["id", "created_at", "updated_at", "title", "album", "position", "year", "artist"];
|
|
477
|
+
export declare const SONG_FILTER_COLUMNS: readonly ["id", "created_at", "updated_at", "title", "album", "position", "year", "artist", "language"];
|
|
449
478
|
/** Columns the backend will accept in `modifiers[order]`. */
|
|
450
479
|
export declare const SONG_ORDER_COLUMNS: readonly string[];
|
|
451
480
|
/** Filters shared by `GET /songs` and `GET /songs/albums`. */
|
|
@@ -503,6 +532,15 @@ export interface SongFilters {
|
|
|
503
532
|
* excluded even if they also appear as a feature.
|
|
504
533
|
*/
|
|
505
534
|
readonly artistRole?: SongArtistRole;
|
|
535
|
+
/**
|
|
536
|
+
* Exact language code, or a list of them, sent as `exact_search[language]`.
|
|
537
|
+
*
|
|
538
|
+
* Matches {@link Song.language}, with the same caveat: rows the server could
|
|
539
|
+
* not classify hold `null` and are NOT returned by any code you ask for.
|
|
540
|
+
* Pass `null` to get exactly those - the unclassified pile - which is a
|
|
541
|
+
* different question from "songs with no words".
|
|
542
|
+
*/
|
|
543
|
+
readonly language?: string | string[] | null;
|
|
506
544
|
}
|
|
507
545
|
/** Arguments for {@link MusicSongsNamespace.list}. */
|
|
508
546
|
export interface ListSongsParams extends SongFilters, ListParams<(typeof SONG_FILTER_COLUMNS)[number]> {
|
package/package.json
CHANGED