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 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, movie, TV, and person details, and TV seasons, episodes, and episode groups.
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.7.1` covers **109 of 152 TMDB v3 operations**, including all 48 TV 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.
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
- Version `0.7.1` moves the npm package from `lumosovich` to `tmdb-lumosovich`; the public `Lumosovich` class and TMDB API coverage are unchanged. Previously published `lumosovich` versions remain installable, but new projects should use `tmdb-lumosovich`. The read-only live smoke test exercises all 99 public read operations. Account-state and rating methods require a TMDB session and are verified by offline request contracts.
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 180 offline tests and TypeScript declaration checks. See the [changelog](https://github.com/namipsg/lumosovich/blob/main/CHANGELOG.md) for earlier releases.
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 99 public read operations. Account-state and rating methods require a TMDB
439
- session and are covered by offline contracts; the live test makes read-only requests.
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
- People, lists, authentication, and the remaining TMDB resources will expand coverage in future releases. The [coverage ledger](https://github.com/namipsg/lumosovich/blob/main/spec/coverage.json) tracks implemented and remaining operations.
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
 
@@ -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,3 @@
1
+ import { TmdbHttpClient } from './http.js';
2
+ import type { GuestSessionMethods } from './guest-session-types.js';
3
+ export declare function createGuestSessionMethods(http: TmdbHttpClient): GuestSessionMethods;
@@ -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: Record<string, unknown>): Promise<unknown>;
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
  }