itube-modern-player 0.1.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/LICENSE +21 -0
- package/README.md +630 -0
- package/dist/core/dom.d.ts +18 -0
- package/dist/core/events.d.ts +10 -0
- package/dist/core/icons.d.ts +3 -0
- package/dist/core/labels.d.ts +2 -0
- package/dist/core/localeRegistry.d.ts +7 -0
- package/dist/core/locales.d.ts +11 -0
- package/dist/core.cjs +5 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.js +1668 -0
- package/dist/core.js.map +1 -0
- package/dist/coreEntry.d.ts +14 -0
- package/dist/features/ads/manager.d.ts +48 -0
- package/dist/features/ads/vast.d.ts +9 -0
- package/dist/features/chapters.d.ts +12 -0
- package/dist/features/heatmap.d.ts +17 -0
- package/dist/features/hls.d.ts +26 -0
- package/dist/features/thumbnails.d.ts +11 -0
- package/dist/global.d.ts +2 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/itube-modern-player.iife.js +5 -0
- package/dist/itube-modern-player.iife.js.map +1 -0
- package/dist/labels-C3gAZEm-.js +41 -0
- package/dist/labels-C3gAZEm-.js.map +1 -0
- package/dist/labels-DTgTxMuq.cjs +2 -0
- package/dist/labels-DTgTxMuq.cjs.map +1 -0
- package/dist/lazy.cjs +2 -0
- package/dist/lazy.cjs.map +1 -0
- package/dist/lazy.d.ts +23 -0
- package/dist/lazy.js +60 -0
- package/dist/lazy.js.map +1 -0
- package/dist/locales.cjs +2 -0
- package/dist/locales.cjs.map +1 -0
- package/dist/locales.js +380 -0
- package/dist/locales.js.map +1 -0
- package/dist/localesEntry.d.ts +8 -0
- package/dist/player.d.ts +142 -0
- package/dist/style.css +1 -0
- package/dist/types.d.ts +443 -0
- package/dist/ui/controls.d.ts +90 -0
- package/dist/ui/menu.d.ts +29 -0
- package/dist/ui/overlays.d.ts +51 -0
- package/dist/ui/playlistPanel.d.ts +16 -0
- package/dist/ui/progress.d.ts +41 -0
- package/dist/ui/scenesPanel.d.ts +19 -0
- package/dist/vue.cjs +2 -0
- package/dist/vue.cjs.map +1 -0
- package/dist/vue.d.ts +67 -0
- package/dist/vue.js +89 -0
- package/dist/vue.js.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Player } from '../player';
|
|
2
|
+
import { ProgressBar } from './progress';
|
|
3
|
+
/**
|
|
4
|
+
* Control bar. A single flex row inside the overlay grid. When the player is
|
|
5
|
+
* too narrow, lower-priority controls collapse into the ⋯ menu dynamically
|
|
6
|
+
* (ResizeObserver-driven) so nothing ever overflows the player edge.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Controls {
|
|
9
|
+
private player;
|
|
10
|
+
readonly root: HTMLElement;
|
|
11
|
+
readonly progress: ProgressBar;
|
|
12
|
+
private playBtn;
|
|
13
|
+
private muteBtn;
|
|
14
|
+
private volumeSlider;
|
|
15
|
+
private timeLabel;
|
|
16
|
+
private chapterLabel;
|
|
17
|
+
private liveBadge;
|
|
18
|
+
private fullscreenBtn;
|
|
19
|
+
private row;
|
|
20
|
+
private rightGroup;
|
|
21
|
+
private subtitlesBtn;
|
|
22
|
+
private settingsBtn;
|
|
23
|
+
private settingsMenu;
|
|
24
|
+
private subtitlesMenu;
|
|
25
|
+
private moreMenu;
|
|
26
|
+
private moreWrap;
|
|
27
|
+
private qualityBtn;
|
|
28
|
+
private qualityMenu;
|
|
29
|
+
private scenesBtn;
|
|
30
|
+
private likeBtn;
|
|
31
|
+
private dislikeBtn;
|
|
32
|
+
private likeCountEl;
|
|
33
|
+
private dislikeCountEl;
|
|
34
|
+
private prevBtn;
|
|
35
|
+
private nextBtn;
|
|
36
|
+
private seekBackBtn;
|
|
37
|
+
private seekFwdBtn;
|
|
38
|
+
/** Center overlay cluster (prev · −Ns · play · +Ns · next), shown on mobile. */
|
|
39
|
+
readonly center: HTMLElement;
|
|
40
|
+
private centerPlayBtn;
|
|
41
|
+
private centerPrevBtn;
|
|
42
|
+
private centerNextBtn;
|
|
43
|
+
/** Controls that live in the center cluster on mobile (not in the bottom bar). */
|
|
44
|
+
private static readonly CENTER_KEYS;
|
|
45
|
+
private collapsibles;
|
|
46
|
+
private overflowed;
|
|
47
|
+
private resizeObserver;
|
|
48
|
+
private reflowScheduled;
|
|
49
|
+
/** Consumer actions (share/report/…) always live in the ⋯ menu. */
|
|
50
|
+
private actionItems;
|
|
51
|
+
private wasPlayingBeforeScrub;
|
|
52
|
+
private disposers;
|
|
53
|
+
constructor(player: Player);
|
|
54
|
+
private onWindowResize;
|
|
55
|
+
private registerCollapsible;
|
|
56
|
+
/** Center-cluster button — deliberately NOT `.imp-btn` (own sizing/look). */
|
|
57
|
+
private makeCenterButton;
|
|
58
|
+
/** like/dislike — visible buttons (collapsible), highlightable via `setLikeState`. */
|
|
59
|
+
private buildLikeDislike;
|
|
60
|
+
private attachCountTooltip;
|
|
61
|
+
private wrapWithTooltip;
|
|
62
|
+
private setCountText;
|
|
63
|
+
setLikeCounts(likeCount?: number | string, dislikeCount?: number | string): void;
|
|
64
|
+
setLikeState(state: 'like' | 'dislike' | null): void;
|
|
65
|
+
private buildMoreDropdown;
|
|
66
|
+
/** ⋯ menu = overflowed controls (in display order) + consumer actions. */
|
|
67
|
+
private buildMoreSections;
|
|
68
|
+
private simpleSection;
|
|
69
|
+
private speedSection;
|
|
70
|
+
private qualitySection;
|
|
71
|
+
private subtitlesSection;
|
|
72
|
+
private scheduleReflow;
|
|
73
|
+
/**
|
|
74
|
+
* Hide controls that don't fit (lowest priority first) and remember them so
|
|
75
|
+
* the ⋯ menu can offer them. Unavailable controls are hidden outright.
|
|
76
|
+
*/
|
|
77
|
+
private reflow;
|
|
78
|
+
private overflowsRow;
|
|
79
|
+
private bind;
|
|
80
|
+
private syncPlayState;
|
|
81
|
+
private syncVolume;
|
|
82
|
+
/** Called by the player when per-source data (chapters/quality/subtitles) changes. */
|
|
83
|
+
syncFeatureButtons(): void;
|
|
84
|
+
private syncPlaylistButtons;
|
|
85
|
+
private closeMenus;
|
|
86
|
+
private toggleSettingsMenu;
|
|
87
|
+
private toggleQualityMenu;
|
|
88
|
+
private toggleSubtitlesMenu;
|
|
89
|
+
destroy(): void;
|
|
90
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface MenuItem {
|
|
2
|
+
label: string;
|
|
3
|
+
value: string;
|
|
4
|
+
active: boolean;
|
|
5
|
+
/** Optional icon: raw `<svg>` markup or an image URL. */
|
|
6
|
+
icon?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface MenuSection {
|
|
9
|
+
title: string;
|
|
10
|
+
items: MenuItem[];
|
|
11
|
+
onSelect(value: string): void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Popup menu anchored above a control button. One instance per button;
|
|
15
|
+
* rebuilt from data on every open, closed on outside click / Escape.
|
|
16
|
+
*/
|
|
17
|
+
export declare class Menu {
|
|
18
|
+
private anchor;
|
|
19
|
+
readonly root: HTMLElement;
|
|
20
|
+
/** Dim layer behind the mobile bottom-sheet; tap to dismiss. Hidden on desktop. */
|
|
21
|
+
private backdrop;
|
|
22
|
+
private outsideListener;
|
|
23
|
+
constructor(anchor: HTMLElement);
|
|
24
|
+
get open(): boolean;
|
|
25
|
+
toggle(sections: MenuSection[]): void;
|
|
26
|
+
show(sections: MenuSection[]): void;
|
|
27
|
+
close(): void;
|
|
28
|
+
destroy(): void;
|
|
29
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Player } from '../player';
|
|
2
|
+
import { PlayerLabels, RelatedOptions, VideoSource } from '../types';
|
|
3
|
+
/** Preview poster: cover image + big play button, shown until the first play. */
|
|
4
|
+
export declare class PosterOverlay {
|
|
5
|
+
readonly root: HTMLElement;
|
|
6
|
+
private image;
|
|
7
|
+
constructor(player: Player);
|
|
8
|
+
setSource(source: VideoSource | null): void;
|
|
9
|
+
show(): void;
|
|
10
|
+
hide(): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Pause screen "slot". Default content is title + description of the current
|
|
14
|
+
* source; consumers can replace it with any element (or a factory), and the
|
|
15
|
+
* Vue wrapper feeds a real <slot> through the same API.
|
|
16
|
+
*/
|
|
17
|
+
export declare class PauseScreen {
|
|
18
|
+
private labels;
|
|
19
|
+
readonly root: HTMLElement;
|
|
20
|
+
private defaultContent;
|
|
21
|
+
private channel;
|
|
22
|
+
private channelAvatar;
|
|
23
|
+
private channelName;
|
|
24
|
+
private channelUrl;
|
|
25
|
+
private title;
|
|
26
|
+
private description;
|
|
27
|
+
private sponsor;
|
|
28
|
+
private sponsorLabel;
|
|
29
|
+
private sponsorText;
|
|
30
|
+
private custom;
|
|
31
|
+
constructor(labels: PlayerLabels);
|
|
32
|
+
setCustomContent(element: HTMLElement | null): void;
|
|
33
|
+
/** True while consumer-provided custom content is mounted. */
|
|
34
|
+
get hasCustom(): boolean;
|
|
35
|
+
setSource(source: VideoSource | null): void;
|
|
36
|
+
show(): void;
|
|
37
|
+
hide(): void;
|
|
38
|
+
}
|
|
39
|
+
/** Grid of related videos, shown on pause and/or ended. */
|
|
40
|
+
export declare class RelatedOverlay {
|
|
41
|
+
private player;
|
|
42
|
+
readonly root: HTMLElement;
|
|
43
|
+
private grid;
|
|
44
|
+
private heading;
|
|
45
|
+
constructor(player: Player);
|
|
46
|
+
setOptions(options: RelatedOptions | undefined): void;
|
|
47
|
+
private buildCard;
|
|
48
|
+
get visible(): boolean;
|
|
49
|
+
show(): void;
|
|
50
|
+
hide(): void;
|
|
51
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Player } from '../player';
|
|
2
|
+
/** Slide-in panel listing playlist items; survives source changes. */
|
|
3
|
+
export declare class PlaylistPanel {
|
|
4
|
+
private player;
|
|
5
|
+
readonly root: HTMLElement;
|
|
6
|
+
private list;
|
|
7
|
+
private shuffleBtn;
|
|
8
|
+
private repeatBtn;
|
|
9
|
+
constructor(player: Player, layout?: 'sidebar' | 'bottom', playlistTitle?: string);
|
|
10
|
+
private syncModes;
|
|
11
|
+
rebuild(): void;
|
|
12
|
+
get visible(): boolean;
|
|
13
|
+
show(): void;
|
|
14
|
+
hide(): void;
|
|
15
|
+
toggle(): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NormalizedChapter } from '../features/chapters';
|
|
2
|
+
import { ThumbnailTrack } from '../features/thumbnails';
|
|
3
|
+
interface ProgressCallbacks {
|
|
4
|
+
onSeek(time: number): void;
|
|
5
|
+
onScrubStart(): void;
|
|
6
|
+
onScrubEnd(): void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Seek bar. Chapters render as flex segments (YouTube-style), each with its
|
|
10
|
+
* own fill — no absolutely positioned tick marks.
|
|
11
|
+
*/
|
|
12
|
+
export declare class ProgressBar {
|
|
13
|
+
private cb;
|
|
14
|
+
readonly root: HTMLElement;
|
|
15
|
+
private track;
|
|
16
|
+
private buffered;
|
|
17
|
+
private segments;
|
|
18
|
+
private handle;
|
|
19
|
+
private heatmap;
|
|
20
|
+
private tooltip;
|
|
21
|
+
private tooltipThumb;
|
|
22
|
+
private tooltipChapter;
|
|
23
|
+
private tooltipTime;
|
|
24
|
+
private duration;
|
|
25
|
+
private chapters;
|
|
26
|
+
private thumbnails;
|
|
27
|
+
private scrubbing;
|
|
28
|
+
constructor(cb: ProgressCallbacks);
|
|
29
|
+
/** Render the popularity curve (empty array hides it). */
|
|
30
|
+
setHeatmap(values: number[]): void;
|
|
31
|
+
setDuration(duration: number): void;
|
|
32
|
+
setChapters(chapters: NormalizedChapter[]): void;
|
|
33
|
+
setThumbnails(track: ThumbnailTrack | null): void;
|
|
34
|
+
update(currentTime: number, duration: number, bufferedEnd: number): void;
|
|
35
|
+
private render;
|
|
36
|
+
private timeFromEvent;
|
|
37
|
+
private bindPointer;
|
|
38
|
+
private showTooltip;
|
|
39
|
+
private hideTooltip;
|
|
40
|
+
}
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Player } from '../player';
|
|
2
|
+
/**
|
|
3
|
+
* Scene list — the chapters of the current video as a clickable panel, each
|
|
4
|
+
* with a preview cropped from the VTT sprite at the scene start.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ScenesPanel {
|
|
7
|
+
private player;
|
|
8
|
+
readonly root: HTMLElement;
|
|
9
|
+
private list;
|
|
10
|
+
private items;
|
|
11
|
+
constructor(player: Player, layout?: 'sidebar' | 'bottom');
|
|
12
|
+
/** Re-render from the player's current chapters + thumbnail track. */
|
|
13
|
+
rebuild(): void;
|
|
14
|
+
private syncActive;
|
|
15
|
+
get visible(): boolean;
|
|
16
|
+
show(): void;
|
|
17
|
+
hide(): void;
|
|
18
|
+
toggle(): void;
|
|
19
|
+
}
|
package/dist/vue.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue");require("./index.cjs");const c=require("./core.cjs"),s=["ready","play","pause","ended","timeupdate","progress","volumechange","ratechange","seeking","seeked","sourcechange","playlistitemchange","chapterchange","fullscreenchange","pipchange","qualitychange","subtitlechange","relatedshow","relatedclick","action","customaction","adstart","adend","adskip","adclick","adpause","adresume","aderror","error","destroy"],i=e.defineComponent({name:"ITubePlayer",props:{source:{type:[Object,Array],required:!1,default:void 0},options:{type:Object,default:()=>({})}},emits:[...s],setup(n,{emit:d,slots:u,expose:p}){const t=e.ref(null),l=e.ref(null),a=e.shallowRef(null);e.onMounted(()=>{if(!t.value)return;const r=new c.Player(t.value,{...n.options,source:n.source,...u.pauseScreen?{pauseScreen:!0}:{}}),f=d;for(const o of s)r.on(o,y=>f(o,y));u.pauseScreen&&l.value&&r.setPauseScreenContent(l.value),a.value=r}),e.watch(()=>n.source,r=>{r&&a.value&&a.value.load(r)},{deep:!0}),e.onBeforeUnmount(()=>{a.value?.destroy(),a.value=null}),p({player:a});const v=()=>{a.value?.play()};return()=>e.h("div",{class:"imp-vue-host"},[e.h("div",{ref:t}),u.pauseScreen?e.h("div",{ref:l,class:"imp-vue-pause-slot"},u.pauseScreen({player:a.value,close:v})):null])}});exports.Player=c.Player;exports.ITubePlayer=i;exports.default=i;
|
|
2
|
+
//# sourceMappingURL=vue.cjs.map
|
package/dist/vue.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.cjs","sources":["../src/vue.ts"],"sourcesContent":["/**\n * Vue 3 wrapper — a separate entry (`itube-modern-player/vue`) so that plain\n * JS consumers never pull Vue into their graph.\n *\n * ```vue\n * <ITubePlayer :source=\"video\" :options=\"{ seekStep: 5 }\" @ended=\"onEnded\">\n * <template #pauseScreen>\n * <MyPromo />\n * </template>\n * </ITubePlayer>\n * ```\n */\nimport {\n defineComponent,\n h,\n onBeforeUnmount,\n onMounted,\n ref,\n shallowRef,\n watch,\n type PropType,\n} from 'vue'\n// Import via the main entry so the built-in locales are registered for Vue users too.\nimport { Player } from './index'\nimport type { PlayerEventMap, PlayerOptions, VideoSource } from './types'\n\nconst EVENT_NAMES = [\n 'ready', 'play', 'pause', 'ended', 'timeupdate', 'progress', 'volumechange',\n 'ratechange', 'seeking', 'seeked', 'sourcechange', 'playlistitemchange',\n 'chapterchange', 'fullscreenchange', 'pipchange', 'qualitychange',\n 'subtitlechange', 'relatedshow', 'relatedclick', 'action', 'customaction',\n 'adstart', 'adend', 'adskip', 'adclick', 'adpause', 'adresume', 'aderror', 'error', 'destroy',\n] as const satisfies ReadonlyArray<keyof PlayerEventMap>\n\nexport const ITubePlayer = defineComponent({\n name: 'ITubePlayer',\n props: {\n /** Single video or a playlist array. */\n source: {\n type: [Object, Array] as PropType<VideoSource | VideoSource[]>,\n required: false,\n default: undefined,\n },\n /** Everything else from PlayerOptions (source inside is ignored in favor of the prop). */\n options: {\n type: Object as PropType<Omit<PlayerOptions, 'source'>>,\n default: () => ({}),\n },\n },\n emits: [...EVENT_NAMES] as unknown as Record<(typeof EVENT_NAMES)[number], (payload: unknown) => boolean>,\n setup(props, { emit, slots, expose }) {\n const mountEl = ref<HTMLElement | null>(null)\n const slotEl = ref<HTMLElement | null>(null)\n const player = shallowRef<Player | null>(null)\n\n onMounted(() => {\n if (!mountEl.value) return\n const instance = new Player(mountEl.value, {\n ...props.options,\n source: props.source,\n // The slot wins over whatever was passed in options.\n ...(slots.pauseScreen ? { pauseScreen: true } : {}),\n })\n const forward = emit as (event: string, payload?: unknown) => void\n for (const name of EVENT_NAMES) {\n instance.on(name, (payload) => forward(name, payload))\n }\n if (slots.pauseScreen && slotEl.value) {\n instance.setPauseScreenContent(slotEl.value)\n }\n player.value = instance\n })\n\n watch(\n () => props.source,\n (next) => {\n if (next && player.value) player.value.load(next)\n },\n { deep: true },\n )\n\n onBeforeUnmount(() => {\n player.value?.destroy()\n player.value = null\n })\n\n expose({ player })\n\n // \"Close & continue\" — resume playback; the player hides the pause screen\n // on the `play` event, so this both closes the custom block and plays.\n const close = () => {\n void player.value?.play()\n }\n\n return () =>\n h('div', { class: 'imp-vue-host' }, [\n h('div', { ref: mountEl }),\n // Scoped slot: `player` (reactive — null until mounted, then the\n // instance) and `close()` are available to the custom pause content.\n slots.pauseScreen\n ? h('div', { ref: slotEl, class: 'imp-vue-pause-slot' }, slots.pauseScreen({ player: player.value, close }))\n : null,\n ])\n },\n})\n\nexport default ITubePlayer\nexport { Player }\nexport type * from './types'\n"],"names":["EVENT_NAMES","ITubePlayer","defineComponent","props","emit","slots","expose","mountEl","ref","slotEl","player","shallowRef","onMounted","instance","Player","forward","name","payload","watch","next","onBeforeUnmount","close","h"],"mappings":"wLA0BMA,EAAc,CAClB,QAAS,OAAQ,QAAS,QAAS,aAAc,WAAY,eAC7D,aAAc,UAAW,SAAU,eAAgB,qBACnD,gBAAiB,mBAAoB,YAAa,gBAClD,iBAAkB,cAAe,eAAgB,SAAU,eAC3D,UAAW,QAAS,SAAU,UAAW,UAAW,WAAY,UAAW,QAAS,SACtF,EAEaC,EAAcC,EAAAA,gBAAgB,CACzC,KAAM,cACN,MAAO,CAEL,OAAQ,CACN,KAAM,CAAC,OAAQ,KAAK,EACpB,SAAU,GACV,QAAS,MAAA,EAGX,QAAS,CACP,KAAM,OACN,QAAS,KAAO,CAAA,EAAC,CACnB,EAEF,MAAO,CAAC,GAAGF,CAAW,EACtB,MAAMG,EAAO,CAAE,KAAAC,EAAM,MAAAC,EAAO,OAAAC,GAAU,CACpC,MAAMC,EAAUC,EAAAA,IAAwB,IAAI,EACtCC,EAASD,EAAAA,IAAwB,IAAI,EACrCE,EAASC,EAAAA,WAA0B,IAAI,EAE7CC,EAAAA,UAAU,IAAM,CACd,GAAI,CAACL,EAAQ,MAAO,OACpB,MAAMM,EAAW,IAAIC,SAAOP,EAAQ,MAAO,CACzC,GAAGJ,EAAM,QACT,OAAQA,EAAM,OAEd,GAAIE,EAAM,YAAc,CAAE,YAAa,EAAA,EAAS,CAAA,CAAC,CAClD,EACKU,EAAUX,EAChB,UAAWY,KAAQhB,EACjBa,EAAS,GAAGG,EAAOC,GAAYF,EAAQC,EAAMC,CAAO,CAAC,EAEnDZ,EAAM,aAAeI,EAAO,OAC9BI,EAAS,sBAAsBJ,EAAO,KAAK,EAE7CC,EAAO,MAAQG,CACjB,CAAC,EAEDK,EAAAA,MACE,IAAMf,EAAM,OACXgB,GAAS,CACJA,GAAQT,EAAO,OAAOA,EAAO,MAAM,KAAKS,CAAI,CAClD,EACA,CAAE,KAAM,EAAA,CAAK,EAGfC,EAAAA,gBAAgB,IAAM,CACpBV,EAAO,OAAO,QAAA,EACdA,EAAO,MAAQ,IACjB,CAAC,EAEDJ,EAAO,CAAE,OAAAI,EAAQ,EAIjB,MAAMW,EAAQ,IAAM,CACbX,EAAO,OAAO,KAAA,CACrB,EAEA,MAAO,IACLY,EAAAA,EAAE,MAAO,CAAE,MAAO,gBAAkB,CAClCA,EAAAA,EAAE,MAAO,CAAE,IAAKf,EAAS,EAGzBF,EAAM,YACFiB,IAAE,MAAO,CAAE,IAAKb,EAAQ,MAAO,oBAAA,EAAwBJ,EAAM,YAAY,CAAE,OAAQK,EAAO,MAAO,MAAAW,CAAA,CAAO,CAAC,EACzG,IAAA,CACL,CACL,CACF,CAAC"}
|
package/dist/vue.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { PropType } from 'vue';
|
|
2
|
+
import { Player } from './index';
|
|
3
|
+
import { PlayerOptions, VideoSource } from './types';
|
|
4
|
+
export declare const ITubePlayer: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
|
|
5
|
+
/** Single video or a playlist array. */
|
|
6
|
+
source: {
|
|
7
|
+
type: PropType<VideoSource | VideoSource[]>;
|
|
8
|
+
required: false;
|
|
9
|
+
default: undefined;
|
|
10
|
+
};
|
|
11
|
+
/** Everything else from PlayerOptions (source inside is ignored in favor of the prop). */
|
|
12
|
+
options: {
|
|
13
|
+
type: PropType<Omit<PlayerOptions, "source">>;
|
|
14
|
+
default: () => {};
|
|
15
|
+
};
|
|
16
|
+
}>, () => import('vue').VNode<import('vue').RendererNode, import('vue').RendererElement, {
|
|
17
|
+
[key: string]: any;
|
|
18
|
+
}>, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, Record<"progress" | "pause" | "ended" | "play" | "aderror" | "ready" | "timeupdate" | "volumechange" | "ratechange" | "seeking" | "seeked" | "sourcechange" | "playlistitemchange" | "chapterchange" | "fullscreenchange" | "pipchange" | "qualitychange" | "subtitlechange" | "relatedshow" | "relatedclick" | "action" | "customaction" | "adstart" | "adend" | "adskip" | "adclick" | "adpause" | "adresume" | "error" | "destroy", (payload: unknown) => boolean>, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
|
|
19
|
+
/** Single video or a playlist array. */
|
|
20
|
+
source: {
|
|
21
|
+
type: PropType<VideoSource | VideoSource[]>;
|
|
22
|
+
required: false;
|
|
23
|
+
default: undefined;
|
|
24
|
+
};
|
|
25
|
+
/** Everything else from PlayerOptions (source inside is ignored in favor of the prop). */
|
|
26
|
+
options: {
|
|
27
|
+
type: PropType<Omit<PlayerOptions, "source">>;
|
|
28
|
+
default: () => {};
|
|
29
|
+
};
|
|
30
|
+
}>> & Readonly<{
|
|
31
|
+
onProgress?: ((payload: unknown) => any) | undefined;
|
|
32
|
+
onPause?: ((payload: unknown) => any) | undefined;
|
|
33
|
+
onEnded?: ((payload: unknown) => any) | undefined;
|
|
34
|
+
onPlay?: ((payload: unknown) => any) | undefined;
|
|
35
|
+
onAderror?: ((payload: unknown) => any) | undefined;
|
|
36
|
+
onReady?: ((payload: unknown) => any) | undefined;
|
|
37
|
+
onTimeupdate?: ((payload: unknown) => any) | undefined;
|
|
38
|
+
onVolumechange?: ((payload: unknown) => any) | undefined;
|
|
39
|
+
onRatechange?: ((payload: unknown) => any) | undefined;
|
|
40
|
+
onSeeking?: ((payload: unknown) => any) | undefined;
|
|
41
|
+
onSeeked?: ((payload: unknown) => any) | undefined;
|
|
42
|
+
onSourcechange?: ((payload: unknown) => any) | undefined;
|
|
43
|
+
onPlaylistitemchange?: ((payload: unknown) => any) | undefined;
|
|
44
|
+
onChapterchange?: ((payload: unknown) => any) | undefined;
|
|
45
|
+
onFullscreenchange?: ((payload: unknown) => any) | undefined;
|
|
46
|
+
onPipchange?: ((payload: unknown) => any) | undefined;
|
|
47
|
+
onQualitychange?: ((payload: unknown) => any) | undefined;
|
|
48
|
+
onSubtitlechange?: ((payload: unknown) => any) | undefined;
|
|
49
|
+
onRelatedshow?: ((payload: unknown) => any) | undefined;
|
|
50
|
+
onRelatedclick?: ((payload: unknown) => any) | undefined;
|
|
51
|
+
onAction?: ((payload: unknown) => any) | undefined;
|
|
52
|
+
onCustomaction?: ((payload: unknown) => any) | undefined;
|
|
53
|
+
onAdstart?: ((payload: unknown) => any) | undefined;
|
|
54
|
+
onAdend?: ((payload: unknown) => any) | undefined;
|
|
55
|
+
onAdskip?: ((payload: unknown) => any) | undefined;
|
|
56
|
+
onAdclick?: ((payload: unknown) => any) | undefined;
|
|
57
|
+
onAdpause?: ((payload: unknown) => any) | undefined;
|
|
58
|
+
onAdresume?: ((payload: unknown) => any) | undefined;
|
|
59
|
+
onError?: ((payload: unknown) => any) | undefined;
|
|
60
|
+
onDestroy?: ((payload: unknown) => any) | undefined;
|
|
61
|
+
}>, {
|
|
62
|
+
source: VideoSource | VideoSource[];
|
|
63
|
+
options: Omit<PlayerOptions, "source">;
|
|
64
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
65
|
+
export default ITubePlayer;
|
|
66
|
+
export { Player };
|
|
67
|
+
export type * from './types';
|
package/dist/vue.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { defineComponent as m, ref as s, shallowRef as h, onMounted as y, watch as g, onBeforeUnmount as S, h as o } from "vue";
|
|
2
|
+
import "./index.js";
|
|
3
|
+
import { Player as b } from "./core.js";
|
|
4
|
+
const c = [
|
|
5
|
+
"ready",
|
|
6
|
+
"play",
|
|
7
|
+
"pause",
|
|
8
|
+
"ended",
|
|
9
|
+
"timeupdate",
|
|
10
|
+
"progress",
|
|
11
|
+
"volumechange",
|
|
12
|
+
"ratechange",
|
|
13
|
+
"seeking",
|
|
14
|
+
"seeked",
|
|
15
|
+
"sourcechange",
|
|
16
|
+
"playlistitemchange",
|
|
17
|
+
"chapterchange",
|
|
18
|
+
"fullscreenchange",
|
|
19
|
+
"pipchange",
|
|
20
|
+
"qualitychange",
|
|
21
|
+
"subtitlechange",
|
|
22
|
+
"relatedshow",
|
|
23
|
+
"relatedclick",
|
|
24
|
+
"action",
|
|
25
|
+
"customaction",
|
|
26
|
+
"adstart",
|
|
27
|
+
"adend",
|
|
28
|
+
"adskip",
|
|
29
|
+
"adclick",
|
|
30
|
+
"adpause",
|
|
31
|
+
"adresume",
|
|
32
|
+
"aderror",
|
|
33
|
+
"error",
|
|
34
|
+
"destroy"
|
|
35
|
+
], P = m({
|
|
36
|
+
name: "ITubePlayer",
|
|
37
|
+
props: {
|
|
38
|
+
/** Single video or a playlist array. */
|
|
39
|
+
source: {
|
|
40
|
+
type: [Object, Array],
|
|
41
|
+
required: !1,
|
|
42
|
+
default: void 0
|
|
43
|
+
},
|
|
44
|
+
/** Everything else from PlayerOptions (source inside is ignored in favor of the prop). */
|
|
45
|
+
options: {
|
|
46
|
+
type: Object,
|
|
47
|
+
default: () => ({})
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
emits: [...c],
|
|
51
|
+
setup(n, { emit: d, slots: r, expose: i }) {
|
|
52
|
+
const t = s(null), u = s(null), e = h(null);
|
|
53
|
+
y(() => {
|
|
54
|
+
if (!t.value) return;
|
|
55
|
+
const a = new b(t.value, {
|
|
56
|
+
...n.options,
|
|
57
|
+
source: n.source,
|
|
58
|
+
// The slot wins over whatever was passed in options.
|
|
59
|
+
...r.pauseScreen ? { pauseScreen: !0 } : {}
|
|
60
|
+
}), f = d;
|
|
61
|
+
for (const l of c)
|
|
62
|
+
a.on(l, (v) => f(l, v));
|
|
63
|
+
r.pauseScreen && u.value && a.setPauseScreenContent(u.value), e.value = a;
|
|
64
|
+
}), g(
|
|
65
|
+
() => n.source,
|
|
66
|
+
(a) => {
|
|
67
|
+
a && e.value && e.value.load(a);
|
|
68
|
+
},
|
|
69
|
+
{ deep: !0 }
|
|
70
|
+
), S(() => {
|
|
71
|
+
e.value?.destroy(), e.value = null;
|
|
72
|
+
}), i({ player: e });
|
|
73
|
+
const p = () => {
|
|
74
|
+
e.value?.play();
|
|
75
|
+
};
|
|
76
|
+
return () => o("div", { class: "imp-vue-host" }, [
|
|
77
|
+
o("div", { ref: t }),
|
|
78
|
+
// Scoped slot: `player` (reactive — null until mounted, then the
|
|
79
|
+
// instance) and `close()` are available to the custom pause content.
|
|
80
|
+
r.pauseScreen ? o("div", { ref: u, class: "imp-vue-pause-slot" }, r.pauseScreen({ player: e.value, close: p })) : null
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
export {
|
|
85
|
+
P as ITubePlayer,
|
|
86
|
+
b as Player,
|
|
87
|
+
P as default
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=vue.js.map
|
package/dist/vue.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.js","sources":["../src/vue.ts"],"sourcesContent":["/**\n * Vue 3 wrapper — a separate entry (`itube-modern-player/vue`) so that plain\n * JS consumers never pull Vue into their graph.\n *\n * ```vue\n * <ITubePlayer :source=\"video\" :options=\"{ seekStep: 5 }\" @ended=\"onEnded\">\n * <template #pauseScreen>\n * <MyPromo />\n * </template>\n * </ITubePlayer>\n * ```\n */\nimport {\n defineComponent,\n h,\n onBeforeUnmount,\n onMounted,\n ref,\n shallowRef,\n watch,\n type PropType,\n} from 'vue'\n// Import via the main entry so the built-in locales are registered for Vue users too.\nimport { Player } from './index'\nimport type { PlayerEventMap, PlayerOptions, VideoSource } from './types'\n\nconst EVENT_NAMES = [\n 'ready', 'play', 'pause', 'ended', 'timeupdate', 'progress', 'volumechange',\n 'ratechange', 'seeking', 'seeked', 'sourcechange', 'playlistitemchange',\n 'chapterchange', 'fullscreenchange', 'pipchange', 'qualitychange',\n 'subtitlechange', 'relatedshow', 'relatedclick', 'action', 'customaction',\n 'adstart', 'adend', 'adskip', 'adclick', 'adpause', 'adresume', 'aderror', 'error', 'destroy',\n] as const satisfies ReadonlyArray<keyof PlayerEventMap>\n\nexport const ITubePlayer = defineComponent({\n name: 'ITubePlayer',\n props: {\n /** Single video or a playlist array. */\n source: {\n type: [Object, Array] as PropType<VideoSource | VideoSource[]>,\n required: false,\n default: undefined,\n },\n /** Everything else from PlayerOptions (source inside is ignored in favor of the prop). */\n options: {\n type: Object as PropType<Omit<PlayerOptions, 'source'>>,\n default: () => ({}),\n },\n },\n emits: [...EVENT_NAMES] as unknown as Record<(typeof EVENT_NAMES)[number], (payload: unknown) => boolean>,\n setup(props, { emit, slots, expose }) {\n const mountEl = ref<HTMLElement | null>(null)\n const slotEl = ref<HTMLElement | null>(null)\n const player = shallowRef<Player | null>(null)\n\n onMounted(() => {\n if (!mountEl.value) return\n const instance = new Player(mountEl.value, {\n ...props.options,\n source: props.source,\n // The slot wins over whatever was passed in options.\n ...(slots.pauseScreen ? { pauseScreen: true } : {}),\n })\n const forward = emit as (event: string, payload?: unknown) => void\n for (const name of EVENT_NAMES) {\n instance.on(name, (payload) => forward(name, payload))\n }\n if (slots.pauseScreen && slotEl.value) {\n instance.setPauseScreenContent(slotEl.value)\n }\n player.value = instance\n })\n\n watch(\n () => props.source,\n (next) => {\n if (next && player.value) player.value.load(next)\n },\n { deep: true },\n )\n\n onBeforeUnmount(() => {\n player.value?.destroy()\n player.value = null\n })\n\n expose({ player })\n\n // \"Close & continue\" — resume playback; the player hides the pause screen\n // on the `play` event, so this both closes the custom block and plays.\n const close = () => {\n void player.value?.play()\n }\n\n return () =>\n h('div', { class: 'imp-vue-host' }, [\n h('div', { ref: mountEl }),\n // Scoped slot: `player` (reactive — null until mounted, then the\n // instance) and `close()` are available to the custom pause content.\n slots.pauseScreen\n ? h('div', { ref: slotEl, class: 'imp-vue-pause-slot' }, slots.pauseScreen({ player: player.value, close }))\n : null,\n ])\n },\n})\n\nexport default ITubePlayer\nexport { Player }\nexport type * from './types'\n"],"names":["EVENT_NAMES","ITubePlayer","defineComponent","props","emit","slots","expose","mountEl","ref","slotEl","player","shallowRef","onMounted","instance","Player","forward","name","payload","watch","next","onBeforeUnmount","close","h"],"mappings":";;;AA0BA,MAAMA,IAAc;AAAA,EAClB;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAAc;AAAA,EAAY;AAAA,EAC7D;AAAA,EAAc;AAAA,EAAW;AAAA,EAAU;AAAA,EAAgB;AAAA,EACnD;AAAA,EAAiB;AAAA,EAAoB;AAAA,EAAa;AAAA,EAClD;AAAA,EAAkB;AAAA,EAAe;AAAA,EAAgB;AAAA,EAAU;AAAA,EAC3D;AAAA,EAAW;AAAA,EAAS;AAAA,EAAU;AAAA,EAAW;AAAA,EAAW;AAAA,EAAY;AAAA,EAAW;AAAA,EAAS;AACtF,GAEaC,IAAcC,EAAgB;AAAA,EACzC,MAAM;AAAA,EACN,OAAO;AAAA;AAAA,IAEL,QAAQ;AAAA,MACN,MAAM,CAAC,QAAQ,KAAK;AAAA,MACpB,UAAU;AAAA,MACV,SAAS;AAAA,IAAA;AAAA;AAAA,IAGX,SAAS;AAAA,MACP,MAAM;AAAA,MACN,SAAS,OAAO,CAAA;AAAA,IAAC;AAAA,EACnB;AAAA,EAEF,OAAO,CAAC,GAAGF,CAAW;AAAA,EACtB,MAAMG,GAAO,EAAE,MAAAC,GAAM,OAAAC,GAAO,QAAAC,KAAU;AACpC,UAAMC,IAAUC,EAAwB,IAAI,GACtCC,IAASD,EAAwB,IAAI,GACrCE,IAASC,EAA0B,IAAI;AAE7C,IAAAC,EAAU,MAAM;AACd,UAAI,CAACL,EAAQ,MAAO;AACpB,YAAMM,IAAW,IAAIC,EAAOP,EAAQ,OAAO;AAAA,QACzC,GAAGJ,EAAM;AAAA,QACT,QAAQA,EAAM;AAAA;AAAA,QAEd,GAAIE,EAAM,cAAc,EAAE,aAAa,GAAA,IAAS,CAAA;AAAA,MAAC,CAClD,GACKU,IAAUX;AAChB,iBAAWY,KAAQhB;AACjB,QAAAa,EAAS,GAAGG,GAAM,CAACC,MAAYF,EAAQC,GAAMC,CAAO,CAAC;AAEvD,MAAIZ,EAAM,eAAeI,EAAO,SAC9BI,EAAS,sBAAsBJ,EAAO,KAAK,GAE7CC,EAAO,QAAQG;AAAA,IACjB,CAAC,GAEDK;AAAA,MACE,MAAMf,EAAM;AAAA,MACZ,CAACgB,MAAS;AACR,QAAIA,KAAQT,EAAO,SAAOA,EAAO,MAAM,KAAKS,CAAI;AAAA,MAClD;AAAA,MACA,EAAE,MAAM,GAAA;AAAA,IAAK,GAGfC,EAAgB,MAAM;AACpB,MAAAV,EAAO,OAAO,QAAA,GACdA,EAAO,QAAQ;AAAA,IACjB,CAAC,GAEDJ,EAAO,EAAE,QAAAI,GAAQ;AAIjB,UAAMW,IAAQ,MAAM;AAClB,MAAKX,EAAO,OAAO,KAAA;AAAA,IACrB;AAEA,WAAO,MACLY,EAAE,OAAO,EAAE,OAAO,kBAAkB;AAAA,MAClCA,EAAE,OAAO,EAAE,KAAKf,GAAS;AAAA;AAAA;AAAA,MAGzBF,EAAM,cACFiB,EAAE,OAAO,EAAE,KAAKb,GAAQ,OAAO,qBAAA,GAAwBJ,EAAM,YAAY,EAAE,QAAQK,EAAO,OAAO,OAAAW,EAAA,CAAO,CAAC,IACzG;AAAA,IAAA,CACL;AAAA,EACL;AACF,CAAC;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "itube-modern-player",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lightweight, framework-agnostic HTML5 video player: HLS streams, playlists, chapters, VTT subtitles, sprite previews, VAST ads. Ships with a Vue 3 wrapper.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./vue": {
|
|
16
|
+
"types": "./dist/vue.d.ts",
|
|
17
|
+
"import": "./dist/vue.js",
|
|
18
|
+
"require": "./dist/vue.cjs"
|
|
19
|
+
},
|
|
20
|
+
"./core": {
|
|
21
|
+
"types": "./dist/coreEntry.d.ts",
|
|
22
|
+
"import": "./dist/core.js",
|
|
23
|
+
"require": "./dist/core.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./locales": {
|
|
26
|
+
"types": "./dist/localesEntry.d.ts",
|
|
27
|
+
"import": "./dist/locales.js",
|
|
28
|
+
"require": "./dist/locales.cjs"
|
|
29
|
+
},
|
|
30
|
+
"./lazy": {
|
|
31
|
+
"types": "./dist/lazy.d.ts",
|
|
32
|
+
"import": "./dist/lazy.js",
|
|
33
|
+
"require": "./dist/lazy.cjs"
|
|
34
|
+
},
|
|
35
|
+
"./style.css": "./dist/style.css"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
40
|
+
"sideEffects": [
|
|
41
|
+
"**/*.css"
|
|
42
|
+
],
|
|
43
|
+
"unpkg": "./dist/itube-modern-player.iife.js",
|
|
44
|
+
"jsdelivr": "./dist/itube-modern-player.iife.js",
|
|
45
|
+
"scripts": {
|
|
46
|
+
"dev": "vite",
|
|
47
|
+
"build": "vite build && vite build --config vite.config.iife.ts",
|
|
48
|
+
"typecheck": "tsc --noEmit",
|
|
49
|
+
"prepack": "npm run build",
|
|
50
|
+
"prepublishOnly": "npm run typecheck"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"hls.js": "^1.5.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"typescript": "^5.6.0",
|
|
57
|
+
"vite": "^6.0.0",
|
|
58
|
+
"vite-plugin-dts": "^4.3.0",
|
|
59
|
+
"vue": "^3.5.0"
|
|
60
|
+
},
|
|
61
|
+
"keywords": [
|
|
62
|
+
"video",
|
|
63
|
+
"player",
|
|
64
|
+
"html5",
|
|
65
|
+
"hls",
|
|
66
|
+
"m3u8",
|
|
67
|
+
"vast",
|
|
68
|
+
"playlist",
|
|
69
|
+
"chapters",
|
|
70
|
+
"vue",
|
|
71
|
+
"typescript"
|
|
72
|
+
],
|
|
73
|
+
"author": "Aleksandr Shevchenko <aleksandr.shevchenko.forwork@gmail.com>",
|
|
74
|
+
"license": "MIT",
|
|
75
|
+
"repository": {
|
|
76
|
+
"type": "git",
|
|
77
|
+
"url": "git+https://bitbucket.org/luckytube/itube-modern-player.git"
|
|
78
|
+
},
|
|
79
|
+
"homepage": "https://bitbucket.org/luckytube/itube-modern-player#readme",
|
|
80
|
+
"bugs": {
|
|
81
|
+
"url": "https://bitbucket.org/luckytube/itube-modern-player/issues"
|
|
82
|
+
},
|
|
83
|
+
"engines": {
|
|
84
|
+
"node": ">=18"
|
|
85
|
+
}
|
|
86
|
+
}
|