@zivue/zuuid 0.1.0 → 0.2.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/CHANGELOG.md +16 -1
- package/README.md +291 -215
- package/dist/client.d.ts +16 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +22 -0
- package/dist/entity.d.ts.map +1 -1
- package/dist/entity.js +9 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/providers/comicvine/character.d.ts +2 -0
- package/dist/providers/comicvine/character.d.ts.map +1 -0
- package/dist/providers/comicvine/character.js +1 -0
- package/dist/providers/comicvine/constants.d.ts +8 -0
- package/dist/providers/comicvine/constants.d.ts.map +1 -0
- package/dist/providers/comicvine/constants.js +7 -0
- package/dist/providers/comicvine/index.d.ts +9 -0
- package/dist/providers/comicvine/index.d.ts.map +1 -0
- package/dist/providers/comicvine/index.js +8 -0
- package/dist/providers/comicvine/issue.d.ts +2 -0
- package/dist/providers/comicvine/issue.d.ts.map +1 -0
- package/dist/providers/comicvine/issue.js +1 -0
- package/dist/providers/comicvine/person.d.ts +2 -0
- package/dist/providers/comicvine/person.d.ts.map +1 -0
- package/dist/providers/comicvine/person.js +1 -0
- package/dist/providers/comicvine/publisher.d.ts +2 -0
- package/dist/providers/comicvine/publisher.d.ts.map +1 -0
- package/dist/providers/comicvine/publisher.js +1 -0
- package/dist/providers/comicvine/story-arc.d.ts +2 -0
- package/dist/providers/comicvine/story-arc.d.ts.map +1 -0
- package/dist/providers/comicvine/story-arc.js +1 -0
- package/dist/providers/comicvine/transform.d.ts +4 -0
- package/dist/providers/comicvine/transform.d.ts.map +1 -0
- package/dist/providers/comicvine/transform.js +63 -0
- package/dist/providers/comicvine/volume.d.ts +2 -0
- package/dist/providers/comicvine/volume.d.ts.map +1 -0
- package/dist/providers/comicvine/volume.js +1 -0
- package/dist/providers/common.d.ts +24 -0
- package/dist/providers/common.d.ts.map +1 -0
- package/dist/providers/common.js +140 -0
- package/dist/providers/gamesdb/constants.d.ts +4 -0
- package/dist/providers/gamesdb/constants.d.ts.map +1 -0
- package/dist/providers/gamesdb/constants.js +3 -0
- package/dist/providers/gamesdb/game.d.ts +7 -0
- package/dist/providers/gamesdb/game.d.ts.map +1 -0
- package/dist/providers/gamesdb/game.js +61 -0
- package/dist/providers/gamesdb/index.d.ts +4 -0
- package/dist/providers/gamesdb/index.d.ts.map +1 -0
- package/dist/providers/gamesdb/index.js +3 -0
- package/dist/providers/gamesdb/platform.d.ts +4 -0
- package/dist/providers/gamesdb/platform.d.ts.map +1 -0
- package/dist/providers/gamesdb/platform.js +23 -0
- package/dist/providers/jikan/anime.d.ts +2 -0
- package/dist/providers/jikan/anime.d.ts.map +1 -0
- package/dist/providers/jikan/anime.js +1 -0
- package/dist/providers/jikan/character.d.ts +2 -0
- package/dist/providers/jikan/character.d.ts.map +1 -0
- package/dist/providers/jikan/character.js +1 -0
- package/dist/providers/jikan/constants.d.ts +8 -0
- package/dist/providers/jikan/constants.d.ts.map +1 -0
- package/dist/providers/jikan/constants.js +7 -0
- package/dist/providers/jikan/index.d.ts +9 -0
- package/dist/providers/jikan/index.d.ts.map +1 -0
- package/dist/providers/jikan/index.js +8 -0
- package/dist/providers/jikan/magazine.d.ts +2 -0
- package/dist/providers/jikan/magazine.d.ts.map +1 -0
- package/dist/providers/jikan/magazine.js +1 -0
- package/dist/providers/jikan/manga.d.ts +2 -0
- package/dist/providers/jikan/manga.d.ts.map +1 -0
- package/dist/providers/jikan/manga.js +1 -0
- package/dist/providers/jikan/person.d.ts +2 -0
- package/dist/providers/jikan/person.d.ts.map +1 -0
- package/dist/providers/jikan/person.js +1 -0
- package/dist/providers/jikan/producer.d.ts +2 -0
- package/dist/providers/jikan/producer.d.ts.map +1 -0
- package/dist/providers/jikan/producer.js +1 -0
- package/dist/providers/jikan/transform.d.ts +8 -0
- package/dist/providers/jikan/transform.d.ts.map +1 -0
- package/dist/providers/jikan/transform.js +127 -0
- package/dist/providers/musicbrainz/artist.d.ts +4 -0
- package/dist/providers/musicbrainz/artist.d.ts.map +1 -0
- package/dist/providers/musicbrainz/artist.js +28 -0
- package/dist/providers/musicbrainz/constants.d.ts +10 -0
- package/dist/providers/musicbrainz/constants.d.ts.map +1 -0
- package/dist/providers/musicbrainz/constants.js +9 -0
- package/dist/providers/musicbrainz/helpers.d.ts +15 -0
- package/dist/providers/musicbrainz/helpers.d.ts.map +1 -0
- package/dist/providers/musicbrainz/helpers.js +71 -0
- package/dist/providers/musicbrainz/index.d.ts +8 -0
- package/dist/providers/musicbrainz/index.d.ts.map +1 -0
- package/dist/providers/musicbrainz/index.js +7 -0
- package/dist/providers/musicbrainz/label.d.ts +4 -0
- package/dist/providers/musicbrainz/label.d.ts.map +1 -0
- package/dist/providers/musicbrainz/label.js +28 -0
- package/dist/providers/musicbrainz/recording.d.ts +4 -0
- package/dist/providers/musicbrainz/recording.d.ts.map +1 -0
- package/dist/providers/musicbrainz/recording.js +41 -0
- package/dist/providers/musicbrainz/release-group.d.ts +7 -0
- package/dist/providers/musicbrainz/release-group.d.ts.map +1 -0
- package/dist/providers/musicbrainz/release-group.js +28 -0
- package/dist/providers/musicbrainz/release.d.ts +7 -0
- package/dist/providers/musicbrainz/release.d.ts.map +1 -0
- package/dist/providers/musicbrainz/release.js +50 -0
- package/dist/providers/musicbrainz/work.d.ts +4 -0
- package/dist/providers/musicbrainz/work.d.ts.map +1 -0
- package/dist/providers/musicbrainz/work.js +40 -0
- package/dist/providers/openfoodfacts/constants.d.ts +3 -0
- package/dist/providers/openfoodfacts/constants.d.ts.map +1 -0
- package/dist/providers/openfoodfacts/constants.js +2 -0
- package/dist/providers/openfoodfacts/index.d.ts +3 -0
- package/dist/providers/openfoodfacts/index.d.ts.map +1 -0
- package/dist/providers/openfoodfacts/index.js +2 -0
- package/dist/providers/openfoodfacts/product.d.ts +4 -0
- package/dist/providers/openfoodfacts/product.d.ts.map +1 -0
- package/dist/providers/openfoodfacts/product.js +29 -0
- package/dist/providers/openlibrary/author.d.ts +65 -0
- package/dist/providers/openlibrary/author.d.ts.map +1 -0
- package/dist/providers/openlibrary/author.js +228 -0
- package/dist/providers/openlibrary/book.d.ts +138 -0
- package/dist/providers/openlibrary/book.d.ts.map +1 -0
- package/dist/providers/openlibrary/book.js +536 -0
- package/dist/providers/openlibrary/client.d.ts +22 -0
- package/dist/providers/openlibrary/client.d.ts.map +1 -0
- package/dist/providers/openlibrary/client.js +61 -0
- package/dist/providers/openlibrary/constants.d.ts +4 -0
- package/dist/providers/openlibrary/constants.d.ts.map +1 -0
- package/dist/providers/openlibrary/constants.js +3 -0
- package/dist/providers/openlibrary/index.d.ts +6 -0
- package/dist/providers/openlibrary/index.d.ts.map +1 -0
- package/dist/providers/openlibrary/index.js +5 -0
- package/dist/providers/openlibrary/types.d.ts +23 -0
- package/dist/providers/openlibrary/types.d.ts.map +1 -0
- package/dist/providers/openlibrary/types.js +1 -0
- package/dist/providers/openstreetmap/constants.d.ts +6 -0
- package/dist/providers/openstreetmap/constants.d.ts.map +1 -0
- package/dist/providers/openstreetmap/constants.js +5 -0
- package/dist/providers/openstreetmap/index.d.ts +4 -0
- package/dist/providers/openstreetmap/index.d.ts.map +1 -0
- package/dist/providers/openstreetmap/index.js +3 -0
- package/dist/providers/openstreetmap/place.d.ts +4 -0
- package/dist/providers/openstreetmap/place.d.ts.map +1 -0
- package/dist/providers/openstreetmap/place.js +67 -0
- package/dist/providers/openstreetmap/venue.d.ts +2 -0
- package/dist/providers/openstreetmap/venue.d.ts.map +1 -0
- package/dist/providers/openstreetmap/venue.js +1 -0
- package/dist/providers/podcast/constants.d.ts +3 -0
- package/dist/providers/podcast/constants.d.ts.map +1 -0
- package/dist/providers/podcast/constants.js +2 -0
- package/dist/providers/podcast/index.d.ts +3 -0
- package/dist/providers/podcast/index.d.ts.map +1 -0
- package/dist/providers/podcast/index.js +2 -0
- package/dist/providers/podcast/podcast.d.ts +4 -0
- package/dist/providers/podcast/podcast.d.ts.map +1 -0
- package/dist/providers/podcast/podcast.js +30 -0
- package/dist/providers/setlistfm/artist.d.ts +2 -0
- package/dist/providers/setlistfm/artist.d.ts.map +1 -0
- package/dist/providers/setlistfm/artist.js +1 -0
- package/dist/providers/setlistfm/constants.d.ts +5 -0
- package/dist/providers/setlistfm/constants.d.ts.map +1 -0
- package/dist/providers/setlistfm/constants.js +4 -0
- package/dist/providers/setlistfm/index.d.ts +6 -0
- package/dist/providers/setlistfm/index.d.ts.map +1 -0
- package/dist/providers/setlistfm/index.js +5 -0
- package/dist/providers/setlistfm/setlist.d.ts +2 -0
- package/dist/providers/setlistfm/setlist.d.ts.map +1 -0
- package/dist/providers/setlistfm/setlist.js +1 -0
- package/dist/providers/setlistfm/transform.d.ts +7 -0
- package/dist/providers/setlistfm/transform.d.ts.map +1 -0
- package/dist/providers/setlistfm/transform.js +122 -0
- package/dist/providers/setlistfm/venue.d.ts +2 -0
- package/dist/providers/setlistfm/venue.d.ts.map +1 -0
- package/dist/providers/setlistfm/venue.js +1 -0
- package/dist/providers/ticketmaster/attraction.d.ts +2 -0
- package/dist/providers/ticketmaster/attraction.d.ts.map +1 -0
- package/dist/providers/ticketmaster/attraction.js +1 -0
- package/dist/providers/ticketmaster/constants.d.ts +5 -0
- package/dist/providers/ticketmaster/constants.d.ts.map +1 -0
- package/dist/providers/ticketmaster/constants.js +4 -0
- package/dist/providers/ticketmaster/event.d.ts +2 -0
- package/dist/providers/ticketmaster/event.d.ts.map +1 -0
- package/dist/providers/ticketmaster/event.js +1 -0
- package/dist/providers/ticketmaster/index.d.ts +6 -0
- package/dist/providers/ticketmaster/index.d.ts.map +1 -0
- package/dist/providers/ticketmaster/index.js +5 -0
- package/dist/providers/ticketmaster/transform.d.ts +7 -0
- package/dist/providers/ticketmaster/transform.d.ts.map +1 -0
- package/dist/providers/ticketmaster/transform.js +89 -0
- package/dist/providers/ticketmaster/venue.d.ts +2 -0
- package/dist/providers/ticketmaster/venue.d.ts.map +1 -0
- package/dist/providers/ticketmaster/venue.js +1 -0
- package/dist/providers/tmdb/movie.d.ts.map +1 -1
- package/dist/providers/tmdb/movie.js +9 -22
- package/dist/providers/tmdb/tv.d.ts.map +1 -1
- package/dist/providers/tmdb/tv.js +4 -2
- package/dist/providers/wger/constants.d.ts +4 -0
- package/dist/providers/wger/constants.d.ts.map +1 -0
- package/dist/providers/wger/constants.js +3 -0
- package/dist/providers/wger/equipment.d.ts +4 -0
- package/dist/providers/wger/equipment.d.ts.map +1 -0
- package/dist/providers/wger/equipment.js +22 -0
- package/dist/providers/wger/exercise.d.ts +4 -0
- package/dist/providers/wger/exercise.d.ts.map +1 -0
- package/dist/providers/wger/exercise.js +42 -0
- package/dist/providers/wger/index.d.ts +4 -0
- package/dist/providers/wger/index.d.ts.map +1 -0
- package/dist/providers/wger/index.js +3 -0
- package/package.json +201 -5
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { addDetail, addRelation, arrayField, baseDataFromSource, finalizeData, stringField, valueAsString } from "../common.js";
|
|
2
|
+
import { MUSICBRAINZ_ARTIST_CATEGORY, MUSICBRAINZ_PROVIDER, MUSICBRAINZ_RECORDING_CATEGORY } from "./constants.js";
|
|
3
|
+
import { addArtistCreditDetail, addMusicBrainzDescription, addMusicBrainzTags, musicBrainzId, musicBrainzPayload, requireMusicBrainzTitle } from "./helpers.js";
|
|
4
|
+
export async function transformMusicBrainzRecording(source) {
|
|
5
|
+
if (source.source.provider !== MUSICBRAINZ_PROVIDER || source.source.category !== MUSICBRAINZ_RECORDING_CATEGORY) {
|
|
6
|
+
throw new Error(`unsupported MusicBrainz source: ${source.source.provider}:${source.source.category}`);
|
|
7
|
+
}
|
|
8
|
+
const payload = musicBrainzPayload(source.payload);
|
|
9
|
+
const id = musicBrainzId(payload, source.source.externalId);
|
|
10
|
+
const title = requireMusicBrainzTitle(payload);
|
|
11
|
+
if (!id)
|
|
12
|
+
throw new Error("missing required MusicBrainz recording field: id");
|
|
13
|
+
if (!title)
|
|
14
|
+
throw new Error("missing required MusicBrainz recording field: title");
|
|
15
|
+
const data = await baseDataFromSource(source, MUSICBRAINZ_PROVIDER, MUSICBRAINZ_RECORDING_CATEGORY, MUSICBRAINZ_RECORDING_CATEGORY, id, title);
|
|
16
|
+
data.primaryDate = stringField(payload, "first-release-date");
|
|
17
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "first_release_date", stringField(payload, "first-release-date"));
|
|
18
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "length_ms", valueAsString(payload.length));
|
|
19
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "video", valueAsString(payload.video));
|
|
20
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "isrcs", arrayField(payload, "isrcs").filter((value) => typeof value === "string"));
|
|
21
|
+
for (const isrc of arrayField(payload, "isrcs"))
|
|
22
|
+
if (typeof isrc === "string")
|
|
23
|
+
data.externalIds.push({ source: "isrc", category: MUSICBRAINZ_RECORDING_CATEGORY, value: isrc });
|
|
24
|
+
addArtistCreditDetail(data, payload);
|
|
25
|
+
await addArtistCreditRelations(data, payload);
|
|
26
|
+
addMusicBrainzDescription(data, payload);
|
|
27
|
+
addMusicBrainzTags(data, payload);
|
|
28
|
+
return finalizeData(data, source);
|
|
29
|
+
}
|
|
30
|
+
async function addArtistCreditRelations(data, payload) {
|
|
31
|
+
let index = 0;
|
|
32
|
+
for (const credit of arrayField(payload, "artist-credit")) {
|
|
33
|
+
if (!credit || typeof credit !== "object" || Array.isArray(credit))
|
|
34
|
+
continue;
|
|
35
|
+
const artist = credit.artist;
|
|
36
|
+
if (!artist || typeof artist !== "object" || Array.isArray(artist))
|
|
37
|
+
continue;
|
|
38
|
+
await addRelation(data, MUSICBRAINZ_PROVIDER, MUSICBRAINZ_ARTIST_CATEGORY, stringField(artist, "id"), "performed_by", stringField(artist, "name"), { order: index });
|
|
39
|
+
index += 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ZuuidData } from "../../entity.js";
|
|
2
|
+
import type { SourceRecord } from "../../source.js";
|
|
3
|
+
export type MusicBrainzReleaseGroupTransformOptions = {
|
|
4
|
+
coverArtBaseUrl?: string | null;
|
|
5
|
+
};
|
|
6
|
+
export declare function transformMusicBrainzReleaseGroup(source: SourceRecord, options?: MusicBrainzReleaseGroupTransformOptions): Promise<ZuuidData>;
|
|
7
|
+
//# sourceMappingURL=release-group.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-group.d.ts","sourceRoot":"","sources":["../../../src/providers/musicbrainz/release-group.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAKpD,MAAM,MAAM,uCAAuC,GAAG;IACpD,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,CAAC;AAEF,wBAAsB,gCAAgC,CACpD,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,uCAA4C,GACpD,OAAO,CAAC,SAAS,CAAC,CAsBpB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { addDetail, addMedia, arrayField, baseDataFromSource, finalizeData, stringField, valueAsString } from "../common.js";
|
|
2
|
+
import { MUSICBRAINZ_PROVIDER, MUSICBRAINZ_RELEASE_GROUP_CATEGORY, MUSICBRAINZ_RELEASE_GROUP_COVER_ART_BASE_URL } from "./constants.js";
|
|
3
|
+
import { addArtistCreditDetail, addMusicBrainzDescription, addMusicBrainzTags, musicBrainzId, musicBrainzPayload, requireMusicBrainzTitle } from "./helpers.js";
|
|
4
|
+
export async function transformMusicBrainzReleaseGroup(source, options = {}) {
|
|
5
|
+
if (source.source.provider !== MUSICBRAINZ_PROVIDER || source.source.category !== MUSICBRAINZ_RELEASE_GROUP_CATEGORY) {
|
|
6
|
+
throw new Error(`unsupported MusicBrainz source: ${source.source.provider}:${source.source.category}`);
|
|
7
|
+
}
|
|
8
|
+
const payload = musicBrainzPayload(source.payload);
|
|
9
|
+
const id = musicBrainzId(payload, source.source.externalId);
|
|
10
|
+
const title = requireMusicBrainzTitle(payload);
|
|
11
|
+
if (!id)
|
|
12
|
+
throw new Error("missing required MusicBrainz release-group field: id");
|
|
13
|
+
if (!title)
|
|
14
|
+
throw new Error("missing required MusicBrainz release-group field: title");
|
|
15
|
+
const data = await baseDataFromSource(source, MUSICBRAINZ_PROVIDER, MUSICBRAINZ_RELEASE_GROUP_CATEGORY, "release_group", id, title);
|
|
16
|
+
data.primaryDate = stringField(payload, "first-release-date");
|
|
17
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "first_release_date", stringField(payload, "first-release-date"));
|
|
18
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "primary_type", stringField(payload, "primary-type"));
|
|
19
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "secondary_types", arrayField(payload, "secondary-types").filter((value) => typeof value === "string"));
|
|
20
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "release_count", valueAsString(payload["release-count"]));
|
|
21
|
+
addArtistCreditDetail(data, payload);
|
|
22
|
+
addMusicBrainzDescription(data, payload);
|
|
23
|
+
addMusicBrainzTags(data, payload);
|
|
24
|
+
const baseUrl = options.coverArtBaseUrl === undefined ? MUSICBRAINZ_RELEASE_GROUP_COVER_ART_BASE_URL : options.coverArtBaseUrl;
|
|
25
|
+
if (baseUrl)
|
|
26
|
+
addMedia(data, MUSICBRAINZ_PROVIDER, `${baseUrl.replace(/\/$/, "")}/${id}/front`, "cover");
|
|
27
|
+
return finalizeData(data, source);
|
|
28
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ZuuidData } from "../../entity.js";
|
|
2
|
+
import type { SourceRecord } from "../../source.js";
|
|
3
|
+
export type MusicBrainzTransformOptions = {
|
|
4
|
+
coverArtBaseUrl?: string | null;
|
|
5
|
+
};
|
|
6
|
+
export declare function transformMusicBrainzRelease(source: SourceRecord, options?: MusicBrainzTransformOptions): Promise<ZuuidData>;
|
|
7
|
+
//# sourceMappingURL=release.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release.d.ts","sourceRoot":"","sources":["../../../src/providers/musicbrainz/release.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAgBpD,MAAM,MAAM,2BAA2B,GAAG;IACxC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,CAAC;AAEF,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,SAAS,CAAC,CAiCpB"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { MUSICBRAINZ_COVER_ART_BASE_URL, MUSICBRAINZ_PROVIDER, MUSICBRAINZ_RELEASE_CATEGORY } from "./constants.js";
|
|
2
|
+
import { addDescription, addDetail, addMedia, addTag, arrayField, baseDataFromSource, finalizeData, nestedString, stringField, valueAsString } from "../common.js";
|
|
3
|
+
import { addArtistCreditDetail, addMusicBrainzDescription, addMusicBrainzTags, musicBrainzId, musicBrainzPayload, requireMusicBrainzTitle } from "./helpers.js";
|
|
4
|
+
export async function transformMusicBrainzRelease(source, options = {}) {
|
|
5
|
+
if (source.source.provider !== MUSICBRAINZ_PROVIDER || source.source.category !== MUSICBRAINZ_RELEASE_CATEGORY) {
|
|
6
|
+
throw new Error(`unsupported MusicBrainz source: ${source.source.provider}:${source.source.category}`);
|
|
7
|
+
}
|
|
8
|
+
const payload = musicBrainzPayload(source.payload);
|
|
9
|
+
const id = musicBrainzId(payload, source.source.externalId);
|
|
10
|
+
const title = requireMusicBrainzTitle(payload);
|
|
11
|
+
if (!id)
|
|
12
|
+
throw new Error("missing required MusicBrainz release field: id");
|
|
13
|
+
if (!title)
|
|
14
|
+
throw new Error("missing required MusicBrainz release field: title");
|
|
15
|
+
const data = await baseDataFromSource(source, MUSICBRAINZ_PROVIDER, MUSICBRAINZ_RELEASE_CATEGORY, MUSICBRAINZ_RELEASE_CATEGORY, id, title);
|
|
16
|
+
const releaseDate = stringField(payload, "date");
|
|
17
|
+
if (releaseDate && /^\d{4}-\d{2}-\d{2}$/.test(releaseDate))
|
|
18
|
+
data.primaryDate = releaseDate;
|
|
19
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "release_date", releaseDate);
|
|
20
|
+
addArtistCreditDetail(data, payload);
|
|
21
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "labels", joinedLabels(payload));
|
|
22
|
+
for (const key of ["status", "country", "barcode"])
|
|
23
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, key, stringField(payload, key));
|
|
24
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "track_count", valueAsString(payload["track-count"]));
|
|
25
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "media_count", valueAsString(payload["media-count"]));
|
|
26
|
+
addMusicBrainzDescription(data, payload);
|
|
27
|
+
addMusicBrainzTags(data, payload);
|
|
28
|
+
const barcode = stringField(payload, "barcode");
|
|
29
|
+
if (barcode)
|
|
30
|
+
data.externalIds.push({ source: "barcode", category: MUSICBRAINZ_RELEASE_CATEGORY, value: barcode });
|
|
31
|
+
const releaseGroupId = nestedString(payload, ["release-group", "id"]);
|
|
32
|
+
if (releaseGroupId) {
|
|
33
|
+
data.externalIds.push({ source: MUSICBRAINZ_PROVIDER, category: "release_group", value: releaseGroupId });
|
|
34
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "release_group_id", releaseGroupId);
|
|
35
|
+
}
|
|
36
|
+
const archive = payload["cover-art-archive"];
|
|
37
|
+
const hasFront = !!(archive && typeof archive === "object" && !Array.isArray(archive) && archive.front === true);
|
|
38
|
+
const baseUrl = options.coverArtBaseUrl === undefined ? MUSICBRAINZ_COVER_ART_BASE_URL : options.coverArtBaseUrl;
|
|
39
|
+
if (hasFront && baseUrl)
|
|
40
|
+
addMedia(data, MUSICBRAINZ_PROVIDER, `${baseUrl.replace(/\/$/, "")}/${id}/front`, "cover");
|
|
41
|
+
return finalizeData(data, source);
|
|
42
|
+
}
|
|
43
|
+
function joinedLabels(payload) {
|
|
44
|
+
const names = arrayField(payload, "label-info").map((item) => {
|
|
45
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
46
|
+
return undefined;
|
|
47
|
+
return nestedString(item, ["label", "name"]);
|
|
48
|
+
}).filter((value) => !!value);
|
|
49
|
+
return names.length ? names.join(", ") : undefined;
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work.d.ts","sourceRoot":"","sources":["../../../src/providers/musicbrainz/work.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAKpD,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAoBvF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { addDetail, addRelation, addTag, arrayField, baseDataFromSource, finalizeData, stringField } from "../common.js";
|
|
2
|
+
import { MUSICBRAINZ_ARTIST_CATEGORY, MUSICBRAINZ_PROVIDER, MUSICBRAINZ_WORK_CATEGORY } from "./constants.js";
|
|
3
|
+
import { addMusicBrainzAliases, addMusicBrainzDescription, addMusicBrainzTags, musicBrainzId, musicBrainzPayload, requireMusicBrainzTitle } from "./helpers.js";
|
|
4
|
+
export async function transformMusicBrainzWork(source) {
|
|
5
|
+
if (source.source.provider !== MUSICBRAINZ_PROVIDER || source.source.category !== MUSICBRAINZ_WORK_CATEGORY) {
|
|
6
|
+
throw new Error(`unsupported MusicBrainz source: ${source.source.provider}:${source.source.category}`);
|
|
7
|
+
}
|
|
8
|
+
const payload = musicBrainzPayload(source.payload);
|
|
9
|
+
const id = musicBrainzId(payload, source.source.externalId);
|
|
10
|
+
const title = requireMusicBrainzTitle(payload);
|
|
11
|
+
if (!id)
|
|
12
|
+
throw new Error("missing required MusicBrainz work field: id");
|
|
13
|
+
if (!title)
|
|
14
|
+
throw new Error("missing required MusicBrainz work field: title");
|
|
15
|
+
const data = await baseDataFromSource(source, MUSICBRAINZ_PROVIDER, MUSICBRAINZ_WORK_CATEGORY, "musical_work", id, title);
|
|
16
|
+
addMusicBrainzAliases(data, payload, title);
|
|
17
|
+
addMusicBrainzDescription(data, payload);
|
|
18
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "type", stringField(payload, "type"));
|
|
19
|
+
addDetail(data, MUSICBRAINZ_PROVIDER, "iswcs", arrayField(payload, "iswcs").filter((value) => typeof value === "string"));
|
|
20
|
+
for (const iswc of arrayField(payload, "iswcs"))
|
|
21
|
+
if (typeof iswc === "string")
|
|
22
|
+
data.externalIds.push({ source: "iswc", category: MUSICBRAINZ_WORK_CATEGORY, value: iswc });
|
|
23
|
+
await addArtistRelations(data, payload);
|
|
24
|
+
addMusicBrainzTags(data, payload);
|
|
25
|
+
addTag(data, "composition");
|
|
26
|
+
return finalizeData(data, source);
|
|
27
|
+
}
|
|
28
|
+
async function addArtistRelations(data, payload) {
|
|
29
|
+
let index = 0;
|
|
30
|
+
for (const relation of arrayField(payload, "relations")) {
|
|
31
|
+
if (!relation || typeof relation !== "object" || Array.isArray(relation))
|
|
32
|
+
continue;
|
|
33
|
+
const targetType = stringField(relation, "target-type");
|
|
34
|
+
const artist = relation.artist;
|
|
35
|
+
if (targetType !== "artist" || !artist || typeof artist !== "object" || Array.isArray(artist))
|
|
36
|
+
continue;
|
|
37
|
+
await addRelation(data, MUSICBRAINZ_PROVIDER, MUSICBRAINZ_ARTIST_CATEGORY, stringField(artist, "id"), stringField(relation, "type") ?? "artist", stringField(artist, "name"), { order: index });
|
|
38
|
+
index += 1;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/providers/openfoodfacts/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,kBAAkB,CAAC;AACtD,eAAO,MAAM,8BAA8B,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/openfoodfacts/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"product.d.ts","sourceRoot":"","sources":["../../../src/providers/openfoodfacts/product.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAIpD,wBAAsB,6BAA6B,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAmB5F"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { addAlias, addDetail, addMedia, addTag, arrayField, baseDataFromSource, finalizeData, objectPayload, stringField, valueAsString } from "../common.js";
|
|
2
|
+
import { OPENFOODFACTS_PRODUCT_CATEGORY, OPENFOODFACTS_PROVIDER } from "./constants.js";
|
|
3
|
+
export async function transformOpenFoodFactsProduct(source) {
|
|
4
|
+
if (source.source.provider !== OPENFOODFACTS_PROVIDER || source.source.category !== OPENFOODFACTS_PRODUCT_CATEGORY) {
|
|
5
|
+
throw new Error(`unsupported OpenFoodFacts source: ${source.source.provider}:${source.source.category}`);
|
|
6
|
+
}
|
|
7
|
+
const payload = objectPayload(source.payload);
|
|
8
|
+
const id = source.source.externalId.trim() || stringField(payload, "code") || stringField(payload, "_id");
|
|
9
|
+
const title = stringField(payload, "product_name") ?? stringField(payload, "title") ?? stringField(payload, "name");
|
|
10
|
+
if (!id)
|
|
11
|
+
throw new Error("missing required OpenFoodFacts product field: code");
|
|
12
|
+
if (!title)
|
|
13
|
+
throw new Error("missing required OpenFoodFacts product field: product_name");
|
|
14
|
+
const data = await baseDataFromSource(source, OPENFOODFACTS_PROVIDER, OPENFOODFACTS_PRODUCT_CATEGORY, OPENFOODFACTS_PRODUCT_CATEGORY, id, title);
|
|
15
|
+
addAlias(data, title, "title", true, OPENFOODFACTS_PROVIDER, "en");
|
|
16
|
+
addMedia(data, OPENFOODFACTS_PROVIDER, stringField(payload, "image_front_url") ?? stringField(payload, "image_url"), "cover");
|
|
17
|
+
for (const [jsonKey, detailKey] of [["brands", "brands"], ["quantity", "quantity"], ["serving_size", "serving_size"], ["ingredients_text", "ingredients_text"], ["nutriscore_grade", "nutriscore_grade"], ["ecoscore_grade", "ecoscore_grade"], ["allergens", "allergens"]])
|
|
18
|
+
addDetail(data, OPENFOODFACTS_PROVIDER, detailKey, stringField(payload, jsonKey));
|
|
19
|
+
addDetail(data, OPENFOODFACTS_PROVIDER, "barcode", id);
|
|
20
|
+
const nutriments = payload.nutriments && typeof payload.nutriments === "object" && !Array.isArray(payload.nutriments) ? payload.nutriments : {};
|
|
21
|
+
for (const [jsonKey, detailKey] of [["energy-kcal_100g", "calories_100g"], ["fat_100g", "fat_100g"], ["proteins_100g", "protein_100g"], ["carbohydrates_100g", "carbs_100g"], ["sugars_100g", "sugars_100g"], ["fiber_100g", "fiber_100g"], ["salt_100g", "salt_100g"], ["sodium_100g", "sodium_100g"], ["energy-kj_100g", "energy_kj_100g"], ["saturated-fat_100g", "saturated_fat_100g"], ["nutriscore_score", "nutriscore_score"], ["nova-group", "nova_group"]])
|
|
22
|
+
addDetail(data, OPENFOODFACTS_PROVIDER, detailKey, valueAsString(nutriments[jsonKey]));
|
|
23
|
+
addTag(data, "food");
|
|
24
|
+
for (const key of ["categories_tags", "countries_tags"])
|
|
25
|
+
for (const value of arrayField(payload, key))
|
|
26
|
+
if (typeof value === "string")
|
|
27
|
+
addTag(data, value.split(":").at(-1));
|
|
28
|
+
return finalizeData(data, source);
|
|
29
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type SearchResponse, type ZuuidData, type ZuuidSearchResult } from "../../entity.js";
|
|
2
|
+
import { type SourceRecord } from "../../source.js";
|
|
3
|
+
import type { OpenLibraryProvider } from "./client.js";
|
|
4
|
+
import type { OpenLibrarySearchInput, OpenLibraryTransformOptions } from "./types.js";
|
|
5
|
+
export declare const OPEN_LIBRARY_AUTHOR_CATEGORY = "author";
|
|
6
|
+
export type FetchOpenLibraryAuthorInput = {
|
|
7
|
+
id: string;
|
|
8
|
+
workLimit?: number;
|
|
9
|
+
};
|
|
10
|
+
export type OpenLibraryAuthorDescription = string | {
|
|
11
|
+
type?: string;
|
|
12
|
+
value?: string;
|
|
13
|
+
};
|
|
14
|
+
export type OpenLibraryAuthorPayload = {
|
|
15
|
+
key?: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
alternate_names?: string[];
|
|
18
|
+
birth_date?: string;
|
|
19
|
+
death_date?: string;
|
|
20
|
+
date?: string;
|
|
21
|
+
bio?: OpenLibraryAuthorDescription;
|
|
22
|
+
wikipedia?: string;
|
|
23
|
+
links?: {
|
|
24
|
+
title?: string;
|
|
25
|
+
url?: string;
|
|
26
|
+
type?: {
|
|
27
|
+
key?: string;
|
|
28
|
+
};
|
|
29
|
+
}[];
|
|
30
|
+
photos?: number[];
|
|
31
|
+
personal_name?: string;
|
|
32
|
+
remote_ids?: Record<string, string>;
|
|
33
|
+
};
|
|
34
|
+
export type OpenLibraryAuthorWorkPayload = {
|
|
35
|
+
key?: string;
|
|
36
|
+
title?: string;
|
|
37
|
+
first_publish_date?: string;
|
|
38
|
+
covers?: number[];
|
|
39
|
+
};
|
|
40
|
+
export type OpenLibraryAuthorWorksPayload = {
|
|
41
|
+
entries?: OpenLibraryAuthorWorkPayload[];
|
|
42
|
+
size?: number;
|
|
43
|
+
};
|
|
44
|
+
export type OpenLibraryAuthorSourcePayload = {
|
|
45
|
+
author: OpenLibraryAuthorPayload;
|
|
46
|
+
works?: {
|
|
47
|
+
entries: OpenLibraryAuthorWorkPayload[];
|
|
48
|
+
size?: number;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
export type OpenLibraryAuthorSearchDoc = {
|
|
52
|
+
key?: string;
|
|
53
|
+
name?: string;
|
|
54
|
+
alternate_names?: string[];
|
|
55
|
+
birth_date?: string;
|
|
56
|
+
top_work?: string;
|
|
57
|
+
work_count?: number;
|
|
58
|
+
top_subjects?: string[];
|
|
59
|
+
_version_?: number;
|
|
60
|
+
};
|
|
61
|
+
export declare function fetchOpenLibraryAuthorSourceRecord(provider: OpenLibraryProvider, input: FetchOpenLibraryAuthorInput): Promise<SourceRecord | undefined>;
|
|
62
|
+
export declare function searchOpenLibraryAuthorSourceRecords(provider: OpenLibraryProvider, input: OpenLibrarySearchInput): Promise<SearchResponse<SourceRecord>>;
|
|
63
|
+
export declare function searchOpenLibraryAuthors(provider: OpenLibraryProvider, input: OpenLibrarySearchInput): Promise<SearchResponse<ZuuidSearchResult>>;
|
|
64
|
+
export declare function transformOpenLibraryAuthor(source: SourceRecord, options?: OpenLibraryTransformOptions): Promise<ZuuidData>;
|
|
65
|
+
//# sourceMappingURL=author.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"author.d.ts","sourceRoot":"","sources":["../../../src/providers/openlibrary/author.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAE/G,OAAO,EAA4C,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,sBAAsB,EAA6B,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAEjH,eAAO,MAAM,4BAA4B,WAAW,CAAC;AAErD,MAAM,MAAM,2BAA2B,GAAG;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtF,MAAM,MAAM,wBAAwB,GAAG;IACrC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,4BAA4B,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,EAAE,CAAC;IACpE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,CAAC,EAAE,4BAA4B,EAAE,CAAC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,MAAM,EAAE,wBAAwB,CAAC;IACjC,KAAK,CAAC,EAAE;QACN,OAAO,EAAE,4BAA4B,EAAE,CAAC;QACxC,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAsB,kCAAkC,CACtD,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAoBnC;AAED,wBAAsB,oCAAoC,CACxD,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAMvC;AAED,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,CAgC5C;AAED,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,SAAS,CAAC,CAiDpB"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { createZuuidData } from "../../entity.js";
|
|
2
|
+
import { providerZuuid } from "../../identity.js";
|
|
3
|
+
import { attachSourceMetadata, createSourceRecord } from "../../source.js";
|
|
4
|
+
import { OPEN_LIBRARY_COVER_BASE_URL, OPEN_LIBRARY_PROVIDER } from "./constants.js";
|
|
5
|
+
export const OPEN_LIBRARY_AUTHOR_CATEGORY = "author";
|
|
6
|
+
export async function fetchOpenLibraryAuthorSourceRecord(provider, input) {
|
|
7
|
+
const id = normalizeOpenLibraryAuthorId(input.id);
|
|
8
|
+
const author = await provider.getJson(`/authors/${id}.json`, {});
|
|
9
|
+
if (!author) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const works = await provider.getJson(`/authors/${id}/works.json`, {
|
|
13
|
+
limit: String(input.workLimit ?? 20)
|
|
14
|
+
});
|
|
15
|
+
const payload = {
|
|
16
|
+
author,
|
|
17
|
+
...(works ? { works: { entries: works.entries ?? [], size: works.size } } : {})
|
|
18
|
+
};
|
|
19
|
+
return createSourceRecord({
|
|
20
|
+
source: { provider: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_AUTHOR_CATEGORY, externalId: id },
|
|
21
|
+
payload: payload
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export async function searchOpenLibraryAuthorSourceRecords(provider, input) {
|
|
25
|
+
const payload = await fetchOpenLibraryAuthorSearchResults(provider, input);
|
|
26
|
+
return {
|
|
27
|
+
results: await sourceRecordsFromSearchResults(payload.docs),
|
|
28
|
+
pagination: paginationFromOpenLibrarySearchResponse(payload, input)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export async function searchOpenLibraryAuthors(provider, input) {
|
|
32
|
+
const payload = await fetchOpenLibraryAuthorSearchResults(provider, input);
|
|
33
|
+
const results = [];
|
|
34
|
+
for (const item of payload.docs ?? []) {
|
|
35
|
+
const id = normalizeOpenLibraryAuthorIdOrUndefined(item.key);
|
|
36
|
+
const title = stringField(item.name);
|
|
37
|
+
if (!id || !title) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const zuuid = await providerZuuid({ provider: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_AUTHOR_CATEGORY, externalId: id });
|
|
41
|
+
results.push({
|
|
42
|
+
id: zuuid,
|
|
43
|
+
zuuid,
|
|
44
|
+
category: OPEN_LIBRARY_AUTHOR_CATEGORY,
|
|
45
|
+
title,
|
|
46
|
+
date: stringField(item.birth_date) ?? null,
|
|
47
|
+
cover: null,
|
|
48
|
+
rating: null,
|
|
49
|
+
weight: typeof item.work_count === "number" ? item.work_count : null,
|
|
50
|
+
relationType: null,
|
|
51
|
+
attribute: stringField(item.top_work) ?? null,
|
|
52
|
+
order: null,
|
|
53
|
+
source: { source: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_AUTHOR_CATEGORY, value: id }
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
results,
|
|
58
|
+
pagination: paginationFromOpenLibrarySearchResponse(payload, input)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export async function transformOpenLibraryAuthor(source, options = {}) {
|
|
62
|
+
if (source.source.provider !== OPEN_LIBRARY_PROVIDER || source.source.category !== OPEN_LIBRARY_AUTHOR_CATEGORY) {
|
|
63
|
+
throw new Error(`unsupported Open Library source: ${source.source.provider}:${source.source.category}`);
|
|
64
|
+
}
|
|
65
|
+
const payload = openLibraryAuthorSourcePayload(source.payload);
|
|
66
|
+
const author = payload.author;
|
|
67
|
+
const id = normalizeOpenLibraryAuthorId(source.source.externalId || author.key || "");
|
|
68
|
+
const name = stringField(author.name);
|
|
69
|
+
if (!name) {
|
|
70
|
+
throw new Error("missing required Open Library author field: name");
|
|
71
|
+
}
|
|
72
|
+
const zuuid = await providerZuuid({ provider: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_AUTHOR_CATEGORY, externalId: id });
|
|
73
|
+
const data = createZuuidData({ zuuid, category: OPEN_LIBRARY_AUTHOR_CATEGORY, primaryTitle: name });
|
|
74
|
+
const birthDate = stringField(author.birth_date ?? author.date);
|
|
75
|
+
if (birthDate) {
|
|
76
|
+
data.primaryDate = birthDate;
|
|
77
|
+
data.details.push({ key: "birth_date", value: birthDate, source: OPEN_LIBRARY_PROVIDER });
|
|
78
|
+
}
|
|
79
|
+
addDetail(data, "death_date", author.death_date);
|
|
80
|
+
addDetail(data, "personal_name", author.personal_name);
|
|
81
|
+
addDetail(data, "wikipedia", author.wikipedia);
|
|
82
|
+
addStructuredDetail(data, "links", author.links);
|
|
83
|
+
addStructuredDetail(data, "remote_ids", author.remote_ids);
|
|
84
|
+
if (typeof payload.works?.size === "number") {
|
|
85
|
+
data.details.push({ key: "work_count", value: payload.works.size, source: OPEN_LIBRARY_PROVIDER });
|
|
86
|
+
}
|
|
87
|
+
const bio = descriptionValue(author.bio);
|
|
88
|
+
if (bio) {
|
|
89
|
+
data.descriptions.push({ value: bio, source: OPEN_LIBRARY_PROVIDER });
|
|
90
|
+
}
|
|
91
|
+
for (const alias of author.alternate_names ?? []) {
|
|
92
|
+
const value = stringField(alias);
|
|
93
|
+
if (value && value !== name && !data.aliases.some((item) => item.value === value)) {
|
|
94
|
+
data.aliases.push({ value, aliasType: "alternate_name", isPrimary: false, source: OPEN_LIBRARY_PROVIDER });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
addPhoto(data, author.photos?.find((photo) => typeof photo === "number"), options.coverBaseUrl ?? OPEN_LIBRARY_COVER_BASE_URL, true);
|
|
98
|
+
for (const [index, work] of (payload.works?.entries ?? []).entries()) {
|
|
99
|
+
await addWorkRelation(data, work, index, options);
|
|
100
|
+
}
|
|
101
|
+
return attachSourceMetadata(data, source);
|
|
102
|
+
}
|
|
103
|
+
async function fetchOpenLibraryAuthorSearchResults(provider, input) {
|
|
104
|
+
const payload = await provider.getJson("/search/authors.json", {
|
|
105
|
+
q: searchQuery(input.query),
|
|
106
|
+
page: String(input.page ?? 1),
|
|
107
|
+
limit: String(input.limit ?? 20)
|
|
108
|
+
});
|
|
109
|
+
return payload ?? {};
|
|
110
|
+
}
|
|
111
|
+
async function sourceRecordsFromSearchResults(results) {
|
|
112
|
+
const records = [];
|
|
113
|
+
for (const result of results ?? []) {
|
|
114
|
+
const id = normalizeOpenLibraryAuthorIdOrUndefined(result.key);
|
|
115
|
+
if (!id) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
records.push(await createSourceRecord({
|
|
119
|
+
source: { provider: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_AUTHOR_CATEGORY, externalId: id },
|
|
120
|
+
payload: result
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
return records;
|
|
124
|
+
}
|
|
125
|
+
async function addWorkRelation(data, work, index, options) {
|
|
126
|
+
const id = normalizeOpenLibraryWorkIdOrUndefined(work.key);
|
|
127
|
+
const title = stringField(work.title);
|
|
128
|
+
if (!id || !title) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const zuuid = await providerZuuid({ provider: OPEN_LIBRARY_PROVIDER, category: "book", externalId: id });
|
|
132
|
+
data.relations.push({
|
|
133
|
+
id: zuuid,
|
|
134
|
+
zuuid,
|
|
135
|
+
category: "book",
|
|
136
|
+
title,
|
|
137
|
+
date: stringField(work.first_publish_date) ?? null,
|
|
138
|
+
cover: coverUrl(work.covers?.find((cover) => typeof cover === "number"), options.coverBaseUrl ?? OPEN_LIBRARY_COVER_BASE_URL),
|
|
139
|
+
rating: null,
|
|
140
|
+
weight: null,
|
|
141
|
+
relationType: "author_of",
|
|
142
|
+
direction: "outgoing",
|
|
143
|
+
attribute: null,
|
|
144
|
+
order: index,
|
|
145
|
+
source: OPEN_LIBRARY_PROVIDER,
|
|
146
|
+
externalId: id
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function addPhoto(data, photoId, baseUrl, isPrimary) {
|
|
150
|
+
const url = coverUrl(photoId, baseUrl);
|
|
151
|
+
if (!url) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
data.cover = data.cover ?? url;
|
|
155
|
+
data.media.push({
|
|
156
|
+
url,
|
|
157
|
+
mediaType: "image",
|
|
158
|
+
mediaCategory: "profile",
|
|
159
|
+
isPrimary,
|
|
160
|
+
source: OPEN_LIBRARY_PROVIDER
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function addDetail(data, key, value) {
|
|
164
|
+
const clean = stringField(value);
|
|
165
|
+
if (clean) {
|
|
166
|
+
data.details.push({ key, value: clean, source: OPEN_LIBRARY_PROVIDER });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function addStructuredDetail(data, key, value) {
|
|
170
|
+
if (value === undefined || value === null || (Array.isArray(value) && value.length === 0)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
data.details.push({ key, value, source: OPEN_LIBRARY_PROVIDER });
|
|
174
|
+
}
|
|
175
|
+
function paginationFromOpenLibrarySearchResponse(payload, input) {
|
|
176
|
+
const totalResults = payload.numFound ?? payload.num_found ?? 0;
|
|
177
|
+
const limit = input.limit ?? 20;
|
|
178
|
+
return {
|
|
179
|
+
page: input.page ?? 1,
|
|
180
|
+
totalPages: limit > 0 ? Math.ceil(totalResults / limit) : 0,
|
|
181
|
+
totalResults
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function openLibraryAuthorSourcePayload(payload) {
|
|
185
|
+
const value = payload;
|
|
186
|
+
if ("author" in value && value.author) {
|
|
187
|
+
return value;
|
|
188
|
+
}
|
|
189
|
+
return { author: value };
|
|
190
|
+
}
|
|
191
|
+
function normalizeOpenLibraryAuthorId(value) {
|
|
192
|
+
const normalized = stringField(value)?.replace(/^\/authors\//, "").replace(/\.json$/, "");
|
|
193
|
+
if (!normalized) {
|
|
194
|
+
throw new Error("Open Library author id must not be empty");
|
|
195
|
+
}
|
|
196
|
+
if (!/^OL\d+A$/i.test(normalized)) {
|
|
197
|
+
throw new Error(`Open Library author id must look like OL23919A: ${value}`);
|
|
198
|
+
}
|
|
199
|
+
return normalized.toUpperCase();
|
|
200
|
+
}
|
|
201
|
+
function normalizeOpenLibraryAuthorIdOrUndefined(value) {
|
|
202
|
+
const normalized = stringField(value)?.replace(/^\/authors\//, "");
|
|
203
|
+
return normalized && /^OL\d+A$/i.test(normalized) ? normalized.toUpperCase() : undefined;
|
|
204
|
+
}
|
|
205
|
+
function normalizeOpenLibraryWorkIdOrUndefined(value) {
|
|
206
|
+
const normalized = stringField(value)?.replace(/^\/works\//, "");
|
|
207
|
+
return normalized && /^OL\d+W$/i.test(normalized) ? normalized.toUpperCase() : undefined;
|
|
208
|
+
}
|
|
209
|
+
function coverUrl(coverId, baseUrl) {
|
|
210
|
+
if (typeof coverId !== "number" || !baseUrl) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
return `${baseUrl.replace(/\/$/, "")}/${coverId}-L.jpg`;
|
|
214
|
+
}
|
|
215
|
+
function descriptionValue(value) {
|
|
216
|
+
return typeof value === "string" ? stringField(value) : stringField(value?.value);
|
|
217
|
+
}
|
|
218
|
+
function searchQuery(value) {
|
|
219
|
+
const query = stringField(value);
|
|
220
|
+
if (!query) {
|
|
221
|
+
throw new Error("Open Library author search query must not be empty");
|
|
222
|
+
}
|
|
223
|
+
return query;
|
|
224
|
+
}
|
|
225
|
+
function stringField(value) {
|
|
226
|
+
const normalized = value?.trim();
|
|
227
|
+
return normalized ? normalized : undefined;
|
|
228
|
+
}
|