ddex-json-codec 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # ddex-json-codec
2
2
 
3
- DDEX ERN規格のXML/JSON相互変換ライブラリ(TypeScript
3
+ Bidirectional XML/JSON codec for the DDEX ERN (Electronic Release Notification) standard, written in TypeScript.
4
4
 
5
- ## 特徴
5
+ ## Features
6
6
 
7
- - ERN XMLからTypeScript型付きオブジェクトへの変換、およびその逆変換
8
- - ERN 3.8 / 4系に対応(4系は未実装)
9
- - バージョン自動検出
10
- - [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) ベース
7
+ - Convert ERN XML to typed TypeScript objects and back
8
+ - Supports ERN 3.8.x (3.8 - 3.8.3) and ERN 4.x (4.1 - 4.3.2)
9
+ - Automatic version detection from namespace URI
10
+ - Built on [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser)
11
11
  - Node.js >= 20
12
12
 
13
- ## インストール
13
+ ## Install
14
14
 
15
15
  ```bash
16
16
  npm install ddex-json-codec
@@ -18,66 +18,66 @@ npm install ddex-json-codec
18
18
  pnpm add ddex-json-codec
19
19
  ```
20
20
 
21
- ## 使い方
21
+ ## Usage
22
22
 
23
- ### XML JSON
23
+ ### XML to JSON
24
24
 
25
25
  ```typescript
26
26
  import { xmlToJson } from 'ddex-json-codec';
27
27
 
28
28
  const message = xmlToJson(xmlString);
29
- console.log(message.ernVersion); // "3.8.1"
29
+ console.log(message.ernVersion); // "3.8.2"
30
30
  console.log(message.resourceList); // SoundRecording[]
31
31
  ```
32
32
 
33
- ### JSON XML
33
+ ### JSON to XML
34
34
 
35
35
  ```typescript
36
36
  import { jsonToXml } from 'ddex-json-codec';
37
37
 
38
38
  const xml = jsonToXml(message);
39
- // バージョン指定も可
39
+ // Optionally specify a target version
40
40
  const xml382 = jsonToXml(message, '3.8.2');
41
41
  ```
42
42
 
43
- ### バージョン検出
43
+ ### Version Detection
44
44
 
45
45
  ```typescript
46
46
  import { detectVersion } from 'ddex-json-codec';
47
47
 
48
- const version = detectVersion(xmlString); // "3.8.1"
48
+ const version = detectVersion(xmlString); // "4.3"
49
49
  ```
50
50
 
51
- ## 対応バージョン
51
+ ## Supported Versions
52
52
 
53
- | ERN バージョン | 状態 |
53
+ | ERN Version | Status |
54
54
  |---|---|
55
- | 3.8 | 実装済み |
56
- | 3.8.1 | 実装済み |
57
- | 3.8.2 | 実装済み |
58
- | 3.8.3 | 実装済み |
59
- | 4.1 | 未実装 |
60
- | 4.1.1 | 未実装 |
61
- | 4.2 | 未実装 |
62
- | 4.3 | 未実装 |
63
- | 4.3.1 | 未実装 |
64
- | 4.3.2 | 未実装 |
65
-
66
- ## API リファレンス
55
+ | 3.8 | Supported |
56
+ | 3.8.1 | Supported |
57
+ | 3.8.2 | Supported |
58
+ | 3.8.3 | Supported |
59
+ | 4.1 | Supported |
60
+ | 4.1.1 | Supported |
61
+ | 4.2 | Supported |
62
+ | 4.3 | Supported |
63
+ | 4.3.1 | Supported |
64
+ | 4.3.2 | Supported |
65
+
66
+ ## API
67
67
 
68
68
  ### `xmlToJson(xml: string): DdexMessage`
69
69
 
70
- ERN XMLを解析し、型付きオブジェクトを返す。バージョンは自動検出される。
70
+ Parse an ERN XML string into a typed object. Version is auto-detected.
71
71
 
72
72
  ### `jsonToXml(message: DdexMessage, version?: ErnVersion): string`
73
73
 
74
- `DdexMessage` ERN XMLに変換する。`version` 省略時は `message.ernVersion` を使用。
74
+ Convert a `DdexMessage` back to an ERN XML string. Falls back to `message.ernVersion` when `version` is omitted.
75
75
 
76
76
  ### `detectVersion(xml: string): ErnVersion`
77
77
 
78
- ERN XMLのnamespace URIからバージョンを検出して返す。
78
+ Detect the ERN version from namespace URI or `MessageSchemaVersionId` attribute.
79
79
 
80
- ###
80
+ ### Types
81
81
 
82
82
  ```typescript
83
83
  interface DdexMessage {
@@ -85,21 +85,24 @@ interface DdexMessage {
85
85
  messageHeader: MessageHeader;
86
86
  updateIndicator?: string;
87
87
  resourceList: SoundRecording[];
88
+ imageList?: Image[];
88
89
  releaseList: Release[];
89
90
  dealList: ReleaseDeal[];
90
- partyList?: Party[]; // 4系のみ
91
- trackReleaseList?: TrackRelease[]; // 4系のみ
91
+ partyList?: Party[]; // ERN 4.x only
92
+ trackReleaseList?: TrackRelease[]; // ERN 4.x only
92
93
  }
93
94
  ```
94
95
 
95
- エクスポートされる型一覧:
96
+ Exported types:
96
97
 
97
98
  `DdexMessage`, `ErnVersion`, `ErnMajorVersion`, `MessageHeader`, `MessageParty`,
98
- `SoundRecording`, `Release`, `TrackRelease`, `ResourceGroup`, `ReleaseResourceReference`,
99
+ `SoundRecording`, `TechnicalSoundRecordingDetails`,
100
+ `Image`, `ImageId`, `ImageDetailsByTerritory`, `TechnicalImageDetails`, `FileDetails`, `HashSum`,
101
+ `Release`, `TrackRelease`, `ResourceGroup`, `ReleaseResourceReference`,
99
102
  `ReleaseDeal`, `Deal`, `DealTerms`, `Usage`,
100
- `Party`, `Artist`, `DisplayArtist`, `ResourceContributor`, `IndirectResourceContributor`,
101
- `TextWithAttribute`, `Genre`, `PLine`, `CLine`, `Title`
103
+ `Party`, `Artist`, `ArtistRole`, `DisplayArtist`, `ResourceContributor`, `IndirectResourceContributor`, `Contributor`,
104
+ `TextWithAttribute`, `Genre`, `PLine`, `CLine`, `Title`, `DisplayTitle`
102
105
 
103
- ## ライセンス
106
+ ## License
104
107
 
105
108
  [MIT](./LICENSE.md)
package/dist/index.d.mts CHANGED
@@ -45,13 +45,21 @@ interface PartyName {
45
45
  fullNameIndexed?: string;
46
46
  languageAndScriptCode?: string;
47
47
  }
48
+ interface ArtistRole {
49
+ role: string;
50
+ /** UserDefined時の属性 */
51
+ namespace?: string;
52
+ userDefinedValue?: string;
53
+ }
48
54
  interface Artist {
49
- /** 解決済みの名前(3.8系: FullNameから直接取得、4系: PartyListから解決) */
55
+ /** 解決済みのデフォルト名(3.8系: FullNameから直接取得、4系: PartyListの最初のFullName) */
50
56
  name: string;
57
+ /** 4系のみ: 多言語名(PartyListのPartyName[]をそのまま保持) */
58
+ names?: PartyName[];
51
59
  /** 4系のみ: 元の参照ID(ラウンドトリップ用) */
52
60
  partyReference?: string;
53
61
  partyId?: string[];
54
- roles?: string[];
62
+ roles?: ArtistRole[];
55
63
  }
56
64
  interface DisplayArtist {
57
65
  artist: Artist;
@@ -79,6 +87,43 @@ interface Contributor {
79
87
  sequenceNumber?: number;
80
88
  }
81
89
  //#endregion
90
+ //#region src/types/image.d.ts
91
+ interface Image {
92
+ resourceReference: string;
93
+ type?: string;
94
+ imageId?: ImageId;
95
+ /** 3.8系: territory別の詳細 */
96
+ detailsByTerritory?: ImageDetailsByTerritory[];
97
+ /** 4系: フラット */
98
+ parentalWarningType?: string;
99
+ technicalDetails?: TechnicalImageDetails;
100
+ }
101
+ interface ImageId {
102
+ proprietaryId?: string;
103
+ proprietaryIdNamespace?: string;
104
+ }
105
+ interface ImageDetailsByTerritory {
106
+ territoryCode: string[];
107
+ parentalWarningType?: string;
108
+ technicalDetails?: TechnicalImageDetails;
109
+ }
110
+ interface TechnicalImageDetails {
111
+ technicalResourceDetailsReference?: string;
112
+ imageCodecType?: string;
113
+ imageHeight?: number;
114
+ imageWidth?: number;
115
+ file?: FileDetails;
116
+ }
117
+ interface FileDetails {
118
+ fileName?: string;
119
+ uri?: string;
120
+ hashSum?: HashSum;
121
+ }
122
+ interface HashSum {
123
+ algorithm?: string;
124
+ hashSumValue?: string;
125
+ }
126
+ //#endregion
82
127
  //#region src/types/sound-recording.d.ts
83
128
  interface SoundRecording {
84
129
  resourceReference: string;
@@ -107,9 +152,21 @@ interface ReferenceTitle$1 {
107
152
  titleText: string;
108
153
  subTitle?: string;
109
154
  }
155
+ interface TechnicalSoundRecordingDetails {
156
+ technicalResourceDetailsReference?: string;
157
+ audioCodecType?: string;
158
+ bitRate?: number;
159
+ bitRateUnit?: string;
160
+ numberOfChannels?: number;
161
+ samplingRate?: number;
162
+ samplingRateUnit?: string;
163
+ isPreview?: boolean;
164
+ file?: FileDetails;
165
+ }
110
166
  interface SoundRecordingDetailsByTerritory {
111
167
  territoryCode: string[];
112
168
  displayArtists?: DisplayArtist[];
169
+ displayArtistName?: string;
113
170
  titles?: Title[];
114
171
  labelName?: string;
115
172
  pLine?: PLine;
@@ -118,6 +175,7 @@ interface SoundRecordingDetailsByTerritory {
118
175
  sequenceNumber?: number;
119
176
  resourceContributors?: ResourceContributor[];
120
177
  indirectResourceContributors?: IndirectResourceContributor[];
178
+ technicalDetails?: TechnicalSoundRecordingDetails[];
121
179
  }
122
180
  //#endregion
123
181
  //#region src/types/release.d.ts
@@ -255,6 +313,7 @@ interface DdexMessage {
255
313
  messageHeader: MessageHeader;
256
314
  updateIndicator?: string;
257
315
  resourceList: SoundRecording[];
316
+ imageList?: Image[];
258
317
  releaseList: Release[];
259
318
  dealList: ReleaseDeal[];
260
319
  /** 4系のみ: PartyList(ラウンドトリップ用に保持) */
@@ -287,4 +346,4 @@ declare function jsonToXml(message: DdexMessage, version?: ErnVersion): string;
287
346
  */
288
347
  declare function detectVersion(xml: string): ErnVersion;
289
348
  //#endregion
290
- export { type Artist, type CLine, type Contributor, type DdexMessage, type Deal, type DealTerms, type DisplayArtist, type DisplayTitle, type ErnMajorVersion, type ErnVersion, type Genre, type IndirectResourceContributor, type MessageHeader, type MessageParty, type PLine, type Party, type Release, type ReleaseDeal, type ReleaseResourceReference, type ResourceContributor, type ResourceGroup, type SoundRecording, type TextWithAttribute, type Title, type TrackRelease, type Usage, detectVersion, jsonToXml, xmlToJson };
349
+ export { type Artist, type ArtistRole, type CLine, type Contributor, type DdexMessage, type Deal, type DealTerms, type DisplayArtist, type DisplayTitle, type ErnMajorVersion, type ErnVersion, type FileDetails, type Genre, type HashSum, type Image, type ImageDetailsByTerritory, type ImageId, type IndirectResourceContributor, type MessageHeader, type MessageParty, type PLine, type Party, type Release, type ReleaseDeal, type ReleaseResourceReference, type ResourceContributor, type ResourceGroup, type SoundRecording, type TechnicalImageDetails, type TechnicalSoundRecordingDetails, type TextWithAttribute, type Title, type TrackRelease, type Usage, detectVersion, jsonToXml, xmlToJson };
package/dist/index.mjs CHANGED
@@ -146,6 +146,7 @@ var Ern38Converter = class {
146
146
  messageHeader: this.parseMessageHeader(root.MessageHeader),
147
147
  updateIndicator: root.UpdateIndicator ?? void 0,
148
148
  resourceList: this.parseSoundRecordings(root.ResourceList),
149
+ imageList: this.parseImages(root.ResourceList),
149
150
  releaseList: this.parseReleases(root.ReleaseList),
150
151
  dealList: this.parseDealList(root.DealList)
151
152
  };
@@ -209,6 +210,7 @@ var Ern38Converter = class {
209
210
  return {
210
211
  territoryCode: ensureArray(raw.TerritoryCode),
211
212
  displayArtists: this.parseDisplayArtists(raw.DisplayArtist),
213
+ displayArtistName: Array.isArray(raw.DisplayArtistName) ? raw.DisplayArtistName[0] : raw.DisplayArtistName ?? void 0,
212
214
  titles: this.parseTitles(raw.Title),
213
215
  labelName: raw.LabelName ?? void 0,
214
216
  pLine: raw.PLine ? this.parsePLine(ensureArray(raw.PLine)[0]) : void 0,
@@ -216,7 +218,74 @@ var Ern38Converter = class {
216
218
  parentalWarningType: raw.ParentalWarningType ?? void 0,
217
219
  sequenceNumber: raw.SequenceNumber ? Number(raw.SequenceNumber) : void 0,
218
220
  resourceContributors: this.parseResourceContributors(raw.ResourceContributor),
219
- indirectResourceContributors: this.parseIndirectResourceContributors(raw.IndirectResourceContributor)
221
+ indirectResourceContributors: this.parseIndirectResourceContributors(raw.IndirectResourceContributor),
222
+ technicalDetails: raw.TechnicalSoundRecordingDetails ? ensureArray(raw.TechnicalSoundRecordingDetails).map((td) => this.parseTechnicalSoundRecordingDetails(td)) : void 0
223
+ };
224
+ }
225
+ parseTechnicalSoundRecordingDetails(raw) {
226
+ const bitRate = raw.BitRate;
227
+ const samplingRate = raw.SamplingRate;
228
+ return {
229
+ technicalResourceDetailsReference: raw.TechnicalResourceDetailsReference ?? void 0,
230
+ audioCodecType: raw.AudioCodecType ?? void 0,
231
+ bitRate: bitRate ? Number(typeof bitRate === "string" ? bitRate : bitRate["#text"]) : void 0,
232
+ bitRateUnit: typeof bitRate === "object" ? bitRate["@_UnitOfMeasure"] ?? void 0 : void 0,
233
+ numberOfChannels: raw.NumberOfChannels ? Number(raw.NumberOfChannels) : void 0,
234
+ samplingRate: samplingRate ? Number(typeof samplingRate === "string" ? samplingRate : samplingRate["#text"]) : void 0,
235
+ samplingRateUnit: typeof samplingRate === "object" ? samplingRate["@_UnitOfMeasure"] ?? void 0 : void 0,
236
+ isPreview: raw.IsPreview === "true" ? true : raw.IsPreview === "false" ? false : void 0,
237
+ file: raw.File ? this.parseFileDetails(raw.File) : void 0
238
+ };
239
+ }
240
+ parseImages(resourceList) {
241
+ if (!resourceList) return void 0;
242
+ const images = ensureArray(resourceList.Image);
243
+ if (images.length === 0) return void 0;
244
+ return images.map((img) => this.parseImage(img));
245
+ }
246
+ parseImage(raw) {
247
+ return {
248
+ resourceReference: raw.ResourceReference,
249
+ type: raw.ImageType ?? void 0,
250
+ imageId: raw.ImageId ? this.parseImageId(raw.ImageId) : void 0,
251
+ detailsByTerritory: raw.ImageDetailsByTerritory ? ensureArray(raw.ImageDetailsByTerritory).map((d) => this.parseImageDetailsByTerritory(d)) : void 0
252
+ };
253
+ }
254
+ parseImageId(raw) {
255
+ const propId = raw.ProprietaryId;
256
+ if (!propId) return void 0;
257
+ return {
258
+ proprietaryId: typeof propId === "string" ? propId : propId["#text"] ?? "",
259
+ proprietaryIdNamespace: typeof propId === "object" ? propId["@_Namespace"] ?? void 0 : void 0
260
+ };
261
+ }
262
+ parseImageDetailsByTerritory(raw) {
263
+ return {
264
+ territoryCode: ensureArray(raw.TerritoryCode),
265
+ parentalWarningType: raw.ParentalWarningType ?? void 0,
266
+ technicalDetails: raw.TechnicalImageDetails ? this.parseTechnicalImageDetails(Array.isArray(raw.TechnicalImageDetails) ? raw.TechnicalImageDetails[0] : raw.TechnicalImageDetails) : void 0
267
+ };
268
+ }
269
+ parseTechnicalImageDetails(raw) {
270
+ return {
271
+ technicalResourceDetailsReference: raw.TechnicalResourceDetailsReference ?? void 0,
272
+ imageCodecType: raw.ImageCodecType ?? void 0,
273
+ imageHeight: raw.ImageHeight ? Number(raw.ImageHeight) : void 0,
274
+ imageWidth: raw.ImageWidth ? Number(raw.ImageWidth) : void 0,
275
+ file: raw.File ? this.parseFileDetails(raw.File) : void 0
276
+ };
277
+ }
278
+ parseFileDetails(raw) {
279
+ return {
280
+ fileName: raw.FileName ?? void 0,
281
+ uri: raw.URI ?? void 0,
282
+ hashSum: raw.HashSum ? this.parseHashSum(raw.HashSum) : void 0
283
+ };
284
+ }
285
+ parseHashSum(raw) {
286
+ return {
287
+ algorithm: raw.HashSumAlgorithmType ?? raw.Algorithm ?? void 0,
288
+ hashSumValue: raw.HashSum ?? raw.HashSumValue ?? void 0
220
289
  };
221
290
  }
222
291
  parseReleases(releaseList) {
@@ -351,11 +420,21 @@ var Ern38Converter = class {
351
420
  return artists.map((a) => ({
352
421
  artist: {
353
422
  name: a.PartyName?.FullName ?? "",
354
- roles: a.ArtistRole ? ensureArray(a.ArtistRole) : void 0
423
+ roles: a.ArtistRole ? this.parseArtistRoles(a.ArtistRole) : void 0
355
424
  },
356
425
  sequenceNumber: a["@_SequenceNumber"] ? Number(a["@_SequenceNumber"]) : void 0
357
426
  }));
358
427
  }
428
+ parseArtistRoles(raw) {
429
+ return ensureArray(raw).map((r) => {
430
+ if (typeof r === "string") return { role: r };
431
+ return {
432
+ role: r["#text"] ?? "",
433
+ namespace: r["@_Namespace"] ?? void 0,
434
+ userDefinedValue: r["@_UserDefinedValue"] ?? void 0
435
+ };
436
+ });
437
+ }
359
438
  parseResourceContributors(raw) {
360
439
  if (!raw) return void 0;
361
440
  const contributors = ensureArray(raw);
@@ -432,6 +511,7 @@ var Ern4Converter = class {
432
511
  ernVersion: version,
433
512
  messageHeader: this.parseMessageHeader(root.MessageHeader),
434
513
  resourceList: this.parseSoundRecordings(root.ResourceList),
514
+ imageList: this.parseImages(root.ResourceList),
435
515
  releaseList: this.parseReleases(root.ReleaseList),
436
516
  dealList: this.parseDealList(root.DealList),
437
517
  partyList,
@@ -472,10 +552,13 @@ var Ern4Converter = class {
472
552
  partyId: partyIds.length > 0 ? partyIds : void 0
473
553
  };
474
554
  }
475
- resolveArtistName(partyReference) {
555
+ resolveParty(partyReference) {
476
556
  const party = this.partyIndex.get(partyReference);
477
- if (!party?.partyName?.length) return partyReference;
478
- return party.partyName[0].fullName;
557
+ if (!party?.partyName?.length) return { name: partyReference };
558
+ return {
559
+ name: party.partyName[0].fullName,
560
+ names: party.partyName.length > 1 ? party.partyName : void 0
561
+ };
479
562
  }
480
563
  parseMessageHeader(raw) {
481
564
  return {
@@ -538,6 +621,43 @@ var Ern4Converter = class {
538
621
  catalogNumber: raw.CatalogNumber ?? void 0
539
622
  };
540
623
  }
624
+ parseImages(resourceList) {
625
+ if (!resourceList) return void 0;
626
+ const images = ensureArray(resourceList.Image);
627
+ if (images.length === 0) return void 0;
628
+ return images.map((img) => {
629
+ const result = {
630
+ resourceReference: img.ResourceReference,
631
+ type: img.Type ?? void 0,
632
+ parentalWarningType: img.ParentalWarningType ?? void 0
633
+ };
634
+ if (img.ResourceId) {
635
+ const propId = img.ResourceId.ProprietaryId;
636
+ if (propId) result.imageId = {
637
+ proprietaryId: typeof propId === "string" ? propId : propId["#text"] ?? "",
638
+ proprietaryIdNamespace: typeof propId === "object" ? propId["@_Namespace"] ?? void 0 : void 0
639
+ };
640
+ }
641
+ if (img.TechnicalDetails) {
642
+ const td = Array.isArray(img.TechnicalDetails) ? img.TechnicalDetails[0] : img.TechnicalDetails;
643
+ result.technicalDetails = {
644
+ technicalResourceDetailsReference: td.TechnicalResourceDetailsReference ?? void 0,
645
+ imageCodecType: td.ImageCodecType ?? void 0,
646
+ imageHeight: td.ImageHeight ? Number(td.ImageHeight) : void 0,
647
+ imageWidth: td.ImageWidth ? Number(td.ImageWidth) : void 0,
648
+ file: td.File ? {
649
+ uri: td.File.URI ?? void 0,
650
+ fileName: td.File.FileName ?? void 0,
651
+ hashSum: td.File.HashSum ? {
652
+ algorithm: td.File.HashSum.Algorithm ?? void 0,
653
+ hashSumValue: td.File.HashSum.HashSumValue ?? void 0
654
+ } : void 0
655
+ } : void 0
656
+ };
657
+ }
658
+ return result;
659
+ });
660
+ }
541
661
  parseReleases(releaseList) {
542
662
  if (!releaseList) return [];
543
663
  return ensureArray(releaseList.Release).map((r) => this.parseRelease(r));
@@ -675,23 +795,38 @@ var Ern4Converter = class {
675
795
  if (artists.length === 0) return void 0;
676
796
  return artists.map((a) => {
677
797
  const partyRef = a.ArtistPartyReference;
798
+ const resolved = partyRef ? this.resolveParty(partyRef) : {
799
+ name: a.PartyName?.FullName ?? "",
800
+ names: void 0
801
+ };
678
802
  return {
679
803
  artist: {
680
- name: partyRef ? this.resolveArtistName(partyRef) : a.PartyName?.FullName ?? "",
804
+ name: resolved.name,
805
+ names: resolved.names,
681
806
  partyReference: partyRef ?? void 0,
682
- roles: a.DisplayArtistRole ? ensureArray(a.DisplayArtistRole) : void 0
807
+ roles: a.DisplayArtistRole ? this.parseArtistRoles(a.DisplayArtistRole) : void 0
683
808
  },
684
809
  sequenceNumber: a["@_SequenceNumber"] ? Number(a["@_SequenceNumber"]) : void 0
685
810
  };
686
811
  });
687
812
  }
813
+ parseArtistRoles(raw) {
814
+ return ensureArray(raw).map((r) => {
815
+ if (typeof r === "string") return { role: r };
816
+ return {
817
+ role: r["#text"] ?? "",
818
+ namespace: r["@_Namespace"] ?? void 0,
819
+ userDefinedValue: r["@_UserDefinedValue"] ?? void 0
820
+ };
821
+ });
822
+ }
688
823
  parseContributors(raw) {
689
824
  if (!raw) return void 0;
690
825
  const contributors = ensureArray(raw);
691
826
  if (contributors.length === 0) return void 0;
692
827
  return contributors.map((c) => {
693
828
  const partyRef = c.ContributorPartyReference;
694
- const name = partyRef ? this.resolveArtistName(partyRef) : void 0;
829
+ const name = partyRef ? this.resolveParty(partyRef).name : void 0;
695
830
  return {
696
831
  contributorPartyReference: partyRef ?? "",
697
832
  name,
@@ -749,7 +884,7 @@ var Ern38Builder = class {
749
884
  "@_xsi:schemaLocation": `${nsUri} ${nsUri}/release-notification.xsd`,
750
885
  MessageHeader: this.buildMessageHeader(message.messageHeader),
751
886
  ...message.updateIndicator ? { UpdateIndicator: message.updateIndicator } : {},
752
- ResourceList: this.buildResourceList(message.resourceList),
887
+ ResourceList: this.buildResourceList(message.resourceList, message.imageList),
753
888
  ReleaseList: this.buildReleaseList(message.releaseList),
754
889
  DealList: this.buildDealList(message.dealList)
755
890
  } };
@@ -775,8 +910,10 @@ var Ern38Builder = class {
775
910
  if (party.tradingName) result.TradingName = party.tradingName;
776
911
  return result;
777
912
  }
778
- buildResourceList(soundRecordings) {
779
- return { SoundRecording: soundRecordings.map((sr) => this.buildSoundRecording(sr)) };
913
+ buildResourceList(soundRecordings, images) {
914
+ const result = { SoundRecording: soundRecordings.map((sr) => this.buildSoundRecording(sr)) };
915
+ if (images?.length) result.Image = images.map((img) => this.buildImage(img));
916
+ return result;
780
917
  }
781
918
  buildSoundRecording(sr) {
782
919
  const result = {};
@@ -804,6 +941,7 @@ var Ern38Builder = class {
804
941
  result.TerritoryCode = d.territoryCode;
805
942
  if (d.titles) result.Title = d.titles.map((t) => this.buildTitle(t));
806
943
  if (d.displayArtists) result.DisplayArtist = d.displayArtists.map((a) => this.buildDisplayArtist(a));
944
+ if (d.displayArtistName) result.DisplayArtistName = d.displayArtistName;
807
945
  if (d.resourceContributors) result.ResourceContributor = d.resourceContributors.map((c) => this.buildResourceContributor(c));
808
946
  if (d.indirectResourceContributors) result.IndirectResourceContributor = d.indirectResourceContributors.map((c) => this.buildIndirectResourceContributor(c));
809
947
  if (d.labelName) result.LabelName = d.labelName;
@@ -811,6 +949,74 @@ var Ern38Builder = class {
811
949
  if (d.sequenceNumber != null) result.SequenceNumber = String(d.sequenceNumber);
812
950
  if (d.genre) result.Genre = this.buildGenre(d.genre);
813
951
  if (d.parentalWarningType) result.ParentalWarningType = d.parentalWarningType;
952
+ if (d.technicalDetails) result.TechnicalSoundRecordingDetails = d.technicalDetails.map((td) => this.buildTechnicalSoundRecordingDetails(td));
953
+ return result;
954
+ }
955
+ buildTechnicalSoundRecordingDetails(td) {
956
+ const result = {};
957
+ if (td.technicalResourceDetailsReference) result.TechnicalResourceDetailsReference = td.technicalResourceDetailsReference;
958
+ if (td.audioCodecType) result.AudioCodecType = td.audioCodecType;
959
+ if (td.bitRate != null) result.BitRate = td.bitRateUnit ? {
960
+ "#text": String(td.bitRate),
961
+ "@_UnitOfMeasure": td.bitRateUnit
962
+ } : String(td.bitRate);
963
+ if (td.numberOfChannels != null) result.NumberOfChannels = String(td.numberOfChannels);
964
+ if (td.samplingRate != null) result.SamplingRate = td.samplingRateUnit ? {
965
+ "#text": String(td.samplingRate),
966
+ "@_UnitOfMeasure": td.samplingRateUnit
967
+ } : String(td.samplingRate);
968
+ if (td.isPreview != null) result.IsPreview = String(td.isPreview);
969
+ if (td.file) {
970
+ const file = {};
971
+ if (td.file.fileName) file.FileName = td.file.fileName;
972
+ if (td.file.uri) file.URI = td.file.uri;
973
+ if (td.file.hashSum) {
974
+ file.HashSum = {};
975
+ if (td.file.hashSum.algorithm) file.HashSum.HashSumAlgorithmType = td.file.hashSum.algorithm;
976
+ if (td.file.hashSum.hashSumValue) file.HashSum.HashSum = td.file.hashSum.hashSumValue;
977
+ }
978
+ result.File = file;
979
+ }
980
+ return result;
981
+ }
982
+ buildImage(img) {
983
+ const result = {};
984
+ if (img.type) result.ImageType = img.type;
985
+ if (img.imageId) {
986
+ const id = {};
987
+ if (img.imageId.proprietaryId) id.ProprietaryId = img.imageId.proprietaryIdNamespace ? {
988
+ "#text": img.imageId.proprietaryId,
989
+ "@_Namespace": img.imageId.proprietaryIdNamespace
990
+ } : img.imageId.proprietaryId;
991
+ result.ImageId = id;
992
+ }
993
+ result.ResourceReference = img.resourceReference;
994
+ if (img.detailsByTerritory) result.ImageDetailsByTerritory = img.detailsByTerritory.map((d) => {
995
+ const dbt = {};
996
+ dbt.TerritoryCode = d.territoryCode;
997
+ if (d.parentalWarningType) dbt.ParentalWarningType = d.parentalWarningType;
998
+ if (d.technicalDetails) dbt.TechnicalImageDetails = this.buildTechnicalImageDetails(d.technicalDetails);
999
+ return dbt;
1000
+ });
1001
+ return result;
1002
+ }
1003
+ buildTechnicalImageDetails(td) {
1004
+ const result = {};
1005
+ if (td.technicalResourceDetailsReference) result.TechnicalResourceDetailsReference = td.technicalResourceDetailsReference;
1006
+ if (td.imageCodecType) result.ImageCodecType = td.imageCodecType;
1007
+ if (td.imageHeight != null) result.ImageHeight = String(td.imageHeight);
1008
+ if (td.imageWidth != null) result.ImageWidth = String(td.imageWidth);
1009
+ if (td.file) {
1010
+ const file = {};
1011
+ if (td.file.fileName) file.FileName = td.file.fileName;
1012
+ if (td.file.uri) file.URI = td.file.uri;
1013
+ if (td.file.hashSum) {
1014
+ file.HashSum = {};
1015
+ if (td.file.hashSum.algorithm) file.HashSum.HashSumAlgorithmType = td.file.hashSum.algorithm;
1016
+ if (td.file.hashSum.hashSumValue) file.HashSum.HashSum = td.file.hashSum.hashSumValue;
1017
+ }
1018
+ result.File = file;
1019
+ }
814
1020
  return result;
815
1021
  }
816
1022
  buildReleaseList(releases) {
@@ -909,9 +1115,20 @@ var Ern38Builder = class {
909
1115
  const result = {};
910
1116
  if (da.sequenceNumber != null) result["@_SequenceNumber"] = String(da.sequenceNumber);
911
1117
  result.PartyName = { FullName: da.artist.name };
912
- if (da.artist.roles?.length) result.ArtistRole = da.artist.roles.length === 1 ? da.artist.roles[0] : da.artist.roles;
1118
+ if (da.artist.roles?.length) {
1119
+ const builtRoles = da.artist.roles.map((r) => this.buildArtistRole(r));
1120
+ result.ArtistRole = builtRoles.length === 1 ? builtRoles[0] : builtRoles;
1121
+ }
913
1122
  return result;
914
1123
  }
1124
+ buildArtistRole(r) {
1125
+ if (r.userDefinedValue) return {
1126
+ "#text": r.role,
1127
+ ...r.namespace ? { "@_Namespace": r.namespace } : {},
1128
+ "@_UserDefinedValue": r.userDefinedValue
1129
+ };
1130
+ return r.role;
1131
+ }
915
1132
  buildResourceContributor(c) {
916
1133
  const result = {};
917
1134
  if (c.sequenceNumber != null) result["@_SequenceNumber"] = String(c.sequenceNumber);
@@ -970,7 +1187,7 @@ var Ern4Builder = class {
970
1187
  "@_LanguageAndScriptCode": "en",
971
1188
  MessageHeader: this.buildMessageHeader(message.messageHeader),
972
1189
  PartyList: this.buildPartyList(message.partyList ?? []),
973
- ResourceList: this.buildResourceList(message.resourceList, version),
1190
+ ResourceList: this.buildResourceList(message.resourceList, message.imageList, version),
974
1191
  ReleaseList: this.buildReleaseList(message.releaseList, message.trackReleaseList),
975
1192
  DealList: this.buildDealList(message.dealList)
976
1193
  } };
@@ -1009,8 +1226,40 @@ var Ern4Builder = class {
1009
1226
  if (party.partyId?.length) result.PartyId = party.partyId.map((id) => id);
1010
1227
  return result;
1011
1228
  }
1012
- buildResourceList(soundRecordings, _version) {
1013
- return { SoundRecording: soundRecordings.map((sr) => this.buildSoundRecording(sr)) };
1229
+ buildResourceList(soundRecordings, images, _version) {
1230
+ const result = { SoundRecording: soundRecordings.map((sr) => this.buildSoundRecording(sr)) };
1231
+ if (images?.length) result.Image = images.map((img) => this.buildImage(img));
1232
+ return result;
1233
+ }
1234
+ buildImage(img) {
1235
+ const result = {};
1236
+ result.ResourceReference = img.resourceReference;
1237
+ if (img.type) result.Type = img.type;
1238
+ if (img.imageId?.proprietaryId) result.ResourceId = { ProprietaryId: img.imageId.proprietaryIdNamespace ? {
1239
+ "#text": img.imageId.proprietaryId,
1240
+ "@_Namespace": img.imageId.proprietaryIdNamespace
1241
+ } : img.imageId.proprietaryId };
1242
+ if (img.parentalWarningType) result.ParentalWarningType = img.parentalWarningType;
1243
+ if (img.technicalDetails) {
1244
+ const td = {};
1245
+ if (img.technicalDetails.technicalResourceDetailsReference) td.TechnicalResourceDetailsReference = img.technicalDetails.technicalResourceDetailsReference;
1246
+ if (img.technicalDetails.imageCodecType) td.ImageCodecType = img.technicalDetails.imageCodecType;
1247
+ if (img.technicalDetails.imageHeight != null) td.ImageHeight = String(img.technicalDetails.imageHeight);
1248
+ if (img.technicalDetails.imageWidth != null) td.ImageWidth = String(img.technicalDetails.imageWidth);
1249
+ if (img.technicalDetails.file) {
1250
+ const file = {};
1251
+ if (img.technicalDetails.file.uri) file.URI = img.technicalDetails.file.uri;
1252
+ if (img.technicalDetails.file.fileName) file.FileName = img.technicalDetails.file.fileName;
1253
+ if (img.technicalDetails.file.hashSum) {
1254
+ file.HashSum = {};
1255
+ if (img.technicalDetails.file.hashSum.algorithm) file.HashSum.Algorithm = img.technicalDetails.file.hashSum.algorithm;
1256
+ if (img.technicalDetails.file.hashSum.hashSumValue) file.HashSum.HashSumValue = img.technicalDetails.file.hashSum.hashSumValue;
1257
+ }
1258
+ td.File = file;
1259
+ }
1260
+ result.TechnicalDetails = td;
1261
+ }
1262
+ return result;
1014
1263
  }
1015
1264
  buildSoundRecording(sr) {
1016
1265
  const result = {};
@@ -1131,9 +1380,20 @@ var Ern4Builder = class {
1131
1380
  const result = {};
1132
1381
  if (da.sequenceNumber != null) result["@_SequenceNumber"] = String(da.sequenceNumber);
1133
1382
  if (da.artist.partyReference) result.ArtistPartyReference = da.artist.partyReference;
1134
- if (da.artist.roles?.length) result.DisplayArtistRole = da.artist.roles.length === 1 ? da.artist.roles[0] : da.artist.roles;
1383
+ if (da.artist.roles?.length) {
1384
+ const builtRoles = da.artist.roles.map((r) => this.buildArtistRole(r));
1385
+ result.DisplayArtistRole = builtRoles.length === 1 ? builtRoles[0] : builtRoles;
1386
+ }
1135
1387
  return result;
1136
1388
  }
1389
+ buildArtistRole(r) {
1390
+ if (r.userDefinedValue) return {
1391
+ "#text": r.role,
1392
+ ...r.namespace ? { "@_Namespace": r.namespace } : {},
1393
+ "@_UserDefinedValue": r.userDefinedValue
1394
+ };
1395
+ return r.role;
1396
+ }
1137
1397
  buildContributor(c) {
1138
1398
  const result = {};
1139
1399
  if (c.sequenceNumber != null) result["@_SequenceNumber"] = String(c.sequenceNumber);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ddex-json-codec",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "description": "DDEX ERN XML ↔ JSON converter for TypeScript",
6
6
  "license": "MIT",
7
7
  "author": "kazhs",