@fritill/nuxt-video-player
v1.0.3
Published
Nuxt video player module with HLS and DASH streaming support
Readme
Video Player Module
Lightweight Nuxt module that registers DX video player components, composables, and i18n locales. Use DXVideoPlayer as the main entry point and consume the exposed refs (videoElement, controls, streaming, keyMomentsState) via defineExpose when needed.
Usage (quick)
- Install the module and add to
nuxt.configundermodules(config key:videoPlayer). - Pass a
source{ type: 'hls' | 'dash' | 'file', src, drm? }toDXVideoPlayerplus optionalposter,keyMoments,autoplay,muted. - Listen to emits:
ready,play,pause,ended,timeupdate(time),error(err). - App config overrides: set
appConfig.videoPlayer.uito merge custom class names fromtypes/ui.ts.
Architecture at a glance
- Components:
runtime/components/DXVideoPlayer*is the primary UI; helper controls and key-moment panel live in the same folder. LegacyKeyMoments.vue/ProgressBar.vueare pending consolidation. - Composables: controls (
useVideoControls), streaming adapters (useVideoStreaming), UI config (useVideoPlayerUI), key moments, progress bar, frame preview, and context helpers. - Types & i18n: shared contracts in
runtime/types, locales inruntime/i18n/locale(en/ar).
Refactoring Plan (Clean, DRY, Expandable)
Phase 1: Core Architecture & State Management
Goal: Establish single source of truth for state and eliminate duplication
1.1 State Unification ✅
- [x] Decision: Keep
useVideoControlsas primary state (it's closer to video element), removeuseVideoPlayer - [x] Move analytics/stats tracking from
useVideoPlayerinto separatecomposables/analytics/useVideoAnalytics.ts - [x] Move playlist/source management from
useVideoPlayerintocomposables/core/useVideoPlaylist.ts - [x] Delete
useVideoPlayer.tsonce its features are extracted
1.2 Context System ✅
- [x] Fix context mismatch:
DXVideoPlayer.vueuses string'video-controls'butuseVideoControlsContext.tsuses Symbol - [x] Standardize on Symbol-based keys in
composables/core/context/keys.ts:VIDEO_CONTROLS_KEYVIDEO_STREAMING_KEYVIDEO_UI_KEYVIDEO_KEY_MOMENTS_KEY
- [x] Create typed provider composables:
useProvideVideoControls(controls)useProvideVideoStreaming(streaming)useProvideVideoUI(ui)useProvideKeyMoments(keyMoments)
- [x] Update
DXVideoPlayer.vueto use new provider helpers - [x] Ensure all child components use
useVideoControlsContext()instead of prop drilling
Phase 2: Shared Utilities & DRY
Goal: Extract reusable helpers and eliminate code duplication
2.1 Core Utilities (composables/core/utils/) ✅
- [x] Create
timeFormatting.ts:formatTime(seconds: number): string(HH:MM:SS or MM:SS)parseTimeToSeconds(timeString: string): numberformatDuration(seconds: number): { hours, minutes, seconds }
- [x] Create
fullscreenHelpers.ts:requestFullscreen(element: HTMLElement): Promise<void>exitFullscreen(): Promise<void>getFullscreenElement(): Element | nullisFullscreen(element: HTMLElement): boolean- Handle webkit prefixes centrally
- [x] Create
keyboardShortcuts.ts:type KeyboardShortcut = { code: string; handler: () => void; preventDefault?: boolean }createKeyboardHandler(shortcuts: KeyboardShortcut[]): (e: KeyboardEvent) => void- Define default video shortcuts map
2.2 Remove Duplications ✅
- [x] Replace all inline
formatTimewith imported utility - [x] Replace fullscreen logic in
useVideoControlswith imported helpers - [x] Consolidate keyboard handling using new keyboard shortcuts system
Phase 3: Component Architecture
Goal: Clean component hierarchy and remove legacy components
3.1 Key Moments Consolidation ✅
- [x] Decide on component structure:
- Keep:
DXVideoPlayer/KeyMomentsPanel.vue(main panel) - Keep:
DXVideoPlayer/KeyMoments/Item.vue(moment item) - Keep:
DXVideoPlayer/KeyMoments/Header.vue(panel header) - Delete:
KeyMoments.vue(legacy, replace usage with KeyMomentsPanel)
- Keep:
- [x] Complete
Control/KeyMomentsToggle.vueimplementation - [x] Ensure
useKeyMomentscomposable is consumed via context - [x] Share key moments state between progress bar hover and panel
3.2 Progress Bar Enhancement ✅
- [x] Ensure
ProgressBar.vueusesuseVideoControlsContext()for state (nowProgress/Bar.vue) - [x] Extract progress calculation logic to
composables/core/progress/useProgressCalculations.ts(integrated inuseProgressBar) - [x] Use shared time formatting utilities
- [x] Document hover tooltip frame preview integration (separate
HoverTooltip.vue)
3.3 Control Buttons Modernization ✅
- [x] Refactor
ControlButtons.vueto use context instead of props where possible - [x] Extract settings menu into separate component:
- Create
DXVideoPlayer/Control/SettingsMenu.vue- Combined speed & quality menu - Use current design/implementation but as standalone component
- Menu should include both speed and quality sections
- Import and use in
ControlButtons.vueor make it a sibling component
- Create
- [x] Create/verify individual control components in
DXVideoPlayer/Control/:PlayPause.vue(using Toggle.vue)Volume.vue✓Time.vue✓Fullscreen.vue(using Toggle.vue)KeyMomentsToggle.vue✓
- [x] Route all styling through
useVideoPlayerUIconfig - [x] Add slot support for custom controls on the right side of control bar
- Define
#controls-rightslot inControlButtons.vuewith typed scoped props - Provides access to controls, streaming, and keyMoments contexts
- Define
3.4 On-Video Interactive Controls
- [ ] Create overlay control components in
DXVideoPlayer/Overlay/:CenterPlayButton.vue- Large play/pause button in center of videoSeekButtons.vue- Forward/backward 5sec buttons overlaid on video
- [ ] Implement double-tap zones:
- Left side: seek backward 5 seconds
- Right side: seek forward 5 seconds
- Center: toggle play/pause
- [ ] Add touch-friendly hit areas for mobile
- [ ] Animate seek feedback (show "+5s" or "-5s" indicator)
- [ ] Make overlay controls configurable via props
3.5 Playlist Navigation UI
- [ ] Create
DXVideoPlayer/Playlist/components:NextVideoPopup.vue- Popup showing next video previewPreviousVideoPopup.vue- Popup showing previous video previewPlaylistControls.vue- Next/Previous buttons for control bar
- [ ] Popup should display:
- Video thumbnail
- Video title/name
- Video duration
- Auto-advance countdown (if enabled)
- [ ] Add playlist navigation to control bar (when playlist is active)
- [ ] Connect to
useVideoPlaylistcomposable from Phase 1 - [ ] Add keyboard shortcuts: 'N' for next, 'P' for previous
Phase 4: Streaming & Source Management
Goal: Clean separation of concerns for video initialization
4.1 Streaming Adapters
- [ ] Create
composables/streaming/adapters/:hlsAdapter.ts- HLS initialization, quality selection, DRMdashAdapter.ts- DASH initialization, quality selection, DRMfileAdapter.ts- Native HTML5 video playback
- [ ] Define typed DRM config interfaces in
types/player.ts:DRMConfig { licenseUrl, headers, certificateUrl? }HLSDRMConfig extends DRMConfigDASHDRMConfig extends DRMConfig
- [ ] Refactor
useVideoStreaming.ts:- Use adapters for init logic
- Enforce cleanup → init pattern on source change
- Return consistent quality/error interfaces
4.2 Initialization Flow
- [ ] Extract video initialization from
DXVideoPlayer.vueintocomposables/core/useVideoInitializer.ts - [ ] Handle autoplay/mute/volume settings in one place
- [ ] Centralize error handling and emit patterns
- [ ] Support source change watchers cleanly
Phase 5: UI System & Theming
Goal: Consistent, configurable styling system
5.1 UI Configuration
- [ ] Audit all components for hardcoded classes (e.g.,
"absolute inset-0 flex...") - [ ] Move all class strings to
useVideoPlayerUIconfig structure - [ ] Define UI config schema in
types/ui.ts:interface VideoPlayerUIConfig { root: { base, video, overlay } controls: { container, button, progressBar } keyMoments: { panel, item, marker } playlist: { popup, thumbnail, controls } overlay: { centerPlay, seekButtons } // ... etc } - [ ] Document class override examples in README
5.3 Control Visibility & Feature Toggles
- [ ] Add comprehensive props to
DXVideoPlayerfor control visibility:interface ControlsConfig { showPlayPause?: boolean showVolume?: boolean showSpeed?: boolean showQuality?: boolean showFullscreen?: boolean showKeyMoments?: boolean showProgressBar?: boolean showTime?: boolean showPlaylist?: boolean // On-video controls showCenterPlayButton?: boolean showSeekButtons?: boolean seekButtonSeconds?: number // default: 5 } - [ ] Add props to disable specific functions (not just hide):
interface DisabledFeatures { disableSeek?: boolean disableVolumeChange?: boolean disableSpeedChange?: boolean disableQualityChange?: boolean disableFullscreen?: boolean disableKeyboardShortcuts?: boolean } - [ ] Merge both into single
controlsprop:<DXVideoPlayer :source="source" :controls="{ showSpeed: false, showQuality: false, disableSeek: true, seekButtonSeconds: 10 }" /> - [ ] Apply visibility/disabled logic in all control components
- [ ] Document all available control options in README
5.2 Dark/Light Mode Support
- [ ] Add theme variants to UI config
- [ ] Allow
appConfig.videoPlayer.ui.themeoverride - [ ] Test with different Tailwind themes
Phase 6: Internationalization
Goal: Full i18n support for all user-facing strings
6.1 String Extraction
- [ ] Extract all hardcoded strings:
- Control tooltips: "Play", "Pause", "Mute", "Fullscreen"
- Key moments: "Key Moments", "Preview"
- Settings: "Speed", "Quality", "Auto"
- Status: "Buffering...", "Loading..."
- [ ] Add to
runtime/i18n/locale/en.tsandar.ts - [ ] Use
$t()or composable in all components
6.2 Accessibility
- [ ] Add ARIA labels to all interactive controls
- [ ] Add
roleattributes to custom controls - [ ] Ensure keyboard navigation works for all controls
- [ ] Add screen reader announcements for state changes
Phase 7: TypeScript & Type Safety
Goal: Comprehensive type coverage and IntelliSense
7.1 Type Definitions
- [ ] Ensure all composables export return types:
UseVideoControlsReturnUseVideoStreamingReturnUseKeyMomentsReturnUseVideoPlayerUIReturn
- [ ] Add JSDoc comments to all public APIs
- [ ] Define strict prop types for all components
- [ ] Add generic types for extensibility (e.g., custom quality types)
7.2 Type Validation
- [ ] Add runtime validation for source prop (Zod or similar)
- [ ] Validate DRM configs at initialization
- [ ] Provide helpful TypeScript errors for misuse
Phase 8: Testing & Documentation
Goal: Maintainable, well-tested codebase
8.1 Unit Tests
- [ ] Test utilities:
- Time formatting edge cases
- Progress calculations (0%, 100%, NaN handling)
- Keyboard shortcut handlers
- [ ] Test composables:
- State management in
useVideoControls - Frame preview throttling in
useFramePreview - Key moments filtering and seeking
- State management in
- [ ] Test streaming adapters with mocks
8.2 Component Tests
- [ ] Test
DXVideoPlayerinitialization flows - [ ] Test control interactions (play/pause, seek, volume)
- [ ] Test keyboard shortcuts
- [ ] Test fullscreen transitions
8.3 Stories/Examples
- [ ] Create Storybook stories or playground:
- HLS video with DRM
- DASH video with quality switching
- File video with key moments
- Playlist example
- [ ] Add code examples to README:
- Basic usage
- With key moments
- Custom UI config
- Playlist mode
Phase 9: Performance & Optimization
Goal: Smooth, efficient playback experience
9.1 Optimization
- [ ] Audit reactive dependencies (avoid unnecessary re-renders)
- [ ] Throttle/debounce expensive operations:
- Progress tooltip updates
- Frame preview generation
- Buffer progress calculations
- [ ] Lazy load streaming libraries (HLS.js, DASH.js) only when needed
- [ ] Use
shallowReffor DOM refs to avoid deep reactivity
9.2 Bundle Size
- [ ] Code-split streaming adapters
- [ ] Tree-shake unused i18n locales
- [ ] Analyze bundle with Nuxt analyze
Phase 10: Extensibility & Plugin System
Goal: Easy to extend without modifying core
10.1 Plugin Architecture
- [ ] Define plugin interface:
interface VideoPlayerPlugin { name: string setup(player: VideoPlayerContext): void destroy?(): void } - [ ] Add plugin registration in module config
- [ ] Example plugins:
- Analytics plugin (Google Analytics, Plausible)
- Chromecast support
- Picture-in-Picture
- Custom overlays (ads, watermarks)
10.2 Custom Controls & Slots
- [ ] Implement comprehensive slot system:
#controls-left- Custom controls on left side of control bar#controls-right- Custom controls on right side of control bar (HIGH PRIORITY)#controls-center- Custom controls in center#overlay- Custom overlay content (above video)#playlist-popup- Custom playlist navigation popup#loading- Custom loading/buffering indicator
- [ ] Provide scoped slot props with player state:
<template #controls-right="{ controls, streaming, keyMoments }"> <MyCustomButton @click="controls.skip(30)" /> </template> - [ ] Document how to add custom control buttons with examples
- [ ] Allow control layout overrides via UI config
Implementation Priority
High Priority (Do First):
- Phase 1: State & Context unification
- Phase 2: DRY utilities
- Phase 3: Component cleanup
- Priority within Phase 3:
- 3.1 & 3.2: Foundation (Key Moments, Progress Bar)
- 3.4: On-Video Controls (center play, seek buttons) - HIGH PRIORITY
- 3.5: Playlist Navigation UI - HIGH PRIORITY
- 3.3: Control modernization & slots
- Priority within Phase 3:
- Phase 4: Streaming refactor
Medium Priority (Do Next): 5. Phase 5: UI system
- 5.3: Control Visibility Props - MEDIUM-HIGH PRIORITY
- 5.1 & 5.2: Theming
- Phase 6: i18n & a11y
- Phase 7: TypeScript improvements
Low Priority (Nice to Have): 8. Phase 8: Testing & docs 9. Phase 9: Performance 10. Phase 10: Extensibility (already partially addressed in Phase 3 with slots)
Success Criteria
Core Architecture
✅ Single source of truth for state (no duplication between composables)
✅ All components use typed context (no prop drilling)
✅ All utilities are reusable and in core/
✅ Clean component hierarchy (legacy components removed)
✅ Streaming adapters are modular and tested
User Experience
✅ On-video controls (center play button, seek +5/-5 buttons)
✅ Playlist navigation with preview popups
✅ Fully configurable control visibility
✅ Custom control slots (especially #controls-right)
✅ Combined settings menu component (speed + quality in one menu, extracted from ControlButtons)
Developer Experience
✅ Zero hardcoded strings (full i18n support) ✅ Zero hardcoded classes (all via UI config) ✅ TypeScript errors guide correct usage ✅ Comprehensive docs with copy-paste examples ✅ Ready for plugins and custom extensions ✅ Props to enable/disable any control or feature
