mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(monorepo): migrate prototype code to organized package structure
Migrate all prototype code from vjs10-prototype repository into the monorepo structure following established dependency hierarchy and package organization patterns. Core packages migrated: - @vjs-10/media-store: Complete media store with nanostores integration - @vjs-10/playback-engine: HLS.js playback engine implementation - @vjs-10/media: Core media state owner and interfaces HTML packages migrated: - @vjs-10/html-icons: Web component icons with proper base classes - @vjs-10/html-media-elements: Context provider integration - @vjs-10/html: Main HTML UI components, skins, and utilities React packages migrated: - @vjs-10/react-icons: React icon components - @vjs-10/react-media-elements: Video component with state integration - @vjs-10/react-media-store: MediaProvider and React hooks - @vjs-10/react: Main React UI components and skins Changes made: - Updated all imports to use monorepo package names (@vjs-10/*) - Distributed dependencies to specific packages that use them - Preserved all prototype functionality and component structure - Created proper index files for all packages - Fixed workspace protocol usage for npm compatibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
fc5a0e46fd
commit
4b472ec49c
@@ -1,69 +1 @@
|
||||
export type MediaReadyState = 0 | 1 | 2 | 3 | 4;
|
||||
|
||||
export const READY_STATE = {
|
||||
HAVE_NOTHING: 0 as MediaReadyState,
|
||||
HAVE_METADATA: 1 as MediaReadyState,
|
||||
HAVE_CURRENT_DATA: 2 as MediaReadyState,
|
||||
HAVE_FUTURE_DATA: 3 as MediaReadyState,
|
||||
HAVE_ENOUGH_DATA: 4 as MediaReadyState,
|
||||
} as const;
|
||||
|
||||
export type MediaNetworkState = 0 | 1 | 2 | 3;
|
||||
|
||||
export const NETWORK_STATE = {
|
||||
EMPTY: 0 as MediaNetworkState,
|
||||
IDLE: 1 as MediaNetworkState,
|
||||
LOADING: 2 as MediaNetworkState,
|
||||
NO_SOURCE: 3 as MediaNetworkState,
|
||||
} as const;
|
||||
|
||||
export interface MediaElementLike {
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
paused: boolean;
|
||||
ended: boolean;
|
||||
volume: number;
|
||||
muted: boolean;
|
||||
playbackRate: number;
|
||||
readyState: MediaReadyState;
|
||||
networkState: MediaNetworkState;
|
||||
|
||||
play(): Promise<void>;
|
||||
pause(): void;
|
||||
load(): void;
|
||||
}
|
||||
|
||||
export function isMediaElement(element: any): element is HTMLMediaElement {
|
||||
return element &&
|
||||
typeof element.play === 'function' &&
|
||||
typeof element.pause === 'function' &&
|
||||
typeof element.load === 'function' &&
|
||||
typeof element.currentTime === 'number';
|
||||
}
|
||||
|
||||
export function createMediaElementAdapter(element: HTMLMediaElement): MediaElementLike {
|
||||
return {
|
||||
get currentTime() { return element.currentTime; },
|
||||
set currentTime(value: number) { element.currentTime = value; },
|
||||
|
||||
get duration() { return element.duration; },
|
||||
get paused() { return element.paused; },
|
||||
get ended() { return element.ended; },
|
||||
|
||||
get volume() { return element.volume; },
|
||||
set volume(value: number) { element.volume = value; },
|
||||
|
||||
get muted() { return element.muted; },
|
||||
set muted(value: boolean) { element.muted = value; },
|
||||
|
||||
get playbackRate() { return element.playbackRate; },
|
||||
set playbackRate(value: number) { element.playbackRate = value; },
|
||||
|
||||
get readyState() { return element.readyState as MediaReadyState; },
|
||||
get networkState() { return element.networkState as MediaNetworkState; },
|
||||
|
||||
play: () => element.play(),
|
||||
pause: () => element.pause(),
|
||||
load: () => element.load(),
|
||||
};
|
||||
}
|
||||
export * from './playable';
|
||||
@@ -0,0 +1,112 @@
|
||||
import type { IBasePlaybackEngine } from '@vjs-10/playback-engine';
|
||||
import { createPlaybackEngine } from '@vjs-10/playback-engine';
|
||||
/** @TODO Split out "playable" vs. "audible" and compose via factory (current mixin pattern or spreadable mixin pattern) (CJP) */
|
||||
export const Events = ['volumechange', 'pause', 'play', 'playing'] as const;
|
||||
|
||||
export interface IBaseMediaStateOwner<
|
||||
T extends Pick<HTMLMediaElement, 'src'> = Pick<HTMLMediaElement, 'src'>,
|
||||
> extends EventTarget,
|
||||
Pick<HTMLMediaElement, 'src'> {
|
||||
mediaElement?: T;
|
||||
}
|
||||
|
||||
export interface IPlayableMediaStateOwner
|
||||
extends EventTarget,
|
||||
IBaseMediaStateOwner,
|
||||
Pick<HTMLMediaElement, 'play' | 'pause' | 'paused'> {}
|
||||
|
||||
export interface IAudibleMediaStateOwner
|
||||
extends EventTarget,
|
||||
IBaseMediaStateOwner,
|
||||
Pick<HTMLMediaElement, 'muted' | 'volume'> {}
|
||||
|
||||
export class PlayableMediaStateOwner
|
||||
extends EventTarget
|
||||
implements IPlayableMediaStateOwner, IAudibleMediaStateOwner
|
||||
{
|
||||
protected _playbackEngine: IBasePlaybackEngine;
|
||||
constructor() {
|
||||
super();
|
||||
this._playbackEngine = createPlaybackEngine();
|
||||
}
|
||||
|
||||
get mediaElement() {
|
||||
return this._playbackEngine.mediaElement;
|
||||
}
|
||||
|
||||
set mediaElement(value) {
|
||||
if (this.mediaElement === value) return;
|
||||
if (this.mediaElement != null) {
|
||||
Events.forEach((eventType) => {
|
||||
this.mediaElement?.removeEventListener(eventType, this);
|
||||
});
|
||||
}
|
||||
this._playbackEngine.mediaElement = value;
|
||||
if (this.mediaElement) {
|
||||
Events.forEach((eventType) => {
|
||||
this.mediaElement?.addEventListener(eventType, this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get paused() {
|
||||
return this.mediaElement?.paused ?? false;
|
||||
}
|
||||
|
||||
pause() {
|
||||
/** @TODO implement deferred state etc. for cases where media has yet to be set */
|
||||
if (!this.mediaElement) return;
|
||||
return this.mediaElement.pause();
|
||||
}
|
||||
|
||||
play() {
|
||||
/** @TODO implement deferred state etc. for cases where media has yet to be set */
|
||||
if (!this.mediaElement) return Promise.reject();
|
||||
return this.mediaElement.play();
|
||||
}
|
||||
|
||||
get muted() {
|
||||
return this.mediaElement?.muted ?? false;
|
||||
}
|
||||
|
||||
set muted(value) {
|
||||
if (value === this.muted) return;
|
||||
/** @TODO implement deferred state etc. for cases where media has yet to be set */
|
||||
if (!this.mediaElement) return;
|
||||
this.mediaElement.muted = value;
|
||||
}
|
||||
|
||||
get volume() {
|
||||
return this.mediaElement?.volume ?? 0;
|
||||
}
|
||||
|
||||
set volume(value) {
|
||||
if (value === this.volume) return;
|
||||
/** @TODO implement deferred state etc. for cases where media has yet to be set */
|
||||
if (!this.mediaElement) return;
|
||||
this.mediaElement.volume = value;
|
||||
}
|
||||
|
||||
get src() {
|
||||
return this._playbackEngine.src ?? '';
|
||||
}
|
||||
|
||||
set src(value) {
|
||||
this._playbackEngine.src = value;
|
||||
}
|
||||
|
||||
handleEvent(event: Event): void {
|
||||
if (event.target === this.mediaElement) {
|
||||
// @ts-ignore
|
||||
const clonedEvent = new event.constructor(event.type, event);
|
||||
this.dispatchEvent(clonedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.mediaElement = undefined;
|
||||
this._playbackEngine.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
export const createMediaStateOwner = () => new PlayableMediaStateOwner();
|
||||
Reference in New Issue
Block a user