tmdb-lumosovich 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -7
- package/dist/detail-types.d.ts +3 -1
- package/dist/details.js +2 -0
- package/dist/guest-session-types.d.ts +26 -0
- package/dist/guest-session-types.js +1 -0
- package/dist/guest-sessions.d.ts +3 -0
- package/dist/guest-sessions.js +37 -0
- package/dist/http.d.ts +1 -1
- package/dist/index.cjs +361 -36
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/list-types.d.ts +74 -0
- package/dist/list-types.js +1 -0
- package/dist/list-validation.d.ts +5 -0
- package/dist/list-validation.js +54 -0
- package/dist/lists.d.ts +3 -0
- package/dist/lists.js +80 -0
- package/dist/person-resources.d.ts +4 -0
- package/dist/person-resources.js +72 -0
- package/dist/person-types.d.ts +86 -0
- package/dist/person-types.js +1 -0
- package/dist/person-validation.d.ts +5 -0
- package/dist/person-validation.js +40 -0
- package/dist/watch-provider-types.d.ts +22 -0
- package/dist/watch-provider-types.js +1 -0
- package/dist/watch-providers.d.ts +3 -0
- package/dist/watch-providers.js +37 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
A small, typed Node.js wrapper for the TMDB API, including IMDb ID lookup through TMDB.
|
|
8
8
|
|
|
9
|
-
**Lumosovich** is being built incrementally toward full TMDB v3 API coverage. It currently supports external-ID lookup, search, discovery, trending, movie and TV series catalogs and metadata, reviews, keywords, networks, collections, companies, configuration,
|
|
9
|
+
**Lumosovich** is being built incrementally toward full TMDB v3 API coverage. It currently supports external-ID lookup, search, discovery, trending, movie and TV series catalogs and metadata, reviews, keywords, networks, collections, companies, configuration, people and credit resources, lists, guest-session ratings, watch-provider catalogs, and TV seasons, episodes, and episode groups.
|
|
10
10
|
|
|
11
11
|
The name comes from **Lumos** — a spell for bringing light — with a little Russian-style `-ovich` treatment.
|
|
12
12
|
|
|
@@ -14,11 +14,11 @@ Light up the movie database.
|
|
|
14
14
|
|
|
15
15
|
## Current status
|
|
16
16
|
|
|
17
|
-
Install [tmdb-lumosovich on npm](https://www.npmjs.com/package/tmdb-lumosovich), with source and issues on [GitHub](https://github.com/namipsg/lumosovich). Version `0.
|
|
17
|
+
Install [tmdb-lumosovich on npm](https://www.npmjs.com/package/tmdb-lumosovich), with source and issues on [GitHub](https://github.com/namipsg/lumosovich). Version `0.9.0` covers **134 of 152 TMDB v3 operations**, including all TV, person, credit, list, guest-session rating, and watch-provider catalog operations, measured against the official specification pinned on September 27, 2026. This is an early `0.x` library; full API coverage is still in progress. See the [coverage baseline](https://github.com/namipsg/lumosovich/blob/main/spec/README.md) for how coverage is checked.
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
The package moved from `lumosovich` to `tmdb-lumosovich` in version `0.7.1`; the public `Lumosovich` class kept its name. Previously published `lumosovich` versions remain installable, but new projects should use `tmdb-lumosovich`. The live smoke test exercises all 119 public read routes without changing account data. A new, unrated guest session currently receives HTTP 404 from TMDB on its rated-content routes. List mutations, account-state methods, and ratings require a TMDB session and are verified by offline request contracts.
|
|
20
20
|
|
|
21
|
-
The release passes
|
|
21
|
+
The release passes 245 offline tests and TypeScript declaration checks. See the [changelog](https://github.com/namipsg/lumosovich/blob/main/CHANGELOG.md) for earlier releases.
|
|
22
22
|
|
|
23
23
|
## Installation
|
|
24
24
|
|
|
@@ -348,6 +348,71 @@ const group = groups.results[0]
|
|
|
348
348
|
|
|
349
349
|
`changeList()` reports changed series IDs. Episode-group IDs are strings supplied by TMDB. The methods return TMDB's original response fields and preserve empty collections.
|
|
350
350
|
|
|
351
|
+
## People and credits
|
|
352
|
+
|
|
353
|
+
Use numeric TMDB person IDs for people resources. Credit details use the string `credit_id` returned in a person's cast or crew record.
|
|
354
|
+
|
|
355
|
+
```js
|
|
356
|
+
const personId = 31;
|
|
357
|
+
const popularPeople = await tmdb.people.popular({ page: 1 });
|
|
358
|
+
const movieCredits = await tmdb.people.movieCredits(personId);
|
|
359
|
+
const tvCredits = await tmdb.people.tvCredits(personId);
|
|
360
|
+
const combinedCredits = await tmdb.people.combinedCredits(personId);
|
|
361
|
+
const creditId = movieCredits.cast[0]?.credit_id;
|
|
362
|
+
const credit = creditId ? await tmdb.credits.get(creditId) : undefined;
|
|
363
|
+
|
|
364
|
+
const externalIds = await tmdb.people.externalIds(personId);
|
|
365
|
+
const portraits = await tmdb.people.images(personId);
|
|
366
|
+
const tagged = await tmdb.people.taggedImages(personId, { page: 1 });
|
|
367
|
+
const translations = await tmdb.people.translations(personId);
|
|
368
|
+
const latestPerson = await tmdb.people.latest();
|
|
369
|
+
const changedPeople = await tmdb.people.changeList({ page: 1 });
|
|
370
|
+
const changes = await tmdb.people.changes(personId);
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
The three credit collections return `cast` and `crew` arrays with TMDB's original fields; `combinedCredits()` includes both movie and TV roles. `taggedImages()` has paginated image records, including episode stills, while `images()` has portrait profiles. TMDB can report `page: 0` for tagged images even when page 1 is requested. `changeList()` and `changes()` accept optional `startDate`, `endDate`, and `page`; dates use `YYYY-MM-DD`. `popular()` and `taggedImages()` accept a positive page. Credit details, popular people, and the three credit collections support a `language` override. `latest()` can be incomplete while a new person record is being edited on TMDB.
|
|
374
|
+
|
|
375
|
+
## Lists, guest sessions, and watch providers
|
|
376
|
+
|
|
377
|
+
Public lists use numeric TMDB list IDs. Reading list details and checking whether a movie is present require no user session:
|
|
378
|
+
|
|
379
|
+
```js
|
|
380
|
+
const list = await tmdb.lists.get(1, { page: 1 });
|
|
381
|
+
const status = await tmdb.lists.itemStatus(1, 550);
|
|
382
|
+
console.log(list.name, status.item_present);
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Creating or changing a list requires an existing TMDB **user** session. Supply `sessionId` explicitly for each write; a guest session cannot manage lists.
|
|
386
|
+
|
|
387
|
+
```js
|
|
388
|
+
const sessionId = process.env.TMDB_SESSION_ID;
|
|
389
|
+
if (!sessionId) throw new Error('Set TMDB_SESSION_ID first');
|
|
390
|
+
const created = await tmdb.lists.create({
|
|
391
|
+
name: 'Movies to watch', description: 'My picks', language: 'en', sessionId,
|
|
392
|
+
});
|
|
393
|
+
await tmdb.lists.addMovie(created.list_id, 550, { sessionId });
|
|
394
|
+
await tmdb.lists.removeMovie(created.list_id, 550, { sessionId });
|
|
395
|
+
await tmdb.lists.clear(created.list_id, { sessionId, confirm: true });
|
|
396
|
+
await tmdb.lists.delete(created.list_id, { sessionId });
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
`clear()` requires an explicit boolean `confirm`; deleting a list and removing an item are separate operations. TMDB can return list IDs as strings in read responses, so inspect the returned `id` rather than assuming its runtime type. List writes are contract-tested offline; the live smoke test does not alter an account.
|
|
400
|
+
|
|
401
|
+
```js
|
|
402
|
+
const guestId = process.env.TMDB_GUEST_SESSION_ID;
|
|
403
|
+
if (guestId) {
|
|
404
|
+
const ratedMovies = await tmdb.guestSessions.ratedMovies(guestId);
|
|
405
|
+
const ratedShows = await tmdb.guestSessions.ratedTv(guestId);
|
|
406
|
+
const ratedEpisodes = await tmdb.guestSessions.ratedTvEpisodes(guestId);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const regions = await tmdb.watchProviders.regions();
|
|
410
|
+
const movieProviders = await tmdb.watchProviders.movies({ watchRegion: 'US' });
|
|
411
|
+
const tvProviders = await tmdb.watchProviders.tv({ watchRegion: 'US' });
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Guest rating lists accept `language`, positive `page`, and `sortBy: 'created_at.asc' | 'created_at.desc'`. A new unrated guest session may receive HTTP 404. Provider catalogs return a `results` array; movie and TV catalogs accept `watchRegion`, while the regions catalog accepts only `language`. Guest-session creation is part of the upcoming authentication group.
|
|
415
|
+
|
|
351
416
|
## Image URLs
|
|
352
417
|
|
|
353
418
|
TMDB detail responses contain image paths. Build a safe public image URL with:
|
|
@@ -435,12 +500,14 @@ TMDB_API_KEY=... npm run test:live
|
|
|
435
500
|
```
|
|
436
501
|
|
|
437
502
|
The credential is read from the environment, never from a tracked file. This checks
|
|
438
|
-
all
|
|
439
|
-
|
|
503
|
+
all 119 public read routes. The test creates a temporary guest session for the
|
|
504
|
+
three rated-content routes and accepts TMDB's HTTP 404 for an unrated session.
|
|
505
|
+
List and account mutations require a user session and are covered by offline
|
|
506
|
+
contracts; the live test does not change account data.
|
|
440
507
|
|
|
441
508
|
## Roadmap
|
|
442
509
|
|
|
443
|
-
|
|
510
|
+
Account resources, authentication, and the final parity audit will complete coverage in future releases. The [coverage ledger](https://github.com/namipsg/lumosovich/blob/main/spec/coverage.json) tracks implemented and remaining operations.
|
|
444
511
|
|
|
445
512
|
The goal is to cover the TMDB API while keeping the library:
|
|
446
513
|
|
package/dist/detail-types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TmdbId } from './types.js';
|
|
2
|
+
import type { PersonResourceMethods } from './person-types.js';
|
|
2
3
|
import type { MovieMetadataMethods } from './metadata-types.js';
|
|
3
4
|
import type { MovieCatalogMethods } from './catalog-types.js';
|
|
4
5
|
import type { TvCatalogMethods, TvMetadataMethods } from './tv-types.js';
|
|
@@ -87,6 +88,7 @@ export interface TvContentRatingsResponse {
|
|
|
87
88
|
}
|
|
88
89
|
export interface PersonCredit {
|
|
89
90
|
id: TmdbId;
|
|
91
|
+
credit_id?: string;
|
|
90
92
|
media_type?: 'movie' | 'tv';
|
|
91
93
|
title?: string;
|
|
92
94
|
name?: string;
|
|
@@ -251,6 +253,6 @@ export interface TvMethods extends TvMetadataMethods, TvCatalogMethods, TvSeries
|
|
|
251
253
|
get(seriesId: TmdbId, seasonNumber: number, episodeNumber: number, options?: EpisodeDetailOptions): Promise<TvEpisodeDetails>;
|
|
252
254
|
};
|
|
253
255
|
}
|
|
254
|
-
export interface PersonMethods {
|
|
256
|
+
export interface PersonMethods extends PersonResourceMethods {
|
|
255
257
|
get(id: TmdbId, options?: PersonDetailOptions): Promise<PersonDetails>;
|
|
256
258
|
}
|
package/dist/details.js
CHANGED
|
@@ -5,6 +5,7 @@ import { createTvMetadataMethods } from './tv-metadata.js';
|
|
|
5
5
|
import { createTvSeriesResourceMethods, createTvEpisodeGroupMethods } from './tv-series-resources.js';
|
|
6
6
|
import { createTvSeasonResourceMethods } from './tv-season-resources.js';
|
|
7
7
|
import { createTvEpisodeResourceMethods } from './tv-episode-resources.js';
|
|
8
|
+
import { createPersonResourceMethods } from './person-resources.js';
|
|
8
9
|
import { positiveId, slotNumber } from './ids.js';
|
|
9
10
|
import { validateDetailResponse, validateEpisodeResponse, validateSeasonResponse, } from './validation.js';
|
|
10
11
|
const MOVIE_APPEND = [
|
|
@@ -104,6 +105,7 @@ export function createTvMethods(http) {
|
|
|
104
105
|
}
|
|
105
106
|
export function createPersonMethods(http) {
|
|
106
107
|
return {
|
|
108
|
+
...createPersonResourceMethods(http),
|
|
107
109
|
async get(id, options = {}) {
|
|
108
110
|
const validId = positiveId(id);
|
|
109
111
|
const payload = await http.get(`person/${validId}`, detailParameters(options, PERSON_APPEND));
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FindEpisodeHit, MovieSearchHit, SearchPage, TvSearchHit } from './types.js';
|
|
2
|
+
export interface GuestSessionRatedOptions {
|
|
3
|
+
language?: string;
|
|
4
|
+
page?: number;
|
|
5
|
+
sortBy?: 'created_at.asc' | 'created_at.desc';
|
|
6
|
+
}
|
|
7
|
+
export interface GuestRatedMovie extends MovieSearchHit {
|
|
8
|
+
title: string;
|
|
9
|
+
rating: number;
|
|
10
|
+
}
|
|
11
|
+
export interface GuestRatedTv extends TvSearchHit {
|
|
12
|
+
name: string;
|
|
13
|
+
rating: number;
|
|
14
|
+
}
|
|
15
|
+
export interface GuestRatedEpisode extends FindEpisodeHit {
|
|
16
|
+
name: string;
|
|
17
|
+
show_id: number;
|
|
18
|
+
season_number: number;
|
|
19
|
+
episode_number: number;
|
|
20
|
+
rating: number;
|
|
21
|
+
}
|
|
22
|
+
export interface GuestSessionMethods {
|
|
23
|
+
ratedMovies(guestSessionId: string, options?: GuestSessionRatedOptions): Promise<SearchPage<GuestRatedMovie>>;
|
|
24
|
+
ratedTv(guestSessionId: string, options?: GuestSessionRatedOptions): Promise<SearchPage<GuestRatedTv>>;
|
|
25
|
+
ratedTvEpisodes(guestSessionId: string, options?: GuestSessionRatedOptions): Promise<SearchPage<GuestRatedEpisode>>;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { pathId } from './ids.js';
|
|
2
|
+
import { invalidResponse, validateSearchPage } from './validation.js';
|
|
3
|
+
function parameters(options) {
|
|
4
|
+
if (options.page !== undefined && (!Number.isSafeInteger(options.page) || options.page < 1)) {
|
|
5
|
+
throw new TypeError('page must be a positive integer');
|
|
6
|
+
}
|
|
7
|
+
if (options.sortBy !== undefined && !['created_at.asc', 'created_at.desc'].includes(options.sortBy)) {
|
|
8
|
+
throw new TypeError('sortBy must be created_at.asc or created_at.desc');
|
|
9
|
+
}
|
|
10
|
+
return { language: options.language, page: options.page, sort_by: options.sortBy };
|
|
11
|
+
}
|
|
12
|
+
function validateRatedPage(value, nameField, episodes = false) {
|
|
13
|
+
const page = validateSearchPage(value);
|
|
14
|
+
if (!page.results.every((item) => typeof item[nameField] === 'string' &&
|
|
15
|
+
typeof item.rating === 'number' && Number.isFinite(item.rating) &&
|
|
16
|
+
item.rating >= 0.5 && item.rating <= 10 && Number.isInteger(item.rating * 2) &&
|
|
17
|
+
(!episodes || (Number.isSafeInteger(item.show_id) && Number(item.show_id) > 0 &&
|
|
18
|
+
Number.isSafeInteger(item.season_number) && Number(item.season_number) >= 0 &&
|
|
19
|
+
Number.isSafeInteger(item.episode_number) && Number(item.episode_number) >= 0)))) {
|
|
20
|
+
invalidResponse();
|
|
21
|
+
}
|
|
22
|
+
return page;
|
|
23
|
+
}
|
|
24
|
+
export function createGuestSessionMethods(http) {
|
|
25
|
+
const path = (id) => `guest_session/${encodeURIComponent(pathId(id, 'guestSessionId'))}/rated`;
|
|
26
|
+
return {
|
|
27
|
+
async ratedMovies(guestSessionId, options = {}) {
|
|
28
|
+
return validateRatedPage(await http.get(`${path(guestSessionId)}/movies`, parameters(options)), 'title');
|
|
29
|
+
},
|
|
30
|
+
async ratedTv(guestSessionId, options = {}) {
|
|
31
|
+
return validateRatedPage(await http.get(`${path(guestSessionId)}/tv`, parameters(options)), 'name');
|
|
32
|
+
},
|
|
33
|
+
async ratedTvEpisodes(guestSessionId, options = {}) {
|
|
34
|
+
return validateRatedPage(await http.get(`${path(guestSessionId)}/tv/episodes`, parameters(options)), 'name', true);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
package/dist/http.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export declare class TmdbHttpClient {
|
|
|
13
13
|
private readonly fetchRequest;
|
|
14
14
|
constructor(options: LumosovichOptions);
|
|
15
15
|
get(path: string, parameters?: QueryParameters, options?: GetOptions): Promise<unknown>;
|
|
16
|
-
post(path: string, parameters: QueryParameters, body
|
|
16
|
+
post(path: string, parameters: QueryParameters, body?: Record<string, unknown>): Promise<unknown>;
|
|
17
17
|
delete(path: string, parameters: QueryParameters): Promise<unknown>;
|
|
18
18
|
private request;
|
|
19
19
|
}
|