drop-player v1.5.6 日本語

drop-player

A comprehensive, universal, open-source React media player powering drop.mov.

React media player for video (HLS / progressive), audio, image, and PDF. One unified component — media mode is inferred from the source URL.

Install

npm i drop-player lucide-react

Required peers: react >=18, react-dom >=18, lucide-react >=0.300

Optional peers (install only when needed):

Package When
hls.js >=1.4 HLS video (.m3u8)
pdfjs-dist >=4.0 PDF canvas rendering (zoom, pan, page navigation)
waveform-data >=4.5 Audio waveform display

Quick start

import { Player } from 'drop-player';
import 'drop-player/styles.css';

function App() {
  return (
    <div style={{ width: 640, height: 360 }}>
      <Player sources="https://example.com/video.mp4" />
    </div>
  );
}

The stylesheet is required — it provides drop-player-prefixed classes and CSS variables.

Components

Export Intent
Player Generic — mode inferred from URL
VideoPlayer Video
AudioPlayer Audio
ImageViewer Image
PdfViewer PDF

VideoPlayer, AudioPlayer etc. are aliases for readability — they all share the same PlayerProps and PlayerRef, and media mode is always auto-detected from the source URL. Use Player when the media type is mixed or unknown.

Supported media

Mode Formats
Video HLS (.m3u8), MP4, WebM
Audio MP3, WAV, Ogg, AAC, FLAC, M4A, WebM, Opus
Image JPEG, PNG, GIF, WebP, AVIF, SVG
PDF Canvas rendering via pdf.js (zoom, pan, page navigation); falls back to browser-native <object> when pdfjs-dist is not installed

Browser-only runtime. Safe to import in SSR frameworks (Next.js, Remix, Astro, etc.) — components render nothing on the server and activate on the client.

Sources

// String — type inferred from extension
<VideoPlayer sources="https://example.com/video.mp4" />

// Object — HLS with original-quality fallback
<VideoPlayer sources={{ url: 'stream.m3u8', originalUrl: 'original.mp4' }} />

// Array — source selector shown
<VideoPlayer sources={[
  { url: 'stream.m3u8', label: 'HLS' },
  { url: 'original.mp4', label: 'Original' },
]} />

// Explicit MIME type — useful when URL has no extension (e.g. CDN, signed URL)
<Player sources={{ url: 'https://cdn.example.com/abc123', mimeType: 'video/mp4' }} />

// null — shows error UI
<VideoPlayer sources={null} />

Type is detected from path extension (.mp4, .m3u8, .pdf, .jpg, …) or mimeType.

Props

Props are organised into four groups to keep the surface area manageable:

<VideoPlayer
  sources="video.mp4"
  className="rounded-lg"
  crossOrigin="anonymous"
  poster="poster.jpg"

  playback={{ autoPlay: true, volume: 0.8 }}
  ui={{ features: { saveCapture: true, copyCapture: true }, locale: 'ja' }}
  slots={{ topRightOverlay: <Badge /> }}
  events={{ onPlay: () => log('playing') }}
/>

Top-level

Prop Type Default Description
sources string | MediaSource | MediaSource[] | null Media source(s)
className string CSS class on root container
crossOrigin 'anonymous' | 'use-credentials' 'anonymous' CORS for media elements
poster string Poster image (video)
storageKey string Custom prefix for localStorage keys (default: drop_player_, custom: <storageKey>_). Muted state is persisted automatically.
storage StorageAdapter Custom storage backend (must implement getItem, setItem, removeItem). Defaults to localStorage (SSR-safe).
hlsConfig Partial<HlsConfig> Custom hls.js config overrides

playbackPlayerPlaybackConfig

Key Type Default Description
autoPlay boolean false Start immediately
loop boolean false Loop playback
muted boolean false Start muted
volume number 1 Initial volume (0–1)
initialTime number 0 Start position in seconds
seekStep number 10 Seconds to skip with arrow left/right keys

uiPlayerUiConfig

