@vplayer/core 0.2.1 → 0.2.3

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.
@@ -11,10 +11,15 @@ export interface PlaybackProgressStore {
11
11
  export interface PlaybackProgressOptions {
12
12
  id?: string;
13
13
  store?: PlaybackProgressStore;
14
+ /** Normalize the media URL used as the progress key. Defaults to true. */
15
+ normalizeUrl?: boolean;
16
+ /** Milliseconds before the resume prompt dismisses itself. Use 0 to keep it visible. Defaults to 5000. */
17
+ resumePromptTimeout?: number;
14
18
  }
15
19
  export declare class LocalPlaybackProgressStore implements PlaybackProgressStore {
16
20
  private readonly storage;
17
- constructor(storage?: Storage);
21
+ private readonly maxEntries;
22
+ constructor(storage?: Storage, maxEntries?: number);
18
23
  load(mediaId: string): Promise<PlaybackProgress | null>;
19
24
  save(mediaId: string, progress: PlaybackProgress): Promise<void>;
20
25
  clear(mediaId: string): Promise<void>;
@@ -10,6 +10,7 @@ export interface RemoteRef {
10
10
  seek: (time: number) => void;
11
11
  resumeFromSavedProgress: () => void;
12
12
  startPlaybackOver: () => void;
13
+ dismissSavedProgress: () => void;
13
14
  skip: (seconds: number) => void;
14
15
  setVolume: (v: number) => void;
15
16
  toggleMute: () => void;
@@ -16,7 +16,7 @@ import type { PlaybackProgress } from '../playback-progress';
16
16
  import type { ControlRegistration, ContextMenuItem, FlipState, AspectRatioState } from '../plugin-api';
17
17
  import type { LayerRegistration, SettingRegistration } from '../plugin-api';
18
18
  import type { PlayerSource } from '../source-resolver';
19
- import type { SubtitleTrack, ThumbnailCue } from '../subtitle-parser';
19
+ import type { CaptionSettings, SubtitleCue, SubtitleProviderInfo, SubtitleProviderResult, SubtitleTrack, ThumbnailCue } from '../subtitle-parser';
20
20
  export type PlaybackStatus = 'idle' | 'loading' | 'ready' | 'playing' | 'paused' | 'buffering' | 'seeking' | 'ended' | 'error';
21
21
  export interface MediaSlice {
22
22
  status: PlaybackStatus;
@@ -41,6 +41,14 @@ export interface PreferencesSlice {
41
41
  aspectRatio: AspectRatioState;
42
42
  activeSubtitle: SubtitleTrack | null;
43
43
  subtitleTracks: SubtitleTrack[];
44
+ subtitleCues: SubtitleCue[];
45
+ subtitleStatus: 'idle' | 'loading' | 'ready' | 'error';
46
+ subtitleError: string | null;
47
+ subtitleProviders: SubtitleProviderInfo[];
48
+ subtitleSearchResults: SubtitleProviderResult[];
49
+ subtitleSearchStatus: 'idle' | 'loading' | 'ready' | 'error';
50
+ subtitleSearchError: string | null;
51
+ captionSettings: CaptionSettings;
44
52
  activeQuality: string;
45
53
  qualities: string[];
46
54
  }
@@ -71,8 +79,16 @@ export interface ErrorSlice {
71
79
  isReconnecting: boolean;
72
80
  } | null;
73
81
  }
82
+ export type ResumeState = {
83
+ status: 'idle';
84
+ } | {
85
+ status: 'loading';
86
+ } | {
87
+ status: 'prompt';
88
+ progress: PlaybackProgress;
89
+ };
74
90
  export interface PlaybackProgressSlice {
75
- resumeProgress: PlaybackProgress | null;
91
+ resumeState: ResumeState;
76
92
  }
77
93
  /**
78
94
  * Full MediaState — flat at runtime, logically organized here.
@@ -97,7 +113,7 @@ export declare const selectMedia: (s: MediaState) => {
97
113
  bufferedPercent: number;
98
114
  playbackRate: number;
99
115
  isLive: boolean;
100
- resumeProgress: PlaybackProgress | null;
116
+ resumeState: ResumeState;
101
117
  };
102
118
  export declare const selectAudio: (s: MediaState) => {
103
119
  volume: number;
@@ -109,6 +125,14 @@ export declare const selectPreferences: (s: MediaState) => {
109
125
  aspectRatio: AspectRatioState;
110
126
  activeSubtitle: SubtitleTrack | null;
111
127
  subtitleTracks: SubtitleTrack[];
128
+ subtitleCues: SubtitleCue[];
129
+ subtitleStatus: "error" | "idle" | "loading" | "ready";
130
+ subtitleError: string | null;
131
+ subtitleProviders: SubtitleProviderInfo[];
132
+ subtitleSearchResults: SubtitleProviderResult[];
133
+ subtitleSearchStatus: "error" | "idle" | "loading" | "ready";
134
+ subtitleSearchError: string | null;
135
+ captionSettings: CaptionSettings;
112
136
  activeQuality: string;
113
137
  qualities: string[];
114
138
  };
package/dist/storage.d.ts CHANGED
@@ -13,12 +13,13 @@ export declare class Storage {
13
13
  }
14
14
  /** Pre-defined persistence keys */
15
15
  export declare const STORAGE_KEYS: {
16
- readonly VOLUME: "volume";
17
- readonly MUTED: "muted";
18
- readonly PLAYBACK_RATE: "playbackRate";
19
- readonly SUBTITLE_LANG: "subtitleLang";
20
- readonly QUALITY: "quality";
21
- readonly LOOP: "loop";
22
- readonly FLIP: "flip";
23
- readonly ASPECT_RATIO: "aspectRatio";
16
+ readonly VOLUME: 'volume';
17
+ readonly MUTED: 'muted';
18
+ readonly PLAYBACK_RATE: 'playbackRate';
19
+ readonly SUBTITLE_LANG: 'subtitleLang';
20
+ readonly CAPTION_SETTINGS: 'captionSettings';
21
+ readonly QUALITY: 'quality';
22
+ readonly LOOP: 'loop';
23
+ readonly FLIP: 'flip';
24
+ readonly ASPECT_RATIO: 'aspectRatio';
24
25
  };
@@ -1,23 +1,105 @@
1
+ export type SubtitleFormat = 'vtt' | 'srt';
1
2
  export interface SubtitleCue {
2
3
  index: number;
3
4
  start: number;
4
5
  end: number;
5
6
  text: string;
7
+ id?: string;
8
+ settings?: string;
6
9
  }
7
10
  export interface SubtitleTrack {
8
- src: string;
11
+ id?: string;
12
+ src?: string;
13
+ content?: string;
9
14
  lang: string;
10
15
  label: string;
16
+ format?: SubtitleFormat;
11
17
  default?: boolean;
18
+ local?: boolean;
19
+ }
20
+ export interface SubtitleSearchQuery {
21
+ query?: string;
22
+ title?: string;
23
+ year?: number;
24
+ season?: number;
25
+ episode?: number;
26
+ imdbId?: string;
27
+ languages?: string[];
28
+ providerId?: string;
29
+ }
30
+ export interface SubtitleSearchItem {
31
+ id: string;
32
+ label: string;
33
+ language: string;
34
+ format?: SubtitleFormat;
35
+ release?: string;
36
+ downloads?: number;
37
+ hearingImpaired?: boolean;
38
+ machineTranslated?: boolean;
39
+ providerData?: unknown;
40
+ }
41
+ export interface SubtitleProviderResult extends SubtitleSearchItem {
42
+ providerId: string;
43
+ providerLabel: string;
44
+ }
45
+ export interface SubtitleSearchResult {
46
+ items: SubtitleSearchItem[];
47
+ }
48
+ export type SubtitleSource = {
49
+ content: string;
50
+ src?: never;
51
+ format?: SubtitleFormat;
52
+ } | {
53
+ src: string;
54
+ content?: never;
55
+ format?: SubtitleFormat;
56
+ };
57
+ export interface SubtitleProvider {
58
+ id: string;
59
+ label: string;
60
+ search(query: SubtitleSearchQuery, signal: AbortSignal): Promise<SubtitleSearchResult | SubtitleSearchItem[]>;
61
+ fetch(item: SubtitleSearchItem, signal: AbortSignal): Promise<SubtitleSource>;
62
+ }
63
+ export type SubtitleProviderInfo = Pick<SubtitleProvider, 'id' | 'label'>;
64
+ export interface CaptionSettings {
65
+ fontSize: 'small' | 'medium' | 'large';
66
+ fontScale: number;
67
+ fontFamily: 'sans' | 'serif' | 'monospace';
68
+ textColor: string;
69
+ textOpacity: number;
70
+ backgroundColor: string;
71
+ backgroundOpacity: number;
72
+ edgeStyle: 'none' | 'shadow' | 'outline';
73
+ edgeColor: string;
74
+ position: number;
75
+ lineHeight: number;
76
+ delay: number;
77
+ }
78
+ export declare const DEFAULT_CAPTION_SETTINGS: CaptionSettings;
79
+ export interface SubtitleParseError {
80
+ code: 'unsupported-format' | 'malformed';
81
+ message: string;
12
82
  }
13
- /** Parse HH:MM:SS.mmm or HH:MM:SS,mmm timestamp → total seconds */
14
- export declare function parseTimestamp(ts: string): number;
15
- /** Parse SRT format text into cues */
83
+ export type SubtitleParseResult = {
84
+ ok: true;
85
+ format: SubtitleFormat;
86
+ cues: SubtitleCue[];
87
+ } | {
88
+ ok: false;
89
+ format?: undefined;
90
+ error: SubtitleParseError;
91
+ };
92
+ /** Parse HH:MM:SS.mmm, MM:SS.mmm, and comma-separated variants. */
93
+ export declare function parseTimestamp(timestamp: string): number;
16
94
  export declare function parseSRT(content: string): SubtitleCue[];
17
- /** Parse VTT format text into cues */
18
95
  export declare function parseVTT(content: string): SubtitleCue[];
19
- /** Fetch and parse subtitles from URL */
20
- export declare function fetchSubtitles(track: SubtitleTrack): Promise<SubtitleCue[]>;
96
+ export declare function parseSubtitles(content: string, hint?: {
97
+ format?: SubtitleFormat;
98
+ fileName?: string;
99
+ mimeType?: string;
100
+ }): SubtitleParseResult;
101
+ /** Fetch and parse subtitles from URL. */
102
+ export declare function fetchSubtitles(track: SubtitleTrack, signal?: AbortSignal): Promise<SubtitleCue[]>;
21
103
  /** Get active cue at a given time */
22
104
  export declare function getActiveCue(cues: SubtitleCue[], time: number): SubtitleCue | null;
23
105
  export interface ThumbnailCue {
package/dist/types.d.ts CHANGED
@@ -8,11 +8,10 @@ import type { PlaybackProgressOptions } from './playback-progress';
8
8
  import type { PlayerPlugin } from './plugin-api';
9
9
  import type { AspectRatioState, FlipState } from './plugin-api';
10
10
  import type { PlayerSource } from './source-resolver';
11
- import type { PlaybackStatus } from './state/slices';
12
- import type { MediaState } from './state/slices';
11
+ import type { MediaState, PlaybackStatus, ResumeState } from './state/slices';
13
12
  import type { Storage } from './storage';
14
- import type { SubtitleTrack } from './subtitle-parser';
15
- export type { MediaState };
13
+ import type { CaptionSettings, SubtitleProvider, SubtitleProviderResult, SubtitleSearchQuery, SubtitleTrack } from './subtitle-parser';
14
+ export type { MediaState, ResumeState };
16
15
  export interface PlayerOptions {
17
16
  src: string;
18
17
  /** MIME/content type hint used by source provider selection. */
@@ -21,6 +20,7 @@ export interface PlayerOptions {
21
20
  title?: string;
22
21
  poster?: string;
23
22
  subtitles?: SubtitleTrack[];
23
+ subtitleProviders?: SubtitleProvider[];
24
24
  qualities?: string[];
25
25
  autoPlay?: boolean;
26
26
  thumbnails?: string;
@@ -80,6 +80,7 @@ export interface MediaRemote {
80
80
  togglePlay: () => void;
81
81
  resumeFromSavedProgress: () => void;
82
82
  startPlaybackOver: () => void;
83
+ dismissSavedProgress: () => void;
83
84
  seek: (time: number) => void;
84
85
  skip: (seconds: number) => void;
85
86
  setVolume: (v: number) => void;
@@ -88,6 +89,13 @@ export interface MediaRemote {
88
89
  toggleFullscreen: () => void;
89
90
  togglePiP: () => void;
90
91
  setActiveSubtitle: (track: SubtitleTrack | null) => void;
92
+ addSubtitleTrack: (track: SubtitleTrack) => void;
93
+ removeSubtitleTrack: (id: string) => void;
94
+ searchSubtitles: (query?: SubtitleSearchQuery) => void;
95
+ selectSubtitleResult: (result: SubtitleProviderResult) => void;
96
+ clearSubtitleSearch: () => void;
97
+ setCaptionSettings: (patch: Partial<CaptionSettings>) => void;
98
+ resetCaptionSettings: () => void;
91
99
  setActiveQuality: (q: string) => void;
92
100
  takeScreenshot: () => void;
93
101
  setFlip: (flip: FlipState) => void;
@@ -113,6 +121,38 @@ export interface PlayerLabels {
113
121
  quality: string;
114
122
  subtitles: string;
115
123
  off: string;
124
+ loadSubtitleFile: string;
125
+ findSubtitlesOnline: string;
126
+ subtitleSearch: string;
127
+ subtitleSearchPlaceholder: string;
128
+ subtitleSource: string;
129
+ subtitleAllSources: string;
130
+ subtitleLanguage: string;
131
+ subtitleAllLanguages: string;
132
+ subtitleNoResults: string;
133
+ subtitleSearching: string;
134
+ captionAppearance: string;
135
+ captionSize: string;
136
+ captionTextColor: string;
137
+ captionBackgroundColor: string;
138
+ captionBackgroundOpacity: string;
139
+ captionSmall: string;
140
+ captionMedium: string;
141
+ captionLarge: string;
142
+ captionFontFamily: string;
143
+ captionFontScale: string;
144
+ captionTextOpacity: string;
145
+ captionEdgeStyle: string;
146
+ captionEdgeNone: string;
147
+ captionEdgeShadow: string;
148
+ captionEdgeOutline: string;
149
+ captionEdgeColor: string;
150
+ captionPosition: string;
151
+ captionLineHeight: string;
152
+ captionDelay: string;
153
+ captionPreview: string;
154
+ captionReset: string;
155
+ subtitleLoadError: string;
116
156
  endedTitle: string;
117
157
  screenshot: string;
118
158
  flip: string;
@@ -129,6 +169,7 @@ export interface PlayerLabels {
129
169
  continue: string;
130
170
  continuePlay: string;
131
171
  continueStartOver: string;
172
+ dismiss: string;
132
173
  error: string;
133
174
  retry: string;
134
175
  contextMenuPlay: string;
@@ -152,7 +193,7 @@ export interface PlayerInstance {
152
193
  hotkeys: HotkeyRegistry;
153
194
  /** The active media engine. Null before mount and after unmount. */
154
195
  engine: MediaEngine | null;
155
- updateOptions(opts: Partial<Pick<PlayerOptions, 'src' | 'type' | 'title' | 'poster' | 'autoPlay' | 'subtitles' | 'qualities' | 'thumbnails' | 'transformThumbnailVTT' | 'playbackProgress'>>): void;
196
+ updateOptions(opts: Partial<Pick<PlayerOptions, 'src' | 'type' | 'title' | 'poster' | 'autoPlay' | 'subtitles' | 'subtitleProviders' | 'qualities' | 'thumbnails' | 'transformThumbnailVTT' | 'playbackProgress'>>): void;
156
197
  setThumbnails(url?: string): void;
157
198
  initPlugins(plugins: PlayerPlugin[]): () => void;
158
199
  mount(videoEl: HTMLVideoElement, containerEl: HTMLDivElement): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vplayer/core",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,12 +33,12 @@
33
33
  "test:coverage": "vitest run --coverage"
34
34
  },
35
35
  "dependencies": {
36
- "@tanstack/store": "^0.8.0",
37
- "dashjs": "^5.1.1",
38
- "hls.js": "^1.6.16"
36
+ "@tanstack/store": "^0.11.1",
37
+ "dashjs": "^5.2.1",
38
+ "hls.js": "^1.7.1"
39
39
  },
40
40
  "devDependencies": {
41
- "typescript": "^6.0.2",
42
- "vite": "^8.0.0"
41
+ "typescript": "^7.0.2",
42
+ "vite": "^8.2.2"
43
43
  }
44
44
  }