Framework-agnostic TypeScript REST/wire DTO types plus a handful of tiny pure helpers for Phlix. This is the single source of truth for the media / playback / auth / hub / library / event shapes that the JS clients (mobile, windows, tizen) consume, so they stop redeclaring divergent local types.
- No runtime framework deps — no Vue, React, axios, or Pinia. Types + pure functions only.
- Mirrors the PHP
detain/phlix-sharedpackage and the Phlix server's JSON response shapes. - REST/wire types use the server's
snake_casefield names verbatim. - Hub DTOs use the
camelCasepayload keys the PHPtoPayload()emits.
npm install @phlix/contractsimport type {
MediaItem,
MediaItemsResponse,
PlaybackInfo,
LibraryQuery,
HeartbeatDto,
JwtClaims,
} from '@phlix/contracts';
import {
buildPhlixHeaders,
ticksToSeconds,
secondsToTicks,
formatRuntime,
TICKS_PER_SECOND,
EVENT,
X_PHLIX_DEVICE_ID,
} from '@phlix/contracts';
const headers = buildPhlixHeaders({
deviceId: 'win-123',
deviceName: 'Living Room PC',
deviceType: 'windows',
token: accessToken,
sessionId,
});
// `runtime` is in minutes (TMDB); `duration` is probed seconds.
// Prefer the probed seconds field when present:
const runtimeTicks = secondsToTicks(item.duration ?? (item.runtime ?? 0) * 60);
const seconds = ticksToSeconds(runtimeTicks);
const label = formatRuntime(runtimeTicks); // "1h 30m"| Module | Exports |
|---|---|
media.ts |
MediaType, ContentRating, MediaItem, MediaPerson, ProductionCompany, MediaStream, UserData, Library, MediaItemsResponse, MediaItemResponse, Series, Movie, Season, Episode |
playback.ts |
StreamProtocol, StreamInfo, MediaSource, SubtitleTrack, AudioTrack, DeviceProfile, WindowsDeviceProfile, SkipButtonSpec/SkipMarkers/PlaybackMarkers, TimeMarker, ChapterMarker, PlaybackInfo, PlaybackBundle, PlaybackInfoResponse, PlaybackStartResponse, PlaybackProgress, PlaybackSession |
auth.ts |
User, UserInfo, AuthResult, ProviderAuthResult, Session, JwtClaims, JWT_ISS/JWT_AUD/JWT_TYPE (+ type aliases) |
hub.ts |
HeartbeatDto, HeartbeatLibrary, ServerInfoDto, ClaimRequest, ClaimResponse, SERVER_STATUS/ServerStatus |
library.ts |
LibraryQuery, LibrarySort, SortOrder, ServerSettings, SignupMode |
events.ts |
event payload interfaces + PLUGIN_EVENT, WEBHOOK_EVENT, WEBHOOK_EVENT_RESERVED, EVENT |
headers.ts |
X_PHLIX_* header-name constants, DeviceType, buildPhlixHeaders |
ticks.ts |
TICKS_PER_SECOND/_MINUTE/_HOUR, ticksToSeconds, secondsToTicks, ticksToMinutes, ticksToHms, formatRuntime, formatDuration |
- Ticks are 100-nanosecond units (Plex/Jellyfin convention):
1 second = 10,000,000 ticks.runtimeonMediaItemis in minutes (TMDB),durationis the probed length in seconds — neither is ticks; playback positions are in ticks. MediaItem.actorsis a flatstring[]and stays flat. The rich cast objects live on the detail-onlycast[].- Detail-only fields (
cast,crew,production_companies,studio,streams,stream_url) appear only onGET /api/v1/media/{id}.
npm install
npm run lint # eslint (no-explicit-any)
npm run typecheck # tsc --noEmit (strict)
npm run build # typecheck + vite lib (ES+CJS) + d.ts emit
npm run test:run # vitest runMIT