Key Type Default Description
showControls boolean true Show bottom control bar
showTitle boolean auto Show source title/selector
showStatusOverlay boolean false Show status pill overlay on player actions (play, seek, volume, etc.)
features PlayerFeatures defaultFeatures Toggle individual controls
locale string 'en' Display language (built-in: 'en', 'ja')
translations Partial<Translations> Custom translation overrides, merged on top of the locale's built-in strings
frameRate number auto / 30 Frame rate for timecode/frames display. Auto-detected from HLS manifest when available; falls back to 30.
timeDisplayFormats TimeDisplayFormat[] defaultTimeDisplayFormats Time display formats to cycle through on click
filmGauge number 16 Frames per foot for 'feet-frames' display (e.g. 16 for 35mm, 40 for 16mm)
bpm number 120 BPM for 'bars-beats' display
timeSignature string '4/4' Time signature for 'bars-beats' display (e.g. '3/4', '6/8')
markers Marker[] [] Seekbar markers — see Marker types

Marker types

Marker is a discriminated union. Each type renders a different shape on the seekbar track:

type Shape Extra fields
'circle' (default) Filled dot
'line' Vertical line
'square' Filled square
'custom' Your ReactNode content: ReactNode (required)

All types share these base fields:

Field Type Default Description
time number Position in seconds
color string CSS var CSS color value
snap boolean false Enable snap-to on seek
snapThreshold number 12 Snap threshold in px (only when snap: true)
import type { Marker } from 'drop-player';

const markers: Marker[] = [
  { time: 10, type: 'circle', snap: true },
  { time: 30, type: 'line' },
  { time: 60, type: 'square', color: 'red' },
  {
    time: 90,
    type: 'custom',
    content: <div style={{ ... }}>Label</div>,
  },
];

<VideoPlayer sources={url} ui={{ markers }} />

slotsPlayerSlots

Slot Type Position
overlay ReactNode Full-area overlay between media and controls
controlsStart ReactNode Left of control bar
controlsEnd ReactNode Right of control bar (before fullscreen)
topLeftOverlay ReactNode Top-left corner
topRightOverlay ReactNode Top-right corner
loadingIndicator ReactNode Centre (while loading)
errorDisplay (error: Error) => ReactNode Centre (on error)

Note: overlay has pointer-events: none by default. Add pointer-events: auto to child elements that need to be interactive.

eventsPlayerEvents

Event Payload When
onStateChange PlayerState Any state change (consolidated snapshot)
onPlay Playback starts
onPause Playback pauses
onEnded Playback ends
onTimeUpdate time Current time changes
onDurationChange duration Duration known/changed
onVolumeChange volume, muted Volume or mute changes
onPlaybackRateChange rate Playback rate changes
onError Error Playback error
onLoadedMetadata VideoMetadata Video metadata loaded
onLoadStart Loading begins
onProgress TimeRanges Buffering progress
onWaiting Waiting for data
onCanPlay Ready to play
onPlaying Actually playing
onSeekStart time Seek begins
onSeeking time During seek
onSeekEnd time Seek ends
onFullscreenChange boolean Fullscreen toggled
onFrameCapture FrameCapture Frame captured
onActiveSourceChange index Active source changed
onQualityLevelChange QualityLevel Quality level changed
onFallback FallbackEvent Fell back to original URL
onTimeDisplayFormatChange TimeDisplayFormat Time display format changed

Features

ui.features toggles individual controls. Omitted keys inherit from defaultFeatures.

Two presets are exported for convenience:

Export Description
defaultFeatures Most controls on, opt-in features off
noFeatures All controls off — use as a base for minimal builds
import { noFeatures } from 'drop-player';

// Default — most controls on
<VideoPlayer sources={url} />

// Add capture buttons to defaults
<VideoPlayer sources={url} ui={{ features: { saveCapture: true, copyCapture: true } }} />

// Remove loop from defaults
<VideoPlayer sources={url} ui={{ features: { loop: false } }} />

// Build from scratch
<VideoPlayer sources={url} ui={{ features: { ...noFeatures, playButton: true, fullscreen: true } }} />
Flag Default Applies to
playButton true video, audio
loop true video, audio
timeDisplay true video, audio
seekBar true video, audio
volume true video, audio
saveCapture false video, image
copyCapture false video, image
qualitySelector true video (HLS)
fullscreen true all
zoom true image, PDF (requires pdfjs-dist)
sourceNavigation true all (when multiple sources)
playbackSpeed true video, audio
pip true video (browser PiP API required)
keyboardShortcuts true video, audio

