@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,138 @@
|
|
|
1
|
+
import { type SearchResponse, type ZuuidData, type ZuuidSearchResult } from "../../entity.js";
|
|
2
|
+
import { type SourceRecord } from "../../source.js";
|
|
3
|
+
import type { JsonValue } from "../../types.js";
|
|
4
|
+
import type { OpenLibraryProvider } from "./client.js";
|
|
5
|
+
import type { OpenLibrarySearchInput, OpenLibraryTransformOptions } from "./types.js";
|
|
6
|
+
export declare const OPEN_LIBRARY_BOOK_CATEGORY = "book";
|
|
7
|
+
export type FetchOpenLibraryBookInput = {
|
|
8
|
+
id: string;
|
|
9
|
+
editionLimit?: number;
|
|
10
|
+
};
|
|
11
|
+
export type OpenLibraryDescription = string | {
|
|
12
|
+
type?: string;
|
|
13
|
+
value?: string;
|
|
14
|
+
};
|
|
15
|
+
export type OpenLibraryAuthorRef = {
|
|
16
|
+
author?: {
|
|
17
|
+
key?: string;
|
|
18
|
+
};
|
|
19
|
+
type?: {
|
|
20
|
+
key?: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export type OpenLibraryWorkPayload = {
|
|
24
|
+
key?: string;
|
|
25
|
+
title?: string;
|
|
26
|
+
description?: OpenLibraryDescription;
|
|
27
|
+
covers?: number[];
|
|
28
|
+
subjects?: string[];
|
|
29
|
+
subject_places?: string[];
|
|
30
|
+
subject_people?: string[];
|
|
31
|
+
subject_times?: string[];
|
|
32
|
+
first_publish_date?: string;
|
|
33
|
+
authors?: OpenLibraryAuthorRef[];
|
|
34
|
+
links?: {
|
|
35
|
+
title?: string;
|
|
36
|
+
url?: string;
|
|
37
|
+
type?: {
|
|
38
|
+
key?: string;
|
|
39
|
+
};
|
|
40
|
+
}[];
|
|
41
|
+
excerpts?: {
|
|
42
|
+
excerpt?: string;
|
|
43
|
+
pages?: string;
|
|
44
|
+
comment?: string;
|
|
45
|
+
}[];
|
|
46
|
+
series?: JsonValue[];
|
|
47
|
+
cover_edition?: {
|
|
48
|
+
key?: string;
|
|
49
|
+
};
|
|
50
|
+
created?: JsonValue;
|
|
51
|
+
last_modified?: JsonValue;
|
|
52
|
+
latest_revision?: number;
|
|
53
|
+
revision?: number;
|
|
54
|
+
};
|
|
55
|
+
export type OpenLibraryBookAuthorPayload = {
|
|
56
|
+
key?: string;
|
|
57
|
+
name?: string;
|
|
58
|
+
birth_date?: string;
|
|
59
|
+
death_date?: string;
|
|
60
|
+
bio?: OpenLibraryDescription;
|
|
61
|
+
photos?: number[];
|
|
62
|
+
};
|
|
63
|
+
export type OpenLibraryEditionPayload = {
|
|
64
|
+
key?: string;
|
|
65
|
+
title?: string;
|
|
66
|
+
subtitle?: string;
|
|
67
|
+
full_title?: string;
|
|
68
|
+
edition_name?: string;
|
|
69
|
+
description?: OpenLibraryDescription;
|
|
70
|
+
notes?: OpenLibraryDescription;
|
|
71
|
+
publish_date?: string;
|
|
72
|
+
publishers?: string[];
|
|
73
|
+
number_of_pages?: number;
|
|
74
|
+
physical_format?: string;
|
|
75
|
+
isbn_10?: string[];
|
|
76
|
+
isbn_13?: string[];
|
|
77
|
+
languages?: {
|
|
78
|
+
key?: string;
|
|
79
|
+
}[];
|
|
80
|
+
translated_from?: {
|
|
81
|
+
key?: string;
|
|
82
|
+
}[];
|
|
83
|
+
translation_of?: string;
|
|
84
|
+
covers?: number[];
|
|
85
|
+
contributors?: {
|
|
86
|
+
role?: string;
|
|
87
|
+
name?: string;
|
|
88
|
+
}[];
|
|
89
|
+
identifiers?: Record<string, JsonValue>;
|
|
90
|
+
classifications?: Record<string, JsonValue>;
|
|
91
|
+
local_id?: string[];
|
|
92
|
+
source_records?: string[];
|
|
93
|
+
};
|
|
94
|
+
export type OpenLibraryEditionsPayload = {
|
|
95
|
+
entries?: OpenLibraryEditionPayload[];
|
|
96
|
+
size?: number;
|
|
97
|
+
};
|
|
98
|
+
export type OpenLibraryBookPayload = {
|
|
99
|
+
work: OpenLibraryWorkPayload;
|
|
100
|
+
search?: OpenLibrarySearchDoc;
|
|
101
|
+
editions?: {
|
|
102
|
+
entries: OpenLibraryEditionPayload[];
|
|
103
|
+
size?: number;
|
|
104
|
+
};
|
|
105
|
+
authors?: OpenLibraryBookAuthorPayload[];
|
|
106
|
+
ratings?: OpenLibraryRatingsPayload;
|
|
107
|
+
};
|
|
108
|
+
export type OpenLibraryRatingsPayload = {
|
|
109
|
+
summary?: {
|
|
110
|
+
average?: number;
|
|
111
|
+
count?: number;
|
|
112
|
+
sortable?: number;
|
|
113
|
+
};
|
|
114
|
+
counts?: Record<string, number>;
|
|
115
|
+
};
|
|
116
|
+
export type OpenLibrarySearchDoc = {
|
|
117
|
+
key?: string;
|
|
118
|
+
title?: string;
|
|
119
|
+
author_name?: string[];
|
|
120
|
+
author_key?: string[];
|
|
121
|
+
first_publish_year?: number;
|
|
122
|
+
first_publish_date?: string;
|
|
123
|
+
cover_i?: number;
|
|
124
|
+
ratings_average?: number;
|
|
125
|
+
edition_count?: number;
|
|
126
|
+
language?: string[];
|
|
127
|
+
isbn?: string[];
|
|
128
|
+
ia?: string[];
|
|
129
|
+
subject?: string[];
|
|
130
|
+
ddc?: string[];
|
|
131
|
+
lcc?: string[];
|
|
132
|
+
lccn?: string[];
|
|
133
|
+
};
|
|
134
|
+
export declare function fetchOpenLibraryBookSourceRecord(provider: OpenLibraryProvider, input: FetchOpenLibraryBookInput): Promise<SourceRecord | undefined>;
|
|
135
|
+
export declare function searchOpenLibraryBookSourceRecords(provider: OpenLibraryProvider, input: OpenLibrarySearchInput): Promise<SearchResponse<SourceRecord>>;
|
|
136
|
+
export declare function searchOpenLibraryBooks(provider: OpenLibraryProvider, input: OpenLibrarySearchInput, options?: OpenLibraryTransformOptions): Promise<SearchResponse<ZuuidSearchResult>>;
|
|
137
|
+
export declare function transformOpenLibraryBook(source: SourceRecord, options?: OpenLibraryTransformOptions): Promise<ZuuidData>;
|
|
138
|
+
//# sourceMappingURL=book.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"book.d.ts","sourceRoot":"","sources":["../../../src/providers/openlibrary/book.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;AAC9F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,sBAAsB,EAA6B,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAEjH,eAAO,MAAM,0BAA0B,SAAS,CAAC;AAEjD,MAAM,MAAM,yBAAyB,GAAG;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,IAAI,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACjC,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,QAAQ,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACpE,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB,aAAa,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjC,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,sBAAsB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC5C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,CAAC,EAAE,yBAAyB,EAAE,CAAC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,QAAQ,CAAC,EAAE;QACT,OAAO,EAAE,yBAAyB,EAAE,CAAC;QACrC,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,OAAO,CAAC,EAAE,4BAA4B,EAAE,CAAC;IACzC,OAAO,CAAC,EAAE,yBAAyB,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CA0BnC;AAED,wBAAsB,kCAAkC,CACtD,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAMvC;AAED,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,sBAAsB,EAC7B,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,CAiC5C;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,SAAS,CAAC,CAwFpB"}
|
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
import { createZuuidData } from "../../entity.js";
|
|
2
|
+
import { providerZuuid } from "../../identity.js";
|
|
3
|
+
import { attachSourceMetadata, createSourceRecord } from "../../source.js";
|
|
4
|
+
import { normalizeRating } from "../common.js";
|
|
5
|
+
import { OPEN_LIBRARY_COVER_BASE_URL, OPEN_LIBRARY_PROVIDER } from "./constants.js";
|
|
6
|
+
export const OPEN_LIBRARY_BOOK_CATEGORY = "book";
|
|
7
|
+
export async function fetchOpenLibraryBookSourceRecord(provider, input) {
|
|
8
|
+
const id = normalizeOpenLibraryBookId(input.id);
|
|
9
|
+
const work = await provider.getJson(`/works/${id}.json`, {});
|
|
10
|
+
if (!work) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const search = await fetchOpenLibraryBookSearchDoc(provider, id, work);
|
|
14
|
+
const editions = await provider.getJson(`/works/${id}/editions.json`, {
|
|
15
|
+
limit: String(input.editionLimit ?? 50)
|
|
16
|
+
});
|
|
17
|
+
const ratings = await provider.getJson(`/works/${id}/ratings.json`, {});
|
|
18
|
+
const authors = await fetchOpenLibraryAuthors(provider, work);
|
|
19
|
+
const payload = {
|
|
20
|
+
work,
|
|
21
|
+
...(search ? { search } : {}),
|
|
22
|
+
...(editions ? { editions: { entries: editions.entries ?? [], size: editions.size } } : {}),
|
|
23
|
+
...(authors.length ? { authors } : {}),
|
|
24
|
+
...(ratings ? { ratings } : {})
|
|
25
|
+
};
|
|
26
|
+
return createSourceRecord({
|
|
27
|
+
source: { provider: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_BOOK_CATEGORY, externalId: id },
|
|
28
|
+
payload: payload
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export async function searchOpenLibraryBookSourceRecords(provider, input) {
|
|
32
|
+
const payload = await fetchOpenLibraryBookSearchResults(provider, input);
|
|
33
|
+
return {
|
|
34
|
+
results: await sourceRecordsFromSearchResults(payload.docs),
|
|
35
|
+
pagination: paginationFromOpenLibrarySearchResponse(payload, input)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export async function searchOpenLibraryBooks(provider, input, options = {}) {
|
|
39
|
+
const payload = await fetchOpenLibraryBookSearchResults(provider, input);
|
|
40
|
+
const results = [];
|
|
41
|
+
for (const item of payload.docs ?? []) {
|
|
42
|
+
const id = openLibraryWorkIdFromKey(item.key);
|
|
43
|
+
const title = stringField(item.title);
|
|
44
|
+
if (!id || !title) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const zuuid = await providerZuuid({ provider: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_BOOK_CATEGORY, externalId: id });
|
|
48
|
+
const rating = typeof item.ratings_average === "number" ? item.ratings_average : null;
|
|
49
|
+
results.push({
|
|
50
|
+
id: zuuid,
|
|
51
|
+
zuuid,
|
|
52
|
+
category: OPEN_LIBRARY_BOOK_CATEGORY,
|
|
53
|
+
title,
|
|
54
|
+
date: item.first_publish_year === undefined ? stringField(item.first_publish_date) ?? null : String(item.first_publish_year),
|
|
55
|
+
cover: coverUrl(item.cover_i, options.coverBaseUrl ?? OPEN_LIBRARY_COVER_BASE_URL),
|
|
56
|
+
rating,
|
|
57
|
+
weight: typeof item.edition_count === "number" ? item.edition_count : rating,
|
|
58
|
+
relationType: null,
|
|
59
|
+
attribute: item.author_name?.filter(Boolean).join(", ") || null,
|
|
60
|
+
order: null,
|
|
61
|
+
source: { source: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_BOOK_CATEGORY, value: id }
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
results,
|
|
66
|
+
pagination: paginationFromOpenLibrarySearchResponse(payload, input)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export async function transformOpenLibraryBook(source, options = {}) {
|
|
70
|
+
if (source.source.provider !== OPEN_LIBRARY_PROVIDER || source.source.category !== OPEN_LIBRARY_BOOK_CATEGORY) {
|
|
71
|
+
throw new Error(`unsupported Open Library source: ${source.source.provider}:${source.source.category}`);
|
|
72
|
+
}
|
|
73
|
+
const payload = openLibraryBookPayload(source.payload);
|
|
74
|
+
const work = payload.work;
|
|
75
|
+
const id = normalizeOpenLibraryBookId(source.source.externalId || work.key || "");
|
|
76
|
+
const title = stringField(work.title);
|
|
77
|
+
if (!title) {
|
|
78
|
+
throw new Error("missing required Open Library book field: title");
|
|
79
|
+
}
|
|
80
|
+
const zuuid = await providerZuuid({ provider: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_BOOK_CATEGORY, externalId: id });
|
|
81
|
+
const data = createZuuidData({ zuuid, category: OPEN_LIBRARY_BOOK_CATEGORY, primaryTitle: title });
|
|
82
|
+
const preferredEdition = preferredOpenLibraryEdition(payload);
|
|
83
|
+
const rating = numberField(payload.ratings?.summary?.average ?? payload.ratings?.summary?.sortable ?? payload.search?.ratings_average);
|
|
84
|
+
if (rating !== undefined) {
|
|
85
|
+
data.rating = normalizeRating(rating, 0, 5);
|
|
86
|
+
data.details.push({ key: "rating_average", value: rating, source: OPEN_LIBRARY_PROVIDER });
|
|
87
|
+
}
|
|
88
|
+
const ratingCount = numberField(payload.ratings?.summary?.count);
|
|
89
|
+
if (ratingCount !== undefined) {
|
|
90
|
+
data.details.push({ key: "rating_count", value: ratingCount, source: OPEN_LIBRARY_PROVIDER });
|
|
91
|
+
}
|
|
92
|
+
const firstPublishYear = numberField(payload.search?.first_publish_year);
|
|
93
|
+
const firstPublishDate = stringField(work.first_publish_date) ?? stringField(payload.search?.first_publish_date);
|
|
94
|
+
if (firstPublishYear !== undefined) {
|
|
95
|
+
data.primaryDate = String(firstPublishYear);
|
|
96
|
+
data.details.push({ key: "first_publish_year", value: firstPublishYear, source: OPEN_LIBRARY_PROVIDER });
|
|
97
|
+
if (firstPublishDate && firstPublishDate !== String(firstPublishYear)) {
|
|
98
|
+
data.details.push({ key: "work_first_publish_date", value: firstPublishDate, source: OPEN_LIBRARY_PROVIDER });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else if (firstPublishDate) {
|
|
102
|
+
data.primaryDate = firstPublishDate;
|
|
103
|
+
data.details.push({ key: "first_publish_date", value: firstPublishDate, source: OPEN_LIBRARY_PROVIDER });
|
|
104
|
+
}
|
|
105
|
+
const description = descriptionValue(work.description);
|
|
106
|
+
if (description) {
|
|
107
|
+
data.descriptions.push({ value: description, source: OPEN_LIBRARY_PROVIDER });
|
|
108
|
+
}
|
|
109
|
+
for (const edition of payload.editions?.entries ?? []) {
|
|
110
|
+
const editionDescription = descriptionValue(edition.description);
|
|
111
|
+
if (editionDescription && !data.descriptions.some((item) => item.value === editionDescription)) {
|
|
112
|
+
data.descriptions.push({ value: editionDescription, source: OPEN_LIBRARY_PROVIDER });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const primaryCover = work.covers?.find((cover) => typeof cover === "number") ??
|
|
116
|
+
preferredEdition?.covers?.find((cover) => typeof cover === "number") ??
|
|
117
|
+
payload.search?.cover_i;
|
|
118
|
+
const primaryCoverUrl = coverUrl(primaryCover, options.coverBaseUrl ?? OPEN_LIBRARY_COVER_BASE_URL);
|
|
119
|
+
if (primaryCoverUrl) {
|
|
120
|
+
data.cover = primaryCoverUrl;
|
|
121
|
+
data.media.push({
|
|
122
|
+
url: primaryCoverUrl,
|
|
123
|
+
mediaType: "image",
|
|
124
|
+
mediaCategory: "cover",
|
|
125
|
+
isPrimary: true,
|
|
126
|
+
source: OPEN_LIBRARY_PROVIDER
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
addArrayDetail(data, "subjects", work.subjects);
|
|
130
|
+
addArrayDetail(data, "subject_places", work.subject_places);
|
|
131
|
+
addArrayDetail(data, "subject_people", work.subject_people);
|
|
132
|
+
addArrayDetail(data, "subject_times", work.subject_times);
|
|
133
|
+
addStructuredDetail(data, "links", work.links);
|
|
134
|
+
addStructuredDetail(data, "excerpts", work.excerpts);
|
|
135
|
+
addStructuredDetail(data, "series", work.series);
|
|
136
|
+
addStructuredDetail(data, "cover_edition", work.cover_edition);
|
|
137
|
+
addOpenLibraryBookAggregateDetails(data, payload, preferredEdition);
|
|
138
|
+
if (typeof payload.editions?.size === "number") {
|
|
139
|
+
data.details.push({ key: "edition_count", value: payload.editions.size, source: OPEN_LIBRARY_PROVIDER });
|
|
140
|
+
}
|
|
141
|
+
else if (typeof payload.search?.edition_count === "number") {
|
|
142
|
+
data.details.push({ key: "edition_count", value: payload.search.edition_count, source: OPEN_LIBRARY_PROVIDER });
|
|
143
|
+
}
|
|
144
|
+
for (const subject of work.subjects ?? []) {
|
|
145
|
+
addTag(data, subject);
|
|
146
|
+
}
|
|
147
|
+
await addAuthorRelations(data, payload);
|
|
148
|
+
return attachSourceMetadata(data, source);
|
|
149
|
+
}
|
|
150
|
+
async function fetchOpenLibraryBookSearchDoc(provider, id, work) {
|
|
151
|
+
const byId = await fetchOpenLibraryBookSearchDocByQuery(provider, id, id, 5);
|
|
152
|
+
if (byId) {
|
|
153
|
+
return byId;
|
|
154
|
+
}
|
|
155
|
+
const title = stringField(work.title);
|
|
156
|
+
return title ? fetchOpenLibraryBookSearchDocByQuery(provider, id, title, 20) : undefined;
|
|
157
|
+
}
|
|
158
|
+
async function fetchOpenLibraryBookSearchDocByQuery(provider, id, query, limit) {
|
|
159
|
+
const payload = await provider.getJson("/search.json", {
|
|
160
|
+
q: query,
|
|
161
|
+
limit: String(limit),
|
|
162
|
+
fields: openLibraryBookSearchFields().join(",")
|
|
163
|
+
});
|
|
164
|
+
return payload?.docs?.find((doc) => openLibraryWorkIdFromKey(doc.key) === id);
|
|
165
|
+
}
|
|
166
|
+
async function fetchOpenLibraryAuthors(provider, work) {
|
|
167
|
+
const authors = [];
|
|
168
|
+
for (const ref of work.authors ?? []) {
|
|
169
|
+
const id = openLibraryAuthorIdFromKey(ref.author?.key);
|
|
170
|
+
if (!id) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const author = await provider.getJson(`/authors/${id}.json`, {});
|
|
174
|
+
if (author) {
|
|
175
|
+
authors.push(author);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return authors;
|
|
179
|
+
}
|
|
180
|
+
async function fetchOpenLibraryBookSearchResults(provider, input) {
|
|
181
|
+
const query = searchQuery(input.query);
|
|
182
|
+
const fields = input.fields ?? openLibraryBookSearchFields();
|
|
183
|
+
const payload = await provider.getJson("/search.json", {
|
|
184
|
+
q: query,
|
|
185
|
+
page: String(input.page ?? 1),
|
|
186
|
+
limit: String(input.limit ?? 20),
|
|
187
|
+
fields: fields.join(","),
|
|
188
|
+
...(input.language ? { language: input.language } : {})
|
|
189
|
+
});
|
|
190
|
+
return payload ?? {};
|
|
191
|
+
}
|
|
192
|
+
function openLibraryBookSearchFields() {
|
|
193
|
+
return [
|
|
194
|
+
"key",
|
|
195
|
+
"title",
|
|
196
|
+
"author_name",
|
|
197
|
+
"author_key",
|
|
198
|
+
"first_publish_year",
|
|
199
|
+
"first_publish_date",
|
|
200
|
+
"cover_i",
|
|
201
|
+
"ratings_average",
|
|
202
|
+
"edition_count",
|
|
203
|
+
"language",
|
|
204
|
+
"isbn",
|
|
205
|
+
"ia",
|
|
206
|
+
"subject",
|
|
207
|
+
"ddc",
|
|
208
|
+
"lcc",
|
|
209
|
+
"lccn"
|
|
210
|
+
];
|
|
211
|
+
}
|
|
212
|
+
async function sourceRecordsFromSearchResults(results) {
|
|
213
|
+
const records = [];
|
|
214
|
+
for (const result of results ?? []) {
|
|
215
|
+
const id = openLibraryWorkIdFromKey(result.key);
|
|
216
|
+
if (!id) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
records.push(await createSourceRecord({
|
|
220
|
+
source: { provider: OPEN_LIBRARY_PROVIDER, category: OPEN_LIBRARY_BOOK_CATEGORY, externalId: id },
|
|
221
|
+
payload: result
|
|
222
|
+
}));
|
|
223
|
+
}
|
|
224
|
+
return records;
|
|
225
|
+
}
|
|
226
|
+
async function addAuthorRelations(data, payload) {
|
|
227
|
+
for (const [index, ref] of (payload.work.authors ?? []).entries()) {
|
|
228
|
+
const id = openLibraryAuthorIdFromKey(ref.author?.key);
|
|
229
|
+
if (!id) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const author = payload.authors?.find((item) => openLibraryAuthorIdFromKey(item.key) === id);
|
|
233
|
+
const zuuid = await providerZuuid({ provider: OPEN_LIBRARY_PROVIDER, category: "author", externalId: id });
|
|
234
|
+
data.relations.push({
|
|
235
|
+
id: zuuid,
|
|
236
|
+
zuuid,
|
|
237
|
+
category: "author",
|
|
238
|
+
title: stringField(author?.name) ?? id,
|
|
239
|
+
date: stringField(author?.birth_date) ?? null,
|
|
240
|
+
cover: coverUrl(author?.photos?.find((photo) => typeof photo === "number"), OPEN_LIBRARY_COVER_BASE_URL),
|
|
241
|
+
rating: null,
|
|
242
|
+
weight: null,
|
|
243
|
+
relationType: "authored_by",
|
|
244
|
+
direction: "outgoing",
|
|
245
|
+
attribute: null,
|
|
246
|
+
order: index,
|
|
247
|
+
source: OPEN_LIBRARY_PROVIDER,
|
|
248
|
+
externalId: id
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function addOpenLibraryBookAggregateDetails(data, payload, preferredEdition) {
|
|
253
|
+
const editions = payload.editions?.entries ?? [];
|
|
254
|
+
if (!editions.length && !payload.search) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
addArrayDetail(data, "languages", uniqueStrings([...(payload.search?.language ?? []), ...editions.flatMap((edition) => languageKeys(edition.languages))]));
|
|
258
|
+
addArrayDetail(data, "isbn_10", uniqueStrings(editions.flatMap((edition) => edition.isbn_10 ?? [])));
|
|
259
|
+
addArrayDetail(data, "isbn_13", uniqueStrings(editions.flatMap((edition) => edition.isbn_13 ?? [])));
|
|
260
|
+
addArrayDetail(data, "isbn", uniqueStrings([...(payload.search?.isbn ?? []), ...editions.flatMap((edition) => [...(edition.isbn_10 ?? []), ...(edition.isbn_13 ?? [])])]));
|
|
261
|
+
addArrayDetail(data, "ia", payload.search?.ia);
|
|
262
|
+
addArrayDetail(data, "publishers", uniqueStrings(editions.flatMap((edition) => edition.publishers ?? [])));
|
|
263
|
+
addArrayDetail(data, "source_records", uniqueStrings(editions.flatMap((edition) => edition.source_records ?? [])));
|
|
264
|
+
addArrayDetail(data, "local_id", uniqueStrings(editions.flatMap((edition) => edition.local_id ?? [])));
|
|
265
|
+
addArrayDetail(data, "dewey_decimal_class", uniqueStrings([...(payload.search?.ddc ?? []), ...classificationSubjects(payload.work.subjects, "dewey")]));
|
|
266
|
+
addArrayDetail(data, "lc_classifications", uniqueStrings([...(payload.search?.lcc ?? []), ...classificationSubjects(payload.work.subjects, "lc")]));
|
|
267
|
+
addArrayDetail(data, "lccn", payload.search?.lccn);
|
|
268
|
+
addArrayDetail(data, "translated_from", uniqueStrings(editions.flatMap((edition) => languageKeys(edition.translated_from))));
|
|
269
|
+
addArrayDetail(data, "translation_of", uniqueStrings(editions.map((edition) => stringField(edition.translation_of)).filter((item) => Boolean(item))));
|
|
270
|
+
const contributors = uniqueObjects(editions.flatMap((edition) => (edition.contributors ?? [])
|
|
271
|
+
.map((contributor) => ({
|
|
272
|
+
role: stringField(contributor.role),
|
|
273
|
+
name: stringField(contributor.name)
|
|
274
|
+
}))
|
|
275
|
+
.filter((contributor) => contributor.name)));
|
|
276
|
+
addStructuredDetail(data, "contributors", contributors);
|
|
277
|
+
const classifications = combineEditionRecords(editions, "classifications");
|
|
278
|
+
addStructuredDetail(data, "classifications", classifications);
|
|
279
|
+
const identifiers = combineEditionRecords(editions, "identifiers");
|
|
280
|
+
addStructuredDetail(data, "identifiers", identifiers);
|
|
281
|
+
if (preferredEdition) {
|
|
282
|
+
const preferred = editionSummary(preferredEdition);
|
|
283
|
+
addStructuredDetail(data, "preferred_edition", preferred);
|
|
284
|
+
addDetail(data, "publish_date", preferredEdition.publish_date);
|
|
285
|
+
addDetail(data, "edition_name", preferredEdition.edition_name);
|
|
286
|
+
addDetail(data, "full_title", preferredEdition.full_title);
|
|
287
|
+
addDetail(data, "subtitle", preferredEdition.subtitle);
|
|
288
|
+
if (typeof preferredEdition.number_of_pages === "number") {
|
|
289
|
+
data.details.push({ key: "number_of_pages", value: preferredEdition.number_of_pages, source: OPEN_LIBRARY_PROVIDER });
|
|
290
|
+
}
|
|
291
|
+
addDetail(data, "physical_format", preferredEdition.physical_format);
|
|
292
|
+
}
|
|
293
|
+
const summaries = editions.map((edition) => editionSummary(edition)).filter((edition) => Object.keys(edition).length > 0);
|
|
294
|
+
addStructuredDetail(data, "editions", summaries);
|
|
295
|
+
}
|
|
296
|
+
function preferredOpenLibraryEdition(payload) {
|
|
297
|
+
const editions = payload.editions?.entries ?? [];
|
|
298
|
+
if (!editions.length) {
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
const coverEditionId = openLibraryEditionIdFromKey(payload.work.cover_edition?.key);
|
|
302
|
+
const coverEdition = editions.find((edition) => openLibraryEditionIdFromKey(edition.key) === coverEditionId);
|
|
303
|
+
if (coverEdition) {
|
|
304
|
+
return coverEdition;
|
|
305
|
+
}
|
|
306
|
+
return [...editions].sort((a, b) => editionScore(b, payload) - editionScore(a, payload))[0];
|
|
307
|
+
}
|
|
308
|
+
function editionScore(edition, payload) {
|
|
309
|
+
let score = 0;
|
|
310
|
+
const title = stringField(edition.title)?.toLowerCase();
|
|
311
|
+
const workTitle = stringField(payload.work.title)?.toLowerCase();
|
|
312
|
+
const languages = languageKeys(edition.languages);
|
|
313
|
+
if (title && workTitle && title === workTitle) {
|
|
314
|
+
score += 20;
|
|
315
|
+
}
|
|
316
|
+
if (languages.includes("eng")) {
|
|
317
|
+
score += 15;
|
|
318
|
+
}
|
|
319
|
+
if (edition.isbn_13?.length) {
|
|
320
|
+
score += 8;
|
|
321
|
+
}
|
|
322
|
+
if (edition.isbn_10?.length) {
|
|
323
|
+
score += 6;
|
|
324
|
+
}
|
|
325
|
+
if (edition.covers?.length) {
|
|
326
|
+
score += 5;
|
|
327
|
+
}
|
|
328
|
+
if (edition.publish_date) {
|
|
329
|
+
score += 4;
|
|
330
|
+
}
|
|
331
|
+
if (edition.publishers?.length) {
|
|
332
|
+
score += 3;
|
|
333
|
+
}
|
|
334
|
+
if (typeof edition.number_of_pages === "number") {
|
|
335
|
+
score += 2;
|
|
336
|
+
}
|
|
337
|
+
return score;
|
|
338
|
+
}
|
|
339
|
+
function editionSummary(edition) {
|
|
340
|
+
const summary = {};
|
|
341
|
+
addSummaryString(summary, "id", openLibraryEditionIdFromKey(edition.key));
|
|
342
|
+
addSummaryString(summary, "title", edition.title);
|
|
343
|
+
addSummaryString(summary, "subtitle", edition.subtitle);
|
|
344
|
+
addSummaryString(summary, "fullTitle", edition.full_title);
|
|
345
|
+
addSummaryString(summary, "editionName", edition.edition_name);
|
|
346
|
+
addSummaryString(summary, "publishDate", edition.publish_date);
|
|
347
|
+
addSummaryString(summary, "physicalFormat", edition.physical_format);
|
|
348
|
+
addSummaryNumber(summary, "numberOfPages", edition.number_of_pages);
|
|
349
|
+
addSummaryArray(summary, "publishers", edition.publishers);
|
|
350
|
+
addSummaryArray(summary, "isbn10", edition.isbn_10);
|
|
351
|
+
addSummaryArray(summary, "isbn13", edition.isbn_13);
|
|
352
|
+
addSummaryArray(summary, "languages", languageKeys(edition.languages));
|
|
353
|
+
addSummaryArray(summary, "translatedFrom", languageKeys(edition.translated_from));
|
|
354
|
+
addSummaryString(summary, "translationOf", edition.translation_of);
|
|
355
|
+
addSummaryArray(summary, "covers", edition.covers);
|
|
356
|
+
addSummaryArray(summary, "sourceRecords", edition.source_records);
|
|
357
|
+
addSummaryArray(summary, "localId", edition.local_id);
|
|
358
|
+
if (edition.contributors?.length) {
|
|
359
|
+
summary.contributors = edition.contributors;
|
|
360
|
+
}
|
|
361
|
+
if (edition.identifiers && Object.keys(edition.identifiers).length) {
|
|
362
|
+
summary.identifiers = edition.identifiers;
|
|
363
|
+
}
|
|
364
|
+
if (edition.classifications && Object.keys(edition.classifications).length) {
|
|
365
|
+
summary.classifications = edition.classifications;
|
|
366
|
+
}
|
|
367
|
+
return summary;
|
|
368
|
+
}
|
|
369
|
+
function addSummaryString(summary, key, value) {
|
|
370
|
+
const clean = stringField(value);
|
|
371
|
+
if (clean) {
|
|
372
|
+
summary[key] = clean;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function addSummaryNumber(summary, key, value) {
|
|
376
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
377
|
+
summary[key] = value;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function addSummaryArray(summary, key, value) {
|
|
381
|
+
if (value?.length) {
|
|
382
|
+
summary[key] = value;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function languageKeys(value) {
|
|
386
|
+
return uniqueStrings((value ?? [])
|
|
387
|
+
.map((item) => stringField(item.key)?.replace(/^\/languages\//, ""))
|
|
388
|
+
.filter((item) => Boolean(item)));
|
|
389
|
+
}
|
|
390
|
+
function combineEditionRecords(editions, key) {
|
|
391
|
+
const combined = {};
|
|
392
|
+
for (const edition of editions) {
|
|
393
|
+
const record = edition[key];
|
|
394
|
+
if (!record) {
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
for (const [recordKey, value] of Object.entries(record)) {
|
|
398
|
+
if (value === undefined || value === null || (Array.isArray(value) && value.length === 0)) {
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
const values = Array.isArray(value) ? value : [value];
|
|
402
|
+
for (const item of values) {
|
|
403
|
+
combined[recordKey] = combined[recordKey] ?? [];
|
|
404
|
+
if (!combined[recordKey].some((existing) => JSON.stringify(existing) === JSON.stringify(item))) {
|
|
405
|
+
combined[recordKey].push(item);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return Object.keys(combined).length ? combined : undefined;
|
|
411
|
+
}
|
|
412
|
+
function classificationSubjects(subjects, type) {
|
|
413
|
+
const values = [];
|
|
414
|
+
for (const subject of subjects ?? []) {
|
|
415
|
+
const clean = stringField(subject);
|
|
416
|
+
if (!clean) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
if (type === "dewey" && /^\d{3}(?:\/|\.)/.test(clean)) {
|
|
420
|
+
values.push(clean);
|
|
421
|
+
}
|
|
422
|
+
if (type === "lc" && /^[A-Za-z]{1,3}\d{1,5}(?:\.\w+)?(?:\s+\w+)*$/.test(clean) && !/^\d/.test(clean)) {
|
|
423
|
+
values.push(clean);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return uniqueStrings(values);
|
|
427
|
+
}
|
|
428
|
+
function uniqueStrings(values) {
|
|
429
|
+
const seen = new Set();
|
|
430
|
+
const result = [];
|
|
431
|
+
for (const value of values ?? []) {
|
|
432
|
+
const clean = stringField(value);
|
|
433
|
+
if (clean && !seen.has(clean)) {
|
|
434
|
+
seen.add(clean);
|
|
435
|
+
result.push(clean);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return result;
|
|
439
|
+
}
|
|
440
|
+
function uniqueObjects(values) {
|
|
441
|
+
const seen = new Set();
|
|
442
|
+
const result = [];
|
|
443
|
+
for (const value of values) {
|
|
444
|
+
const key = JSON.stringify(value);
|
|
445
|
+
if (!seen.has(key)) {
|
|
446
|
+
seen.add(key);
|
|
447
|
+
result.push(value);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return result;
|
|
451
|
+
}
|
|
452
|
+
function addTag(data, value) {
|
|
453
|
+
const tag = stringField(value)?.toLowerCase();
|
|
454
|
+
if (tag && !data.tags.includes(tag)) {
|
|
455
|
+
data.tags.push(tag);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
function addArrayDetail(data, key, value) {
|
|
459
|
+
const clean = value?.map((item) => stringField(item)).filter((item) => Boolean(item));
|
|
460
|
+
if (clean?.length) {
|
|
461
|
+
data.details.push({ key, value: clean, source: OPEN_LIBRARY_PROVIDER });
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function addDetail(data, key, value) {
|
|
465
|
+
const clean = stringField(value);
|
|
466
|
+
if (clean) {
|
|
467
|
+
data.details.push({ key, value: clean, source: OPEN_LIBRARY_PROVIDER });
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
function addStructuredDetail(data, key, value) {
|
|
471
|
+
if (value === undefined || value === null || (Array.isArray(value) && value.length === 0)) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
data.details.push({ key, value, source: OPEN_LIBRARY_PROVIDER });
|
|
475
|
+
}
|
|
476
|
+
function paginationFromOpenLibrarySearchResponse(payload, input) {
|
|
477
|
+
const totalResults = payload.numFound ?? payload.num_found ?? 0;
|
|
478
|
+
const limit = input.limit ?? 20;
|
|
479
|
+
return {
|
|
480
|
+
page: input.page ?? 1,
|
|
481
|
+
totalPages: limit > 0 ? Math.ceil(totalResults / limit) : 0,
|
|
482
|
+
totalResults
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
function normalizeOpenLibraryBookId(value) {
|
|
486
|
+
const normalized = stringField(value)?.replace(/^\/works\//, "").replace(/\.json$/, "");
|
|
487
|
+
if (!normalized) {
|
|
488
|
+
throw new Error("Open Library book id must not be empty");
|
|
489
|
+
}
|
|
490
|
+
if (!/^OL\d+W$/i.test(normalized)) {
|
|
491
|
+
throw new Error(`Open Library book id must be a work id like OL82563W: ${value}`);
|
|
492
|
+
}
|
|
493
|
+
return normalized.toUpperCase();
|
|
494
|
+
}
|
|
495
|
+
function openLibraryWorkIdFromKey(value) {
|
|
496
|
+
const normalized = stringField(value)?.replace(/^\/works\//, "");
|
|
497
|
+
return normalized && /^OL\d+W$/i.test(normalized) ? normalized.toUpperCase() : undefined;
|
|
498
|
+
}
|
|
499
|
+
function openLibraryEditionIdFromKey(value) {
|
|
500
|
+
const normalized = stringField(value)?.replace(/^\/books\//, "");
|
|
501
|
+
return normalized && /^OL\d+M$/i.test(normalized) ? normalized.toUpperCase() : undefined;
|
|
502
|
+
}
|
|
503
|
+
function openLibraryAuthorIdFromKey(value) {
|
|
504
|
+
const normalized = stringField(value)?.replace(/^\/authors\//, "");
|
|
505
|
+
return normalized && /^OL\d+A$/i.test(normalized) ? normalized.toUpperCase() : undefined;
|
|
506
|
+
}
|
|
507
|
+
function coverUrl(coverId, baseUrl) {
|
|
508
|
+
if (typeof coverId !== "number" || !baseUrl) {
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
return `${baseUrl.replace(/\/$/, "")}/${coverId}-L.jpg`;
|
|
512
|
+
}
|
|
513
|
+
function descriptionValue(value) {
|
|
514
|
+
return typeof value === "string" ? stringField(value) : stringField(value?.value);
|
|
515
|
+
}
|
|
516
|
+
function openLibraryBookPayload(payload) {
|
|
517
|
+
const value = payload;
|
|
518
|
+
if ("work" in value && value.work) {
|
|
519
|
+
return value;
|
|
520
|
+
}
|
|
521
|
+
return { work: value };
|
|
522
|
+
}
|
|
523
|
+
function searchQuery(value) {
|
|
524
|
+
const query = stringField(value);
|
|
525
|
+
if (!query) {
|
|
526
|
+
throw new Error("Open Library search query must not be empty");
|
|
527
|
+
}
|
|
528
|
+
return query;
|
|
529
|
+
}
|
|
530
|
+
function stringField(value) {
|
|
531
|
+
const normalized = value?.trim();
|
|
532
|
+
return normalized ? normalized : undefined;
|
|
533
|
+
}
|
|
534
|
+
function numberField(value) {
|
|
535
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
536
|
+
}
|