@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.
- package/dist/index.d.ts +3 -3
- package/dist/index.js +666 -324
- package/dist/playback-progress.d.ts +6 -1
- package/dist/plugin-api.d.ts +1 -0
- package/dist/state/slices.d.ts +27 -3
- package/dist/storage.d.ts +9 -8
- package/dist/subtitle-parser.d.ts +89 -7
- package/dist/types.d.ts +46 -5
- package/package.json +6 -6
|
@@ -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
|
-
|
|
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>;
|
package/dist/plugin-api.d.ts
CHANGED
|
@@ -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;
|
package/dist/state/slices.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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:
|
|
17
|
-
readonly MUTED:
|
|
18
|
-
readonly PLAYBACK_RATE:
|
|
19
|
-
readonly SUBTITLE_LANG:
|
|
20
|
-
readonly
|
|
21
|
-
readonly
|
|
22
|
-
readonly
|
|
23
|
-
readonly
|
|
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
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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.
|
|
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.
|
|
37
|
-
"dashjs": "^5.
|
|
38
|
-
"hls.js": "^1.
|
|
36
|
+
"@tanstack/store": "^0.11.1",
|
|
37
|
+
"dashjs": "^5.2.1",
|
|
38
|
+
"hls.js": "^1.7.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"typescript": "^
|
|
42
|
-
"vite": "^8.
|
|
41
|
+
"typescript": "^7.0.2",
|
|
42
|
+
"vite": "^8.2.2"
|
|
43
43
|
}
|
|
44
44
|
}
|