Time display formats

ui.timeDisplayFormats controls which time formats the user can cycle through by clicking the time display. Two presets are exported:

Export Formats
defaultTimeDisplayFormats ['elapsed-total', 'remaining']
allTimeDisplayFormats ['elapsed-total', 'remaining', 'timecode', 'frames', 'seconds-frames', 'feet-frames', 'bars-beats']
import { defaultTimeDisplayFormats, allTimeDisplayFormats } from 'drop-player';

// Default — elapsed / total and remaining
<VideoPlayer sources={url} />

// Add timecode and frames
<VideoPlayer sources={url} ui={{ timeDisplayFormats: allTimeDisplayFormats }} />

// Film workflow — add feet+frames (35mm)
<VideoPlayer sources={url} ui={{
  timeDisplayFormats: [...defaultTimeDisplayFormats, 'timecode', 'feet-frames'],
  filmGauge: 16,
}} />

// Music — add bars:beats
<AudioPlayer sources={url} ui={{
  timeDisplayFormats: [...defaultTimeDisplayFormats, 'bars-beats'],
  bpm: 92,
  timeSignature: '4/4',
}} />

// Lock to single format (no cycling)
<VideoPlayer sources={url} ui={{ timeDisplayFormats: ['timecode'] }} />
Format Display Extra props
'elapsed-total' 1:23 / 5:00
'remaining' -3:37
'timecode' 00:01:23:15 frameRate
'frames' 2475 / 9000 frameRate
'seconds-frames' 83+15 / 300+00 frameRate
'feet-frames' 154+11 / 562+08 frameRate, filmGauge
'bars-beats' 47:4 / 192:1 bpm, timeSignature

Frame rate is auto-detected from HLS manifests when available. For progressive sources, set ui.frameRate explicitly.

Theming

Override CSS variables on .drop-player:

@import 'drop-player/styles.css';

.drop-player {
  --drop-player-blue: oklch(70.7% 0.165 254.624);
  --drop-player-yellow: oklch(85.2% 0.199 91.936);
  --drop-player-green: oklch(79.2% 0.209 151.711);
  --drop-player-red: oklch(70.4% 0.191 22.216);
  --drop-player-muted: oklch(55.2% 0.016 285.938);
  --drop-player-marker-circle: var(--drop-player-yellow);
  --drop-player-border-radius: 8px;
}
Variable Default Description
--drop-player-blue oklch(70.7% 0.165 254.624) Accent color (active states, focus ring)
--drop-player-yellow oklch(85.2% 0.199 91.936) Highlight color
--drop-player-green oklch(79.2% 0.209 151.711) Positive color (check marks, best quality)
--drop-player-red oklch(70.4% 0.191 22.216) Negative color (errors)
--drop-player-muted oklch(55.2% 0.016 285.938) Muted / inactive color
--drop-player-marker-circle var(--drop-player-yellow) Marker color (circle, line, square)
--drop-player-border-radius 0 Container border radius
--drop-player-aspect-ratio varies Aspect ratio (16/9 video, 32/9 audio, 4/3 image, 1/1.414 PDF)

Ref API

Access imperative methods via PlayerRef:

const ref = useRef<PlayerRef>(null);

<VideoPlayer ref={ref} sources={url} />

// Later:
ref.current?.play();
ref.current?.seek(30);
Method Returns Notes
play() Promise<void>
pause() void
toggle() void
seek(time) void
seekRelative(delta) void
seekToFrame(frame) void Video only
getCurrentTime() number
getDuration() number
getVolume() number
isMuted() boolean
isPaused() boolean
isFullscreen() boolean
getPlaybackRate() number
setVolume(n) void
setMuted(bool) void
setPlaybackRate(rate) void
getTimeDisplayFormat() TimeDisplayFormat
setTimeDisplayFormat(format) void
captureFrame(opts?) Promise<FrameCapture> Video/image; throws for audio/PDF
requestFullscreen() Promise<void>
exitFullscreen() Promise<void>
toggleFullscreen() void
getVideoElement() HTMLVideoElement | null
getContainerElement() HTMLDivElement | null

Error handling

events.onError and slots.errorDisplay receive an Error with a typed name:

