Jump to content

Quick start for developers

Quick guide for developers contributing to or extending VidPly.

VidPly Logo

Setup

 
# Clone repository
git clone github.com/MatthiasPeltzer/vidply.git
cd vidply

# Install dependencies
npm install

# Build production files
npm run build

# Start dev server
npm run dev
 

The development server runs on localhost


 

Directory structure

 
vidply/
├── build/                       # Build scripts
│   ├── build.js                 # TypeScript bundling (esbuild → ESM + IIFE)
│   ├── build-css.js             # CSS build (clean-css)
│   ├── watch.js                 # Watch mode
│   └── clean.js                 # Clean dist
├── demo/                        # Demo pages
│   ├── demo.html                # Main demo
│   ├── playlist-audio.html      # Audio playlist demo
│   ├── playlist-video.html      # Video playlist demo
│   ├── hls-test.html            # HLS streaming demo
│   ├── dash-test.html           # DASH streaming demo
│   └── single-player-dash.html  # Single DASH player demo
├── dist/                        # Built files (generated)
│   ├── prod/vidply.esm.min.js   # ES Module (production)
│   ├── legacy/vidply.min.js     # IIFE (production)
│   ├── types/index.d.ts         # TypeScript declarations
│   └── vidply.min.css           # Styles (production)
├── docs/                        # Documentation
├── src/                         # Source code (strict TypeScript)
│   ├── core/
│   │   └── Player.ts            # Main Player class
│   ├── controls/
│   │   ├── ControlBar.ts        # Control bar UI (incl. download button, buffering spinner)
│   │   ├── CaptionManager.ts    # Caption handling
│   │   ├── KeyboardManager.ts   # Keyboard shortcuts
│   │   ├── SettingsDialog.ts    # Settings menu
│   │   └── TranscriptManager.ts
│   ├── features/
│   │   └── PlaylistManager.ts   # Playlist support
│   ├── i18n/
│   │   ├── i18n.ts              # i18n system
│   │   ├── translations.ts      # Translation loader
│   │   └── languages/           # Built-in languages
│   │       ├── en.ts
│   │       ├── de.ts
│   │       ├── es.ts
│   │       ├── fr.ts
│   │       └── ja.ts
│   ├── icons/
│   │   └── Icons.ts             # SVG icon definitions
│   ├── renderers/
│   │   ├── HTML5Renderer.ts     # Native HTML5 video/audio
│   │   ├── YouTubeRenderer.ts   # YouTube iframe API
│   │   ├── VimeoRenderer.ts     # Vimeo Player API
│   │   ├── SoundCloudRenderer.ts# SoundCloud Widget API
│   │   ├── HLSRenderer.ts       # hls.js integration + native iOS bridge
│   │   └── DASHRenderer.ts      # dash.js integration
│   ├── styles/
│   │   └── vidply.css           # Main stylesheet
│   ├── types/                   # Shared TypeScript types
│   │   ├── options.ts           # PlayerOptions
│   │   └── globals.d.ts         # Ambient declarations (Hls, dashjs, …)
│   ├── utils/
│   │   ├── DOMUtils.ts          # DOM helpers
│   │   ├── EventEmitter.ts      # Event system
│   │   ├── TimeUtils.ts         # Time formatting
│   │   ├── FocusUtils.ts        # Focus management
│   │   ├── MenuUtils.ts         # Menu helpers
│   │   ├── StorageManager.ts    # localStorage wrapper
│   │   └── ...
│   └── index.ts                 # Main entry point
├── index.html                   # Development page
├── tsconfig.json                # Strict TypeScript config
├── package.json
└── server.js                    # Dev server
 
 

NPM scripts

ScriptDescription
npm run buildBuild everything (TypeScript bundles + types + CSS)
npm run build:jsBundle TypeScript with esbuild (ESM + IIFE)
npm run build:typesType declarations .d.ts Type declarations dist/types/
npm run build:cssBuild CSS only
npm run typechecktsc --noEmit Strict type checking in src/
npm run watchWatch mode (automatic rebuild)
npm run devStart development server
npm run cleanClean up the “dist” directory
npm run startBuild + start development server
npm run testVitest unit tests
npm run test:e2ePlaywright end-to-end tests

 

Build system

TypeScript bundles (esbuild)

build/build.js uses the TypeScript source files directly (esbuild handles the transpilation) and generates:

FileFormatUsage
dev/vidply.esm.jsES moduleDevelopment
prod/vidply.esm.min.jsES moduleProduction
legacy/vidply.jsIIFEDevelopment
legacy/vidply.min.jsIIFEProduction

Features:

  • Code splitting (renderers are loaded on demand)
  • Tree shaking
  • Source maps (development)
  • Minification (production)

Type declarations (tsc)

npm run build:types Runs tsc --emitDeclarationOnly against tsconfig.build.json and outputs:

