{"_id":"@alekstar79/video-player","_rev":"2-7e407895a003c0953ddf35eee2d050d6","name":"@alekstar79/video-player","dist-tags":{"latest":"2.0.1"},"versions":{"2.0.0":{"name":"@alekstar79/video-player","version":"2.0.0","keywords":["typescript","video","player","vue"],"author":{"name":"Aleksey Tarasenko","email":"alekstar79@yandex.ru"},"license":"MIT","_id":"@alekstar79/video-player@2.0.0","maintainers":[{"name":"alekstar79","email":"alekstar79@yandex.ru"}],"homepage":"https://github.com/alekstar79/video-player.git","bugs":{"url":"https://github.com/alekstar79/video-player/issues"},"dist":{"shasum":"51f6f52f1ea47124667b1bce798dfda475e654c0","tarball":"https://registry.npmjs.org/@alekstar79/video-player/-/video-player-2.0.0.tgz","fileCount":54,"integrity":"sha512-USFmaRxuNBgxXSeKhWk/ZGPU/of1HdB0PQ7r/rzzsHZK+m4iAW0LO0av4ckzREOZsU0ypxBemT+pj1bLvAco7A==","signatures":[{"sig":"MEUCIG/IQMwbp8PAHADwNBIJGDXOG9X1R+9se0x4b7RSy8pzAiEA2fX7Nec9Ez90TRw5muLL79wREeBhhzjTEJp5w0lMj5A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":397142},"main":"lib/video-player.js","type":"module","types":"lib/index.d.ts","module":"lib/video-player.js","exports":{".":{"types":"./lib/index.d.ts","import":"./lib/video-player.js"},"./vue":{"types":"./lib/vue.d.ts","import":"./lib/video-player-vue.js"},"./lib/styles.css":"./lib/styles.css"},"gitHead":"4e94898974ed1cb10cf764ed24442ca86e3996f2","scripts":{"dev":"vite","test":"vitest run","build":"npm run build:lib && npm run build:demo","preview":"vite preview","test:ui":"vitest run --ui","build:lib":"vue-tsc -p tsconfig.lib.json --noEmit && vite build --config vite.lib.config.ts","build:demo":"vite build --config vite.config.ts","type-check":"tsc --noEmit","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"alekstar79","email":"alekstar79@yandex.ru"},"repository":{"url":"git+https://github.com/alekstar79/video-player.git","type":"git"},"_npmVersion":"10.9.4","description":"Video player built with TypeScript and Vite","directories":{},"_nodeVersion":"22.21.1","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.13","sass":"^1.97.3","vite":"^7.3.1","jsdom":"^28.1.0","terser":"^5.46.0","vitest":"^4.0.18","vue-tsc":"^2.2.8","@vitest/ui":"^4.0.18","typescript":"^5.8.3","@types/node":"^25.2.3","vite-plugin-dts":"^4.5.3","@vitejs/plugin-vue":"^6.0.4","@vitest/coverage-v8":"^4.0.18","vite-plugin-static-copy":"^3.2.0","@alekstar79/context-menu":"^2.1.3","@testing-library/jest-dom":"^6.9.1"},"peerDependencies":{"vue":"^3.5.13","@alekstar79/context-menu":"^2.1.3"},"peerDependenciesMeta":{"vue":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/video-player_2.0.0_1773230781162_0.17286132636376328","host":"s3://npm-registry-packages-npm-production"}},"2.0.1":{"name":"@alekstar79/video-player","description":"Video player built with TypeScript and Vite","version":"2.0.1","type":"module","license":"MIT","author":{"name":"Aleksey Tarasenko","email":"alekstar79@yandex.ru"},"main":"lib/video-player.js","module":"lib/video-player.js","types":"lib/index.d.ts","homepage":"https://github.com/alekstar79/video-player.git","repository":{"type":"git","url":"git+https://github.com/alekstar79/video-player.git"},"bugs":{"url":"https://github.com/alekstar79/video-player/issues"},"exports":{".":{"import":"./lib/video-player.js","types":"./lib/index.d.ts"},"./vue":{"import":"./lib/video-player-vue.js","types":"./lib/vue.d.ts"},"./lib/styles.css":"./lib/styles.css"},"keywords":["typescript","video","player","vue"],"scripts":{"dev":"vite","build":"npm run build:lib && npm run build:demo","build:lib":"vue-tsc -p tsconfig.lib.json --noEmit && vite build --config vite.lib.config.ts","build:demo":"vite build --config vite.config.ts","type-check":"tsc --noEmit","preview":"vite preview","test":"vitest run","test:ui":"vitest run --ui","test:coverage":"vitest run --coverage"},"peerDependencies":{"@alekstar79/context-menu":"^2.1.3","vue":"^3.5.13"},"peerDependenciesMeta":{"vue":{"optional":true}},"devDependencies":{"@alekstar79/context-menu":"^2.1.3","@testing-library/jest-dom":"^6.9.1","@types/node":"^25.2.3","@vitejs/plugin-vue":"^6.0.4","@vitest/coverage-v8":"^4.0.18","@vitest/ui":"^4.0.18","jsdom":"^28.1.0","sass":"^1.97.3","terser":"^5.46.0","typescript":"^5.8.3","vite":"^7.3.1","vite-plugin-dts":"^4.5.3","vite-plugin-static-copy":"^3.2.0","vitest":"^4.0.18","vue":"^3.5.13","vue-tsc":"^2.2.8"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_id":"@alekstar79/video-player@2.0.1","gitHead":"8cefe1a231453ae0cc4f9fe8be3f95e4f53d69b5","_nodeVersion":"22.21.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-IwodOUqj7gXs9LxOFEi6r7NILPrSLltdNog+7XSZbCkXLykHY75yoKKBOUjGOeVq2mAaHESbzb2honQmKx72Fw==","shasum":"e2dc8cf238b87bc05a3f1344b753fd8cdd367a39","tarball":"https://registry.npmjs.org/@alekstar79/video-player/-/video-player-2.0.1.tgz","fileCount":54,"unpackedSize":472254,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC4R9+MuMque7Rj69JH+NobxUyVTmrlzlkiGa+PaDxkkAIgUyggVBqbyRXX/HcQFUZumroaMBVZsdKbjl+LQqYsPiA="}]},"_npmUser":{"name":"alekstar79","email":"alekstar79@yandex.ru"},"directories":{},"maintainers":[{"name":"alekstar79","email":"alekstar79@yandex.ru"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/video-player_2.0.1_1773232068453_0.22270598769493133"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-11T12:06:21.070Z","modified":"2026-03-11T12:27:48.717Z","2.0.0":"2026-03-11T12:06:21.337Z","2.0.1":"2026-03-11T12:27:48.604Z"},"bugs":{"url":"https://github.com/alekstar79/video-player/issues"},"author":{"name":"Aleksey Tarasenko","email":"alekstar79@yandex.ru"},"license":"MIT","homepage":"https://github.com/alekstar79/video-player.git","keywords":["typescript","video","player","vue"],"repository":{"type":"git","url":"git+https://github.com/alekstar79/video-player.git"},"description":"Video player built with TypeScript and Vite","maintainers":[{"name":"alekstar79","email":"alekstar79@yandex.ru"}],"readme":"# Video Player TS (Vanilla/Vue3)\n\n[![NPM Version](https://img.shields.io/npm/v/video-player.svg)](https://www.npmjs.com/package/@alekstar79/video-player)\n[![License](https://img.shields.io/badge/License-MIT-blue)](LICENSE)\n[![GitHub](https://img.shields.io/badge/github-repo-green.svg?style=flat)](https://github.com/alekstar79/video-player)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue?style=flat-square)](https://www.typescriptlang.org)\n[![Coverage](https://img.shields.io/badge/coverage-81.65%25-brightgreen.svg)](https://github.com/alekstar79/video-player)\n[![Vue 3](https://img.shields.io/badge/Vue%203-4FC08D?style=flat&logo=vuedotjs&logoColor=white)](https://vuejs.org)\n[![Vite](https://img.shields.io/badge/Vite-646CFF?style=flat&logo=vite&logoColor=white)](https://vite.dev)\n\n_A modern, feature-rich video player built with TypeScript and Web Components.\nSupports native JavaScript and Vue 3 integration. Includes a customizable circular context menu,\nplaylist management, thumbnail generation, metadata extraction, and more._\n\n![review](review.svg)\n\n**[View Live Demo](https://alekstar79.github.io/video-player)**\n\n<!-- TOC -->\n* [Video Player TS (Vanilla/Vue3)](#video-player-ts-vanillavue3)\n  * [Features](#features)\n  * [Installation](#installation)\n  * [Usage](#usage)\n    * [Vanilla JavaScript / TypeScript  [example]](#vanilla-javascript--typescript-example)\n    * [Vue 3  [example]](#vue-3-example)\n  * [Configuration](#configuration)\n    * [Player Options (`VideoPlayerConfig`)](#player-options-videoplayerconfig)\n    * [`VideoSource` Object](#videosource-object)\n    * [`ControlsVisibility` (keys)](#controlsvisibility-keys)\n  * [API](#api)\n    * [Player Instance Methods](#player-instance-methods)\n    * [Events (`PlayerEventMap`)](#events-playereventmap)\n    * [Context Menu](#context-menu)\n  * [Thumbnail Generation & Metadata](#thumbnail-generation--metadata)\n    * [Metadata Extraction](#metadata-extraction)\n    * [Preview Panel](#preview-panel)\n    * [Programmatic Thumbnails](#programmatic-thumbnails)\n  * [Styling and Customization](#styling-and-customization)\n    * [Icons](#icons)\n  * [Examples](#examples)\n    * [Vanilla JS with dynamic source switching](#vanilla-js-with-dynamic-source-switching)\n    * [Vue 3 – accessing player methods via ref](#vue-3--accessing-player-methods-via-ref)\n    * [Listening to events](#listening-to-events)\n    * [Building from Source](#building-from-source)\n  * [License](#license)\n<!-- TOC -->\n\n## Features\n\n- **Web Component based** – works with Vue3 framework or vanilla (JS / TS).\n- **Vue 3 wrapper** – seamless integration with Vue 3 reactivity.\n- **Configurable controls** – show/hide individual buttons, auto‑hide.\n- **Playlist support** – multiple sources, next/previous navigation.\n- **Loop modes** – none, single, all.\n- **Picture‑in‑Picture** – if supported by the browser.\n- **Fullscreen** – with custom controller and event handling.\n- **Volume control** – horizontal/vertical orientation adapts to available space.\n- **Timeline seeking** – hover preview and click seeking.\n- **Playback speed** – selectable from predefined speeds.\n- **Built‑in file opener** – uses modern File System Access API with fallback.\n- **Thumbnail generation** – capture current frame, view details, save as JPEG.\n- **MP4 metadata extraction** – title, artist, album, cover art.\n- **Circular context menu** – powered by @alekstar79/context-menu (optional).\n- **Event emitter** – subscribe to all player actions.\n- **Fully typed** – written in TypeScript, includes type definitions.\n\n## Installation\n\n```bash\nnpm install @alekstar79/video-player\n```\n\nOptional peer dependencies (install only if needed):\n\n```bash\nnpm install @alekstar79/context-menu   # for context menu\nnpm install vue                        # for Vue wrapper\n```\n\n\n## Usage\n\n### Vanilla JavaScript / TypeScript  [[example]](src/examples/vanilla)\n\n1. **Import the styles and register the custom elements**\n\n```ts\nimport { registerComponents, createPlayer } from '@alekstar79/video-player'\nimport '@alekstar79/video-player/lib/styles.css'\n\n// Register web components (required once)\nregisterComponents()\n```\n2. Create a container element in your HTML\n\n\n```html\n<div id=\"player-container\"></div>\n```\n\n3. Initialize the player\n\n```ts\nconst container = document.getElementById('player-container')\n\nconst player = await createPlayer(container, {\n  initialSources: [\n    'video.mp4',\n    {\n      title: 'My Video',\n      url: 'https://example.com/vid.mp4',\n      thumb: 'poster.jpg'\n    }\n  ],\n  autoPlay: false,\n  loopMode: 'all',\n  contextMenu: true // enables default context menu\n})\n```\n\n### Vue 3  [[example]](src/examples/vue)\n\n```vue\n<template>\n  <VideoPlayer\n    :initialSources=\"sources\"\n    :autoPlay=\"false\"\n    loopMode=\"all\"\n    :contextMenu=\"true\"\n    @play=\"onPlay\"\n    @timeupdate=\"onTimeUpdate\"\n  />\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { VideoPlayer } from '@alekstar79/video-player/vue'\nimport '@alekstar79/video-player/lib/styles.css'\n\nconst sources = ref([\n  'video.mp4',\n  { title: 'Big Buck Bunny', url: 'https://...', thumb: 'poster.jpg' }\n])\n\nfunction onPlay() { console.log('playing') }\nfunction onTimeUpdate(data) { console.log(data.currentTime) }\n</script>\n```\n\n\n## Configuration\n\n### Player Options (`VideoPlayerConfig`)\n\n| Option               | Type                                 | Default    | Description                                        |\n|----------------------|--------------------------------------|------------|----------------------------------------------------|\n| `initialSources`     | `(string \\| Partial<VideoSource>)[]` | `[]`       | Array of video sources.                            |\n| `container`          | `HTMLElement`                        | required   | Container element (only for `createPlayer`).       |\n| `autoPlay`           | `boolean`                            | `false`    | Start playback immediately.                        |\n| `initialVolume`      | `number`                             | `0.7`      | Initial volume (0–1).                              |\n| `playbackRate`       | `number`                             | `1.0`      | Playback speed.                                    |\n| `logging`            | `boolean`                            | `false`    | Enable console logs.                               |\n| `loopMode`           | `'none' \\| 'one' \\| 'all'`           | `'none'`   | Loop behavior.                                     |\n| `muted`              | `boolean`                            | `false`    | Start muted.                                       |\n| `showControls`       | `boolean`                            | `true`     | Show/hide all controls.                            |\n| `contextMenu`        | `boolean \\| IConfig`                 | `false`    | Enable context menu with default or custom config. |\n| `controlsVisibility` | `Partial<ControlsVisibility>`        | all `true` | Individual control visibility.                     |\n| `maxWidth`           | `string \\| number`                   | `'960px'`  | Maximum width of player.                           |\n| `width`              | `string \\| number`                   | –          | Explicit width.                                    |\n| `height`             | `string \\| number`                   | –          | Explicit height.                                   |\n| `aspectRatio`        | `string`                             | `'16:9'`   | Aspect ratio (e.g. `'4:3'`).                       |\n| `nextButton`         | `boolean`                            | `true`     | Show next source button (if multiple sources).     |\n| `prevButton`         | `boolean`                            | `true`     | Show previous source button.                       |\n\n### `VideoSource` Object\n\n| Property      | Type                | Description                               |\n|---------------|---------------------|-------------------------------------------|\n| `title`       | `string`            | Display title.                            |\n| `url`         | `string`            | Video URL.                                |\n| `description` | `string` (optional) | Video description.                        |\n| `thumb`       | `string` (optional) | Poster image URL.                         |\n| `file`        | `File` (optional)   | File object (used when loaded from disk). |\n\n### `ControlsVisibility` (keys)\n\n| Key               | Description                         |\n|-------------------|-------------------------------------|\n| `showOpenFile`    | Show \"Open file\" button.            |\n| `showPlayPause`   | Show play/pause button.             |\n| `showSkipButtons` | Show skip forward/backward buttons. |\n| `showVolume`      | Show volume control.                |\n| `showTimeDisplay` | Show current time / duration.       |\n| `showSpeed`       | Show speed selector.                |\n| `showPip`         | Show Picture‑in‑Picture button.     |\n| `showFullscreen`  | Show fullscreen button.             |\n| `showLoop`        | Show loop mode button.              |\n| `showTimeline`    | Show timeline/progress bar.         |\n\n\n## API\n\n### Player Instance Methods\n\nAfter creating the player (via `createPlayer` or Vue ref), you can call:\n\n| Method                                                                                             | Description                                         |\n|----------------------------------------------------------------------------------------------------|-----------------------------------------------------|\n| `play(): Promise<void>`                                                                            | Start playback.                                     |\n| `pause(): void`                                                                                    | Pause playback.                                     |\n| `togglePlay(): Promise<void>`                                                                      | Toggle play/pause.                                  |\n| `toggleFullscreen(): Promise<void>`                                                                | Toggle fullscreen mode.                             |\n| `togglePictureInPicture(): Promise<void>`                                                          | Toggle PiP (if supported).                          |\n| `setVolume(volume: number): void`                                                                  | Set volume (0–1).                                   |\n| `setMuted(muted: boolean): void`                                                                   | Mute/unmute.                                        |\n| `setPlaybackRate(rate: number): void`                                                              | Set playback speed.                                 |\n| `setLoopMode(mode: LoopMode): void`                                                                | Set loop mode.                                      |\n| `toggleLoop(): void`                                                                               | Cycle loop modes (`none` → `one` → `all` → `none`). |\n| `skip(seconds: number): void`                                                                      | Skip forward/backward.                              |\n| `seekTo(time: number): void`                                                                       | Jump to specific time.                              |\n| `setSources(sources: (string \\| Partial<VideoSource>)[]): void`                                    | Replace playlist.                                   |\n| `addSource(source: Partial<VideoSource>): void`                                                    | Add source to playlist.                             |\n| `getSources(): VideoSource[]`                                                                      | Get all sources.                                    |\n| `getCurrentSource(): VideoSource \\| undefined`                                                     | Get current source.                                 |\n| `getCurrentSourceIndex(): number`                                                                  | Get index of current source.                        |\n| `nextSource(): Promise<void>`                                                                      | Switch to next source.                              |\n| `previousSource(): Promise<void>`                                                                  | Switch to previous source.                          |\n| `switchToSource(index: number): Promise<void>`                                                     | Switch to source by index.                          |\n| `switchToSourceByUrl(url: string): Promise<void>`                                                  | Switch to source by URL.                            |\n| `loadVideoFile(): Promise<void>`                                                                   | Open file picker and load video.                    |\n| `loadVideoFromUrl(url: string): Promise<void>`                                                     | Load video from URL.                                |\n| `showControls(): void`                                                                             | Show all controls.                                  |\n| `hideControls(): void`\t                                                                            | Hide all controls.                                  |\n| `toggleControls(): void`                                                                           | Toggle controls visibility.                         |\n| `showControl(key: keyof ControlsVisibility): void`                                                 | Show specific control.                              |\n| `hideControl(key: keyof ControlsVisibility): void`                                                 | Hide specific control.                              |\n| `toggleControl(key: keyof ControlsVisibility): void`                                               | Toggle specific control.                            |\n| `setControlsVisibility(visibility: Partial<ControlsVisibility>): void`                             | Set multiple visibility flags.                      |\n| `getControlVisibility(key: keyof ControlsVisibility): boolean`                                     | Get control visibility.                             |\n| `getCurrentTime(): number`                                                                         | Get current playback time.                          |\n| `getDuration(): number`                                                                            | Get video duration.                                 |\n| `getVolume(): number`                                                                              | Get current volume.                                 |\n| `getIsPlaying(): boolean`                                                                          | Check if playing.                                   |\n| `getIsMuted(): boolean`                                                                            | Check if muted.                                     |\n| `getPlaybackRate(): number`                                                                        | Get current playback rate.                          |\n| `getLoopMode(): LoopMode`                                                                          | Get current loop mode.                              |\n| `on<K extends keyof PlayerEventMap>(event: K, callback: (data: PlayerEventMap[K]) => void): void`  | Subscribe to event.                                 |\n| `off<K extends keyof PlayerEventMap>(event: K, callback: (data: PlayerEventMap[K]) => void): void` | Unsubscribe from event.                             |\n| `destroy(): void`                                                                                  | Clean up resources.                                 |\n\n\n### Events (`PlayerEventMap`)\n\n| Event                | Payload                                                                                                            | Description                                     |\n|----------------------|--------------------------------------------------------------------------------------------------------------------|-------------------------------------------------|\n| `play`               | `void`                                                                                                             | Playback started.                               |\n| `pause`              | `void`                                                                                                             | Playback paused.                                |\n| `ended`              | `void`                                                                                                             | Video ended.                                    |\n| `timeupdate`         | `{ currentTime: number, duration: number, progress: number, formattedCurrent: string, formattedDuration: string }` | Time update.                                    |\n| `volumechange`       | `{ volume: number, muted: boolean }`                                                                               | Volume or mute changed.                         |\n| `playbackratechange` | `{ playbackRate: number }`                                                                                         | Speed changed.                                  |\n| `fullscreenchange`   | `boolean`                                                                                                          | Fullscreen state changed (`true` = fullscreen). |\n| `loadedmetadata`     | `void`                                                                                                             | Metadata loaded.                                |\n| `error`              | `Error`                                                                                                            | An error occurred.                              |\n| `sourcechanged`      | `number`                                                                                                           | Source index changed.                           |\n| `loopmodechanged`    | `LoopMode`                                                                                                         | Loop mode changed.                              |\n| `mounted`            | `void`                                                                                                             | Player instance mounted and ready.              |\n| `context`            | `ISector`                                                                                                          | Context menu item clicked (sector data).        |\n\n### Context Menu\n\nThe player integrates a circular context menu from [@alekstar79/context-menu](https://github.com/alekstar79/context-menu).\nWhen enabled (contextMenu: true), right‑clicking on the player shows a menu with the following actions (depending on state):\n\n- Play / Pause – central button icon toggles.\n- Playlist – opens the playlist panel.\n- Next / Previous – switch sources.\n- Fullscreen – toggle fullscreen (icon changes).\n- Open – open file dialog.\n- Preview – open preview panel.\n\nYou can customize the menu by passing an IConfig object (see context‑menu documentation). Example:\n\n```ts\nimport { defineConfig } from '@alekstar79/context-menu'\n\ncreatePlayer(container, {\n  contextMenu: defineConfig({\n    sectors: [\n      { icon: 'playlist', hint: 'My Playlist' },\n      { icon: 'next', hint: 'Next', rotate: -90 },\n      // ... your custom sectors\n    ]\n  })\n})\n```\n\n## Thumbnail Generation & Metadata\n\n### Metadata Extraction\n\nWhen a file is loaded via the \"Open file\" button, the player automatically reads metadata from MP4 files using a built‑in parser. It extracts:\n\n- Title (©nam)\n- Artist (©art)\n- Album (©alb)\n- Cover art (covr)\n\nThe extracted title is displayed briefly when the source changes.\n\n### Preview Panel\n\nClick the **Preview** button (or use the context menu item) to open a draggable panel that shows:\n\n- A snapshot of the current video frame.\n- Resolution, timestamp, file size.\n- A suggested filename (based on the video title or source name).\n- Buttons to Generate a new snapshot (at the current time) and Save the snapshot as a JPEG file.\n\n### Programmatic Thumbnails\n\nFor advanced use, you can import the thumbnail generator:\n\n```ts\nimport { getThumbnails } from '@alekstar79/video-player/core/metadata'\n\nconst thumbnails = await getThumbnails(file, {\n  interval: 5,   // seconds between thumbnails\n  start: 0,\n  end: 30,\n  scale: 0.5,    // scale factor\n  quality: 0.8\n})\n```\n\n## Styling and Customization\n\n- **CSS import**: `import '@alekstar79/video-player/lib/styles.css'` (optional).\n- The player uses **Shadow DOM** for its internal components, so its styles are encapsulated.\n- You can still style the outer container (the custom element `<video-player>`) using normal CSS.\n- For deep customisation, consider forking the repository and modifying the SCSS sources.\n\n### Icons\n\nThe player optionally expects an SVG sprite at `/icons.svg` (relative to your deployment root).\nDuring the build, the file is copied from `public/icons.svg`. If you need a different location,\nprovide your own sprite or adjust the `IconHelper` logic.\n\n\n## Examples\n\n### Vanilla JS with dynamic source switching\n\n```html\n<div id=\"player\"></div>\n<button id=\"next\">Next</button>\n\n<script type=\"module\">\n  import { registerComponents, createPlayer } from '@alekstar79/video-player'\n\n  registerComponents()\n\n  const container = document.getElementById('player')\n  const player = await createPlayer(container, {\n    initialSources: ['vid1.mp4', 'vid2.mp4'],\n    loopMode: 'all'\n  })\n\n  document.getElementById('next').onclick = () => player.nextSource()\n</script>\n```\n\n### Vue 3 – accessing player methods via ref\n\n```vue\n<template>\n  <div>\n    <VideoPlayer ref=\"playerRef\" :initialSources=\"sources\" />\n    <button @click=\"toggle\">Toggle Play</button>\n  </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { VideoPlayer } from '@alekstar79/video-player/vue'\n\nconst playerRef = ref()\nconst sources = ref(['video.mp4'])\n\nfunction toggle() {\n  playerRef.value?.togglePlay()\n}\n</script>\n```\n\n### Listening to events\n\n```ts\nplayer.on('timeupdate', (data) => {\n  console.log(`Progress: ${(data.progress * 100).toFixed(1)}%`)\n});\n\nplayer.on('error', (err) => {\n  console.error('Player error:', err)\n})\n```\n\n### Building from Source\n\nClone the repository and install dependencies:\n\n```bash\ngit clone https://github.com/alekstar79/video-player.git\ncd video-player\nnpm install\n```\n\n| Script               | Description                                |\n|----------------------|--------------------------------------------|\n| `npm run dev`        | Start development server.                  |\n| `npm run build`      | Build both library and demo.               |\n| `npm run build:lib`  | Build only the library (output in `lib/`). |\n| `npm run type-check` | Run TypeScript checks.                     |\n| `npm run preview`    | Preview the built demo.                    |\n\n\n## License\nMIT © [alekstar79](https://github.com/alekstar79)\n","readmeFilename":"README.md"}