error.name Meaning
errorNoSources sources is null or empty
errorAborted Playback aborted
errorNetwork Network error
errorDecode Decode failure
errorNotSupported Format not supported
errorUnknown Unknown error

Built-in UI uses ui.locale to localise these. Override with slots.errorDisplay for custom rendering.

Translations

Built-in locales: 'en' and 'ja'. Set via ui.locale.

To add a new locale or override specific strings, use ui.translations:

<VideoPlayer
  sources="video.mp4"
  ui={{
    locale: 'en',
    translations: {
      play: 'Reproducir',
      pause: 'Pausar',
      mute: 'Silenciar',
      unmute: 'Activar sonido',
      fullscreen: 'Pantalla completa',
      exitFullscreen: 'Salir de pantalla completa',
    },
  }}
/>

Translations are merged on top of the base locale — you only need to provide the keys you want to change.

Custom storage

Player preferences (muted state) are persisted to localStorage by default. Use storageKey to namespace keys or storage to replace the backend entirely.

// Custom namespace
<VideoPlayer sources="video.mp4" storageKey="my_app" />
// Keys stored as: my_app_muted, my_app_volume, etc.

// Custom backend (e.g. Supabase)
const supabaseStorage = {
  getItem: (key: string) => {
    // Read from Supabase cache or return null
    return localStorage.getItem(key);
  },
  setItem: (key: string, value: string) => {
    localStorage.setItem(key, value);
    supabase.from('preferences').upsert({ key, value });
  },
  removeItem: (key: string) => {
    localStorage.removeItem(key);
    supabase.from('preferences').delete().eq('key', key);
  },
};

<VideoPlayer sources="video.mp4" storage={supabaseStorage} />

The StorageAdapter interface requires three methods: getItem, setItem, and removeItem. Operations should be synchronous (return values immediately); use a local cache with async sync for remote backends.

Utilities

Helper functions exported for use outside the player (custom UI, playlists, timecode overlays, etc.):

// From the main entry (includes React)
import {
  formatTime, formatTimecode, secondsToFrames, parseFrameRate,
  formatFeetFrames, formatSecondsFrames, formatBarsBeats,
} from 'drop-player';

// From the lightweight sub-path (no React dependency — safe for Server Components)
import {
  formatTime, formatTimecode, secondsToFrames, parseFrameRate,
  formatFeetFrames, formatSecondsFrames, formatBarsBeats,
} from 'drop-player/utils';

formatTime(125);                    // "02:05"
formatTime(3661);                   // "01:01:01"
formatTimecode(125.5, 30);          // "00:02:05:15"
formatTimecode(125.5, '30000/1001');// "00:02:05:14" (29.97fps)
secondsToFrames(10, 24);           // 240
parseFrameRate('30000/1001');       // 29.97...
formatSecondsFrames(83.5, 30);     // "83+15"
formatFeetFrames(10, 24, 16);      // "15+00"
formatBarsBeats(10, 120, '4/4');   // "5:1"
Function Signature Description
formatTime (seconds?) => string Format as MM:SS or HH:MM:SS
formatTimecode (seconds?, frameRate?) => string Format as SMPTE timecode HH:MM:SS:FF
secondsToFrames (seconds?, frameRate?) => number Convert seconds to frame number
parseFrameRate (frameRate?) => number Parse frame rate string (e.g. "30000/1001") to number
formatSecondsFrames (seconds?, frameRate?) => string Format as seconds+frames S+FF
formatFeetFrames (seconds?, frameRate?, filmGauge?) => string Format as feet+frames FFF+FF
formatBarsBeats (seconds?, bpm?, timeSignature?) => string Format as bars:beats B:b

License

MIT

Changelog

1.5.5

Features

  • Add drop-player/utils sub-path export — pure formatter functions (formatTime, formatTimecode, secondsToFrames, parseFrameRate, formatSecondsFrames, formatFeetFrames, formatBarsBeats) importable without React, safe for Server Components

1.5.4

Features

  • Add overlay slot — full-area overlay layer between media and controls (pointer-events: none by default)

1.5.2

Bug Fixes

  • Fix SSR safety: guard ResizeObserver usage in useElementWidths hook with typeof check to prevent ReferenceError in server-side rendering environments