FileUse
dist/types/index.d.tsPublic type entry, referenced by package.json#types
dist/types/**/*.d.tsModule-related declarations for ‘tree-shakable’ named imports

CSS (clean-css)

build/build-css.js generated:

FileUsage
vidply.cssDevelopment
vidply.min.cssProduction

 

Architecture

Key components

 
Player
├── ControlBar           # UI controls
│   ├── PlayButton
│   ├── ProgressBar
│   ├── VolumeControl
│   ├── DownloadButton    # opt-in via downloadButton/downloadUrl
│   ├── PipButton         # native PiP, or pin/unpin custom floating player when floating: true
│   ├── SettingsButton
│   └── FullscreenButton
├── BufferingOverlay     # Centered loading spinner (.vidply-loading)
├── CaptionManager       # Captions/subtitles
├── KeyboardManager      # Keyboard shortcuts
├── TranscriptManager    # Interactive transcript
├── FloatingPlayerManager # Custom in-page miniplayer ("own PiP"), opt-in via floating: true
├── Renderer             # Media playback
│   ├── HTML5Renderer
│   ├── YouTubeRenderer
│   ├── VimeoRenderer
│   ├── SoundCloudRenderer
│   ├── HLSRenderer       # hls.js + native iOS TextTrack bridge
│   └── DASHRenderer
└── PlaylistManager      # Playlist handling
 

Event flow

 
User Action → KeyboardManager/ControlBar → Player → Renderer → DOM
                                              ↓
                                         EventEmitter → External Listeners
 

Renderer selection

 
// src/core/Player.ts
selectRenderer(src: string): RendererCtor {
  if (isYouTubeUrl(src)) return YouTubeRenderer;
  if (isVimeoUrl(src)) return VimeoRenderer;
  if (src.includes('soundcloud.com')) return SoundCloudRenderer;
  if (isHLSUrl(src)) return HLSRenderer;   // hls.js (or native HLS on iOS/iPadOS)
  if (isDASHUrl(src)) return DASHRenderer;
  return HTML5Renderer;
}
 

The HLS renderer decides for itself whether hls.js (Chrome / Firefox / Edge / Desktop Safari) or the native <video> HLS support (iOS / iPadOS, where MSE is not available). On the native path, it integrates TextTrack into the VidPly user interface for subtitles, transcripts and quality, ensuring that feature parity is maintained.


 

Adding features

New button

  1. Add to control bar (src/controls/ControlBar.ts):
createMyButton(): HTMLButtonElement {
  const button = document.createElement('button');
  button.className = 'vidply-button vidply-my-button';
  button.setAttribute('aria-label', i18n.t('player.myButton'));
  button.innerHTML = Icons.myIcon;
  button.addEventListener('click', () => this.player.myAction());
  return button;
}
  1. Add icon (src/icons/Icons.ts):
export const Icons = {
  // ...existing icons
  myIcon: `<svg>...</svg>`,
} as const;
  1. Add translation (src/i18n/languages/en.ts):
export default {
  player: {
    // ...existing
    myButton: 'My Button',
  }
};
  1. Add to Player API (src/core/Player.ts):
myAction(): void {
  this.emit('myaction');
}
 

New renderer

  1. Create renderer (src/renderers/MyRenderer.ts):
import type { Player } from '../core/Player';

export class MyRenderer {
  constructor(public player: Player) {}

  async init(): Promise<void> { /* ... */ }
  async load(src: string): Promise<void> { /* ... */ }
  play(): void { /* ... */ }
  pause(): void { /* ... */ }
  seek(time: number): void { /* ... */ }
  setVolume(vol: number): void { /* ... */ }
  getCurrentTime(): number { /* ... */ }
  getDuration(): number { /* ... */ }
  destroy(): void { /* ... */ }
}
  1. Register in the player:
import { MyRenderer } from '../renderers/MyRenderer';

// In selectRenderer()
if (isMyServiceUrl(src)) return MyRenderer;
 

New language

  1. Create language file (src/i18n/languages/pt.ts):
export default {
  player: {
    play: 'Reproduzir',
    pause: 'Pausar',
    // ... all translations
  }
};
  1. Register (src/i18n/translations.ts):
import pt from './languages/pt';

export const translations = {
  en, de, es, fr, ja,
  pt,
};
 
 

CSS architecture

Variables

 
:root {
  /* Colors */
  --vidply-primary-color: #3b82f6;
  --vidply-background: rgba(0, 0, 0, 0.8);
  --vidply-text-color: #ffffff;
  
  /* Sizing */
  --vidply-button-size: 40px;
  --vidply-icon-size: 20px;
  
  /* Spacing */
  --vidply-gap-sm: 4px;
  --vidply-gap-md: 8px;
  --vidply-gap-lg: 12px;
}
 

