Implement VideoView for web

This commit is contained in:
2025-09-29 12:37:03 +02:00
parent d2b2dac8c6
commit f877c1f915
6 changed files with 182 additions and 309 deletions

View File

@@ -108,6 +108,9 @@
"packages/react-native-video": {
"name": "react-native-video",
"version": "7.0.0-alpha.4",
"dependencies": {
"@types/react-native-web": "^0.19.2",
},
"devDependencies": {
"@expo/config-plugins": "^10.0.2",
"@react-native/eslint-config": "^0.77.0",
@@ -126,6 +129,7 @@
"typescript": "^5.2.2",
},
"optionalDependencies": {
"react-native-web": "^0.21.1",
"shaka-player": "^4.15.9",
},
"peerDependencies": {
@@ -1084,6 +1088,8 @@
"@types/react": ["@types/react@18.3.20", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg=="],
"@types/react-native-web": ["@types/react-native-web@0.19.2", "", { "dependencies": { "@types/react": "*", "react-native": "*" } }, "sha512-TAgyUi1XcYYUM5j3IwBDufPUD0NaOJiz8ftld6oJPtuIT5lc+uRZZmTcTdflpsVzCtiZ6AjSyLFZ/xD0OtCfJA=="],
"@types/react-router": ["@types/react-router@5.1.20", "", { "dependencies": { "@types/history": "^4.7.11", "@types/react": "*" } }, "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q=="],
"@types/react-router-config": ["@types/react-router-config@5.0.11", "", { "dependencies": { "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router": "^5.1.0" } }, "sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw=="],
@@ -1544,6 +1550,8 @@
"create-require": ["create-require@1.1.1", "", {}, "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="],
"cross-fetch": ["cross-fetch@3.2.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"crypto-random-string": ["crypto-random-string@4.0.0", "", { "dependencies": { "type-fest": "^1.0.1" } }, "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA=="],
@@ -1554,6 +1562,8 @@
"css-has-pseudo": ["css-has-pseudo@7.0.2", "", { "dependencies": { "@csstools/selector-specificity": "^5.0.0", "postcss-selector-parser": "^7.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-nzol/h+E0bId46Kn2dQH5VElaknX2Sr0hFuB/1EomdC7j+OISt2ZzK7EHX9DZDY53WbIVAR7FYKSO2XnSf07MQ=="],
"css-in-js-utils": ["css-in-js-utils@3.1.0", "", { "dependencies": { "hyphenate-style-name": "^1.0.3" } }, "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A=="],
"css-loader": ["css-loader@6.11.0", "", { "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.33", "postcss-modules-extract-imports": "^3.1.0", "postcss-modules-local-by-default": "^4.0.5", "postcss-modules-scope": "^3.2.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", "semver": "^7.5.4" }, "peerDependencies": { "@rspack/core": "0.x || 1.x", "webpack": "^5.0.0" }, "optionalPeers": ["@rspack/core", "webpack"] }, "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g=="],
"css-minimizer-webpack-plugin": ["css-minimizer-webpack-plugin@5.0.1", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", "cssnano": "^6.0.1", "jest-worker": "^29.4.3", "postcss": "^8.4.24", "schema-utils": "^4.0.1", "serialize-javascript": "^6.0.1" }, "peerDependencies": { "webpack": "^5.0.0" } }, "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg=="],
@@ -1864,6 +1874,10 @@
"fb-watchman": ["fb-watchman@2.0.2", "", { "dependencies": { "bser": "2.1.1" } }, "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA=="],
"fbjs": ["fbjs@3.0.5", "", { "dependencies": { "cross-fetch": "^3.1.5", "fbjs-css-vars": "^1.0.0", "loose-envify": "^1.0.0", "object-assign": "^4.1.0", "promise": "^7.1.1", "setimmediate": "^1.0.5", "ua-parser-js": "^1.0.35" } }, "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg=="],
"fbjs-css-vars": ["fbjs-css-vars@1.0.2", "", {}, "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ=="],
"feed": ["feed@4.2.2", "", { "dependencies": { "xml-js": "^1.6.11" } }, "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ=="],
"figures": ["figures@3.2.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg=="],
@@ -2086,6 +2100,8 @@
"human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
"hyphenate-style-name": ["hyphenate-style-name@1.1.0", "", {}, "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw=="],
"iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
"icss-utils": ["icss-utils@5.1.0", "", { "peerDependencies": { "postcss": "^8.1.0" } }, "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA=="],
@@ -2120,6 +2136,8 @@
"inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="],
"inline-style-prefixer": ["inline-style-prefixer@7.0.1", "", { "dependencies": { "css-in-js-utils": "^3.1.0" } }, "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw=="],
"inquirer": ["inquirer@9.3.2", "", { "dependencies": { "@inquirer/figures": "^1.0.3", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "external-editor": "^3.1.0", "mute-stream": "1.0.0", "ora": "^5.4.1", "run-async": "^3.0.0", "rxjs": "^7.8.1", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.1" } }, "sha512-+ynEbhWKhyomnaX0n2aLIMSkgSlGB5RrWbNXnEqj6mdaIydu6y40MdBjL38SAB0JcdmOaIaMua1azdjLEr3sdw=="],
"internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
@@ -2720,6 +2738,8 @@
"node-emoji": ["node-emoji@2.2.0", "", { "dependencies": { "@sindresorhus/is": "^4.6.0", "char-regex": "^1.0.2", "emojilib": "^2.4.0", "skin-tone": "^2.0.0" } }, "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw=="],
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"node-forge": ["node-forge@1.3.1", "", {}, "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="],
"node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="],
@@ -3100,6 +3120,8 @@
"react-native-video-example": ["react-native-video-example@workspace:example"],
"react-native-web": ["react-native-web@0.21.1", "", { "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", "fbjs": "^3.0.4", "inline-style-prefixer": "^7.0.1", "memoize-one": "^6.0.0", "nullthrows": "^1.1.1", "postcss-value-parser": "^4.2.0", "styleq": "^0.1.3" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-BeNsgwwe4AXUFPAoFU+DKjJ+CVQa3h54zYX77p7GVZrXiiNo3vl03WYDYVEy5R2J2HOPInXtQZB5gmj3vuzrKg=="],
"react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="],
"react-router": ["react-router@5.3.4", "", { "dependencies": { "@babel/runtime": "^7.12.13", "history": "^4.9.0", "hoist-non-react-statics": "^3.1.0", "loose-envify": "^1.3.1", "path-to-regexp": "^1.7.0", "prop-types": "^15.6.2", "react-is": "^16.6.0", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0" }, "peerDependencies": { "react": ">=15" } }, "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA=="],
@@ -3282,6 +3304,8 @@
"set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="],
"setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="],
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
"shaka-player": ["shaka-player@4.15.9", "", {}, "sha512-x8TElKG1eaj3nlcO9whvH1SbGvJ1gwSdgYlbvcJBNWsMJal70C2ZLk4w8qi8uXpcDjzSR8p28gYgD7WfOdHDGA=="],
@@ -3424,6 +3448,8 @@
"stylehacks": ["stylehacks@6.1.1", "", { "dependencies": { "browserslist": "^4.23.0", "postcss-selector-parser": "^6.0.16" }, "peerDependencies": { "postcss": "^8.4.31" } }, "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg=="],
"styleq": ["styleq@0.1.3", "", {}, "sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA=="],
"sudo-prompt": ["sudo-prompt@9.2.1", "", {}, "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw=="],
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
@@ -3476,6 +3502,8 @@
"totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
"trim-newlines": ["trim-newlines@4.1.1", "", {}, "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ=="],
@@ -3522,6 +3550,8 @@
"typescript-eslint": ["typescript-eslint@8.29.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.29.1", "@typescript-eslint/parser": "8.29.1", "@typescript-eslint/utils": "8.29.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-f8cDkvndhbQMPcysk6CUSGBWV+g1utqdn71P5YKwMumVMOG/5k7cHq0KyG4O52nB0oKS4aN2Tp5+wB4APJGC+w=="],
"ua-parser-js": ["ua-parser-js@1.0.41", "", { "bin": { "ua-parser-js": "script/cli.js" } }, "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug=="],
"uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="],
"uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="],
@@ -3620,6 +3650,8 @@
"web-namespaces": ["web-namespaces@1.1.4", "", {}, "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw=="],
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
"webpack": ["webpack@5.99.9", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.14.0", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.2", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg=="],
"webpack-bundle-analyzer": ["webpack-bundle-analyzer@4.10.2", "", { "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", "acorn-walk": "^8.0.0", "commander": "^7.2.0", "debounce": "^1.2.1", "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", "html-escaper": "^2.0.2", "opener": "^1.5.2", "picocolors": "^1.0.0", "sirv": "^2.0.3", "ws": "^7.3.1" }, "bin": { "webpack-bundle-analyzer": "lib/bin/analyzer.js" } }, "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw=="],
@@ -3640,6 +3672,8 @@
"whatwg-fetch": ["whatwg-fetch@3.6.20", "", {}, "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg=="],
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
"when-exit": ["when-exit@2.1.4", "", {}, "sha512-4rnvd3A1t16PWzrBUcSDZqcAmsUIy4minDXT/CZ8F2mVDgd65i4Aalimgz1aQkRGU0iH5eT5+6Rx2TK8o443Pg=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
@@ -4066,6 +4100,8 @@
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"fbjs/promise": ["promise@7.3.1", "", { "dependencies": { "asap": "~2.0.3" } }, "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg=="],
"figures/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
"file-loader/schema-utils": ["schema-utils@3.3.0", "", { "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } }, "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg=="],
@@ -4436,6 +4472,10 @@
"react-native-video-example/@react-native/eslint-config": ["@react-native/eslint-config@0.77.2", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/eslint-parser": "^7.25.1", "@react-native/eslint-plugin": "0.77.2", "@typescript-eslint/eslint-plugin": "^7.1.1", "@typescript-eslint/parser": "^7.1.1", "eslint-config-prettier": "^8.5.0", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-ft-flow": "^2.0.1", "eslint-plugin-jest": "^27.9.0", "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-native": "^4.0.0" }, "peerDependencies": { "eslint": ">=8", "prettier": ">=2" } }, "sha512-buxBnJU0YLxdkUqn85ZG7BoCjSEjia4HMcnl4X81UQSLM8Z0xCL01QeqHhxxfhYFFkiHwsJILBgHEZizx/hsdQ=="],
"react-native-web/@react-native/normalize-colors": ["@react-native/normalize-colors@0.74.89", "", {}, "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg=="],
"react-native-web/memoize-one": ["memoize-one@6.0.0", "", {}, "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="],
"react-router/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
"read-package-up/read-pkg": ["read-pkg@9.0.1", "", { "dependencies": { "@types/normalize-package-data": "^2.4.3", "normalize-package-data": "^6.0.0", "parse-json": "^8.0.0", "type-fest": "^4.6.0", "unicorn-magic": "^0.1.0" } }, "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA=="],

View File

@@ -152,6 +152,10 @@
"version": "0.41.2"
},
"optionalDependencies": {
"react-native-web": "^0.21.1",
"shaka-player": "^4.15.9"
},
"dependencies": {
"@types/react-native-web": "^0.19.2"
}
}

View File

@@ -1,6 +1,4 @@
import { Platform } from 'react-native';
import { NitroModules } from 'react-native-nitro-modules';
import { type VideoPlayer as VideoPlayerImpl } from '../spec/nitro/VideoPlayer.nitro';
import shaka from 'shaka-player';
import type { VideoPlayerSource } from '../spec/nitro/VideoPlayerSource.nitro';
import type { IgnoreSilentSwitchMode } from './types/IgnoreSilentSwitchMode';
import type { MixAudioMode } from './types/MixAudioMode';
@@ -13,20 +11,14 @@ import {
} from './types/VideoError';
import type { VideoPlayerBase } from './types/VideoPlayerBase';
import type { VideoPlayerStatus } from './types/VideoPlayerStatus';
import { createPlayer } from './utils/playerFactory';
import { createSource } from './utils/sourceFactory';
import { VideoPlayerEvents } from './VideoPlayerEvents';
class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase {
protected player: VideoPlayerImpl;
protected player = new shaka.Player();
constructor(source: VideoSource | VideoConfig | VideoPlayerSource) {
const hybridSource = createSource(source);
const player = createPlayer(hybridSource);
// Initialize events
super(player.eventEmitter);
this.player = player;
}
/**
@@ -35,8 +27,7 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase {
* @internal
*/
__destroy() {
this.clearAllEvents();
this.player.dispose();
this.player.destroy();
}
/**
@@ -153,12 +144,11 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase {
}
set ignoreSilentSwitchMode(value: IgnoreSilentSwitchMode) {
if (__DEV__ && !['ios'].includes(Platform.OS)) {
if (__DEV__) {
console.warn(
'ignoreSilentSwitchMode is not supported on this platform, it wont have any effect'
);
}
this.player.ignoreSilentSwitchMode = value;
}

View File

@@ -1,74 +1,14 @@
import * as React from 'react';
import type { ViewProps, ViewStyle } from 'react-native';
import type { ViewStyle } from 'react-native';
import { NitroModules } from 'react-native-nitro-modules';
import type {
VideoViewViewManager,
VideoViewViewManagerFactory,
} from '../../spec/nitro/VideoViewViewManager.nitro';
import type { VideoViewEvents } from '../types/Events';
import type { ResizeMode } from '../types/ResizeMode';
import { tryParseNativeVideoError, VideoError } from '../types/VideoError';
import type { VideoPlayer } from '../VideoPlayer';
import { NativeVideoView } from './NativeVideoView';
export interface VideoViewProps extends Partial<VideoViewEvents>, ViewProps {
/**
* The player to play the video - {@link VideoPlayer}
*/
player: VideoPlayer;
/**
* The style of the video view - {@link ViewStyle}
*/
style?: ViewStyle;
/**
* Whether to show the controls. Defaults to false.
*/
controls?: boolean;
/**
* Whether to enable & show the picture in picture button in native controls. Defaults to false.
*/
pictureInPicture?: boolean;
/**
* Whether to automatically enter picture in picture mode when the video is playing. Defaults to false.
*/
autoEnterPictureInPicture?: boolean;
/**
* How the video should be resized to fit the view. Defaults to 'none'.
* - 'contain': Scale the video uniformly (maintain aspect ratio) so that it fits entirely within the view
* - 'cover': Scale the video uniformly (maintain aspect ratio) so that it fills the entire view (may crop)
* - 'stretch': Scale the video to fill the entire view without maintaining aspect ratio
* - 'none': Do not resize the video
*/
resizeMode?: ResizeMode;
/**
* Whether to keep the screen awake while the video view is mounted. Defaults to true.
*/
keepScreenAwake?: boolean;
}
export interface VideoViewRef {
/**
* Enter fullscreen mode
*/
enterFullscreen: () => void;
/**
* Exit fullscreen mode
*/
exitFullscreen: () => void;
/**
* Enter picture in picture mode
*/
enterPictureInPicture: () => void;
/**
* Exit picture in picture mode
*/
exitPictureInPicture: () => void;
/**
* Check if picture in picture mode is supported
* @returns true if picture in picture mode is supported, false otherwise
*/
canEnterPictureInPicture: () => boolean;
}
import type { VideoViewProps, VideoViewRef } from './ViewViewProps';
let nitroIdCounter = 1;
const VideoViewViewManagerFactory =

View File

@@ -1,110 +1,20 @@
import * as React from 'react';
import type { ViewProps, ViewStyle } from 'react-native';
import { NitroModules } from 'react-native-nitro-modules';
import type {
VideoViewViewManager,
VideoViewViewManagerFactory,
} from '../../spec/nitro/VideoViewViewManager.nitro';
import type { VideoViewEvents } from '../types/Events';
import type { ResizeMode } from '../types/ResizeMode';
import { tryParseNativeVideoError, VideoError } from '../types/VideoError';
import type { VideoPlayer } from '../VideoPlayer';
import { NativeVideoView } from './NativeVideoView';
import {
forwardRef,
type HTMLProps,
memo,
useEffect,
useImperativeHandle,
useRef,
} from "react";
import type { ViewProps, ViewStyle } from "react-native";
import { unstable_createElement } from "react-native-web";
import { VideoError } from "../types/VideoError";
import type { VideoPlayer } from "../VideoPlayer.web";
import type { VideoViewProps, VideoViewRef } from "./ViewViewProps";
export interface VideoViewProps extends Partial<VideoViewEvents>, ViewProps {
/**
* The player to play the video - {@link VideoPlayer}
*/
player: VideoPlayer;
/**
* The style of the video view - {@link ViewStyle}
*/
style?: ViewStyle;
/**
* Whether to show the controls. Defaults to false.
*/
controls?: boolean;
/**
* Whether to enable & show the picture in picture button in native controls. Defaults to false.
*/
pictureInPicture?: boolean;
/**
* Whether to automatically enter picture in picture mode when the video is playing. Defaults to false.
*/
autoEnterPictureInPicture?: boolean;
/**
* How the video should be resized to fit the view. Defaults to 'none'.
* - 'contain': Scale the video uniformly (maintain aspect ratio) so that it fits entirely within the view
* - 'cover': Scale the video uniformly (maintain aspect ratio) so that it fills the entire view (may crop)
* - 'stretch': Scale the video to fill the entire view without maintaining aspect ratio
* - 'none': Do not resize the video
*/
resizeMode?: ResizeMode;
/**
* Whether to keep the screen awake while the video view is mounted. Defaults to true.
*/
keepScreenAwake?: boolean;
}
export interface VideoViewRef {
/**
* Enter fullscreen mode
*/
enterFullscreen: () => void;
/**
* Exit fullscreen mode
*/
exitFullscreen: () => void;
/**
* Enter picture in picture mode
*/
enterPictureInPicture: () => void;
/**
* Exit picture in picture mode
*/
exitPictureInPicture: () => void;
/**
* Check if picture in picture mode is supported
* @returns true if picture in picture mode is supported, false otherwise
*/
canEnterPictureInPicture: () => boolean;
}
let nitroIdCounter = 1;
const VideoViewViewManagerFactory =
NitroModules.createHybridObject<VideoViewViewManagerFactory>(
'VideoViewViewManagerFactory'
);
const wrapNativeViewManagerFunction = <T,>(
manager: VideoViewViewManager | null,
func: (manager: VideoViewViewManager) => T
) => {
try {
if (manager === null) {
throw new VideoError('view/not-found', 'View manager not found');
}
return func(manager);
} catch (error) {
throw tryParseNativeVideoError(error);
}
};
const updateProps = (manager: VideoViewViewManager, props: VideoViewProps) => {
manager.player = props.player.__getNativePlayer();
manager.controls = props.controls ?? false;
manager.pictureInPicture = props.pictureInPicture ?? false;
manager.autoEnterPictureInPicture = props.autoEnterPictureInPicture ?? false;
manager.resizeMode = props.resizeMode ?? 'none';
manager.onPictureInPictureChange = props.onPictureInPictureChange;
manager.onFullscreenChange = props.onFullscreenChange;
manager.willEnterFullscreen = props.willEnterFullscreen;
manager.willExitFullscreen = props.willExitFullscreen;
manager.willEnterPictureInPicture = props.willEnterPictureInPicture;
manager.willExitPictureInPicture = props.willExitPictureInPicture;
manager.keepScreenAwake = props.keepScreenAwake ?? true;
};
const Video = (
props: Omit<HTMLProps<HTMLVideoElement>, keyof ViewProps> & ViewProps,
) => unstable_createElement("video", props);
/**
* VideoView is a component that allows you to display a video from a {@link VideoPlayer}.
@@ -117,135 +27,61 @@ const updateProps = (manager: VideoViewViewManager, props: VideoViewProps) => {
* when the video is playing. Defaults to false.
* @param resizeMode - How the video should be resized to fit the view. Defaults to 'none'.
*/
const VideoView = React.forwardRef<VideoViewRef, VideoViewProps>(
const VideoView = forwardRef<VideoViewRef, VideoViewProps>(
(
{
player,
controls = false,
resizeMode = "none",
style,
// auto pip is unsupported
pictureInPicture = false,
autoEnterPictureInPicture = false,
resizeMode = 'none',
keepScreenAwake = true,
...props
},
ref
ref,
) => {
const nitroId = React.useMemo(() => nitroIdCounter++, []);
const nitroViewManager = React.useRef<VideoViewViewManager | null>(null);
const vRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
const webPlayer = player as unknown as VideoPlayer;
if (vRef.current) webPlayer.__getNativePlayer().attach(vRef.current);
}, [player]);
const setupViewManager = React.useCallback(
(id: number) => {
try {
if (nitroViewManager.current === null) {
nitroViewManager.current =
VideoViewViewManagerFactory.createViewManager(id);
// Should never happen
if (!nitroViewManager.current) {
throw new VideoError(
'view/not-found',
'Failed to create View Manager'
);
}
}
// Updates props to native view
updateProps(nitroViewManager.current, {
...props,
player: player,
controls: controls,
pictureInPicture: pictureInPicture,
autoEnterPictureInPicture: autoEnterPictureInPicture,
resizeMode: resizeMode,
});
} catch (error) {
throw tryParseNativeVideoError(error);
}
},
[
props,
player,
controls,
pictureInPicture,
autoEnterPictureInPicture,
resizeMode,
]
);
const onNitroIdChange = React.useCallback(
(event: { nativeEvent: { nitroId: number } }) => {
setupViewManager(event.nativeEvent.nitroId);
},
[setupViewManager]
);
React.useImperativeHandle(
useImperativeHandle(
ref,
() => ({
enterFullscreen: () => {
wrapNativeViewManagerFunction(nitroViewManager.current, (manager) => {
manager.enterFullscreen();
});
vRef.current?.requestFullscreen({ navigationUI: "hide" });
},
exitFullscreen: () => {
wrapNativeViewManagerFunction(nitroViewManager.current, (manager) => {
manager.exitFullscreen();
});
document.exitFullscreen();
},
enterPictureInPicture: () => {
wrapNativeViewManagerFunction(nitroViewManager.current, (manager) => {
manager.enterPictureInPicture();
});
vRef.current?.requestPictureInPicture();
},
exitPictureInPicture: () => {
wrapNativeViewManagerFunction(nitroViewManager.current, (manager) => {
manager.exitPictureInPicture();
});
},
canEnterPictureInPicture: () => {
return wrapNativeViewManagerFunction(
nitroViewManager.current,
(manager) => {
return manager.canEnterPictureInPicture();
}
);
document.exitPictureInPicture();
},
canEnterPictureInPicture: () => document.pictureInPictureEnabled,
}),
[]
[],
);
React.useEffect(() => {
if (!nitroViewManager.current) {
return;
}
// Updates props to native view
updateProps(nitroViewManager.current, {
...props,
player: player,
controls: controls,
pictureInPicture: pictureInPicture,
autoEnterPictureInPicture: autoEnterPictureInPicture,
resizeMode: resizeMode,
});
}, [
player,
controls,
pictureInPicture,
autoEnterPictureInPicture,
resizeMode,
props,
]);
return (
<NativeVideoView
nitroId={nitroId}
onNitroIdChange={onNitroIdChange}
<Video
ref={vRef}
controls={controls}
style={[
style,
{ objectFit: resizeMode === "stretch" ? "fill" : resizeMode },
]}
{...props}
/>
);
}
},
);
VideoView.displayName = 'VideoView';
VideoView.displayName = "VideoView";
export default React.memo(VideoView);
export default memo(VideoView);

View File

@@ -0,0 +1,63 @@
import type { ViewProps, ViewStyle } from "react-native";
import type { VideoViewEvents } from "../types/Events";
import type { ResizeMode } from "../types/ResizeMode";
import type { VideoPlayer } from "../VideoPlayer";
export interface VideoViewProps extends Partial<VideoViewEvents>, ViewProps {
/**
* The player to play the video - {@link VideoPlayer}
*/
player: VideoPlayer;
/**
* The style of the video view - {@link ViewStyle}
*/
style?: ViewStyle;
/**
* Whether to show the controls. Defaults to false.
*/
controls?: boolean;
/**
* Whether to enable & show the picture in picture button in native controls. Defaults to false.
*/
pictureInPicture?: boolean;
/**
* Whether to automatically enter picture in picture mode when the video is playing. Defaults to false.
*/
autoEnterPictureInPicture?: boolean;
/**
* How the video should be resized to fit the view. Defaults to 'none'.
* - 'contain': Scale the video uniformly (maintain aspect ratio) so that it fits entirely within the view
* - 'cover': Scale the video uniformly (maintain aspect ratio) so that it fills the entire view (may crop)
* - 'stretch': Scale the video to fill the entire view without maintaining aspect ratio
* - 'none': Do not resize the video
*/
resizeMode?: ResizeMode;
/**
* Whether to keep the screen awake while the video view is mounted. Defaults to true.
*/
keepScreenAwake?: boolean;
}
export interface VideoViewRef {
/**
* Enter fullscreen mode
*/
enterFullscreen: () => void;
/**
* Exit fullscreen mode
*/
exitFullscreen: () => void;
/**
* Enter picture in picture mode
*/
enterPictureInPicture: () => void;
/**
* Exit picture in picture mode
*/
exitPictureInPicture: () => void;
/**
* Check if picture in picture mode is supported
* @returns true if picture in picture mode is supported, false otherwise
*/
canEnterPictureInPicture: () => boolean;
}