diff --git a/bun.lock b/bun.lock index 3f66896c..e20ee426 100644 --- a/bun.lock +++ b/bun.lock @@ -79,7 +79,7 @@ }, "packages/drm-plugin": { "name": "@twg/react-native-video-drm", - "version": "0.1.0", + "version": "7.0.0-alpha.5", "devDependencies": { "@react-native/babel-preset": "0.79.2", "@release-it/conventional-changelog": "^9.0.2", @@ -108,13 +108,11 @@ "packages/react-native-video": { "name": "react-native-video", "version": "7.0.0-alpha.5", - "dependencies": { - "@types/react-native-web": "^0.19.2", - }, "devDependencies": { "@expo/config-plugins": "^10.0.2", "@react-native/eslint-config": "^0.77.0", "@types/react": "^18.2.44", + "@types/react-native-web": "^0.19.2", "del-cli": "^5.1.0", "eslint": "^8.51.0", "eslint-config-prettier": "^9.0.0", @@ -125,11 +123,8 @@ "react-native": "^0.77.0", "react-native-builder-bob": "^0.40.0", "react-native-nitro-modules": "^0.29.0", - "shaka-player": "^4.15.9", "typescript": "^5.2.2", - }, - "optionalDependencies": { - "shaka-player": "^4.15.9", + "video.js": "^8.23.4", }, "peerDependencies": { "react": "*", @@ -1159,6 +1154,12 @@ "@vercel/oidc": ["@vercel/oidc@3.0.1", "", {}, "sha512-V/YRVrJDqM6VaMBjRUrd6qRMrTKvZjHdVdEmdXsOZMulTa3iK98ijKTc3wldBmst6W5rHpqMoKllKcBAHgN7GQ=="], + "@videojs/http-streaming": ["@videojs/http-streaming@3.17.2", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@videojs/vhs-utils": "^4.1.1", "aes-decrypter": "^4.0.2", "global": "^4.4.0", "m3u8-parser": "^7.2.0", "mpd-parser": "^1.3.1", "mux.js": "7.1.0", "video.js": "^7 || ^8" } }, "sha512-VBQ3W4wnKnVKb/limLdtSD2rAd5cmHN70xoMf4OmuDd0t2kfJX04G+sfw6u2j8oOm2BXYM9E1f4acHruqKnM1g=="], + + "@videojs/vhs-utils": ["@videojs/vhs-utils@4.1.1", "", { "dependencies": { "@babel/runtime": "^7.12.5", "global": "^4.4.0" } }, "sha512-5iLX6sR2ownbv4Mtejw6Ax+naosGvoT9kY+gcuHzANyUZZ+4NpeNdKMUhb6ag0acYej1Y7cmr/F2+4PrggMiVA=="], + + "@videojs/xhr": ["@videojs/xhr@2.7.0", "", { "dependencies": { "@babel/runtime": "^7.5.5", "global": "~4.4.0", "is-function": "^1.0.1" } }, "sha512-giab+EVRanChIupZK7gXjHy90y3nncA2phIOyG3Ne5fvpiMJzvqYwiTOnEVW2S4CoYcuKJkomat7bMXA/UoUZQ=="], + "@webassemblyjs/ast": ["@webassemblyjs/ast@1.14.1", "", { "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ=="], "@webassemblyjs/floating-point-hex-parser": ["@webassemblyjs/floating-point-hex-parser@1.13.2", "", {}, "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA=="], @@ -1217,6 +1218,8 @@ "address": ["address@1.2.2", "", {}, "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA=="], + "aes-decrypter": ["aes-decrypter@4.0.2", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@videojs/vhs-utils": "^4.1.1", "global": "^4.4.0", "pkcs7": "^1.0.4" } }, "sha512-lc+/9s6iJvuaRe5qDlMTpCFjnwpkeOXp8qP3oiZ5jsj1MRg+SBVUmmICrhxHvc8OELSmc+fEyyxAuppY6hrWzw=="], + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="], @@ -1697,6 +1700,8 @@ "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], + "dom-walk": ["dom-walk@0.1.2", "", {}, "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="], + "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], @@ -1981,6 +1986,8 @@ "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="], + "global": ["global@4.4.0", "", { "dependencies": { "min-document": "^2.19.0", "process": "^0.11.10" } }, "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w=="], + "global-directory": ["global-directory@4.0.1", "", { "dependencies": { "ini": "4.1.1" } }, "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q=="], "global-dirs": ["global-dirs@0.1.1", "", { "dependencies": { "ini": "^1.3.4" } }, "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg=="], @@ -2191,6 +2198,8 @@ "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + "is-function": ["is-function@1.0.2", "", {}, "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ=="], + "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], "is-git-dirty": ["is-git-dirty@2.0.2", "", { "dependencies": { "execa": "^4.0.3", "is-git-repository": "^2.0.0" } }, "sha512-U3YCo+GKR/rDsY7r0v/LBICbQwsx859tDQnAT+v0E/zCDeWbQ1TUt1FtyExeyik7VIJlYOLHCIifLdz71HDalg=="], @@ -2459,6 +2468,8 @@ "lunr-languages": ["lunr-languages@1.14.0", "", {}, "sha512-hWUAb2KqM3L7J5bcrngszzISY4BxrXn/Xhbb9TTCJYEGqlR1nG67/M14sp09+PTIRklobrn57IAxcdcO/ZFyNA=="], + "m3u8-parser": ["m3u8-parser@7.2.0", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@videojs/vhs-utils": "^4.1.1", "global": "^4.4.0" } }, "sha512-CRatFqpjVtMiMaKXxNvuI3I++vUumIXVVT/JpCpdU/FynV/ceVw1qpPyyBNindL+JlPMSesx+WX1QJaZEJSaMQ=="], + "macos-release": ["macos-release@3.4.0", "", {}, "sha512-wpGPwyg/xrSp4H4Db4xYSeAr6+cFQGHfspHzDUdYxswDnUW0L5Ov63UuJiSr8NMSpyaChO4u1n0MXUvVPtrN6A=="], "make-dir": ["make-dir@2.1.0", "", { "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" } }, "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA=="], @@ -2657,6 +2668,8 @@ "mimic-response": ["mimic-response@4.0.0", "", {}, "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="], + "min-document": ["min-document@2.19.0", "", { "dependencies": { "dom-walk": "^0.1.0" } }, "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ=="], + "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], "mini-css-extract-plugin": ["mini-css-extract-plugin@2.9.4", "", { "dependencies": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" }, "peerDependencies": { "webpack": "^5.0.0" } }, "sha512-ZWYT7ln73Hptxqxk2DxPU9MmapXRhxkJD6tkSR04dnQxm8BGu2hzgKLugK5yySD97u/8yy7Ma7E76k9ZdvtjkQ=="], @@ -2673,6 +2686,8 @@ "mkdirp": ["mkdirp@0.3.0", "", {}, "sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew=="], + "mpd-parser": ["mpd-parser@1.3.1", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@videojs/vhs-utils": "^4.0.0", "@xmldom/xmldom": "^0.8.3", "global": "^4.4.0" }, "bin": { "mpd-to-m3u8-json": "bin/parse.js" } }, "sha512-1FuyEWI5k2HcmhS1HkKnUAQV7yFPfXPht2DnRRGtoiiAAW+ESTbtEXIDpRkwdU+XyrQuwrIym7UkoPKsZ0SyFw=="], + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -2681,6 +2696,8 @@ "mute-stream": ["mute-stream@1.0.0", "", {}, "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA=="], + "mux.js": ["mux.js@7.1.0", "", { "dependencies": { "@babel/runtime": "^7.11.2", "global": "^4.4.0" }, "bin": { "muxjs-transmux": "bin/transmux.js" } }, "sha512-NTxawK/BBELJrYsZThEulyUMDVlLizKdxyAsMuzoCD1eFj97BVaA8D/CvKsKu6FOLYkFojN5CbM9h++ZTZtknA=="], + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], @@ -2851,6 +2868,8 @@ "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], + "pkcs7": ["pkcs7@1.0.4", "", { "dependencies": { "@babel/runtime": "^7.5.5" }, "bin": { "pkcs7": "bin/cli.js" } }, "sha512-afRERtHn54AlwaF2/+LFszyAANTCggGilmcmILUzEjvs3XgFZT+xE6+QWQcAGmu4xajy+Xtj7acLOPdx5/eXWQ=="], + "pkg-dir": ["pkg-dir@7.0.0", "", { "dependencies": { "find-up": "^6.3.0" } }, "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA=="], "plist": ["plist@3.1.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ=="], @@ -3005,6 +3024,8 @@ "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], + "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], + "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], "promise": ["promise@8.3.0", "", { "dependencies": { "asap": "~2.0.6" } }, "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg=="], @@ -3257,8 +3278,6 @@ "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], - "shaka-player": ["shaka-player@4.16.3", "", {}, "sha512-KFvcg78uwoAyYJAlEa8y673XpPZeQCVG9L1ikoCSUMhCHeNNkNiG1FB4LZwGYN1IrwVEPH3IJVpWoB7RU4d0HA=="], - "shallow-clone": ["shallow-clone@3.0.1", "", { "dependencies": { "kind-of": "^6.0.2" } }, "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA=="], "shallowequal": ["shallowequal@1.1.0", "", {}, "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="], @@ -3587,6 +3606,14 @@ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + "video.js": ["video.js@8.23.4", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@videojs/http-streaming": "^3.17.2", "@videojs/vhs-utils": "^4.1.1", "@videojs/xhr": "2.7.0", "aes-decrypter": "^4.0.2", "global": "4.4.0", "m3u8-parser": "^7.2.0", "mpd-parser": "^1.3.1", "mux.js": "^7.0.1", "videojs-contrib-quality-levels": "4.1.0", "videojs-font": "4.2.0", "videojs-vtt.js": "0.15.5" } }, "sha512-qI0VTlYmKzEqRsz1Nppdfcaww4RSxZAq77z2oNSl3cNg2h6do5C8Ffl0KqWQ1OpD8desWXsCrde7tKJ9gGTEyQ=="], + + "videojs-contrib-quality-levels": ["videojs-contrib-quality-levels@4.1.0", "", { "dependencies": { "global": "^4.4.0" }, "peerDependencies": { "video.js": "^8" } }, "sha512-TfrXJJg1Bv4t6TOCMEVMwF/CoS8iENYsWNKip8zfhB5kTcegiFYezEA0eHAJPU64ZC8NQbxQgOwAsYU8VXbOWA=="], + + "videojs-font": ["videojs-font@4.2.0", "", {}, "sha512-YPq+wiKoGy2/M7ccjmlvwi58z2xsykkkfNMyIg4xb7EZQQNwB71hcSsB3o75CqQV7/y5lXkXhI/rsGAS7jfEmQ=="], + + "videojs-vtt.js": ["videojs-vtt.js@0.15.5", "", { "dependencies": { "global": "^4.3.1" } }, "sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ=="], + "vlq": ["vlq@1.0.1", "", {}, "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w=="], "walk-sync": ["walk-sync@2.2.0", "", { "dependencies": { "@types/minimatch": "^3.0.3", "ensure-posix-path": "^1.1.0", "matcher-collection": "^2.0.0", "minimatch": "^3.0.4" } }, "sha512-IC8sL7aB4/ZgFcGI2T1LczZeFWZ06b3zoHH7jBPyHxOtIIz1jppWHjjEXkOFvFojBVAK9pV7g47xOZ4LW3QLfg=="], diff --git a/packages/react-native-video/package.json b/packages/react-native-video/package.json index 55e49b4a..3db527c3 100644 --- a/packages/react-native-video/package.json +++ b/packages/react-native-video/package.json @@ -77,6 +77,7 @@ "@expo/config-plugins": "^10.0.2", "@react-native/eslint-config": "^0.77.0", "@types/react": "^18.2.44", + "@types/react-native-web": "^0.19.2", "del-cli": "^5.1.0", "eslint": "^8.51.0", "eslint-config-prettier": "^9.0.0", @@ -85,10 +86,10 @@ "prettier": "^3.0.3", "react": "18.3.1", "react-native": "^0.77.0", - "shaka-player": "^4.15.9", "react-native-builder-bob": "^0.40.0", "react-native-nitro-modules": "^0.29.0", - "typescript": "^5.2.2" + "typescript": "^5.2.2", + "video.js": "^8.23.4" }, "peerDependencies": { "react": "*", @@ -150,11 +151,5 @@ "type": "view-legacy", "languages": "kotlin-swift", "version": "0.41.2" - }, - "optionalDependencies": { - "shaka-player": "^4.15.9" - }, - "dependencies": { - "@types/react-native-web": "^0.19.2" } } diff --git a/packages/react-native-video/src/core/VideoPlayer.web.ts b/packages/react-native-video/src/core/VideoPlayer.web.ts index d102b216..d5ad5823 100644 --- a/packages/react-native-video/src/core/VideoPlayer.web.ts +++ b/packages/react-native-video/src/core/VideoPlayer.web.ts @@ -1,4 +1,3 @@ -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 +12,37 @@ import type { VideoPlayerBase } from "./types/VideoPlayerBase"; import type { VideoPlayerStatus } from "./types/VideoPlayerStatus"; import { VideoPlayerEvents } from "./VideoPlayerEvents"; import { WebEventEmiter } from "./WebEventEmiter"; +import videojs from "video.js"; + +type VideoJsPlayer = ReturnType; + +// declared https://github.com/videojs/video.js/blob/main/src/js/tracks/track-list.js#L58 +type VideoJsTextTracks = { + length: number; + [i: number]: { + // declared: https://github.com/videojs/video.js/blob/main/src/js/tracks/track.js + id: string; + label: string; + language: string; + // declared https://github.com/videojs/video.js/blob/20f8d76cd24325a97ccedf0b013cd1a90ad0bcd7/src/js/tracks/text-track.js + default: boolean; + mode: "showing" | "disabled" | "hidden"; + }; +}; class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase { - protected player = new shaka.Player(); protected video: HTMLVideoElement; - protected headers: Record = {}; + public player: VideoJsPlayer; constructor(source: VideoSource | VideoConfig | VideoPlayerSource) { const video = document.createElement("video"); - super(new WebEventEmiter(video)); + const player = videojs(video); + + super(new WebEventEmiter(player)); + this.video = video; - this.player.attach(this.video); - this.player.getNetworkingEngine()!.registerRequestFilter((_type, request) => { - request.headers = this.headers; - }); + this.player = player; + this.replaceSourceAsync(source); } @@ -36,7 +52,7 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase { * @internal */ __destroy() { - this.player.destroy(); + this.player.dispose(); } __getNativeRef() { @@ -65,7 +81,7 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase { get source(): VideoPlayerSource { // TODO: properly implement this return { - uri: this.player.getAssetUri()!, + uri: this.player.src(undefined), config: {}, } as any; } @@ -84,52 +100,52 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase { // Duration get duration(): number { - return this.video.duration; + return this.player.duration() ?? NaN; } // Volume get volume(): number { - return this.video.volume; + return this.player.volume() ?? 1; } set volume(value: number) { - this.video.volume = value; + this.player.volume(value); } // Current Time get currentTime(): number { - return this.video.currentTime; + return this.player.currentTime() ?? NaN; } set currentTime(value: number) { - this.video.currentTime = value; + this.player.currentTime(value); } // Muted get muted(): boolean { - return this.video.muted; + return this.player.muted() ?? false; } set muted(value: boolean) { - this.video.muted = value; + this.player.muted(value); } // Loop get loop(): boolean { - return this.video.loop; + return this.player.loop() ?? false; } set loop(value: boolean) { - this.video.loop = value; + this.player.loop(value); } // Rate get rate(): number { - return this.video.playbackRate; + return this.player.playbackRate() ?? 1; } set rate(value: number) { - this.video.playbackRate = value; + this.player.playbackRate(value); } // Mix Audio Mode @@ -137,56 +153,32 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase { return "auto"; } - set mixAudioMode(_: MixAudioMode) { - if (__DEV__) { - console.warn( - "mixAudioMode is not supported on this platform, it wont have any effect", - ); - } - } + set mixAudioMode(_: MixAudioMode) { } // Ignore Silent Switch Mode get ignoreSilentSwitchMode(): IgnoreSilentSwitchMode { return "auto"; } - set ignoreSilentSwitchMode(_: IgnoreSilentSwitchMode) { - if (__DEV__) { - console.warn( - "ignoreSilentSwitchMode is not supported on this platform, it wont have any effect", - ); - } - } + set ignoreSilentSwitchMode(_: IgnoreSilentSwitchMode) { } // Play In Background get playInBackground(): boolean { return true; } - set playInBackground(_: boolean) { - if (__DEV__) { - console.warn( - "playInBackground is not supported on this platform, it wont have any effect", - ); - } - } + set playInBackground(_: boolean) { } // Play When Inactive get playWhenInactive(): boolean { return true; } - set playWhenInactive(_: boolean) { - if (__DEV__) { - console.warn( - "playWhenInactive is not supported on this platform, it wont have any effect", - ); - } - } + set playWhenInactive(_: boolean) { } // Is Playing get isPlaying(): boolean { - return this.status === "readyToPlay" && !this.video.paused; + return !this.player.paused(); } async initialize(): Promise { @@ -194,7 +186,7 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase { } async preload(): Promise { - // we start loading when initializing the source. + this.player.load() } /** @@ -208,35 +200,20 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase { } play(): void { - try { - this.video.play(); - } catch (error) { - this.throwError(error); - } + this.player.play()?.catch(this.throwError); } pause(): void { - try { - this.video.pause(); - } catch (error) { - this.throwError(error); - } + this.player.pause(); } seekBy(time: number): void { - try { - this.video.currentTime += time; - } catch (error) { - this.throwError(error); - } + const now = this.player.currentTime() ?? 0; + this.player.currentTime(now + time); } seekTo(time: number): void { - try { - this.video.currentTime = time; - } catch (error) { - this.throwError(error); - } + this.player.currentTime(time); } async replaceSourceAsync( @@ -251,44 +228,43 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase { ? source.uri : source; if (typeof src === "number") { - console.error("A source uri must be a string. Numbers are only supported on native."); + console.error( + "A source uri must be a string. Numbers are only supported on native.", + ); return; } // TODO: handle start time - this.player.load(src) + this.player.src(src); if (typeof source !== "object") return; - - this.headers = source?.headers ?? {}; - // this.player.configure({ - // drm: undefined, - // streaming: { - // bufferingGoal: source?.bufferConfig?.maxBufferMs, - // }, - // } satisfies Partial); } // Text Track Management + getAvailableTextTracks(): TextTrack[] { - return this.player.getTextTracks().map(x => ({ - id: x.id.toString(), - label: x.label ?? "", - language: x.language, - selected: x.active, + // @ts-expect-error they define length & index properties via prototype + const tracks: VideoJsTextTracks = this.player.textTracks(); + + return [...Array(tracks.length)].map((_, i) => ({ + id: tracks[i]!.id, + label: tracks[i]!.label, + language: tracks[i]!.language, + selected: tracks[i]!.mode === "showing", })); } selectTextTrack(textTrack: TextTrack | null): void { - this.player.setTextTrackVisibility(textTrack !== null) - if (!textTrack) return; - const track = this.player - .getTextTracks() - .find((x) => x.id === Number(textTrack.id)); - if (track) this.player.selectTextTrack(track); + // @ts-expect-error they define length & index properties via prototype + const tracks: VideoJsTextTracks = this.player.textTracks(); + + for (let i = 0; i < tracks.length; i++) { + if (tracks[i]!.mode === "showing") tracks[i]!.mode = "disabled"; + if (tracks[i]!.id === textTrack?.id) tracks[i]!.mode = "showing"; + } } // Selected Text Track get selectedTrack(): TextTrack | undefined { - return this.getAvailableTextTracks().find(x => x.selected); + return this.getAvailableTextTracks().find((x) => x.selected); } } diff --git a/packages/react-native-video/src/core/WebEventEmiter.ts b/packages/react-native-video/src/core/WebEventEmiter.ts index 93defaf9..e41d31d2 100644 --- a/packages/react-native-video/src/core/WebEventEmiter.ts +++ b/packages/react-native-video/src/core/WebEventEmiter.ts @@ -1,3 +1,4 @@ +import type videojs from "video.js"; import type { BandwidthData, onLoadData, @@ -12,91 +13,91 @@ import type { TextTrack } from "./types/TextTrack"; import type { VideoRuntimeError } from "./types/VideoError"; import type { VideoPlayerStatus } from "./types/VideoPlayerStatus"; +type VideoJsPlayer = ReturnType; + export class WebEventEmiter implements PlayerEvents { private _isBuferring = false; - constructor(private video: HTMLVideoElement) { + constructor(private player: VideoJsPlayer) { // TODO: add `onBandwithUpdate` // on buffer this._onCanPlay = this._onCanPlay.bind(this); this._onWaiting = this._onWaiting.bind(this); - this.video.addEventListener("canplay", this._onCanPlay); - this.video.addEventListener("waiting", this._onWaiting); + this.player.on("canplay", this._onCanPlay); + this.player.on("waiting", this._onWaiting); // on end this._onEnded = this._onEnded.bind(this); - this.video.addEventListener("ended", this._onEnded); + this.player.on("ended", this._onEnded); // on load this._onDurationChange = this._onDurationChange.bind(this); - this.video.addEventListener("durationchange", this._onDurationChange); + this.player.on("durationchange", this._onDurationChange); // on load start this._onLoadStart = this._onLoadStart.bind(this); - this.video.addEventListener("loadstart", this._onLoadStart); + this.player.on("loadstart", this._onLoadStart); // on playback state change this._onPlay = this._onPlay.bind(this); this._onPause = this._onPause.bind(this); - this.video.addEventListener("play", this._onPlay); - this.video.addEventListener("pause", this._onPause); + this.player.on("play", this._onPlay); + this.player.on("pause", this._onPause); // on playback rate change this._onRateChange = this._onRateChange.bind(this); - this.video.addEventListener("ratechange", this._onRateChange); + this.player.on("ratechange", this._onRateChange); // on progress this._onTimeUpdate = this._onTimeUpdate.bind(this); - this.video.addEventListener("timeupdate", this._onTimeUpdate); + this.player.on("timeupdate", this._onTimeUpdate); // on ready to play this._onLoadedData = this._onLoadedData.bind(this); - this.video.addEventListener("loadeddata", this._onLoadedData); + this.player.on("loadeddata", this._onLoadedData); // on seek this._onSeeked = this._onSeeked.bind(this); - this.video.addEventListener("seeked", this._onSeeked); + this.player.on("seeked", this._onSeeked); // on volume change this._onVolumeChange = this._onVolumeChange.bind(this); - this.video.addEventListener("volumechange", this._onVolumeChange); + this.player.on("volumechange", this._onVolumeChange); // on status change this._onError = this._onError.bind(this); - this.video.addEventListener("error", this._onError); + this.player.on("error", this._onError); } destroy() { - this.video.removeEventListener("canplay", this._onCanPlay); - this.video.removeEventListener("waiting", this._onWaiting); + this.player.off("canplay", this._onCanPlay); + this.player.off("waiting", this._onWaiting); - this.video.removeEventListener("ended", this._onEnded); + this.player.off("ended", this._onEnded); - this.video.removeEventListener("durationchange", this._onDurationChange); + this.player.off("durationchange", this._onDurationChange); - this.video.removeEventListener("play", this._onPlay); - this.video.removeEventListener("pause", this._onPause); + this.player.off("play", this._onPlay); + this.player.off("pause", this._onPause); - this.video.removeEventListener("ratechange", this._onRateChange); + this.player.off("ratechange", this._onRateChange); - this.video.removeEventListener("timeupdate", this._onTimeUpdate); + this.player.off("timeupdate", this._onTimeUpdate); - this.video.removeEventListener("loadeddata", this._onLoadedData); + this.player.off("loadeddata", this._onLoadedData); - this.video.removeEventListener("seeked", this._onSeeked); + this.player.off("seeked", this._onSeeked); - this.video.removeEventListener("volumechange", this._onVolumeChange); + this.player.off("volumechange", this._onVolumeChange); - this.video.removeEventListener("error", this._onError); + this.player.off("error", this._onError); } _onTimeUpdate() { this.onProgress({ - currentTime: this.video.currentTime, - bufferDuration: this.video.buffered.length - ? this.video.buffered.end(this.video.buffered.length - 1) - : 0, + currentTime: this.player.currentTime() ?? 0, + bufferDuration: this.player.bufferedEnd(), }); } @@ -113,10 +114,10 @@ export class WebEventEmiter implements PlayerEvents { _onDurationChange() { this.onLoad({ - currentTime: this.video.currentTime, - duration: this.video.duration, - width: this.video.width, - height: this.video.height, + currentTime: this.player.currentTime() ?? 0, + duration: this.player.duration() ?? NaN, + width: this.player.width() ?? NaN, + height: this.player.height() ?? NaN, orientation: "unknown", }); } @@ -130,16 +131,16 @@ export class WebEventEmiter implements PlayerEvents { this.onLoadStart({ sourceType: "network", source: { - uri: this.video.currentSrc, + uri: this.player.src(undefined)!, config: { - uri: this.video.currentSrc, + uri: this.player.src(undefined)!, externalSubtitles: [], }, getAssetInformationAsync: async () => { return { - duration: BigInt(this.video.duration), - height: this.video.height, - width: this.video.width, + duration: BigInt(this.player.duration() ?? NaN), + height: this.player.height() ?? NaN, + width: this.player.width() ?? NaN, orientation: "unknown", bitrate: NaN, fileSize: BigInt(NaN), @@ -166,7 +167,7 @@ export class WebEventEmiter implements PlayerEvents { } _onRateChange() { - this.onPlaybackRateChange(this.video.playbackRate); + this.onPlaybackRateChange(this.player.playbackRate() ?? 1); } _onLoadedData() { @@ -174,11 +175,14 @@ export class WebEventEmiter implements PlayerEvents { } _onSeeked() { - this.onSeek(this.video.currentTime); + this.onSeek(this.player.currentTime() ?? 0); } _onVolumeChange() { - this.onVolumeChange({ muted: this.video.muted, volume: this.video.volume }); + this.onVolumeChange({ + muted: this.player.muted() ?? false, + volume: this.player.volume() ?? 1, + }); } _onError() { diff --git a/packages/react-native-video/src/core/shaka.d.ts b/packages/react-native-video/src/core/shaka.d.ts deleted file mode 100644 index ac61c902..00000000 --- a/packages/react-native-video/src/core/shaka.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare module 'shaka-player' { - export = shaka; -} - -declare module 'shaka-player/dist/shaka-player.compiled' { - export = shaka; -} diff --git a/packages/react-native-video/src/core/video-view/VideoView.web.tsx b/packages/react-native-video/src/core/video-view/VideoView.web.tsx index 7b034254..367be1b3 100644 --- a/packages/react-native-video/src/core/video-view/VideoView.web.tsx +++ b/packages/react-native-video/src/core/video-view/VideoView.web.tsx @@ -4,6 +4,7 @@ import { useEffect, useImperativeHandle, useRef, + type CSSProperties, } from "react"; import { View, type ViewStyle } from "react-native"; import type { VideoPlayer } from "../VideoPlayer.web"; @@ -39,22 +40,20 @@ const VideoView = forwardRef( useEffect(() => { const videoElement = player.__getNativeRef(); vRef.current?.appendChild(videoElement); - return () => { - vRef.current?.removeChild(videoElement); - }; + return () => vRef.current?.replaceChildren(); }, [player]); useImperativeHandle( ref, () => ({ enterFullscreen: () => { - player.__getNativeRef().requestFullscreen({ navigationUI: "hide" }); + player.player.requestFullscreen({ navigationUI: "hide" }); }, exitFullscreen: () => { document.exitFullscreen(); }, enterPictureInPicture: () => { - player.__getNativeRef().requestPictureInPicture(); + player.player.requestPictureInPicture(); }, exitPictureInPicture: () => { document.exitPictureInPicture(); @@ -65,14 +64,24 @@ const VideoView = forwardRef( ); useEffect(() => { - player.__getNativeRef().controls = controls; + player.player.controls(controls); }, [player, controls]); + useEffect(() => { + const vid = player.__getNativeRef(); + const objectFit: CSSProperties["objectFit"] = + resizeMode === "stretch" ? "fill" : resizeMode; + vid.style = `position: absolute; inset: 0; width: 100%; height: 100%; object-fit: ${objectFit}`; + }, [player, resizeMode]); + return (
);