{"_id":"@classytic/react-media","_rev":"25-c3e96fe612e0e19a4111ed7df74d7092","name":"@classytic/react-media","dist-tags":{"latest":"0.3.2"},"versions":{"0.1.0":{"name":"@classytic/react-media","version":"0.1.0","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","_id":"@classytic/react-media@0.1.0","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"}],"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","bugs":{"url":"https://github.com/classytic/rtc/issues"},"dist":{"shasum":"37b0f317dc3ea3fe9a0f65859d5f5d76724c5fec","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.1.0.tgz","fileCount":21,"integrity":"sha512-eLPNJi70ucDdZpGAYJ/qI5tA0plCxZi9yIDUydknEX7y1DVsHHJSqq9nLxOEHTIXpMd3GaPoZDpXrLZ8z0zs3A==","signatures":[{"sig":"MEUCIAWbLrG4SggYAXjJ/PLT8N/OGog2zGlHKOomCaoeZi6RAiEAjL6GOZd1WPkWU4Ll5ykg9KWwWYZoKmrfDnsyj7gs7aI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":678882},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./styles.css":"./styles.css","./providers/s3":{"types":"./dist/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./providers/cloudflare":{"types":"./dist/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"}},"gitHead":"dd13c3bdb01b52eee32613cfa09241860efc75cf","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"siam923","email":"classytic.dev@gmail.com"},"repository":{"url":"git+https://github.com/classytic/rtc.git","type":"git","directory":"packages/media"},"_npmVersion":"10.9.3","description":"Production-ready media library for React. Video/audio playback, recording, upload, HLS streaming, captions.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","dependencies":{"zod":"^4.3.5","hls.js":"^1.6.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","react":"^19.0.0","vitest":"^4.0.0","happy-dom":"^20.0.0","react-dom":"^19.0.0","@vitest/ui":"^4.0.0","typescript":"^5.3.0","@types/react":"^19.0.0","@vitest/coverage-v8":"^4.0.0","@vitejs/plugin-react":"^4.2.0","@testing-library/react":"^16.0.0","@testing-library/jest-dom":"^6.1.5"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0"},"optionalDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/react-media_0.1.0_1770049919990_0.7898085427963768","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@classytic/react-media","version":"0.1.1","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","_id":"@classytic/react-media@0.1.1","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"}],"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","bugs":{"url":"https://github.com/classytic/rtc/issues"},"dist":{"shasum":"91808b41b85f847d2dcc2fbbe3f64879f2b941f4","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.1.1.tgz","fileCount":21,"integrity":"sha512-CRoSRywMg/Xjum/S+q0TwRRs/g40/SKg95F9ezlnIR3vsGl9XkGC3qyH250rfbHfJLl3oIXbIwCWFqKSWDzWrw==","signatures":[{"sig":"MEUCIQCvQTqEAd+TMnpHDZ+iL5VbuOH/BiQMmTcEIV9IV98oogIgRSQo82hMN2EGZgBjXbNxWRW3zwyJSe+tq7OzB11xs6A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":679837},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./styles.css":"./styles.css","./providers/s3":{"types":"./dist/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./providers/cloudflare":{"types":"./dist/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"}},"gitHead":"dd13c3bdb01b52eee32613cfa09241860efc75cf","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"siam923","email":"classytic.dev@gmail.com"},"repository":{"url":"git+https://github.com/classytic/rtc.git","type":"git","directory":"packages/media"},"_npmVersion":"10.9.3","description":"Production-ready media library for React. Video/audio playback, recording, upload, HLS streaming, captions.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","dependencies":{"zod":"^4.3.5","hls.js":"^1.6.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","react":"^19.0.0","vitest":"^4.0.0","happy-dom":"^20.0.0","react-dom":"^19.0.0","@vitest/ui":"^4.0.0","typescript":"^5.3.0","@types/react":"^19.0.0","@vitest/coverage-v8":"^4.0.0","@vitejs/plugin-react":"^4.2.0","@testing-library/react":"^16.0.0","@testing-library/jest-dom":"^6.1.5"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0"},"optionalDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/react-media_0.1.1_1770269360493_0.09670357580584432","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@classytic/react-media","version":"0.1.2","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","_id":"@classytic/react-media@0.1.2","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"}],"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","bugs":{"url":"https://github.com/classytic/rtc/issues"},"dist":{"shasum":"563cd144701f7bd9a95556265819e3e7813169e3","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.1.2.tgz","fileCount":22,"integrity":"sha512-dhWBWpB5xxUFKntzo/uNdYGfXY1u+qZe7uG32iLmr7KxLT1tkGVveWaT9pMFN8AtC0lqPdmsbnVHFtDjszXijw==","signatures":[{"sig":"MEYCIQCEGl/IWPbWvr5N4QwrYwEu2iSGNlVZtFbb76hsxA408wIhAOFtUZtnj64WJxlc27lFP6R3fQk14rW4NsVyH349HdYv","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":687794},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./styles.css":"./styles.css","./package.json":"./package.json","./providers/s3":{"types":"./dist/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./providers/cloudflare":{"types":"./dist/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"}},"gitHead":"dd13c3bdb01b52eee32613cfa09241860efc75cf","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"siam923","email":"classytic.dev@gmail.com"},"repository":{"url":"git+https://github.com/classytic/rtc.git","type":"git","directory":"packages/media"},"_npmVersion":"10.9.3","description":"Production-ready media library for React. Video/audio playback, recording, upload, HLS streaming, captions.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","dependencies":{"hls.js":"^1.6.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","react":"^19.0.0","vitest":"^4.0.0","happy-dom":"^20.0.0","react-dom":"^19.0.0","@vitest/ui":"^4.0.0","typescript":"^5.3.0","@types/react":"^19.0.0","@vitest/coverage-v8":"^4.0.0","@vitejs/plugin-react":"^4.2.0","@testing-library/react":"^16.0.0","@testing-library/jest-dom":"^6.1.5"},"peerDependencies":{"zod":"^4.0.0","react":"^19.0.0","react-dom":"^19.0.0"},"optionalDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/react-media_0.1.2_1771492337632_0.3235591192022358","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@classytic/react-media","version":"0.2.0","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","_id":"@classytic/react-media@0.2.0","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"}],"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","bugs":{"url":"https://github.com/classytic/rtc/issues"},"dist":{"shasum":"d18eb29da41fc7f648077d8cf510f17e7b8215f2","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.2.0.tgz","fileCount":305,"integrity":"sha512-MnU9AKiq5vT5XDUwSkErThYmU37oqY8Pin75Q0kor7odNNBLXgg8to1u7jyPNe/H2mg8EFB8ILuQG0/99uHRlw==","signatures":[{"sig":"MEUCIEAPw7+sGV9829/JJ8qQIR2ZJbk6XzOIwOJGnG4oSUYeAiEA6Y+xOChe44Ff1gp6Xm13BNx2juwgLvVLIj55HzmsLVI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1059483},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./video/mse":{"types":"./dist/video/mse/index.d.ts","import":"./dist/video/mse/index.js"},"./styles.css":"./styles.css","./package.json":"./package.json","./providers/s3":{"types":"./dist/core/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./providers/cloudflare":{"types":"./dist/core/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"},"./providers/hls-segment":{"types":"./dist/core/providers/hls-segment.d.ts","import":"./dist/providers/hls-segment.js"}},"gitHead":"dd13c3bdb01b52eee32613cfa09241860efc75cf","scripts":{"dev":"tsdown --watch","test":"vitest run","build":"tsdown && tsc --emitDeclarationOnly --outDir dist","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"siam923","email":"classytic.dev@gmail.com"},"repository":{"url":"git+https://github.com/classytic/rtc.git","type":"git","directory":"packages/media"},"_npmVersion":"10.9.3","description":"Production-ready media library for React. Video/audio playback, recording, upload, HLS streaming, captions.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","hls.js":"^1.6.15","tsdown":"^0.20.3","vitest":"^4.0.0","happy-dom":"^20.0.0","react-dom":"^19.0.0","@vitest/ui":"^4.0.0","typescript":"^5.3.0","@types/react":"^19.0.0","@vitest/coverage-v8":"^4.0.0","@vitejs/plugin-react":"^4.2.0","@testing-library/react":"^16.0.0","@testing-library/jest-dom":"^6.1.5"},"peerDependencies":{"zod":"^4.0.0","react":"^19.0.0","hls.js":"^1.5.0","react-dom":"^19.0.0"},"optionalDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"hls.js":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/react-media_0.2.0_1771894594268_0.6407050907539442","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@classytic/react-media","version":"0.2.1","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","_id":"@classytic/react-media@0.2.1","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"}],"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","bugs":{"url":"https://github.com/classytic/rtc/issues"},"dist":{"shasum":"2f6739d4b4d33e8c9918d9c722b8614ed123e703","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.2.1.tgz","fileCount":313,"integrity":"sha512-DYtxZl4wK30d1XufG63NAYDikEPLnvAFtsHqI3XTcG5/tDjsXe2LlV94Q1GL5rUwhYs0lTP1nBkDYqEw1ksOaw==","signatures":[{"sig":"MEUCIF4gw2oNULGaMtIyj7PyEFeP+0VThrN2UXK0z6i/39XZAiEAh0Be88KxKYhQA+ysIRPMSfJJNHaPuS426k20hNns8Zw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1070829},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./video/mse":{"types":"./dist/video/mse/index.d.ts","import":"./dist/video/mse/index.js"},"./styles.css":"./styles.css","./package.json":"./package.json","./providers/s3":{"types":"./dist/core/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./video/recording":{"types":"./dist/video/recording/index.d.ts","import":"./dist/video/recording/index.js"},"./providers/cloudflare":{"types":"./dist/core/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"},"./providers/mse-segment":{"types":"./dist/core/providers/mse-segment.d.ts","import":"./dist/providers/mse-segment.js"}},"gitHead":"05ac07d693bd1771e0f89cd11ccc166c745d7404","scripts":{"dev":"tsdown --watch","test":"vitest run","build":"tsdown && tsc --emitDeclarationOnly --outDir dist","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"siam923","email":"classytic.dev@gmail.com"},"repository":{"url":"git+https://github.com/classytic/rtc.git","type":"git","directory":"packages/media"},"_npmVersion":"10.9.3","description":"Production-ready media library for React. Video/audio playback, recording, upload, MSE/HLS streaming, captions.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","hls.js":"^1.6.15","tsdown":"^0.20.3","vitest":"^4.0.0","happy-dom":"^20.0.0","react-dom":"^19.0.0","@vitest/ui":"^4.0.0","typescript":"^5.3.0","@types/react":"^19.0.0","@types/react-dom":"^19.2.3","@vitest/coverage-v8":"^4.0.0","@vitejs/plugin-react":"^4.2.0","@testing-library/react":"^16.0.0","@testing-library/jest-dom":"^6.1.5"},"peerDependencies":{"zod":"^4.0.0","react":"^19.0.0","hls.js":"^1.5.0","react-dom":"^19.0.0"},"optionalDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"hls.js":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/react-media_0.2.1_1772336925866_0.021776110381009017","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@classytic/react-media","version":"0.2.2","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","_id":"@classytic/react-media@0.2.2","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"}],"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","bugs":{"url":"https://github.com/classytic/rtc/issues"},"dist":{"shasum":"cd6c9f91307322a2eaebd5243d37ec1b50a916e0","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.2.2.tgz","fileCount":313,"integrity":"sha512-q9e+rn9Yuv4UPOWw/T89IcUBcc0uft8V2agvj+V0fGHllJ+jdMBlxtqZ0uTJc5XJR/ON6r2lCvNZ6GC7ByQRAA==","signatures":[{"sig":"MEUCIQDrmPs/5V4DHy2cjKuJsu9kj9qXUlmu8sKgbI6h5HfAPgIgRAEH/fEO10KV36nF+Q3NlkV+q91WGOfpDEMWzjFZ4rY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1076447},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./video/mse":{"types":"./dist/video/mse/index.d.ts","import":"./dist/video/mse/index.js"},"./styles.css":"./styles.css","./package.json":"./package.json","./providers/s3":{"types":"./dist/core/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./video/recording":{"types":"./dist/video/recording/index.d.ts","import":"./dist/video/recording/index.js"},"./providers/cloudflare":{"types":"./dist/core/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"},"./providers/mse-segment":{"types":"./dist/core/providers/mse-segment.d.ts","import":"./dist/providers/mse-segment.js"}},"gitHead":"05ac07d693bd1771e0f89cd11ccc166c745d7404","scripts":{"dev":"tsdown --watch","test":"vitest run","build":"tsdown && tsc --emitDeclarationOnly --outDir dist","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"siam923","email":"classytic.dev@gmail.com"},"repository":{"url":"git+https://github.com/classytic/rtc.git","type":"git","directory":"packages/media"},"_npmVersion":"10.9.3","description":"Production-ready media library for React. Video/audio playback, recording, upload, MSE/HLS streaming, captions.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","hls.js":"^1.6.15","tsdown":"^0.20.3","vitest":"^4.0.0","happy-dom":"^20.0.0","react-dom":"^19.0.0","@vitest/ui":"^4.0.0","typescript":"^5.3.0","@types/react":"^19.0.0","@types/react-dom":"^19.2.3","@vitest/coverage-v8":"^4.0.0","@vitejs/plugin-react":"^4.2.0","@testing-library/react":"^16.0.0","@testing-library/jest-dom":"^6.1.5"},"peerDependencies":{"zod":"^4.0.0","react":"^19.0.0","hls.js":"^1.5.0","react-dom":"^19.0.0"},"optionalDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"hls.js":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/react-media_0.2.2_1773057045873_0.427220063086565","host":"s3://npm-registry-packages-npm-production"}},"0.2.3":{"name":"@classytic/react-media","version":"0.2.3","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","_id":"@classytic/react-media@0.2.3","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"}],"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","bugs":{"url":"https://github.com/classytic/rtc/issues"},"dist":{"shasum":"f207eaec076c22a8713ae078be422cd583cc4356","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.2.3.tgz","fileCount":313,"integrity":"sha512-83HSXwRmxBOYRsgbdhNbJ4MJ+4vTALERUeZXzsJxiJumqTdGPr3Zm/1ju1h43F6WYbSxgjhFv2nE/JDdSMP6nQ==","signatures":[{"sig":"MEQCICTzEm1GHzSFsNXtlvHIHahuJbjFPMYkdoIonMpovgv0AiBj2zZn/uw2b/YiITT2uV8EU7GZkiObJ0NlBuLihuU2cg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1076529},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./video/mse":{"types":"./dist/video/mse/index.d.ts","import":"./dist/video/mse/index.js"},"./styles.css":"./styles.css","./package.json":"./package.json","./providers/s3":{"types":"./dist/core/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./video/recording":{"types":"./dist/video/recording/index.d.ts","import":"./dist/video/recording/index.js"},"./providers/cloudflare":{"types":"./dist/core/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"},"./providers/mse-segment":{"types":"./dist/core/providers/mse-segment.d.ts","import":"./dist/providers/mse-segment.js"}},"gitHead":"05ac07d693bd1771e0f89cd11ccc166c745d7404","scripts":{"dev":"tsdown --watch","test":"vitest run","build":"tsdown && tsc --emitDeclarationOnly --outDir dist","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"siam923","email":"classytic.dev@gmail.com"},"repository":{"url":"git+https://github.com/classytic/rtc.git","type":"git","directory":"packages/media"},"_npmVersion":"10.9.3","description":"Production-ready media library for React. Video/audio playback, recording, upload, MSE/HLS streaming, captions.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","hls.js":"^1.6.15","tsdown":"^0.20.3","vitest":"^4.0.0","happy-dom":"^20.0.0","react-dom":"^19.0.0","@vitest/ui":"^4.0.0","typescript":"^5.3.0","@types/react":"^19.0.0","@types/react-dom":"^19.2.3","@vitest/coverage-v8":"^4.0.0","@vitejs/plugin-react":"^4.2.0","@testing-library/react":"^16.0.0","@testing-library/jest-dom":"^6.1.5"},"peerDependencies":{"zod":"^4.0.0","react":"^19.0.0","hls.js":"^1.5.0","react-dom":"^19.0.0"},"optionalDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"hls.js":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/react-media_0.2.3_1773642688058_0.99627649992868","host":"s3://npm-registry-packages-npm-production"}},"0.2.4":{"name":"@classytic/react-media","version":"0.2.4","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","_id":"@classytic/react-media@0.2.4","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"}],"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","bugs":{"url":"https://github.com/classytic/rtc/issues"},"dist":{"shasum":"dc08eef15c0e3e284278dda1a8da0e339d58779d","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.2.4.tgz","fileCount":313,"integrity":"sha512-o7lxIw+O28FWsS13JxUFEyGPI+w6xMOGvetCBcNUTkEh/y/x3iyXWyFoBbWvwtuSn8aOpVsAlkvEHCpAH9a2aQ==","signatures":[{"sig":"MEUCIDel15OFBA6z8AIh79ZkirfNgXxCY0Cmg4wx5zSTPQnkAiEArdlQGDmVsPP2jiFaX0ZV3ka01oQeo1mNThJ4Tf1Y+NI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1079103},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./video/mse":{"types":"./dist/video/mse/index.d.ts","import":"./dist/video/mse/index.js"},"./styles.css":"./styles.css","./package.json":"./package.json","./providers/s3":{"types":"./dist/core/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./video/recording":{"types":"./dist/video/recording/index.d.ts","import":"./dist/video/recording/index.js"},"./providers/cloudflare":{"types":"./dist/core/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"},"./providers/mse-segment":{"types":"./dist/core/providers/mse-segment.d.ts","import":"./dist/providers/mse-segment.js"}},"gitHead":"05ac07d693bd1771e0f89cd11ccc166c745d7404","scripts":{"dev":"tsdown --watch","test":"vitest run","build":"tsdown && tsc --emitDeclarationOnly --outDir dist","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"siam923","email":"classytic.dev@gmail.com"},"repository":{"url":"git+https://github.com/classytic/rtc.git","type":"git","directory":"packages/media"},"_npmVersion":"10.9.3","description":"Production-ready media library for React. Video/audio playback, recording, upload, MSE/HLS streaming, captions.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","hls.js":"^1.6.15","tsdown":"^0.20.3","vitest":"^4.0.0","happy-dom":"^20.0.0","react-dom":"^19.0.0","@vitest/ui":"^4.0.0","typescript":"^5.3.0","@types/react":"^19.0.0","@types/react-dom":"^19.2.3","@vitest/coverage-v8":"^4.0.0","@vitejs/plugin-react":"^4.2.0","@testing-library/react":"^16.0.0","@testing-library/jest-dom":"^6.1.5"},"peerDependencies":{"zod":"^4.0.0","react":"^19.0.0","hls.js":"^1.5.0","react-dom":"^19.0.0"},"optionalDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"hls.js":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/react-media_0.2.4_1773671897437_0.9032088113740875","host":"s3://npm-registry-packages-npm-production"}},"0.2.6":{"name":"@classytic/react-media","version":"0.2.6","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","_id":"@classytic/react-media@0.2.6","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"}],"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","bugs":{"url":"https://github.com/classytic/rtc/issues"},"dist":{"shasum":"c03653ba4f430f2b2c8023a58f82bed2329927a2","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.2.6.tgz","fileCount":315,"integrity":"sha512-KF65OnYkrZCoQyDDiDOKVWrMsht0lTJkC/bacfGXhzXcQG53LbA2hfXhyo7scLqCDDG7ggXZeXEoh/0OL4xEdQ==","signatures":[{"sig":"MEUCIHBNra0DlAejjYbGlp8LooTmuok9BMSNpKKmbn931ZdNAiEA0h/Afo8/Y6sfvhOlyrqKBp2g9ZsUTkr2PX2Z5Ww6Wwo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1105080},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./video/mse":{"types":"./dist/video/mse/index.d.ts","import":"./dist/video/mse/index.js"},"./styles.css":"./styles.css","./package.json":"./package.json","./providers/s3":{"types":"./dist/core/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./video/recording":{"types":"./dist/video/recording/index.d.ts","import":"./dist/video/recording/index.js"},"./providers/cloudflare":{"types":"./dist/core/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"},"./providers/mse-segment":{"types":"./dist/core/providers/mse-segment.d.ts","import":"./dist/providers/mse-segment.js"}},"gitHead":"4303381de6a79289ae7c88b275700216d9e564ca","scripts":{"dev":"tsdown --watch","test":"vitest run","build":"tsdown && tsc --emitDeclarationOnly --outDir dist","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"siam923","email":"classytic.dev@gmail.com"},"repository":{"url":"git+https://github.com/classytic/rtc.git","type":"git","directory":"packages/media"},"_npmVersion":"10.9.3","description":"Production-ready media library for React. Video/audio playback, recording, upload, MSE/HLS streaming, captions.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","hls.js":"^1.6.15","tsdown":"^0.20.3","vitest":"^4.0.0","happy-dom":"^20.0.0","react-dom":"^19.0.0","@vitest/ui":"^4.0.0","typescript":"^5.3.0","@types/react":"^19.0.0","@types/react-dom":"^19.2.3","@vitest/coverage-v8":"^4.0.0","@vitejs/plugin-react":"^4.2.0","@testing-library/react":"^16.0.0","@testing-library/jest-dom":"^6.1.5"},"peerDependencies":{"react":"^19.0.0","hls.js":"^1.5.0","react-dom":"^19.0.0"},"optionalDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"hls.js":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/react-media_0.2.6_1773920938491_0.6203986108784261","host":"s3://npm-registry-packages-npm-production"}},"0.2.7":{"name":"@classytic/react-media","version":"0.2.7","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","_id":"@classytic/react-media@0.2.7","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"}],"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","bugs":{"url":"https://github.com/classytic/rtc/issues"},"dist":{"shasum":"d8ed1d916a33cc268cc865038de30ac86eabbeb1","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.2.7.tgz","fileCount":315,"integrity":"sha512-0WZKS2pe/+5tQjEbZQQpOFM41pC9ieALP4U32YonjGi5t/4CG8Nl9KWIpKJrCqNSUzTVkHwy8XLVpkB6yPVZRw==","signatures":[{"sig":"MEYCIQDfBHJMIDvWasQun8xiYMdc4qTIxbjmTEXC9SueVm2SMwIhAMrDG8V99f2nbkwb69AfgpSxVgp4PFq9PhtfPOWk/Wgw","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1108212},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./video/mse":{"types":"./dist/video/mse/index.d.ts","import":"./dist/video/mse/index.js"},"./styles.css":"./styles.css","./package.json":"./package.json","./providers/s3":{"types":"./dist/core/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./video/recording":{"types":"./dist/video/recording/index.d.ts","import":"./dist/video/recording/index.js"},"./providers/cloudflare":{"types":"./dist/core/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"},"./providers/mse-segment":{"types":"./dist/core/providers/mse-segment.d.ts","import":"./dist/providers/mse-segment.js"}},"gitHead":"7e19c5570ecba1c53020f04d92fc03bd0ddbc372","scripts":{"dev":"tsdown --watch","test":"vitest run","build":"tsdown && tsc --emitDeclarationOnly --outDir dist","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"siam923","email":"classytic.dev@gmail.com"},"repository":{"url":"git+https://github.com/classytic/rtc.git","type":"git","directory":"packages/media"},"_npmVersion":"10.9.3","description":"Production-ready media library for React. Video/audio playback, recording, upload, MSE/HLS streaming, captions.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","hls.js":"^1.6.15","tsdown":"^0.20.3","vitest":"^4.0.0","happy-dom":"^20.0.0","react-dom":"^19.0.0","@vitest/ui":"^4.0.0","typescript":"^5.3.0","@types/react":"^19.0.0","@types/react-dom":"^19.2.3","@vitest/coverage-v8":"^4.0.0","@vitejs/plugin-react":"^4.2.0","@testing-library/react":"^16.0.0","@testing-library/jest-dom":"^6.1.5"},"peerDependencies":{"react":"^19.0.0","hls.js":"^1.5.0","react-dom":"^19.0.0"},"optionalDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"hls.js":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/react-media_0.2.7_1773998451748_0.6853255974854278","host":"s3://npm-registry-packages-npm-production"}},"0.2.11":{"name":"@classytic/react-media","version":"0.2.11","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","_id":"@classytic/react-media@0.2.11","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"}],"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","bugs":{"url":"https://github.com/classytic/rtc/issues"},"dist":{"shasum":"0ee55ba682d1005c9032f53c45aeaf45e7424198","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.2.11.tgz","fileCount":315,"integrity":"sha512-taCHh81L0rKk6riLLcsQ1PetnDvo7vXzHlicEBFUdhDRpScMDROXOA3lE9QxAxcaGUx0vlwEcjNwiGAfMNOquA==","signatures":[{"sig":"MEYCIQCTCvx4+VjAr6R7PQ3EZOuBhftv2pCri3lIsBrakDF1AwIhAMDFw0rsfVdoC/NE8WJ86UpKh26bjaQzGXjRSwqS1F/l","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1114925},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./video/mse":{"types":"./dist/video/mse/index.d.ts","import":"./dist/video/mse/index.js"},"./styles.css":"./styles.css","./package.json":"./package.json","./providers/s3":{"types":"./dist/core/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./video/recording":{"types":"./dist/video/recording/index.d.ts","import":"./dist/video/recording/index.js"},"./providers/cloudflare":{"types":"./dist/core/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"},"./providers/mse-segment":{"types":"./dist/core/providers/mse-segment.d.ts","import":"./dist/providers/mse-segment.js"}},"gitHead":"c5f9ee84294b115837475a1df20e22dbec6299da","scripts":{"dev":"tsdown --watch","test":"vitest run","build":"tsdown && tsc --emitDeclarationOnly --outDir dist","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"siam923","email":"classytic.dev@gmail.com"},"repository":{"url":"git+https://github.com/classytic/rtc.git","type":"git","directory":"packages/media"},"_npmVersion":"10.9.3","description":"Production-ready media library for React. Video/audio playback, recording, upload, MSE/HLS streaming, captions.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","hls.js":"^1.6.15","tsdown":"^0.20.3","vitest":"^4.0.0","happy-dom":"^20.0.0","react-dom":"^19.0.0","@vitest/ui":"^4.0.0","typescript":"^5.3.0","@types/react":"^19.0.0","@types/react-dom":"^19.2.3","@vitest/coverage-v8":"^4.0.0","@vitejs/plugin-react":"^4.2.0","@testing-library/react":"^16.0.0","@testing-library/jest-dom":"^6.1.5"},"peerDependencies":{"react":"^19.0.0","hls.js":"^1.5.0","react-dom":"^19.0.0"},"optionalDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"hls.js":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/react-media_0.2.11_1774360859951_0.2241345241785202","host":"s3://npm-registry-packages-npm-production"}},"0.2.12":{"name":"@classytic/react-media","version":"0.2.12","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","_id":"@classytic/react-media@0.2.12","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"},{"name":"classytic-bot","email":"classytic.dev+bot@gmail.com"}],"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","bugs":{"url":"https://github.com/classytic/rtc/issues"},"dist":{"shasum":"c2617412551db2814270b916da4600f15fd29e52","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.2.12.tgz","fileCount":315,"integrity":"sha512-8TTBe8U6FjwWrC+TYv4gIZSwplkgj8duttiMv72wkYkuZqQvp3NasuP9L96ai7gJT7mOaZA7rF9VLfX7LlmStw==","signatures":[{"sig":"MEUCIHQCu1bHkX+264rhyAcnvT2txLJin17pOFtO7/yHYEv7AiEAnimSv+H623z97hcdrs+lM8bzprEu5gD7NCYtbCWlAUw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1119961},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./video/mse":{"types":"./dist/video/mse/index.d.ts","import":"./dist/video/mse/index.js"},"./styles.css":"./styles.css","./package.json":"./package.json","./providers/s3":{"types":"./dist/core/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./video/recording":{"types":"./dist/video/recording/index.d.ts","import":"./dist/video/recording/index.js"},"./providers/cloudflare":{"types":"./dist/core/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"},"./providers/mse-segment":{"types":"./dist/core/providers/mse-segment.d.ts","import":"./dist/providers/mse-segment.js"}},"gitHead":"88ed6a62ed0990723557e164d4d962490eb612dd","scripts":{"dev":"tsdown --watch","push":"classytic-push","test":"vitest run","build":"tsdown && tsc --emitDeclarationOnly --outDir dist","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","release:tag":"node -e \"const v=require('./package.json').version,p=require('node:path').resolve('../..'),{execSync:e}=require('child_process'),r=c=>e(c,{cwd:p,stdio:'inherit'});r('git tag -a media-v'+v+' -m media-v'+v);r('npm run push -- media-v'+v)\"","release:push":"node -e \"require('child_process').execSync('npm run push -- main',{cwd:require('node:path').resolve('../..'),stdio:'inherit'})\"","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"classytic-bot","email":"classytic.dev+bot@gmail.com"},"repository":{"url":"git+https://github.com/classytic/rtc.git","type":"git","directory":"packages/media"},"_npmVersion":"10.9.3","description":"Production-ready media library for React. Video/audio playback, recording, upload, MSE/HLS streaming, captions.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","hls.js":"^1.6.15","tsdown":"^0.20.3","vitest":"^4.0.0","happy-dom":"^20.0.0","react-dom":"^19.0.0","@vitest/ui":"^4.0.0","typescript":"^5.3.0","@types/react":"^19.0.0","@types/react-dom":"^19.2.3","@vitest/coverage-v8":"^4.0.0","@classytic/dev-tools":"^0.2.0","@vitejs/plugin-react":"^4.2.0","@testing-library/react":"^16.0.0","@testing-library/jest-dom":"^6.1.5"},"peerDependencies":{"react":"^19.0.0","hls.js":"^1.5.0","react-dom":"^19.0.0"},"optionalDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"hls.js":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/react-media_0.2.12_1783339192093_0.3812510568123133","host":"s3://npm-registry-packages-npm-production"}},"0.2.13":{"name":"@classytic/react-media","version":"0.2.13","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","_id":"@classytic/react-media@0.2.13","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"},{"name":"classytic-bot","email":"classytic.dev+bot@gmail.com"}],"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","bugs":{"url":"https://github.com/classytic/rtc/issues"},"dist":{"shasum":"1b274151dd9aa2e7b7c85c673313b1dcbf69fe56","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.2.13.tgz","fileCount":315,"integrity":"sha512-wb6DlmBVhz47skHBrqiTNqgPhh7Ypzwfo9aBAtnknEhCl/PqAJSTuZpPyD62ZvdkNoY6BJli/EMqqCM+TqzoqA==","signatures":[{"sig":"MEYCIQDwz1aBqB8dmsTeqBAc+njDPSreHazBjgMuUDHqDrzysgIhAJYUUg0+l1TxQe9KpMlgAVtAnFbKMTc7eWNEdmx42dfm","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1123981},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./video/mse":{"types":"./dist/video/mse/index.d.ts","import":"./dist/video/mse/index.js"},"./styles.css":"./styles.css","./package.json":"./package.json","./providers/s3":{"types":"./dist/core/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./video/recording":{"types":"./dist/video/recording/index.d.ts","import":"./dist/video/recording/index.js"},"./providers/cloudflare":{"types":"./dist/core/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"},"./providers/mse-segment":{"types":"./dist/core/providers/mse-segment.d.ts","import":"./dist/providers/mse-segment.js"}},"gitHead":"4bd902655be5cd25861f5ae2ce23aa1bc98da83f","scripts":{"dev":"tsdown --watch","push":"classytic-push","test":"vitest run","build":"tsdown && tsc --emitDeclarationOnly --outDir dist","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","release:tag":"node -e \"const v=require('./package.json').version,p=require('node:path').resolve('../..'),{execSync:e}=require('child_process'),r=c=>e(c,{cwd:p,stdio:'inherit'});r('git tag -a media-v'+v+' -m media-v'+v);r('npm run push -- media-v'+v)\"","release:push":"node -e \"require('child_process').execSync('npm run push -- main',{cwd:require('node:path').resolve('../..'),stdio:'inherit'})\"","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build"},"_npmUser":{"name":"classytic-bot","email":"classytic.dev+bot@gmail.com"},"repository":{"url":"git+https://github.com/classytic/rtc.git","type":"git","directory":"packages/media"},"_npmVersion":"10.9.3","description":"Production-ready media library for React. Video/audio playback, recording, upload, MSE/HLS streaming, captions.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","hls.js":"^1.6.15","tsdown":"^0.20.3","vitest":"^4.0.0","happy-dom":"^20.0.0","react-dom":"^19.0.0","@vitest/ui":"^4.0.0","typescript":"^5.3.0","@types/react":"^19.0.0","@types/react-dom":"^19.2.3","@vitest/coverage-v8":"^4.0.0","@classytic/dev-tools":"^0.2.0","@vitejs/plugin-react":"^4.2.0","@testing-library/react":"^16.0.0","@testing-library/jest-dom":"^6.1.5"},"peerDependencies":{"react":"^19.0.0","hls.js":"^1.5.0","react-dom":"^19.0.0"},"optionalDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0","class-variance-authority":"^0.7.0"},"peerDependenciesMeta":{"hls.js":{"optional":true},"react-dom":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/react-media_0.2.13_1783342541180_0.009277813197869467","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"@classytic/react-media","version":"0.3.2","description":"Production-ready media library for React. Video/audio playback, recording, upload, MSE/HLS streaming, captions.","type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./video":{"types":"./dist/video/index.d.ts","import":"./dist/video/index.js"},"./audio":{"types":"./dist/audio/index.d.ts","import":"./dist/audio/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./shared":{"types":"./dist/shared/index.d.ts","import":"./dist/shared/index.js"},"./video/mse":{"types":"./dist/video/mse/index.d.ts","import":"./dist/video/mse/index.js"},"./video/recording":{"types":"./dist/video/recording/index.d.ts","import":"./dist/video/recording/index.js"},"./styles.css":"./styles.css","./providers/s3":{"types":"./dist/core/providers/s3.d.ts","import":"./dist/providers/s3.js"},"./providers/cloudflare":{"types":"./dist/core/providers/cloudflare.d.ts","import":"./dist/providers/cloudflare.js"},"./providers/mse-segment":{"types":"./dist/core/providers/mse-segment.d.ts","import":"./dist/providers/mse-segment.js"},"./providers/media-kit":{"types":"./dist/core/providers/media-kit.d.ts","import":"./dist/providers/media-kit.js"},"./providers/hls-segment":{"types":"./dist/core/providers/hls-segment.d.ts","import":"./dist/providers/hls-segment.js"},"./package.json":"./package.json"},"sideEffects":false,"scripts":{"build":"tsdown && tsc --emitDeclarationOnly --outDir dist","dev":"tsdown --watch","clean":"rm -rf dist","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build","push":"classytic-push","release:tag":"node -e \"const v=require('./package.json').version,p=require('node:path').resolve('../..'),{execSync:e}=require('child_process'),r=c=>e(c,{cwd:p,stdio:'inherit'});r('git tag -a media-v'+v+' -m media-v'+v);r('npm run push -- media-v'+v)\"","release:push":"node -e \"require('child_process').execSync('npm run push -- main',{cwd:require('node:path').resolve('../..'),stdio:'inherit'})\""},"keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"author":{"name":"Classytic"},"license":"MIT","publishConfig":{"access":"public"},"peerDependencies":{"hls.js":"^1.5.0","react":"^19.0.0","react-dom":"^19.0.0"},"peerDependenciesMeta":{"react-dom":{"optional":true},"hls.js":{"optional":true}},"dependencies":{"class-variance-authority":"^0.7.0","clsx":"^2.0.0","tailwind-merge":"^2.0.0"},"optionalDependencies":{"mediabunny":"^1.50.7"},"devDependencies":{"@classytic/dev-tools":"^0.2.0","@testing-library/jest-dom":"^6.1.5","@testing-library/react":"^16.0.0","@types/react":"^19.0.0","@types/react-dom":"^19.2.3","@vitejs/plugin-react":"^4.2.0","@vitest/coverage-v8":"^4.0.0","@vitest/ui":"^4.0.0","happy-dom":"^20.0.0","hls.js":"^1.6.15","react":"^19.0.0","react-dom":"^19.0.0","tsdown":"^0.20.3","typescript":"^5.3.0","vitest":"^4.0.0"},"repository":{"type":"git","url":"git+https://github.com/classytic/rtc.git","directory":"packages/media"},"bugs":{"url":"https://github.com/classytic/rtc/issues"},"homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","engines":{"node":">=18.0.0"},"_id":"@classytic/react-media@0.3.2","gitHead":"9b2114aea364812a41c8f929cdcb7d23217e59b2","_nodeVersion":"22.19.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-+8R3w0QwVJICkmgXJJ/55+iaqOAsNkra4riykx/KtO5XQinxmAnSEns0s3ZLSUvle6VpIanshApUKSMEggcVMA==","shasum":"7f0b6f697bbfb31b38e3e15c47e77e55269b6a1a","tarball":"https://registry.npmjs.org/@classytic/react-media/-/react-media-0.3.2.tgz","fileCount":332,"unpackedSize":1248771,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQD3hI0vJgmF80G7VuJvnOlg3kZfeO7gwsOz9ME7wSTmIgIhAPMZuJ7fbwWENm9uhTf9SQoOIMPyJMCCKXC876DFZdcQ"}]},"_npmUser":{"name":"classytic-bot","email":"classytic.dev+bot@gmail.com"},"directories":{},"maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"},{"name":"classytic-bot","email":"classytic.dev+bot@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/react-media_0.3.2_1783501987872_0.36656484546935864"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-02T16:31:59.849Z","modified":"2026-07-08T09:13:08.189Z","0.1.0":"2026-02-02T16:32:00.190Z","0.1.1":"2026-02-05T05:29:20.666Z","0.1.2":"2026-02-19T09:12:17.949Z","0.2.0":"2026-02-24T00:56:34.545Z","0.2.1":"2026-03-01T03:48:46.270Z","0.2.2":"2026-03-09T11:50:46.025Z","0.2.3":"2026-03-16T06:31:28.300Z","0.2.4":"2026-03-16T14:38:17.585Z","0.2.6":"2026-03-19T11:48:58.699Z","0.2.7":"2026-03-20T09:20:51.884Z","0.2.8":"2026-03-24T11:46:24.853Z","0.2.9":"2026-03-24T11:55:20.829Z","0.2.10":"2026-03-24T12:21:26.155Z","0.2.11":"2026-03-24T14:01:00.143Z","0.2.12":"2026-07-06T11:59:52.240Z","0.2.13":"2026-07-06T12:55:41.359Z","0.3.0":"2026-07-08T08:16:18.891Z","0.3.1":"2026-07-08T08:40:07.346Z","0.3.2":"2026-07-08T09:13:08.073Z"},"bugs":{"url":"https://github.com/classytic/rtc/issues"},"author":{"name":"Classytic"},"license":"MIT","homepage":"https://github.com/classytic/rtc/tree/main/packages/media#readme","keywords":["react","media","video","audio","video-player","audio-player","podcast","recording","upload","playback","captions","subtitles","vtt","srt","hls","streaming","s3","cloudflare-stream","webm","mediarecorder","multipart-upload","typescript"],"repository":{"type":"git","url":"git+https://github.com/classytic/rtc.git","directory":"packages/media"},"description":"Production-ready media library for React. Video/audio playback, recording, upload, MSE/HLS streaming, captions.","maintainers":[{"name":"siam923","email":"classytic.dev@gmail.com"},{"name":"classytic-bot","email":"classytic.dev+bot@gmail.com"}],"readme":"# @classytic/react-media\n\n**The engine for professional video in React.**  \nPerformance-critical, HLS-first, and 100% headless. Built for React 19.\n\n[![React 19](https://img.shields.io/badge/React-19-blue.svg)](https://react.dev)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue.svg)](https://www.typescriptlang.org/)\n[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n\n> **Why another player?**  \n> Most React players are just wrappers around `document.querySelector('video')` or heavy Web Components (like Vidstack/Mux).  \n> **This is a native React engine.** It uses `requestAnimationFrame` loops for 60fps UI updates, `useSyncExternalStore` for tearing-free state, and React 19's `use()` API for clean context consumption. It is built for developers building the next Netflix, not marketing sites hosting a YouTube embed.\n\n---\n\n## ✨ Features\n\n- **🚀 React 19 Native**: Built with `use()`, `useSyncExternalStore`, and Server Components support.\n- **⚡ 60fps UI Sync**: Custom `TimeStore` uses RAF loops to update progress bars/time displays without triggering React re-renders.\n- **🎨 100% Headless**: No default styles. No \"shadow DOM\" fighting. You own every pixel.\n- **📡 HLS First**: Production-ready HLS support (via hls.js) baked in.\n- **🎧 First-Class Audio**: Full HLS audio support for podcasts and radio.\n- **🧹 No Bloat**: No YouTube/Vimeo/DASH adapters. Focused purely on direct file/stream playback.\n\n---\n\n## 📦 Installation\n\n```bash\nnpm install @classytic/react-media\n```\n\n## ⚡ Quick Start (Video)\n\n```tsx\nimport {\n  VideoController,\n  VideoRoot,\n  Video,\n  PlayButton,\n  TimeSlider,\n} from \"@classytic/react-media\";\n\nexport default function Player() {\n  return (\n    // 1. Controller manages state (HLS, buffering, errors)\n    <VideoController src=\"https://stream.mux.com/YOUR_PLAYBACK_ID.m3u8\">\n      {/* 2. Root handles layout & keyboard shortcuts */}\n      <VideoRoot className=\"relative aspect-video bg-black group\">\n        {/* 3. The native video element (handled by controller) */}\n        <Video className=\"w-full h-full object-cover\" />\n\n        {/* 4. Headless Controls (Style with Tailwind) */}\n        <div className=\"absolute bottom-0 w-full p-4 bg-gradient-to-t from-black/80\">\n          <TimeSlider className=\"h-1 bg-white/30 cursor-pointer\">\n            <div className=\"h-full bg-red-500 var-progress\" />\n          </TimeSlider>\n\n          <div className=\"flex gap-4 mt-2\">\n            <PlayButton className=\"text-white hover:text-red-500\">\n              {({ isPlaying }) => (isPlaying ? \"PAUSE\" : \"PLAY\")}\n            </PlayButton>\n          </div>\n        </div>\n      </VideoRoot>\n    </VideoController>\n  );\n}\n```\n\n## 🎧 Quick Start (Audio)\n\nSame high-performance engine, built for Audio.\n\n```tsx\nimport {\n  AudioController,\n  AudioPlayButton,\n  AudioProgress,\n} from \"@classytic/react-media/audio\";\n\nexport default function PodcastPlayer() {\n  return (\n    <AudioController src=\"https://your-hls-stream/radio.m3u8\">\n      <div className=\"p-4 rounded-xl bg-zinc-900 border border-zinc-800\">\n        <div className=\"flex items-center gap-4\">\n          <AudioPlayButton className=\"w-12 h-12 rounded-full bg-white text-black flex items-center justify-center\">\n            {({ isPlaying }) => (isPlaying ? <PauseIcon /> : <PlayIcon />)}\n          </AudioPlayButton>\n\n          <AudioProgress className=\"flex-1 h-2 bg-zinc-800 rounded-full overflow-hidden\">\n            {/* Direct DOM update for 60fps smoothness */}\n            {({ progress }) => (\n              <div\n                className=\"h-full bg-white transition-all duration-75\"\n                style={{ width: `${progress}%` }}\n              />\n            )}\n          </AudioProgress>\n        </div>\n      </div>\n    </AudioController>\n  );\n}\n```\n\n---\n\n## 🏗 Architecture\n\nWe separate state into two stores to ensure the react render cycle never blocks playback UI. For deep dives into the engine's design, fault tolerance (circuit breakers), and HLS manifest polling, **[read the full Architecture Guide](./docs/ARCHITECTURE.md)**.\n\nFor S3 configuration, refer to the **[S3 Setup Guide](../../S3_SETUP_GUIDE.md)** for public vs private livestreaming.\n\n| Store            | Purpose                                | Update Frequency   | Technology                           |\n| ---------------- | -------------------------------------- | ------------------ | ------------------------------------ |\n| **`VideoStore`** | Play/Pause, Buffering, Quality, Errors | Low (Event driven) | `useSyncExternalStore`               |\n| **`TimeStore`**  | CurrentTime, Progress, Duration        | High (4-60Hz)      | `requestAnimationFrame` + Direct DOM |\n\nThis means your progress bar updates smoothly even if your React app is busy rendering a complex component tree.\n\n---\n\n## 📚 API Reference\n\n### `/video` — Video Player\n\n#### Core\n\n| Export                           | Description                                                                                                                                                                    |\n| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `VideoController`                | Headless state provider. Wraps children in video context                                                                                                                       |\n| `useVideoState(selector)`        | Subscribe to video state with selector (e.g., `s => s.isPlaying`)                                                                                                              |\n| `useShallowVideoState(selector)` | Same as above with shallow comparison for object selectors                                                                                                                     |\n| `useVideoActions()`              | Get stable action methods: `play`, `pause`, `seek`, `setVolume`, `toggleMute`, `toggleFullscreen`, `togglePictureInPicture`, `setQuality`, `setPlaybackRate`, `seekToLiveEdge` |\n| `useVideoRefs()`                 | Access `videoRef` and `containerRef` for direct DOM work                                                                                                                       |\n\n#### Time Hooks (Performance)\n\n| Export                  | Description                                         |\n| ----------------------- | --------------------------------------------------- |\n| `useDirectTime(cb)`     | Subscribe to time updates directly (bypasses React) |\n| `useDirectProgress(cb)` | Subscribe to progress % directly                    |\n| `useDirectDuration(cb)` | Subscribe to duration changes                       |\n| `useTimeRef()`          | Returns a ref that auto-updates with formatted time |\n| `useProgressRef()`      | Returns a ref that auto-updates `style.width`       |\n| `useTimeGetter()`       | On-demand getter: `const getTime = useTimeGetter()` |\n| `useDurationGetter()`   | On-demand getter for duration                       |\n| `formatTime(seconds)`   | Formats `90` as `\"1:30\"`                            |\n\n#### Primitives (Headless)\n\n| Export                              | Description                                           |\n| ----------------------------------- | ----------------------------------------------------- |\n| `VideoRoot`                         | Container div with data attributes for state tracking |\n| `Video`                             | The `<video>` element managed by the controller       |\n| `PlayButton`                        | Render-prop button: `{({ isPlaying }) => ...}`        |\n| `MuteButton`                        | Render-prop mute toggle                               |\n| `FullscreenButton`                  | Render-prop fullscreen toggle                         |\n| `PictureInPictureButton`            | Render-prop PiP toggle                                |\n| `TimeSlider`                        | Seek scrubber with drag support                       |\n| `TimeDisplay`                       | Direct-DOM time readout                               |\n| `VolumeSlider`                      | Volume control with drag                              |\n| `QualitySelect`                     | Quality level picker (HLS)                            |\n| `PlaybackSpeed`                     | Playback rate selector                                |\n| `SeekButton`                        | Skip forward/backward                                 |\n| `DoubleTapSeek`                     | Mobile double-tap seek gesture                        |\n| `Captions` / `CaptionsButton`       | Subtitle rendering + toggle                           |\n| `BufferingOverlay` / `ErrorOverlay` | State overlays                                        |\n| `StatsOverlay`                      | Debug \"stats for nerds\" panel                         |\n| `VideoContextMenu`                  | Right-click context menu                              |\n| `ChaptersMenu`                      | Chapter navigation                                    |\n| `ThumbnailPreview`                  | Sprite-based thumbnail on hover                       |\n\n#### Features\n\n| Export                         | Description                                                      |\n| ------------------------------ | ---------------------------------------------------------------- |\n| `useCast()`                    | Chromecast integration                                           |\n| `useAirPlay()`                 | Apple AirPlay integration                                        |\n| `useNetworkQuality()`          | Network bandwidth/RTT monitoring                                 |\n| `useAdaptiveBitrate(options?)` | Automatic quality switching (bandwidth/buffer/hybrid strategies) |\n| `useMediaSession(options?)`    | Lock screen / OS media controls (Media Session API)              |\n| `useAnalytics(options?)`       | Video analytics & telemetry                                      |\n| `useErrorRecovery(options?)`   | Auto-recovery with exponential backoff                           |\n| `useVideoDebug(options?)`      | Debug metrics for \"stats for nerds\"                              |\n| `useFrameCapture()`            | Capture video frames as images                                   |\n| `useCopyVideoUrl(options?)`    | Share URLs with timestamps / embed codes                         |\n\n##### `useAdaptiveBitrate(options?)`\n\n```tsx\nconst { isAutoMode, recommendedQuality, override, enableAuto, bufferHealth } =\n  useAdaptiveBitrate({\n    strategy: \"hybrid\", // 'bandwidth' | 'buffer' | 'hybrid'\n    switchCooldown: 10000, // ms between switches\n    minQuality: 0,\n    maxQuality: 3,\n    onQualitySwitch: (from, to, reason) => console.log(reason),\n  });\n```\n\n##### `useMediaSession(options?)`\n\n```tsx\nuseMediaSession({\n  metadata: { title: \"Interview\", artist: \"Company\" },\n  seekOffset: 10, // seconds for skip buttons\n  autoSync: true, // sync position state with playback\n});\n```\n\n#### Icons\n\nBuilt-in SVG icons (24x24, filled style): `PlayIcon`, `PauseIcon`, `VolumeHighIcon`, `VolumeLowIcon`, `VolumeMutedIcon`, `FullscreenIcon`, `FullscreenExitIcon`, `PictureInPictureIcon`, `CaptionsIcon`, `SettingsIcon`, `ChevronLeftIcon`, `ChevronRightIcon`.\n\n---\n\n### `/audio` — Audio Player\n\n| Export                    | Description                                      |\n| ------------------------- | ------------------------------------------------ |\n| `AudioController`         | Headless audio state provider                    |\n| `AudioPlayButton`         | Render-prop play/pause button                    |\n| `AudioProgress`           | Progress bar with direct DOM updates             |\n| `useAudioState(selector)` | Subscribe to audio state                         |\n| `useAudioActions()`       | Get `play`, `pause`, `seek`, `setVolume` actions |\n| `useAudioCast()`          | Chromecast for audio                             |\n\n---\n\n### `/core` — Upload Engine\n\n| Export                                      | Description                                                   |\n| ------------------------------------------- | ------------------------------------------------------------- |\n| `Upload`                                    | Multi-part upload orchestrator (S3, Cloudflare, HLS segments) |\n| `useUpload(options)`                        | React hook for file uploads with progress                     |\n| `useFileUpload()`                           | Drop-in hook with file picker integration                     |\n| `RetryManager`                              | Exponential backoff retry logic                               |\n| `calculateCRC32(blob)`                      | CRC32 checksum (fast, for chunks)                             |\n| `calculateSHA256(blob)`                     | SHA-256 checksum (Web Crypto)                                 |\n| `verifyChecksum(blob, expected, algorithm)` | Verify integrity                                              |\n| `createMediaKitProvider(config)`            | `createS3Provider` preconfigured for a `@classytic/media-kit` backend — host recipe in media-kit's `react-media-integration` guide |\n\nUpload providers: `@classytic/react-media/providers/s3`, `/cloudflare`, `/hls-segment`, `/mse-segment`, `/media-kit`.\n\n### Live Recording (MSE Segment Upload)\n\n| Export                      | Description                                                        |\n| --------------------------- | ------------------------------------------------------------------ |\n| `useLiveRecorderProfile`    | High-level live recording hook (fMP4 segments to S3)               |\n| `createMseSegmentProvider`  | Upload provider for per-segment S3 uploads with manifest.json      |\n| `useDirectMSE`              | MSE player hook for manifest.json + fMP4/WebM segment playback     |\n| `DirectMSEPlayer`           | Full-featured MSE player component with controls and timeline      |\n\n#### `useDirectMSE(options)`\n\nPlays back fMP4/WebM segments from a `manifest.json` using the browser's MediaSource Extensions API.\n\n```tsx\nimport { useDirectMSE } from '@classytic/react-media/video';\n\nconst videoRef = useRef<HTMLVideoElement>(null);\n\nconst { status, error, segmentCount, totalDuration, isLive, isEnded } = useDirectMSE({\n  manifestUrl: '/api/interviews/123/live/manifest.json',\n  baseUrl: '/api/interviews/123/live/',\n  videoRef,\n  autoPlay: true,\n  bufferAhead: 30,\n  bufferBehind: 30,\n  bufferMode: 'auto',\n  debug: false,         // Enable console logging for troubleshooting\n  onSegment: (count) => console.log(`${count} segments loaded`),\n  onEnded: () => console.log('Stream ended'),\n});\n```\n\n#### Manifest Format (`LiveManifest`)\n\nThe manifest is the contract between the recorder and the player. Both sides use the `LiveManifest` type:\n\n```tsx\nimport type { LiveManifest, LiveManifestSegment } from '@classytic/react-media/video';\n```\n\n```json\n{\n  \"version\": 7,\n  \"targetDuration\": 7,\n  \"initSegment\": \"init.mp4\",\n  \"mimeType\": \"video/mp4;codecs=avc1.42E01E,mp4a.40.2\",\n  \"ended\": false,\n  \"segments\": [\n    { \"uri\": \"segments/seg_0000.m4s\", \"duration\": 6.012 },\n    { \"uri\": \"segments/seg_0001.m4s\", \"duration\": 5.987 }\n  ]\n}\n```\n\nSee [recording.md](./docs/recording.md) for full live recording documentation including manifest lifecycle, data loss scenarios, and backend reconciliation.\n\n#### Recorder lifecycle guards — browser closed mid-recording\n\nOpt-in guards that cover the \"user closes the tab mid-recording\" gap: without\nthem the backend is left with an open multipart upload / stale session until\nits sweep runs. Configure on `useVideoRecorder` (or `RecorderOptions` for the\n`VideoRecorder` / WebCodecs engines directly):\n\n```tsx\nconst recorder = useVideoRecorder({\n  provider: 'media-kit',\n  providerConfig: { apiBase: '/api/media' },\n  lifecycleGuards: {\n    // POST target for the abort notification (string or (ctx) => string)\n    abortBeaconUrl: (ctx) => `/api/media/${ctx.uploadId}/abort`,\n    // Optional custom body — default is a JSON Blob of { key, uploadId, sessionId }\n    beaconPayload: (ctx) => JSON.stringify({ uploadId: ctx.uploadId }),\n    // Native leave-confirmation while recording (default: true)\n    warnOnUnload: true,\n  },\n});\n```\n\nWhile recording is active: `beforeunload` triggers the browser's\nleave-confirmation (`warnOnUnload`), and `pagehide` — which fires far more\nreliably than `beforeunload`, including on mobile tab kills — sends a\n`navigator.sendBeacon` abort notification with the session identifiers. Both\nhandlers are deregistered on clean stop/finalize/abort, so a completed\nrecording never fires a beacon. For **visible-tab cancels** call\n`recorder.abortSession()` — it sends the same abort notification via\n`fetch(..., { keepalive: true })` (beacons are reserved for `pagehide`) and\nthen runs the normal provider abort.\n\n> **Best-effort by design.** Beacons can be lost (crashed tab, killed process,\n> offline). The guarantee is the server-side sweep: media-kit's\n> `purgeStalePending` plus the S3 `AbortIncompleteMultipartUpload` bucket\n> lifecycle rule — see media-kit's `docs/guides/react-media-integration.mdx`,\n> section \"Abandoned sessions\". The guards just make the common case\n> (ordinary tab close) clean up in seconds instead of waiting for the sweep.\n\n---\n\n### `/shared` — Shared Utilities\n\n#### Captions\n\n| Export                      | Description                       |\n| --------------------------- | --------------------------------- |\n| `parseVTT(content)`         | Parse WebVTT → `{ cues, errors }` |\n| `parseSRT(content)`         | Parse SRT subtitles               |\n| `parseSubtitles(content)`   | Auto-detect VTT/SRT               |\n| `getActiveCues(cues, time)` | Filter cues active at time        |\n| `searchCues(cues, query)`   | Full-text search through cues     |\n| `formatTimestamp(seconds)`  | `90.5` → `\"01:30.500\"`            |\n| `vttToHtml(text)`           | Convert VTT formatting to HTML    |\n\n#### Hooks\n\n| Export                              | Description                                   |\n| ----------------------------------- | --------------------------------------------- |\n| `useAutoplay(options)`              | Autoplay policy detection with muted fallback |\n| `useIntersectionObserver(options?)` | Viewport-based lazy loading                   |\n\n##### `useAutoplay(options)`\n\n```tsx\nconst { canAutoplay, canAutoplayMuted, isBlocked, isMuted, unmute, retry } =\n  useAutoplay({\n    mediaRef: videoRef,\n    viewportTrigger: false, // trigger on viewport entry\n    autoDetect: true, // detect on mount\n  });\n\n{\n  canAutoplayMuted && !canAutoplay && (\n    <button onClick={unmute}>Tap to unmute</button>\n  );\n}\n```\n\n#### Other\n\n| Export                         | Description                         |\n| ------------------------------ | ----------------------------------- |\n| `cn(...classes)`               | Class name utility (clsx + twMerge) |\n| `useIntersectionObserver()`    | Viewport observer with `once` mode  |\n| `fetchWithAbort(url, options)` | Fetch with timeout + AbortSignal    |\n| `TIMING`, `DEFAULTS`           | Config constants                    |\n\n---\n\n## 🏗 Architecture\n\nWe separate state into two stores to ensure the react render cycle never blocks playback UI. For deep dives into the engine's design, fault tolerance (circuit breakers), and HLS manifest polling, **[read the full Architecture Guide](./docs/ARCHITECTURE.md)**.\n\nFor S3 configuration, refer to the **[S3 Setup Guide](../../S3_SETUP_GUIDE.md)** for public vs private livestreaming.\n\n| Store            | Purpose                                | Update Frequency   | Technology                           |\n| ---------------- | -------------------------------------- | ------------------ | ------------------------------------ |\n| **`VideoStore`** | Play/Pause, Buffering, Quality, Errors | Low (Event driven) | `useSyncExternalStore`               |\n| **`TimeStore`**  | CurrentTime, Progress, Duration        | High (4-60Hz)      | `requestAnimationFrame` + Direct DOM |\n\nThis means your progress bar updates smoothly even if your React app is busy rendering a complex component tree.\n\n---\n\n## Theming — bring your own design system\n\nThe primitives are headless (positioning + interaction inline, zero visual\nopinion). Hosts restyle at four standardized levels (shadcn/Radix conventions):\n\n**1. `className` + `classNames` slots.** Every component takes `className`\n(root). Composite components with a default multi-part UI ALSO take a\n`classNames` record of named parts, each merged via `cn` (tailwind-merge)\n**after** the defaults — **your classes win conflicts**:\n\n```tsx\n<ErrorOverlay\n  className=\"absolute inset-0\"\n  classNames={{\n    content: 'bg-neutral-900/90',   // beats the default overlay bg\n    message: 'text-rose-400',\n    retry: 'bg-rose-500 hover:bg-rose-600',\n  }}\n/>\n\n<CaptionSelector\n  tracks={tracks}\n  activeTrackId={id}\n  classNames={{ trigger: '…', menu: 'bg-neutral-900', option: '…', optionActive: 'ring-1', divider: '…' }}\n/>\n```\n\nSlots per composite: `BufferingOverlay`/`LoadingOverlay` `{ content, indicator }` ·\n`ErrorOverlay` `{ content, body, message, retry }` · `StatsOverlay` `{ panel,\nheader, row, label, value }` · `CaptionSelector` `{ trigger, menu, option,\noptionActive, divider }` · `CaptionRenderer` `{ window, cueContainer, cue, text }` ·\n`VideoErrorFallback` `{ title, message, retry }` · `VideoContextMenu` — `className`\nstyles the fixed positioning shell (the menu content is yours). Leaf primitives\n(`PlayButton`, `MuteButton`, `TimeSlider.*`, `SeekButton`, cast/AirPlay buttons,\naudio primitives) are single-element and already take `className` directly.\n\n**2. State via data-attributes.** Interactive state is exposed on the DOM, so\nTailwind (`data-[state=playing]:…`) and plain CSS both target it:\n\n| Element | Attributes |\n| --- | --- |\n| `PlayButton` | `data-playing`, `data-paused`, `data-loading`, `data-buffering` |\n| `MuteButton` / `VolumeSlider` | `data-muted`, `data-volume`, `data-dragging`, `data-state=\"muted\\|idle\"` |\n| `TimeSlider` (root) | `data-progress`, `data-seeking`, `data-dragging`, `data-live` |\n| `FullscreenButton` / `PictureInPictureButton` | `data-fullscreen` / `data-pip` |\n| `BufferingOverlay` | `data-state=\"buffering\\|seeking\"`, `data-buffering`, `data-seeking`, `data-loading` |\n| `LoadingOverlay` / `ErrorOverlay` | `data-state=\"loading\"` / `data-state=\"error\"` |\n| `AudioProgress` / `AudioVolume` | `data-state`, `data-progress`/`data-volume`, `data-buffered`, `data-disabled` |\n| `CastButton` / `AirPlayButton` | `data-state`, `data-available`, `data-connecting`/`data-supported` |\n| `CaptionSelector` (trigger + options) | `data-state=\"active\\|idle\"` |\n| `SeekButton` | `data-seek-amount`, `data-seek-direction` |\n| `VideoContextMenu` | `data-state=\"open\\|closed\"` |\n| `CaptionRenderer` (window, when `draggable`) | `data-dragging` |\n\n**3. `--rm-*` design tokens.** Everything the package styles inline (overlays,\nstats panel, error fallback, accent color) reads a CSS custom property with a\nsafe inline fallback — override at `:root` (or any wrapper) to retheme. Defaults\nlive at the top of [styles.css](./styles.css):\n\n| Token | Default | Drives |\n| --- | --- | --- |\n| `--rm-accent` | `var(--color-primary, #8b5cf6)` | active caption track, selected states |\n| `--rm-danger` | `var(--color-destructive, #ef4444)` | error overlay + retry button |\n| `--rm-overlay-bg` | `#000` | loading/error full-cover backdrop |\n| `--rm-stats-bg` / `--rm-stats-fg` | `rgba(0,0,0,.85)` / `#fff` | stats \"for nerds\" panel |\n| `--rm-radius` | `0.5rem` | inline-styled buttons/panels corners |\n| `--rm-font-mono` | system mono stack | stats panel font |\n\n`--rm-accent` falls back to the shadcn `--color-primary`, so one shadcn/Tailwind\ntheme drives react-media, `@classytic/vixel-ui`, and your own UI together.\n\n**Importing `styles.css` stays optional** — components are fully functional\nunstyled (enforced by the no-styles smoke tests); `styles.css` carries only\npresentation (token defaults + `<video>` normalization), never behavior.\n\n### Movable captions — `position` + `draggable`\n\n`CaptionRenderer` (and everything that wraps it: `ConnectedCaptionRenderer`,\n`CaptionOverlay`, `VideoCaptions`) takes a `CaptionPosition`:\n\n```tsx\n// Edge presets — unchanged default behavior\n<ConnectedCaptionRenderer position=\"bottom\" />   // default\n<ConnectedCaptionRenderer position=\"top\" />\n\n// Fractional { x, y } — 0..1 of the container, addressing the WINDOW CENTER.\n// Resolution-independent: survives resizes and fullscreen toggles.\n<ConnectedCaptionRenderer position={{ x: 0.5, y: 0.15 }} />\n\n// YouTube-style movable captions: the window itself is the drag handle\n<ConnectedCaptionRenderer\n  draggable\n  onPositionChange={(pos) => savePreferredPosition(pos)} // { x, y } fractions\n/>\n```\n\nWith `draggable`, pointer-drag repositions the window (clamped inside the\ncontainer, `data-dragging` set on the window while active — style it via\n`classNames.window` + `data-[dragging=true]:…`). The window is focusable and\narrow keys nudge it by 2% per press (keyboard a11y). Controlled vs\nuncontrolled: a fractional `position` prop is controlled and always wins over\ndrag (listen to `onPositionChange` and feed it back); a string preset or no\n`position` leaves the component uncontrolled — the preset seeds the layout and\nthe user's drag position lives in internal state.\n\n### CSS Setup\n\nImport the styles in your global CSS (Tailwind v4):\n\n```css\n@import \"tailwindcss\";\n@import \"@classytic/react-media/styles.css\";\n```\n\nThe `@source` directive in the CSS tells Tailwind to scan the dist folder for class names.\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}