BEM naming convention

 
.vidply-player { }           /* Block */
.vidply-player--fullscreen { } /* Modifier */
.vidply-controls { }         /* Block */
.vidply-controls__left { }   /* Element */
.vidply-button { }           /* Block */
.vidply-button--active { }   /* Modifier */
 

Accessibility

 
/* High contrast mode */
@media (forced-colors: active) {
  .vidply-button {
    border: 1px solid currentColor;
  }
}

/* Focus visible */
.vidply-button:focus-visible {
  outline: 2px solid var(--vidply-primary-color);
  outline-offset: 2px;
}

/* Touch targets */
.vidply-button {
  min-width: 44px;
  min-height: 44px;
}
 
 

Testing

Manual testing

  1. Open localhost by npm run dev
  2. Test demos:
    • demo/demo.html - Full feature set
    • demo/playlist-audio.html - Audio playlists
    • demo/playlist-video.html - Video playlists
    • demo/hls-test.html - HLS streaming
    • demo/dash-test.html - DASH streaming

Accessibility tests

  • Keyboard: Navigation using the keyboard only
  • Screen reader: Testing with NVDA/VoiceOver
  • High contrast: Enable Windows High Contrast Mode
  • Mobile devices: Testing touch interactions

Browser tests

BrowserPriority
ChromeHigh
FirefoxHigh
SafariHigh
EdgeMedium
iOS SafariHigh
Android ChromeMedium

 

Plugin system

EventEmitter

 
// Player extends EventEmitter
player.on('play', () => console.log('Playing'));
player.on('pause', () => console.log('Paused'));
player.on('timeupdate', (time) => console.log(time));

// Custom events
player.emit('mycustomevent', { data: 'value' });
 

Available events

EventDataDescription
ready-Player initialised
play-Playback started
pause-Playback paused
ended-Playback finished
timeupdateTimeCurrent time changed
volumechangeVolumeVolume changed
playbackspeedchangeSpeedSpeed changed
fullscreenchangeFull screenFull screen switched
hlsmanifestparsedDataHLS manifest analysed
dashqualitychangedDataDASH quality changed
textcuesupdate-New text cues available (HLS/DASH)
captionsenabledTrackSubtitles enabled
captionsdisabled-Subtitles disabled
playlisttrackchangeElementTitle changed in playlist
errorErrorAn error has occurred

 

Debugging

Enable debug mode

 
const player = new Player('#video', { debug: true });
 

Console logging

 
if (this.options.debug) {
  console.log('[VidPly]', message, data);
}
 

Common problems

ProblemTroubleshooting steps
Player is not initialisingCheck the data-vidply attribute; check console errors
Renderer is not loadingCheck the format of the source URL; check the network requests
Subtitles are not displayedCheck VTT syntax; check CORS
Keyboard not workingCheck keyboard: true; check focus

 

Documentation files

FileContents
GETTING_STARTED.mdBasic setup
USAGE.mdDetailed API usage
PLAYLIST.mdPlaylist functions
TRANSCRIPT.mdTranscription system
KEYBOARD.mdKeyboard shortcuts
BUILD.mdDetails on the build system
CHANGELOG.mdVersion history
User manual.mdIntegration Guide

 

Quick Reference

Important files

WhatWhere
Entry pointsrc/index.ts
Player classsrc/core/Player.ts
Type of player optionssrc/types/options.ts
Control barsrc/controls/ControlBar.ts
Subtitlessrc/controls/CaptionManager.ts
Keyboardsrc/controls/KeyboardManager.ts
i18nsrc/i18n/i18n.ts
Stylessrc/styles/vidply.css
Iconssrc/icons/Icons.ts
TypeScript configurationtsconfig.json

Build output

FileFormatSize
vidply.esm.min.jsESM~45 KB
vidply.min.jsIIFE~50 KB
vidply.min.cssCSS~15 KB

External dependencies

DependencyPurposeLoaded
hls.jsHLS streaming (Chrome / Firefox / Edge / Desktop Safari)On-demand (CDN)
dash.jsDASH streamingOn-demand (CDN)
YouTube IFrame APIYouTube playbackOn-demand
Vimeo Player APIVimeo playbackOn demand
SoundCloud Widget APISoundCloud playbackOn demand

 

Contribute

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Make changes
  4. Test thoroughly
  5. Create: npm run build
  6. Commit: git commit -m 'Add my feature'
  7. Push: git push origin feature/my-feature
  8. Open pull request

Code style

  • Strict TypeScript (strict: true, noImplicitAny, strictNullChecks)
  • BEM CSS naming convention
  • Meaningful variable names
  • Comment on non-obvious intentions (not what the code does)
  • Add ARIA attributes to every interactive element
  • Execute npm run typecheck and npm run test before committing

Version: 1.1.7 | Licence: GPL-2.0-or-later

 

Share page