diff --git a/packages/pl-api/lib/entities/item.ts b/packages/pl-api/lib/entities/item.ts new file mode 100644 index 000000000..fc1c80930 --- /dev/null +++ b/packages/pl-api/lib/entities/item.ts @@ -0,0 +1,242 @@ +import * as v from 'valibot'; + +import { filteredArray } from './utils'; + +const externalResourceSchema = v.object({ + url: v.string(), +}); + +const baseItemSchema = v.object({ + id: v.string(), + uuid: v.string(), + url: v.string(), + api_url: v.string(), + category: v.picklist(['book', 'movie', 'tv', 'music', 'game', 'podcast', 'performance', 'collection']), + parent_uuid: v.nullable(v.string()), + display_title: v.string(), + external_resources: v.nullable(filteredArray(externalResourceSchema)), + title: v.string(), + description: v.string(), + localized_title: filteredArray(v.object({ + lang: v.string(), + text: v.string(), + })), + localized_description: filteredArray(v.object({ + lang: v.string(), + text: v.string(), + })), + conver_image_url: v.nullable(v.string()), + rating: v.nullable(v.number()), + rating_count: v.nullable(v.number()), + rating_distribution: v.nullable(v.array(v.number())), + tags: v.nullable(v.array(v.string())), +}); + +const editionSchema = v.object({ + ...baseItemSchema.entries, + type: v.literal('Edition'), + subtitle: v.nullable(v.string()), + orig_title: v.nullable(v.string()), + author: v.array(v.string()), + translator: v.array(v.string()), + language: v.array(v.string()), + pub_house: v.nullable(v.string()), + pub_year: v.nullable(v.number()), + pub_month: v.nullable(v.number()), + binding: v.nullable(v.string()), + price: v.nullable(v.string()), + pages: v.nullable(v.union([v.string(), v.number()])), + series: v.nullable(v.string()), + imprint: v.nullable(v.string()), + isbn: v.nullable(v.string()), +}); + +const tvShowSchema = v.object({ + ...baseItemSchema.entries, + type: v.literal('TVShow'), + season_count: v.nullable(v.number()), + orig_title: v.nullable(v.string()), + director: v.array(v.string()), + playwright: v.array(v.string()), + actor: v.array(v.string()), + genre: v.array(v.string()), + language: v.array(v.string()), + area: v.array(v.string()), + year: v.nullable(v.number()), + site: v.nullable(v.string()), + episode_count: v.nullable(v.number()), + season_uuids: v.array(v.string()), + imdb: v.nullable(v.string()), +}); + +const tvSeasonSchema = v.object({ + ...baseItemSchema.entries, + type: v.literal('TVSeason'), + season_number: v.nullable(v.number()), + orig_title: v.nullable(v.string()), + director: v.array(v.string()), + playwright: v.array(v.string()), + actor: v.array(v.string()), + genre: v.array(v.string()), + language: v.array(v.string()), + area: v.array(v.string()), + year: v.nullable(v.number()), + site: v.nullable(v.string()), + episode_count: v.nullable(v.number()), + episode_uuids: v.array(v.string()), + imdb: v.nullable(v.string()), +}); + +const movieSchema = v.object({ + ...baseItemSchema.entries, + type: v.literal('Movie'), + orig_title: v.nullable(v.string()), + director: v.array(v.string()), + playwright: v.array(v.string()), + actor: v.array(v.string()), + genre: v.array(v.string()), + language: v.array(v.string()), + area: v.array(v.string()), + year: v.nullable(v.number()), + site: v.nullable(v.string()), + duration: v.nullable(v.string()), + imdb: v.nullable(v.string()), +}); + +const albumSchema = v.object({ + ...baseItemSchema.entries, + type: v.literal('MusicAlbum'), + genre: v.array(v.string()), + artist: v.array(v.string()), + company: v.array(v.string()), + duration: v.nullable(v.number()), + release_date: v.nullable(v.string()), + track_list: v.nullable(v.string()), + barcode: v.nullable(v.string()), +}); + +const podcastSchema = v.object({ + ...baseItemSchema.entries, + type: v.literal('Podcast'), + genre: v.array(v.string()), + host: v.array(v.string()), + language: v.array(v.string()), + official_site: v.nullable(v.string()), +}); + +const gameSchema = v.object({ + ...baseItemSchema.entries, + type: v.literal('Game'), + genre: v.array(v.string()), + developer: v.array(v.string()), + publisher: v.array(v.string()), + platform: v.array(v.string()), + release_type: v.nullable(v.string()), + release_date: v.nullable(v.string()), + official_site: v.nullable(v.string()), +}); + +const performanceSchema = v.object({ + ...baseItemSchema.entries, + type: v.literal('Performance'), + orig_title: v.nullable(v.string()), + genre: v.array(v.string()), + language: v.array(v.string()), + opening_date: v.nullable(v.string()), + closing_date: v.nullable(v.string()), + director: v.array(v.string()), + playwright: v.array(v.string()), + orig_creator: v.array(v.string()), + composer: v.array(v.string()), + choreographer: v.array(v.string()), + performer: v.array(v.string()), + actor: v.array(v.string()), + crew: v.array(v.string()), + official_site: v.nullable(v.string()), +}); + +const podcastEpisodeSchema = v.object({ + ...baseItemSchema.entries, + type: v.literal('PodcastEpisode'), + guid: v.nullable(v.string()), + pub_date: v.nullable(v.string()), + media_url: v.nullable(v.string()), + link: v.nullable(v.string()), + duration: v.nullable(v.number()), +}); + +const performanceProductionSchema = v.object({ + ...baseItemSchema.entries, + type: v.literal('PerformanceProduction'), + orig_title: v.nullable(v.string()), + language: v.array(v.string()), + opening_date: v.nullable(v.string()), + closing_date: v.nullable(v.string()), + director: v.array(v.string()), + playwright: v.array(v.string()), + orig_creator: v.array(v.string()), + composer: v.array(v.string()), + choreographer: v.array(v.string()), + performer: v.array(v.string()), + actor: v.array(v.string()), + crew: v.array(v.string()), + official_site: v.nullable(v.string()), +}); + +const tvEpisodeSchema = v.object({ + ...baseItemSchema.entries, + type: v.literal('PerformanceProduction'), + episode_number: v.nullable(v.number()), +}); + +/** + * @category Schemas + * @see {@link https://neodb.social/developer} + */ +const itemSchema: v.BaseSchema> = v.pipe( + v.any(), + v.variant('type', [ + editionSchema, + tvShowSchema, + tvSeasonSchema, + movieSchema, + albumSchema, + podcastSchema, + gameSchema, + performanceSchema, + podcastEpisodeSchema, + performanceProductionSchema, + tvEpisodeSchema, + ]), +); + +/** + * @category Entity types + */ +type Item = v.InferOutput< +| typeof editionSchema +| typeof tvShowSchema +| typeof tvSeasonSchema +| typeof movieSchema +| typeof albumSchema +| typeof podcastSchema +| typeof gameSchema +| typeof performanceSchema +| typeof podcastEpisodeSchema +| typeof performanceProductionSchema +| typeof tvEpisodeSchema +>; + +export { + editionSchema, + tvShowSchema, + tvSeasonSchema, + movieSchema, + albumSchema, + podcastSchema, + gameSchema, + performanceSchema, + podcastEpisodeSchema, + itemSchema, + type Item, +}; diff --git a/packages/pl-api/lib/entities/review.ts b/packages/pl-api/lib/entities/review.ts new file mode 100644 index 000000000..3b498607e --- /dev/null +++ b/packages/pl-api/lib/entities/review.ts @@ -0,0 +1,30 @@ +import * as v from 'valibot'; + +import { itemSchema } from './item'; +import { datetimeSchema } from './utils'; + +/** + * @category Schemas + * @see {@link https://neodb.social/developer} + */ +const reviewSchema = v.object({ + url: v.string(), + api_url: v.string(), + visibility: v.pipe(v.number(), v.minValue(0), v.maxValue(2)), + post_id: v.nullable(v.number()), + item: itemSchema, + created_time: datetimeSchema, + title: v.string(), + body: v.string(), + html_content: v.string(), +}); + +/** + * @category Entity types + */ +type Review = v.InferOutput; + +export { + reviewSchema, + type Review, +};