@misofm/sdk 0.4.0 → 0.6.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.
- package/dist/catalog.d.ts +4 -17
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +46 -58
- package/dist/catalog.js.map +1 -1
- package/dist/cover.d.ts +10 -0
- package/dist/cover.d.ts.map +1 -1
- package/dist/cover.js +66 -19
- package/dist/cover.js.map +1 -1
- package/dist/credits.d.ts +10 -0
- package/dist/credits.d.ts.map +1 -1
- package/dist/credits.js +149 -39
- package/dist/credits.js.map +1 -1
- package/dist/drop.d.ts +7 -0
- package/dist/drop.d.ts.map +1 -1
- package/dist/drop.js +84 -34
- package/dist/drop.js.map +1 -1
- package/dist/pressing.d.ts.map +1 -1
- package/dist/pressing.js +59 -16
- package/dist/pressing.js.map +1 -1
- package/dist/read/artist.d.ts +1 -6
- package/dist/read/artist.d.ts.map +1 -1
- package/dist/read/artist.js +39 -22
- package/dist/read/artist.js.map +1 -1
- package/dist/read/catalog.d.ts +24 -3
- package/dist/read/catalog.d.ts.map +1 -1
- package/dist/read/catalog.js +190 -89
- package/dist/read/catalog.js.map +1 -1
- package/dist/read/index.d.ts +6 -4
- package/dist/read/index.d.ts.map +1 -1
- package/dist/read/index.js +3 -3
- package/dist/read/index.js.map +1 -1
- package/dist/read/types.d.ts +21 -1
- package/dist/read/types.d.ts.map +1 -1
- package/dist/read/wallet.d.ts +10 -1
- package/dist/read/wallet.d.ts.map +1 -1
- package/dist/read/wallet.js +28 -0
- package/dist/read/wallet.js.map +1 -1
- package/dist/read/works.d.ts +3 -2
- package/dist/read/works.d.ts.map +1 -1
- package/dist/read/works.js +158 -48
- package/dist/read/works.js.map +1 -1
- package/package.json +2 -2
- package/src/catalog.ts +71 -77
- package/src/cover.ts +107 -26
- package/src/credits.ts +353 -89
- package/src/drop.ts +150 -40
- package/src/pressing.ts +146 -36
- package/src/read/artist.ts +73 -34
- package/src/read/catalog.ts +324 -89
- package/src/read/index.ts +24 -2
- package/src/read/types.ts +26 -2
- package/src/read/wallet.ts +45 -1
- package/src/read/works.ts +224 -59
package/src/credits.ts
CHANGED
|
@@ -31,10 +31,13 @@
|
|
|
31
31
|
import type { ClientWithCoreApi } from "@mysten/sui/client";
|
|
32
32
|
import { bcs } from "@mysten/sui/bcs";
|
|
33
33
|
import { deriveDynamicFieldID } from "@mysten/sui/utils";
|
|
34
|
-
import type {
|
|
34
|
+
import type {
|
|
35
|
+
Transaction,
|
|
36
|
+
TransactionArgument,
|
|
37
|
+
TransactionObjectArgument,
|
|
38
|
+
} from "@mysten/sui/transactions";
|
|
35
39
|
import type { TxThunk } from "./transactions.ts";
|
|
36
40
|
import { OPTION_NONE, OPTION_SOME } from "./internal.ts";
|
|
37
|
-
import { isNotFound } from "./queries.ts";
|
|
38
41
|
import * as compositionCredits from "./contracts/composition_credits/composition_credits.ts";
|
|
39
42
|
import * as compositionPartyRole from "./contracts/composition_credits/composition_party_role.ts";
|
|
40
43
|
import * as recordingCredits from "./contracts/recording_credits/recording_credits.ts";
|
|
@@ -46,7 +49,15 @@ import * as releasePartyRole from "./contracts/release_credits/release_party_rol
|
|
|
46
49
|
|
|
47
50
|
/** A composition writing role. `Custom` carries a free-form (validated) name. */
|
|
48
51
|
export type CompositionRole =
|
|
49
|
-
| {
|
|
52
|
+
| {
|
|
53
|
+
type:
|
|
54
|
+
| "Adapter"
|
|
55
|
+
| "Arranger"
|
|
56
|
+
| "Composer"
|
|
57
|
+
| "Lyricist"
|
|
58
|
+
| "Songwriter"
|
|
59
|
+
| "Translator";
|
|
60
|
+
}
|
|
50
61
|
| { type: "Custom"; name: string };
|
|
51
62
|
|
|
52
63
|
/** Seniority/prominence level for a recording role. */
|
|
@@ -112,9 +123,15 @@ export type ReleaseRole = "Primary" | "Featured";
|
|
|
112
123
|
// the constructor move-call; `tx.add` runs it and yields the role value.
|
|
113
124
|
type RoleThunk = (tx: Transaction) => TransactionObjectArgument;
|
|
114
125
|
type LevelCtor = (options: { package?: string }) => RoleThunk;
|
|
115
|
-
type LeveledRoleCtor = (options: {
|
|
116
|
-
|
|
117
|
-
|
|
126
|
+
type LeveledRoleCtor = (options: {
|
|
127
|
+
package?: string;
|
|
128
|
+
arguments: [TransactionArgument];
|
|
129
|
+
}) => RoleThunk;
|
|
130
|
+
|
|
131
|
+
const COMPOSITION_ROLE_CTOR: Record<
|
|
132
|
+
Exclude<CompositionRole["type"], "Custom">,
|
|
133
|
+
LevelCtor
|
|
134
|
+
> = {
|
|
118
135
|
Adapter: compositionPartyRole.newAdapterRole,
|
|
119
136
|
Arranger: compositionPartyRole.newArrangerRole,
|
|
120
137
|
Composer: compositionPartyRole.newComposerRole,
|
|
@@ -135,7 +152,10 @@ const RECORDING_LEVEL_CTOR: Record<RecordingRoleLevel, LevelCtor> = {
|
|
|
135
152
|
Principal: recordingPartyRole.newPrincipalRoleLevel,
|
|
136
153
|
};
|
|
137
154
|
|
|
138
|
-
const RECORDING_LEVELED_ROLE_CTOR: Record<
|
|
155
|
+
const RECORDING_LEVELED_ROLE_CTOR: Record<
|
|
156
|
+
RecordingLeveledRoleType,
|
|
157
|
+
LeveledRoleCtor
|
|
158
|
+
> = {
|
|
139
159
|
Actor: recordingPartyRole.newActorRole,
|
|
140
160
|
Arranger: recordingPartyRole.newArrangerRole,
|
|
141
161
|
BandLeader: recordingPartyRole.newBandLeaderRole,
|
|
@@ -173,24 +193,44 @@ function levelOption(
|
|
|
173
193
|
level: RecordingRoleLevel | undefined,
|
|
174
194
|
): TransactionArgument {
|
|
175
195
|
const levelType = `${pkg}::recording_party_role::RecordingPartyRoleLevel`;
|
|
176
|
-
if (!level)
|
|
196
|
+
if (!level)
|
|
197
|
+
return tx.moveCall({ target: OPTION_NONE, typeArguments: [levelType] });
|
|
177
198
|
const lvl = tx.add(RECORDING_LEVEL_CTOR[level]({ package: pkg }));
|
|
178
|
-
return tx.moveCall({
|
|
199
|
+
return tx.moveCall({
|
|
200
|
+
target: OPTION_SOME,
|
|
201
|
+
typeArguments: [levelType],
|
|
202
|
+
arguments: [lvl],
|
|
203
|
+
});
|
|
179
204
|
}
|
|
180
205
|
|
|
181
206
|
/** Builds one `CompositionPartyRole` value in the PTB. */
|
|
182
|
-
function buildCompositionRole(
|
|
207
|
+
function buildCompositionRole(
|
|
208
|
+
tx: Transaction,
|
|
209
|
+
pkg: string,
|
|
210
|
+
role: CompositionRole,
|
|
211
|
+
): TransactionObjectArgument {
|
|
183
212
|
if (role.type === "Custom") {
|
|
184
|
-
return tx.add(
|
|
213
|
+
return tx.add(
|
|
214
|
+
compositionPartyRole.newCustomRole({
|
|
215
|
+
package: pkg,
|
|
216
|
+
arguments: [role.name],
|
|
217
|
+
}),
|
|
218
|
+
);
|
|
185
219
|
}
|
|
186
220
|
return tx.add(COMPOSITION_ROLE_CTOR[role.type]({ package: pkg }));
|
|
187
221
|
}
|
|
188
222
|
|
|
189
223
|
/** Builds one `RecordingPartyRole` value in the PTB. */
|
|
190
|
-
function buildRecordingRole(
|
|
224
|
+
function buildRecordingRole(
|
|
225
|
+
tx: Transaction,
|
|
226
|
+
pkg: string,
|
|
227
|
+
role: RecordingRole,
|
|
228
|
+
): TransactionObjectArgument {
|
|
191
229
|
switch (role.type) {
|
|
192
230
|
case "ArtistsAndRepertoire":
|
|
193
|
-
return tx.add(
|
|
231
|
+
return tx.add(
|
|
232
|
+
recordingPartyRole.newArtistsAndRepertoireRole({ package: pkg }),
|
|
233
|
+
);
|
|
194
234
|
case "Copyist":
|
|
195
235
|
return tx.add(recordingPartyRole.newCopyistRole({ package: pkg }));
|
|
196
236
|
case "Instrumentalist":
|
|
@@ -218,8 +258,15 @@ function buildRecordingRole(tx: Transaction, pkg: string, role: RecordingRole):
|
|
|
218
258
|
}
|
|
219
259
|
|
|
220
260
|
/** Builds one `ReleasePartyRole` value in the PTB. */
|
|
221
|
-
function buildReleaseRole(
|
|
222
|
-
|
|
261
|
+
function buildReleaseRole(
|
|
262
|
+
tx: Transaction,
|
|
263
|
+
pkg: string,
|
|
264
|
+
role: ReleaseRole,
|
|
265
|
+
): TransactionObjectArgument {
|
|
266
|
+
const ctor =
|
|
267
|
+
role === "Primary"
|
|
268
|
+
? releasePartyRole.newPrimaryRole
|
|
269
|
+
: releasePartyRole.newFeaturedRole;
|
|
223
270
|
return tx.add(ctor({ package: pkg }));
|
|
224
271
|
}
|
|
225
272
|
|
|
@@ -237,10 +284,13 @@ const MAX_COMPOSITION_ROLES = 5;
|
|
|
237
284
|
const MAX_RECORDING_ROLES = 10;
|
|
238
285
|
|
|
239
286
|
function assertDisplayName(fn: string, displayName: string): void {
|
|
240
|
-
if (displayName.length === 0)
|
|
287
|
+
if (displayName.length === 0)
|
|
288
|
+
throw new Error(`${fn}: displayName must not be empty`);
|
|
241
289
|
const byteLength = new TextEncoder().encode(displayName).length;
|
|
242
290
|
if (byteLength > MAX_DISPLAY_NAME_BYTES) {
|
|
243
|
-
throw new Error(
|
|
291
|
+
throw new Error(
|
|
292
|
+
`${fn}: displayName must be at most ${MAX_DISPLAY_NAME_BYTES} bytes of UTF-8 (got ${byteLength})`,
|
|
293
|
+
);
|
|
244
294
|
}
|
|
245
295
|
}
|
|
246
296
|
|
|
@@ -255,9 +305,16 @@ function roleKey(role: CompositionRole | RecordingRole): string {
|
|
|
255
305
|
]);
|
|
256
306
|
}
|
|
257
307
|
|
|
258
|
-
function assertRoles(
|
|
308
|
+
function assertRoles(
|
|
309
|
+
fn: string,
|
|
310
|
+
roles: (CompositionRole | RecordingRole)[],
|
|
311
|
+
max: number,
|
|
312
|
+
): void {
|
|
259
313
|
if (roles.length < 1) throw new Error(`${fn}: at least one role is required`);
|
|
260
|
-
if (roles.length > max)
|
|
314
|
+
if (roles.length > max)
|
|
315
|
+
throw new Error(
|
|
316
|
+
`${fn}: at most ${max} roles are allowed (got ${roles.length})`,
|
|
317
|
+
);
|
|
261
318
|
const seen = new Set<string>();
|
|
262
319
|
for (const role of roles) {
|
|
263
320
|
const key = roleKey(role);
|
|
@@ -308,18 +365,33 @@ export interface AttachCompositionCreditParams {
|
|
|
308
365
|
* mirroring the Move aborts) when `displayName` is empty or over 200 UTF-8
|
|
309
366
|
* bytes, or `roles` is empty, has more than 5 entries, or contains duplicates.
|
|
310
367
|
*/
|
|
311
|
-
export function attachCompositionCredit(
|
|
368
|
+
export function attachCompositionCredit(
|
|
369
|
+
p: AttachCompositionCreditParams,
|
|
370
|
+
): TxThunk {
|
|
312
371
|
assertDisplayName("attachCompositionCredit", p.displayName);
|
|
313
372
|
assertRoles("attachCompositionCredit", p.roles, MAX_COMPOSITION_ROLES);
|
|
314
373
|
return (tx) => {
|
|
315
374
|
const roleType = `${p.compositionCreditsPackageId}::composition_party_role::CompositionPartyRole`;
|
|
316
|
-
const roleArgs = p.roles.map((r) =>
|
|
317
|
-
|
|
375
|
+
const roleArgs = p.roles.map((r) =>
|
|
376
|
+
buildCompositionRole(tx, p.compositionCreditsPackageId, r),
|
|
377
|
+
);
|
|
378
|
+
const credit = buildCredit(
|
|
379
|
+
tx,
|
|
380
|
+
p.misoCreditPackageId,
|
|
381
|
+
roleType,
|
|
382
|
+
p.displayName,
|
|
383
|
+
roleArgs,
|
|
384
|
+
);
|
|
318
385
|
tx.add(
|
|
319
386
|
compositionCredits.addCredit({
|
|
320
387
|
package: p.compositionCreditsPackageId,
|
|
321
388
|
typeArguments: [p.compositionShareType],
|
|
322
|
-
arguments: [
|
|
389
|
+
arguments: [
|
|
390
|
+
p.compositionId,
|
|
391
|
+
p.compositionAdminCapId,
|
|
392
|
+
p.partyId,
|
|
393
|
+
credit,
|
|
394
|
+
],
|
|
323
395
|
}),
|
|
324
396
|
);
|
|
325
397
|
};
|
|
@@ -357,8 +429,16 @@ export function attachRecordingCredit(p: AttachRecordingCreditParams): TxThunk {
|
|
|
357
429
|
assertRoles("attachRecordingCredit", p.roles, MAX_RECORDING_ROLES);
|
|
358
430
|
return (tx) => {
|
|
359
431
|
const roleType = `${p.recordingCreditsPackageId}::recording_party_role::RecordingPartyRole`;
|
|
360
|
-
const roleArgs = p.roles.map((r) =>
|
|
361
|
-
|
|
432
|
+
const roleArgs = p.roles.map((r) =>
|
|
433
|
+
buildRecordingRole(tx, p.recordingCreditsPackageId, r),
|
|
434
|
+
);
|
|
435
|
+
const credit = buildCredit(
|
|
436
|
+
tx,
|
|
437
|
+
p.misoCreditPackageId,
|
|
438
|
+
roleType,
|
|
439
|
+
p.displayName,
|
|
440
|
+
roleArgs,
|
|
441
|
+
);
|
|
362
442
|
tx.add(
|
|
363
443
|
recordingCredits.addCredit({
|
|
364
444
|
package: p.recordingCreditsPackageId,
|
|
@@ -389,7 +469,9 @@ export interface AddRecordingArtistParams {
|
|
|
389
469
|
* party must be credited first (via `attachRecordingCredit`) and not already a
|
|
390
470
|
* primary or featured artist.
|
|
391
471
|
*/
|
|
392
|
-
export function addRecordingPrimaryArtist(
|
|
472
|
+
export function addRecordingPrimaryArtist(
|
|
473
|
+
p: AddRecordingArtistParams,
|
|
474
|
+
): TxThunk {
|
|
393
475
|
return (tx) => {
|
|
394
476
|
tx.add(
|
|
395
477
|
recordingCredits.addPrimaryArtist({
|
|
@@ -406,7 +488,9 @@ export function addRecordingPrimaryArtist(p: AddRecordingArtistParams): TxThunk
|
|
|
406
488
|
* party must be credited first (via `attachRecordingCredit`) and not already a
|
|
407
489
|
* primary or featured artist.
|
|
408
490
|
*/
|
|
409
|
-
export function addRecordingFeaturedArtist(
|
|
491
|
+
export function addRecordingFeaturedArtist(
|
|
492
|
+
p: AddRecordingArtistParams,
|
|
493
|
+
): TxThunk {
|
|
410
494
|
return (tx) => {
|
|
411
495
|
tx.add(
|
|
412
496
|
recordingCredits.addFeaturedArtist({
|
|
@@ -445,7 +529,13 @@ export function addReleaseCredit(p: AddReleaseCreditParams): TxThunk {
|
|
|
445
529
|
return (tx) => {
|
|
446
530
|
const roleType = `${p.releaseCreditsPackageId}::release_party_role::ReleasePartyRole`;
|
|
447
531
|
const roleArg = buildReleaseRole(tx, p.releaseCreditsPackageId, p.role);
|
|
448
|
-
const credit = buildCredit(
|
|
532
|
+
const credit = buildCredit(
|
|
533
|
+
tx,
|
|
534
|
+
p.misoCreditPackageId,
|
|
535
|
+
roleType,
|
|
536
|
+
p.displayName,
|
|
537
|
+
[roleArg],
|
|
538
|
+
);
|
|
449
539
|
tx.add(
|
|
450
540
|
releaseCredits.addCredit({
|
|
451
541
|
package: p.releaseCreditsPackageId,
|
|
@@ -503,25 +593,127 @@ const ReleaseCreditsField = bcs.struct("Field", {
|
|
|
503
593
|
value: releaseCredits.ReleaseCredits,
|
|
504
594
|
});
|
|
505
595
|
|
|
506
|
-
const COMPOSITION_CREDITS_KEY_BYTES = compositionCredits.ExtensionKey.serialize(
|
|
507
|
-
|
|
508
|
-
|
|
596
|
+
const COMPOSITION_CREDITS_KEY_BYTES = compositionCredits.ExtensionKey.serialize(
|
|
597
|
+
[false],
|
|
598
|
+
).toBytes();
|
|
599
|
+
const RECORDING_CREDITS_KEY_BYTES = recordingCredits.ExtensionKey.serialize([
|
|
600
|
+
false,
|
|
601
|
+
]).toBytes();
|
|
602
|
+
const RELEASE_CREDITS_KEY_BYTES = releaseCredits.ExtensionKey.serialize([
|
|
603
|
+
false,
|
|
604
|
+
]).toBytes();
|
|
605
|
+
|
|
606
|
+
type CreditFieldKind =
|
|
607
|
+
| "composition"
|
|
608
|
+
| "compositionLegacy"
|
|
609
|
+
| "recording"
|
|
610
|
+
| "recordingLegacy"
|
|
611
|
+
| "release";
|
|
612
|
+
|
|
613
|
+
interface CreditFieldTarget {
|
|
614
|
+
workId: string;
|
|
615
|
+
kind: CreditFieldKind;
|
|
616
|
+
fieldId: string;
|
|
617
|
+
}
|
|
509
618
|
|
|
510
|
-
/**
|
|
511
|
-
|
|
619
|
+
/**
|
|
620
|
+
* Fetch many derived credit fields through one Core bulk request.
|
|
621
|
+
*
|
|
622
|
+
* Composition and recording extensions have one legacy key spelling. Both IDs
|
|
623
|
+
* ride in the same request, so fallback compatibility does not add a sequential
|
|
624
|
+
* round trip when the current key is absent.
|
|
625
|
+
*/
|
|
626
|
+
async function fetchCreditFields(
|
|
512
627
|
client: ClientWithCoreApi,
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
if (
|
|
523
|
-
|
|
524
|
-
|
|
628
|
+
targets: readonly CreditFieldTarget[],
|
|
629
|
+
): Promise<Map<string, Uint8Array>> {
|
|
630
|
+
if (targets.length === 0) return new Map();
|
|
631
|
+
const { objects } = await client.core.getObjects({
|
|
632
|
+
objectIds: targets.map((target) => target.fieldId),
|
|
633
|
+
include: { content: true },
|
|
634
|
+
});
|
|
635
|
+
const contents = new Map<string, Uint8Array>();
|
|
636
|
+
objects.forEach((object, index) => {
|
|
637
|
+
if (object instanceof Error || !object.content) return;
|
|
638
|
+
const target = targets[index];
|
|
639
|
+
if (target) contents.set(target.fieldId, object.content);
|
|
640
|
+
});
|
|
641
|
+
return contents;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function compositionCreditTargets(
|
|
645
|
+
compositionIds: readonly string[],
|
|
646
|
+
packageId: string,
|
|
647
|
+
): CreditFieldTarget[] {
|
|
648
|
+
return compositionIds.flatMap((workId) => [
|
|
649
|
+
{
|
|
650
|
+
workId,
|
|
651
|
+
kind: "composition" as const,
|
|
652
|
+
fieldId: deriveDynamicFieldID(
|
|
653
|
+
workId,
|
|
654
|
+
`${packageId}::composition_credits::ExtensionKey`,
|
|
655
|
+
COMPOSITION_CREDITS_KEY_BYTES,
|
|
656
|
+
),
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
workId,
|
|
660
|
+
kind: "compositionLegacy" as const,
|
|
661
|
+
fieldId: deriveDynamicFieldID(
|
|
662
|
+
workId,
|
|
663
|
+
`${packageId}::composition_credits::CompositionCreditsKey`,
|
|
664
|
+
COMPOSITION_CREDITS_KEY_BYTES,
|
|
665
|
+
),
|
|
666
|
+
},
|
|
667
|
+
]);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function recordingCreditTargets(
|
|
671
|
+
recordingIds: readonly string[],
|
|
672
|
+
packageId: string,
|
|
673
|
+
): CreditFieldTarget[] {
|
|
674
|
+
return recordingIds.flatMap((workId) => [
|
|
675
|
+
{
|
|
676
|
+
workId,
|
|
677
|
+
kind: "recording" as const,
|
|
678
|
+
fieldId: deriveDynamicFieldID(
|
|
679
|
+
workId,
|
|
680
|
+
`${packageId}::recording_credits::ExtensionKey`,
|
|
681
|
+
RECORDING_CREDITS_KEY_BYTES,
|
|
682
|
+
),
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
workId,
|
|
686
|
+
kind: "recordingLegacy" as const,
|
|
687
|
+
fieldId: deriveDynamicFieldID(
|
|
688
|
+
workId,
|
|
689
|
+
`${packageId}::recording_credits::RecordingCreditsKey`,
|
|
690
|
+
RECORDING_CREDITS_KEY_BYTES,
|
|
691
|
+
),
|
|
692
|
+
},
|
|
693
|
+
]);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function releaseCreditTargets(
|
|
697
|
+
releaseIds: readonly string[],
|
|
698
|
+
packageId: string,
|
|
699
|
+
): CreditFieldTarget[] {
|
|
700
|
+
return releaseIds.map((workId) => ({
|
|
701
|
+
workId,
|
|
702
|
+
kind: "release",
|
|
703
|
+
fieldId: releaseCreditsFieldId(workId, packageId),
|
|
704
|
+
}));
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/** Deterministic dynamic-field id for a release's billing credits. */
|
|
708
|
+
export function releaseCreditsFieldId(
|
|
709
|
+
releaseId: string,
|
|
710
|
+
releaseCreditsPackageId: string,
|
|
711
|
+
): string {
|
|
712
|
+
return deriveDynamicFieldID(
|
|
713
|
+
releaseId,
|
|
714
|
+
`${releaseCreditsPackageId}::release_credits::ExtensionKey`,
|
|
715
|
+
RELEASE_CREDITS_KEY_BYTES,
|
|
716
|
+
);
|
|
525
717
|
}
|
|
526
718
|
|
|
527
719
|
// The credits container shape, checked structurally against the parse output of
|
|
@@ -530,7 +722,9 @@ async function fetchCreditsField(
|
|
|
530
722
|
// field rename (`contents`/`key`/`value`/`display_name`/`roles`, or the
|
|
531
723
|
// `credits`/`primary_artist_ids`/`featured_artist_ids` fields at the call
|
|
532
724
|
// sites) fails typecheck here instead of silently misparsing.
|
|
533
|
-
type ParsedCreditsMap<Role> = {
|
|
725
|
+
type ParsedCreditsMap<Role> = {
|
|
726
|
+
contents: { key: string; value: { display_name: string; roles: Role[] } }[];
|
|
727
|
+
};
|
|
534
728
|
|
|
535
729
|
function creditViews<Role extends ParsedEnum>(
|
|
536
730
|
credits: ParsedCreditsMap<Role>,
|
|
@@ -553,6 +747,14 @@ function releaseRoleLabel(role: ParsedEnum): string {
|
|
|
553
747
|
return role.$kind;
|
|
554
748
|
}
|
|
555
749
|
|
|
750
|
+
/** Parse a fetched release-credit dynamic field. */
|
|
751
|
+
export function parseReleaseCreditsContent(content: Uint8Array): CreditView[] {
|
|
752
|
+
return creditViews(
|
|
753
|
+
ReleaseCreditsField.parse(content).value.credits,
|
|
754
|
+
releaseRoleLabel,
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
|
|
556
758
|
// `name()` semantics plus the recording extras: the instrument (for
|
|
557
759
|
// `Instrumentalist`) and the optional level, both surfaced into the label.
|
|
558
760
|
function recordingRoleLabel(role: ParsedEnum): string {
|
|
@@ -581,7 +783,9 @@ function recordingRoleLabel(role: ParsedEnum): string {
|
|
|
581
783
|
base = role.$kind;
|
|
582
784
|
const payload = role[role.$kind];
|
|
583
785
|
level =
|
|
584
|
-
payload && typeof payload === "object" && "$kind" in payload
|
|
786
|
+
payload && typeof payload === "object" && "$kind" in payload
|
|
787
|
+
? (payload as ParsedEnum)
|
|
788
|
+
: null;
|
|
585
789
|
break;
|
|
586
790
|
}
|
|
587
791
|
}
|
|
@@ -597,21 +801,45 @@ export async function getCompositionCredits(
|
|
|
597
801
|
compositionId: string,
|
|
598
802
|
compositionCreditsPackageId: string,
|
|
599
803
|
): Promise<CreditView[] | null> {
|
|
600
|
-
|
|
601
|
-
(
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
804
|
+
return (
|
|
805
|
+
(
|
|
806
|
+
await getCompositionCreditsByIds(
|
|
807
|
+
client,
|
|
808
|
+
[compositionId],
|
|
809
|
+
compositionCreditsPackageId,
|
|
810
|
+
)
|
|
811
|
+
)[compositionId] ?? null
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/** Composition credits for many works in one Core bulk request. */
|
|
816
|
+
export async function getCompositionCreditsByIds(
|
|
817
|
+
client: ClientWithCoreApi,
|
|
818
|
+
compositionIdsInput: readonly string[],
|
|
819
|
+
compositionCreditsPackageId: string,
|
|
820
|
+
): Promise<Partial<Record<string, CreditView[]>>> {
|
|
821
|
+
const compositionIds = [...new Set(compositionIdsInput)];
|
|
822
|
+
const targets = compositionCreditTargets(
|
|
823
|
+
compositionIds,
|
|
824
|
+
compositionCreditsPackageId,
|
|
825
|
+
);
|
|
826
|
+
const contents = await fetchCreditFields(client, targets);
|
|
827
|
+
const out: Partial<Record<string, CreditView[]>> = {};
|
|
828
|
+
for (const compositionId of compositionIds) {
|
|
829
|
+
const candidates = targets.filter(
|
|
830
|
+
(target) => target.workId === compositionId,
|
|
831
|
+
);
|
|
832
|
+
const content = candidates
|
|
833
|
+
.map((target) => contents.get(target.fieldId))
|
|
834
|
+
.find((value): value is Uint8Array => value !== undefined);
|
|
835
|
+
if (content) {
|
|
836
|
+
out[compositionId] = creditViews(
|
|
837
|
+
CompositionCreditsField.parse(content).value.credits,
|
|
838
|
+
compositionRoleLabel,
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return out;
|
|
615
843
|
}
|
|
616
844
|
|
|
617
845
|
/**
|
|
@@ -623,26 +851,46 @@ export async function getRecordingCredits(
|
|
|
623
851
|
recordingId: string,
|
|
624
852
|
recordingCreditsPackageId: string,
|
|
625
853
|
): Promise<RecordingCreditsView | null> {
|
|
626
|
-
|
|
627
|
-
(
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
854
|
+
return (
|
|
855
|
+
(
|
|
856
|
+
await getRecordingCreditsByIds(
|
|
857
|
+
client,
|
|
858
|
+
[recordingId],
|
|
859
|
+
recordingCreditsPackageId,
|
|
860
|
+
)
|
|
861
|
+
)[recordingId] ?? null
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/** Recording credits for many works in one Core bulk request. */
|
|
866
|
+
export async function getRecordingCreditsByIds(
|
|
867
|
+
client: ClientWithCoreApi,
|
|
868
|
+
recordingIdsInput: readonly string[],
|
|
869
|
+
recordingCreditsPackageId: string,
|
|
870
|
+
): Promise<Partial<Record<string, RecordingCreditsView>>> {
|
|
871
|
+
const recordingIds = [...new Set(recordingIdsInput)];
|
|
872
|
+
const targets = recordingCreditTargets(
|
|
873
|
+
recordingIds,
|
|
874
|
+
recordingCreditsPackageId,
|
|
875
|
+
);
|
|
876
|
+
const contents = await fetchCreditFields(client, targets);
|
|
877
|
+
const out: Partial<Record<string, RecordingCreditsView>> = {};
|
|
878
|
+
for (const recordingId of recordingIds) {
|
|
879
|
+
const candidates = targets.filter(
|
|
880
|
+
(target) => target.workId === recordingId,
|
|
881
|
+
);
|
|
882
|
+
const content = candidates
|
|
883
|
+
.map((target) => contents.get(target.fieldId))
|
|
884
|
+
.find((value): value is Uint8Array => value !== undefined);
|
|
885
|
+
if (!content) continue;
|
|
886
|
+
const value = RecordingCreditsField.parse(content).value;
|
|
887
|
+
out[recordingId] = {
|
|
888
|
+
credits: creditViews(value.credits, recordingRoleLabel),
|
|
889
|
+
primaryArtistIds: value.primary_artist_ids.contents,
|
|
890
|
+
featuredArtistIds: value.featured_artist_ids.contents,
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
return out;
|
|
646
894
|
}
|
|
647
895
|
|
|
648
896
|
/**
|
|
@@ -654,12 +902,28 @@ export async function getReleaseCredits(
|
|
|
654
902
|
releaseId: string,
|
|
655
903
|
releaseCreditsPackageId: string,
|
|
656
904
|
): Promise<CreditView[] | null> {
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
RELEASE_CREDITS_KEY_BYTES,
|
|
905
|
+
return (
|
|
906
|
+
(
|
|
907
|
+
await getReleaseCreditsByIds(client, [releaseId], releaseCreditsPackageId)
|
|
908
|
+
)[releaseId] ?? null
|
|
662
909
|
);
|
|
663
|
-
|
|
664
|
-
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/** Release billing credits for many releases in one Core bulk request. */
|
|
913
|
+
export async function getReleaseCreditsByIds(
|
|
914
|
+
client: ClientWithCoreApi,
|
|
915
|
+
releaseIdsInput: readonly string[],
|
|
916
|
+
releaseCreditsPackageId: string,
|
|
917
|
+
): Promise<Partial<Record<string, CreditView[]>>> {
|
|
918
|
+
const releaseIds = [...new Set(releaseIdsInput)];
|
|
919
|
+
const targets = releaseCreditTargets(releaseIds, releaseCreditsPackageId);
|
|
920
|
+
const contents = await fetchCreditFields(client, targets);
|
|
921
|
+
const out: Partial<Record<string, CreditView[]>> = {};
|
|
922
|
+
for (const target of targets) {
|
|
923
|
+
const content = contents.get(target.fieldId);
|
|
924
|
+
if (content) {
|
|
925
|
+
out[target.workId] = parseReleaseCreditsContent(content);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
return out;
|
|
665
929
|
}
|