1.5.0

Breaking Changes

  • Remove ambient light featureambientLight has been removed from PlayerFeatures, VideoState, VideoCoreRef, and all related UI/CSS. If you were using features={{ ambientLight: true }}, remove it.

1.4.0

Features

  • Add responsive controls bar — controls dynamically collapse into an overflow menu based on available width
  • Add overflow menu (⋯) that groups secondary controls (ambient light, PiP, playback speed, capture) when space is limited
  • Split capture button into separate CopyCaptureButton and SaveCaptureButton controls
  • Add useFeedback hook for transient action feedback (e.g. "Copied!")
  • Add useMediaQuery and useElementWidths hooks for responsive layout

Bug Fixes

  • Fix marker positions not rendering correctly on the seekbar
  • Fix SEO: add prerender script for demo site, update llms.txt / llms-ja.txt

1.3.0

Breaking Changes

  • Marker API redesignedMarker is now a discriminated union. Replace type: 'scene' with type: 'circle'. The old type: 'custom' (no-op) now requires content: ReactNode.
  • slots.seekbarOverlay removed — use type: 'custom' markers instead.
  • CSS variable --drop-player-marker-scene renamed to --drop-player-marker-circle. --drop-player-marker-custom removed.
  • Snap is now opt-in per marker — add snap: true to each marker that should snap. Previously snap was always active when markers were present.

Features

  • New marker types: 'circle' (renamed from 'scene'), 'line', 'square', 'custom'
  • snap?: boolean — enable snap-to per marker (default: false)
  • snapThreshold?: number — per-marker snap distance in px (default: 12, only used when snap: true)
  • content: ReactNode — render arbitrary content on the seekbar track at a specific time (type: 'custom')
  • Named marker types (CircleMarker, LineMarker, SquareMarker, CustomMarker) exported from package root

1.2.5

  • Revert 1.2.4

1.2.4 (deprecated)

  • Fix Webpack/Turbopack build error (Module not found) when optional peer dependencies are not installed — reverted, webpackIgnore prevents bundlers from resolving installed packages at runtime

1.2.3

Bug Fixes

  • Fix controls overlay not recoverable on mobile for Image, PDF, and Audio modes — tapping the content area now toggles controls visibility, matching Video behavior
  • Fix player blocking page scroll on mobile — allow vertical scrolling (pan-y) for all media types; only block scroll when Image/PDF is zoomed in

1.2.2

Bug Fixes

  • Fix controls UI not showing on tap for Image, PDF, and Audio modes

1.2.1

Bug Fixes

  • Fix PDF cleanup on unmount (add destroyWorker call)
  • Improve StatusOverlay accessibility and translation handling
  • Fix re-exported types from package root

1.2.0

Features

  • Add pdf.js integration as optional peer dependency for canvas-based PDF rendering with real zoom, pan, and page navigation
  • Add PageNavigation control (prev/next page with ChevronUp/ChevronDown icons)
  • Add PdfCoreRef imperative handle (zoomIn, zoomOut, resetZoom, nextPage, prevPage, goToPage)
  • Add keyboard shortcuts for PDF mode (ArrowUp/ArrowDown for pages, +/-/0 for zoom)
  • Add swipe gesture for PDF page navigation (disabled when zoomed)
  • Fallback to browser-native <object> rendering when pdfjs-dist is not installed

Improvements

  • PDF pages fit container at 100% zoom (no excess margins)
  • Zoom re-renders via pdf.js for crisp vector text (debounced 150ms with immediate CSS feedback)
  • Page change resets zoom to 1x
  • Controls and title auto-hide after 3 seconds for image and PDF modes (consistent with video/audio)
  • Source change resets readiness state so loading overlay covers stale content
  • Add tabular-nums to zoom level display
  • Controls overlay now has proper z-index to remain visible during loading

1.1.7

Features

  • Add seekStep playback option to configure arrow key skip duration in seconds (default: 10)
  • Add seekStepButtons feature flag for skip forward/backward buttons
  • Add showStatusOverlay UI option — pill-style overlay showing player actions (play/pause, seek, volume, speed, quality, zoom, loop)

1.1.6

