Rework everything to use videojs instead of shaka

This commit is contained in:
2025-10-13 12:27:40 +02:00
parent f3fdb8b648
commit c356fa10ec
6 changed files with 171 additions and 167 deletions

View File

@@ -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=="],

View File

@@ -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"
}
}

View File

@@ -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<typeof videojs>;
// 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<string, string> = {};
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<void> {
@@ -194,7 +186,7 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase {
}
async preload(): Promise<void> {
// 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<shaka.extern.PlayerConfiguration>);
}
// 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);
}
}

View File

@@ -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<typeof videojs>;
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() {

View File

@@ -1,7 +0,0 @@
declare module 'shaka-player' {
export = shaka;
}
declare module 'shaka-player/dist/shaka-player.compiled' {
export = shaka;
}

View File

@@ -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<VideoViewRef, VideoViewProps>(
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<VideoViewRef, VideoViewProps>(
);
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 (
<View {...props}>
<div
ref={vRef}
style={{ objectFit: resizeMode === "stretch" ? "fill" : resizeMode }}
style={{
position: "absolute",
inset: 0
}}
/>
</View>
);