Features

  • Add multi-source navigation (prev/next buttons, swipe gesture, keyboard navigation)
  • Add sourceNavigation feature flag to show/hide prev/next buttons
  • Add time display formats: seconds-frames, feet-frames, bars-beats
  • Auto-detect frame rate from HLS level metadata
  • Add CSS color variables for easier theme customization

Improvements

  • Expand touch/click target for seekbar and volume slider thumbs without changing visual appearance
  • Style dropdown scrollbar to match player theme

1.1.5

Improvements

  • Add drag-to-seek on audio waveform (click to seek, drag to scrub)
  • Add accessibility attributes on audio waveform area (role="slider", aria-valuemin/max/now)
  • Improve PDF error handling: pre-validate URL reachability, fire onError with errorNetwork on failure
  • Suppress AbortError from play/pause races

Bug Fixes

  • Fix audio player not showing error state when loading fails

Documentation

  • Add CSS variables reference table, feature presets, Translations, Custom storage, Utilities sections to README

1.1.4

Improvements

  • VideoPlayer, AudioPlayer, ImageViewer, PdfViewer now set media mode directly, skipping source type inference
  • Add default aspect-ratio per media mode (video: 16/9, audio: 32/9, image: 4/3, pdf: 1/1.414), overridable via --drop-player-aspect-ratio
  • Warn when URL has no recognizable extension and mimeType is not provided

Bug Fixes

  • Fix min-width: 100% redundancy on player container

1.1.3

Bug Fixes

  • Fix hydration mismatch caused by PiP support detection running during SSR

1.1.2

Bug Fixes

  • Unify mouse/touch handling with Pointer Events for consistent drag-to-seek and controls toggle
  • Improve HLS fatal error handling: network errors now trigger fallback, media error recovery limited to one attempt
  • Type hlsConfig as Partial<HlsConfig> instead of Record<string, unknown>
  • Add network-error to FallbackReason type
  • Rename legacy @dropmov/player references to drop-player in console warnings

Styles

  • Responsive timestamp position in controls bar

1.1.1

Features

  • Add storage prop for custom StorageAdapter (replaces default localStorage)
  • Add translations prop for custom i18n string overrides
  • Widen locale type from 'en' | 'ja' to string for custom locales
  • Stabilize React hooks to avoid unnecessary re-renders with object props

Styles

  • Fix ambient light mode rendering
  • Reposition timestamp display

1.1.0

Features

  • Add storageKey prop to customize localStorage key prefix (default: drop_player_, custom: <storageKey>_)
  • Persist and restore muted state across sessions via localStorage

Breaking Changes

  • Remove persistenceKey prop and playback position persistence feature
  • Remove onPositionSave / onPositionRestore event callbacks

1.0.7

Bug Fixes

  • Fix tooltip not dismissing on Safari and touch devices
  • Fix multiple tooltips displaying simultaneously
  • Auto-dismiss tooltips after timeout on touch interactions
  • Fix muted state behavior

1.0.6

Features

  • Add playback speed selector control
  • Add Picture-in-Picture (PiP) button control

Bug Fixes

  • Fix tooltip position not recalculating when content changes

1.0.5

Features

  • Support hlsConfig prop for custom hls.js configuration
  • Tune ABR settings for smoother adaptive bitrate switching

Bug Fixes

  • Dynamically import waveform-data to reduce initial bundle size
  • Fix tooltip position

1.0.4

Maintenance

  • Disable sourcemap output and enable minification in build
  • Remove unnecessary documentation files

1.0.3

Breaking Changes

  • Remove Tailwind CSS dependency from library output; all styles are now plain CSS with drop-player-* BEM classes
  • Consumers no longer need Tailwind — just import drop-player/styles.css

Bug Fixes

  • Fix controls and seekbar not rendering (Tailwind utility classes missing from build output)

1.0.2

Bug Fixes

  • Fix player and controls not spanning full width in flex/aspect-ratio layouts

1.0.1

Maintenance

  • Update dependency packages

1.0.0

Initial Release

  • Video and audio player component with custom controls
  • Keyboard shortcuts support
  • Marker/chapter support (scene markers, custom markers)
  • Playback speed control
  • Volume control with mute toggle
  • Fullscreen support
  • Ambient light effect
  • Multiple source selector
  • Customizable theming via CSS variables
  • Error handling with retry