Merge pull request #289 from Stremio/core-async-api

Core async api
This commit is contained in:
Nikola Hristov 2022-09-13 08:52:41 +03:00 committed by GitHub
commit a1c008e33f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 842 additions and 620 deletions

479
package-lock.json generated
View file

@ -12,7 +12,7 @@
"@babel/runtime": "7.16.0",
"@sentry/browser": "6.13.3",
"@stremio/stremio-colors": "4.0.1",
"@stremio/stremio-core-web": "0.43.0",
"@stremio/stremio-core-web": "0.44.0",
"@stremio/stremio-icons": "3.0.5",
"@stremio/stremio-video": "0.0.20-rc.4",
"a-color-picker": "1.2.1",
@ -22,13 +22,16 @@
"eventemitter3": "4.0.7",
"filter-invalid-dom-props": "2.1.0",
"lodash.debounce": "4.0.8",
"lodash.intersection": "4.4.0",
"lodash.isequal": "4.5.0",
"lodash.throttle": "4.1.1",
"prop-types": "15.7.2",
"react": "16.12.0",
"react-dom": "16.12.0",
"react-focus-lock": "2.2.1",
"spatial-navigation-polyfill": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6"
"react": "18.2.0",
"react-dom": "18.2.0",
"react-focus-lock": "2.9.1",
"react-is": "18.2.0",
"spatial-navigation-polyfill": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
"url": "0.11.0"
},
"devDependencies": {
"@babel/core": "7.16.0",
@ -283,12 +286,12 @@
}
},
"node_modules/@babel/helper-module-imports": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz",
"integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==",
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
"integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
"dev": true,
"dependencies": {
"@babel/types": "^7.16.0"
"@babel/types": "^7.18.6"
},
"engines": {
"node": ">=6.9.0"
@ -326,9 +329,9 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
"version": "7.14.5",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz",
"integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==",
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.6.tgz",
"integrity": "sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg==",
"dev": true,
"engines": {
"node": ">=6.9.0"
@ -400,9 +403,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.15.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
"integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz",
"integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==",
"dev": true,
"engines": {
"node": ">=6.9.0"
@ -1722,12 +1725,12 @@
}
},
"node_modules/@babel/types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz",
"integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==",
"version": "7.18.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.7.tgz",
"integrity": "sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ==",
"dev": true,
"dependencies": {
"@babel/helper-validator-identifier": "^7.15.7",
"@babel/helper-validator-identifier": "^7.18.6",
"to-fast-properties": "^2.0.0"
},
"engines": {
@ -2613,9 +2616,9 @@
"integrity": "sha512-yT3No1gIWKLV2BhQIeSgG94EzXxmEqXJLulO+pFpziqWNUbmmEKeE+nRvW5wtoIK4SLy+v0bLd0b6HBH3KFfWw=="
},
"node_modules/@stremio/stremio-core-web": {
"version": "0.43.0",
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.43.0.tgz",
"integrity": "sha512-EXr2PQpviUS4kF7gRUnTFDshsDh4gh/pp/ACaIgqXe6qZS4YlmPhLtXDxDKdBnulhjABW29GVul/vuuiEtff3w==",
"version": "0.44.0",
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.0.tgz",
"integrity": "sha512-zTUS6J1RAj6OiFnSD9gTfP8Va4MsgAz9QAynP1T1rO3nVJLCy8PolJf0qZWl/KERoeWuL1IE3X8Zjk+VPMkkeA==",
"dependencies": {
"@babel/runtime": "7.15.4"
}
@ -3970,26 +3973,31 @@
"dev": true
},
"node_modules/browserslist": {
"version": "4.17.5",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.5.tgz",
"integrity": "sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA==",
"version": "4.21.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.1.tgz",
"integrity": "sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
}
],
"dependencies": {
"caniuse-lite": "^1.0.30001271",
"electron-to-chromium": "^1.3.878",
"escalade": "^3.1.1",
"node-releases": "^2.0.1",
"picocolors": "^1.0.0"
"caniuse-lite": "^1.0.30001359",
"electron-to-chromium": "^1.4.172",
"node-releases": "^2.0.5",
"update-browserslist-db": "^1.0.4"
},
"bin": {
"browserslist": "cli.js"
},
"engines": {
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
}
},
"node_modules/bser": {
@ -4105,14 +4113,20 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001274",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz",
"integrity": "sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew==",
"version": "1.0.30001363",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001363.tgz",
"integrity": "sha512-HpQhpzTGGPVMnCjIomjt+jvyUu8vNFo3TaDiZ/RcoTrlOq/5+tC8zHdsbgFB6MxmaY+jCpsH09aD80Bb4Ow3Sg==",
"dev": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
}
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
}
]
},
"node_modules/chalk": {
"version": "2.4.2",
@ -4600,12 +4614,12 @@
}
},
"node_modules/core-js-compat": {
"version": "3.19.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.0.tgz",
"integrity": "sha512-R09rKZ56ccGBebjTLZHvzDxhz93YPT37gBm6qUhnwj3Kt7aCjjZWD1injyNbyeFHxNKfeZBSyds6O9n3MKq1sw==",
"version": "3.23.3",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.23.3.tgz",
"integrity": "sha512-WSzUs2h2vvmKsacLHNTdpyOC9k43AEhcGoFlVgCY4L7aw98oSBKtPL6vD0/TqZjRWRQYdDSLkzZIni4Crbbiqw==",
"dev": true,
"dependencies": {
"browserslist": "^4.17.5",
"browserslist": "^4.21.0",
"semver": "7.0.0"
},
"funding": {
@ -5259,9 +5273,9 @@
"dev": true
},
"node_modules/electron-to-chromium": {
"version": "1.3.885",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.885.tgz",
"integrity": "sha512-JXKFJcVWrdHa09n4CNZYfYaK6EW5aAew7/wr3L1OnsD1L+JHL+RCtd7QgIsxUbFPeTwPlvnpqNNTOLkoefmtXg==",
"version": "1.4.180",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.180.tgz",
"integrity": "sha512-7at5ash3FD9U5gPa3/wPr6OdiZd/zBjvDZaaHBpcqFOFUhZiWnb7stkqk8xUFL9H9nk7Yok5vCCNK8wyC/+f8A==",
"dev": true
},
"node_modules/emittery": {
@ -6334,9 +6348,20 @@
"dev": true
},
"node_modules/focus-lock": {
"version": "0.6.8",
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.6.8.tgz",
"integrity": "sha512-vkHTluRCoq9FcsrldC0ulQHiyBYgVJB2CX53I8r0nTC6KnEij7Of0jpBspjt3/CuNb6fyoj3aOh9J2HgQUM0og=="
"version": "0.11.2",
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.11.2.tgz",
"integrity": "sha512-pZ2bO++NWLHhiKkgP1bEXHhR1/OjVcSvlCJ98aNJDFeb7H5OOQaO+SKOZle6041O9rv2tmbrO4JzClAvDUHf0g==",
"dependencies": {
"tslib": "^2.0.3"
},
"engines": {
"node": ">=10"
}
},
"node_modules/focus-lock/node_modules/tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"node_modules/follow-redirects": {
"version": "1.14.8",
@ -9595,6 +9620,11 @@
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
},
"node_modules/lodash.intersection": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.intersection/-/lodash.intersection-4.4.0.tgz",
"integrity": "sha512-N+L0cCfnqMv6mxXtSPeKt+IavbOBBSiAEkKyLasZ8BVcP9YXQgxLO12oPR8OyURwKV8l5vJKiE1M8aS70heuMg=="
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
@ -10032,9 +10062,9 @@
}
},
"node_modules/node-releases": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz",
"integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==",
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz",
"integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==",
"dev": true
},
"node_modules/normalize-path": {
@ -11293,6 +11323,11 @@
"react-is": "^16.8.1"
}
},
"node_modules/prop-types/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@ -11417,63 +11452,65 @@
}
},
"node_modules/react": {
"version": "16.12.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.12.0.tgz",
"integrity": "sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==",
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2"
"loose-envify": "^1.1.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-clientside-effect": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.5.tgz",
"integrity": "sha512-2bL8qFW1TGBHozGGbVeyvnggRpMjibeZM2536AKNENLECutp2yfs44IL8Hmpn8qjFQ2K7A9PnYf3vc7aQq/cPA==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz",
"integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==",
"dependencies": {
"@babel/runtime": "^7.12.13"
},
"peerDependencies": {
"react": "^15.3.0 || ^16.0.0 || ^17.0.0"
"react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-dom": {
"version": "16.12.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.12.0.tgz",
"integrity": "sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==",
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.18.0"
"scheduler": "^0.23.0"
},
"peerDependencies": {
"react": "^16.0.0"
"react": "^18.2.0"
}
},
"node_modules/react-focus-lock": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.2.1.tgz",
"integrity": "sha512-47g0xYcCTZccdzKRGufepY8oZ3W1Qg+2hn6u9SHZ0zUB6uz/4K4xJe7yYFNZ1qT6m+2JDm82F6QgKeBTbjW4PQ==",
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.1.tgz",
"integrity": "sha512-pSWOQrUmiKLkffPO6BpMXN7SNKXMsuOakl652IBuALAu1esk+IcpJyM+ALcYzPTTFz1rD0R54aB9A4HuP5t1Wg==",
"dependencies": {
"@babel/runtime": "^7.0.0",
"focus-lock": "^0.6.6",
"focus-lock": "^0.11.2",
"prop-types": "^15.6.2",
"react-clientside-effect": "^1.2.2",
"use-callback-ref": "^1.2.1",
"use-sidecar": "^1.0.1"
"react-clientside-effect": "^1.2.6",
"use-callback-ref": "^1.3.0",
"use-sidecar": "^1.1.2"
},
"peerDependencies": {
"react": "^16.8.0"
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
},
"node_modules/readable-stream": {
"version": "3.6.0",
@ -11801,12 +11838,11 @@
}
},
"node_modules/scheduler": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz",
"integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==",
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
"loose-envify": "^1.1.0"
}
},
"node_modules/schema-utils": {
@ -12896,6 +12932,32 @@
"node": ">= 0.8"
}
},
"node_modules/update-browserslist-db": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz",
"integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
}
],
"dependencies": {
"escalade": "^3.1.1",
"picocolors": "^1.0.0"
},
"bin": {
"browserslist-lint": "cli.js"
},
"peerDependencies": {
"browserslist": ">= 4.21.0"
}
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@ -12924,15 +12986,18 @@
}
},
"node_modules/use-callback-ref": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz",
"integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz",
"integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==",
"dependencies": {
"tslib": "^2.0.0"
},
"engines": {
"node": ">=8.5.0"
"node": ">=10"
},
"peerDependencies": {
"@types/react": "^16.8.0 || ^17.0.0",
"react": "^16.8.0 || ^17.0.0"
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
@ -12940,21 +13005,37 @@
}
}
},
"node_modules/use-callback-ref/node_modules/tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"node_modules/use-sidecar": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.0.5.tgz",
"integrity": "sha512-k9jnrjYNwN6xYLj1iaGhonDghfvmeTmYjAiGvOr7clwKfPjMXJf4/HOr7oT5tJwYafgp2tG2l3eZEOfoELiMcA==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
"integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
"dependencies": {
"detect-node-es": "^1.1.0",
"tslib": "^1.9.3"
"tslib": "^2.0.0"
},
"engines": {
"node": ">=8.5.0"
"node": ">=10"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0"
"@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/use-sidecar/node_modules/tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -14023,12 +14104,12 @@
}
},
"@babel/helper-module-imports": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz",
"integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==",
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
"integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
"dev": true,
"requires": {
"@babel/types": "^7.16.0"
"@babel/types": "^7.18.6"
}
},
"@babel/helper-module-transforms": {
@ -14057,9 +14138,9 @@
}
},
"@babel/helper-plugin-utils": {
"version": "7.14.5",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz",
"integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==",
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.6.tgz",
"integrity": "sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg==",
"dev": true
},
"@babel/helper-remap-async-to-generator": {
@ -14113,9 +14194,9 @@
}
},
"@babel/helper-validator-identifier": {
"version": "7.15.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
"integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz",
"integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==",
"dev": true
},
"@babel/helper-validator-option": {
@ -15003,12 +15084,12 @@
}
},
"@babel/types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz",
"integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==",
"version": "7.18.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.7.tgz",
"integrity": "sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.15.7",
"@babel/helper-validator-identifier": "^7.18.6",
"to-fast-properties": "^2.0.0"
}
},
@ -15684,9 +15765,9 @@
"integrity": "sha512-yT3No1gIWKLV2BhQIeSgG94EzXxmEqXJLulO+pFpziqWNUbmmEKeE+nRvW5wtoIK4SLy+v0bLd0b6HBH3KFfWw=="
},
"@stremio/stremio-core-web": {
"version": "0.43.0",
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.43.0.tgz",
"integrity": "sha512-EXr2PQpviUS4kF7gRUnTFDshsDh4gh/pp/ACaIgqXe6qZS4YlmPhLtXDxDKdBnulhjABW29GVul/vuuiEtff3w==",
"version": "0.44.0",
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.0.tgz",
"integrity": "sha512-zTUS6J1RAj6OiFnSD9gTfP8Va4MsgAz9QAynP1T1rO3nVJLCy8PolJf0qZWl/KERoeWuL1IE3X8Zjk+VPMkkeA==",
"requires": {
"@babel/runtime": "7.15.4"
},
@ -16829,16 +16910,15 @@
"dev": true
},
"browserslist": {
"version": "4.17.5",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.5.tgz",
"integrity": "sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA==",
"version": "4.21.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.1.tgz",
"integrity": "sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ==",
"dev": true,
"requires": {
"caniuse-lite": "^1.0.30001271",
"electron-to-chromium": "^1.3.878",
"escalade": "^3.1.1",
"node-releases": "^2.0.1",
"picocolors": "^1.0.0"
"caniuse-lite": "^1.0.30001359",
"electron-to-chromium": "^1.4.172",
"node-releases": "^2.0.5",
"update-browserslist-db": "^1.0.4"
}
},
"bser": {
@ -16930,9 +17010,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001274",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz",
"integrity": "sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew==",
"version": "1.0.30001363",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001363.tgz",
"integrity": "sha512-HpQhpzTGGPVMnCjIomjt+jvyUu8vNFo3TaDiZ/RcoTrlOq/5+tC8zHdsbgFB6MxmaY+jCpsH09aD80Bb4Ow3Sg==",
"dev": true
},
"chalk": {
@ -17312,12 +17392,12 @@
}
},
"core-js-compat": {
"version": "3.19.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.0.tgz",
"integrity": "sha512-R09rKZ56ccGBebjTLZHvzDxhz93YPT37gBm6qUhnwj3Kt7aCjjZWD1injyNbyeFHxNKfeZBSyds6O9n3MKq1sw==",
"version": "3.23.3",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.23.3.tgz",
"integrity": "sha512-WSzUs2h2vvmKsacLHNTdpyOC9k43AEhcGoFlVgCY4L7aw98oSBKtPL6vD0/TqZjRWRQYdDSLkzZIni4Crbbiqw==",
"dev": true,
"requires": {
"browserslist": "^4.17.5",
"browserslist": "^4.21.0",
"semver": "7.0.0"
},
"dependencies": {
@ -17820,9 +17900,9 @@
"dev": true
},
"electron-to-chromium": {
"version": "1.3.885",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.885.tgz",
"integrity": "sha512-JXKFJcVWrdHa09n4CNZYfYaK6EW5aAew7/wr3L1OnsD1L+JHL+RCtd7QgIsxUbFPeTwPlvnpqNNTOLkoefmtXg==",
"version": "1.4.180",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.180.tgz",
"integrity": "sha512-7at5ash3FD9U5gPa3/wPr6OdiZd/zBjvDZaaHBpcqFOFUhZiWnb7stkqk8xUFL9H9nk7Yok5vCCNK8wyC/+f8A==",
"dev": true
},
"emittery": {
@ -18646,9 +18726,19 @@
"dev": true
},
"focus-lock": {
"version": "0.6.8",
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.6.8.tgz",
"integrity": "sha512-vkHTluRCoq9FcsrldC0ulQHiyBYgVJB2CX53I8r0nTC6KnEij7Of0jpBspjt3/CuNb6fyoj3aOh9J2HgQUM0og=="
"version": "0.11.2",
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.11.2.tgz",
"integrity": "sha512-pZ2bO++NWLHhiKkgP1bEXHhR1/OjVcSvlCJ98aNJDFeb7H5OOQaO+SKOZle6041O9rv2tmbrO4JzClAvDUHf0g==",
"requires": {
"tslib": "^2.0.3"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"follow-redirects": {
"version": "1.14.8",
@ -21030,6 +21120,11 @@
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
},
"lodash.intersection": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.intersection/-/lodash.intersection-4.4.0.tgz",
"integrity": "sha512-N+L0cCfnqMv6mxXtSPeKt+IavbOBBSiAEkKyLasZ8BVcP9YXQgxLO12oPR8OyURwKV8l5vJKiE1M8aS70heuMg=="
},
"lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
@ -21372,9 +21467,9 @@
"dev": true
},
"node-releases": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz",
"integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==",
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz",
"integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==",
"dev": true
},
"normalize-path": {
@ -22249,6 +22344,13 @@
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
},
"dependencies": {
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}
}
},
"proxy-addr": {
@ -22340,51 +22442,47 @@
}
},
"react": {
"version": "16.12.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.12.0.tgz",
"integrity": "sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==",
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2"
"loose-envify": "^1.1.0"
}
},
"react-clientside-effect": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.5.tgz",
"integrity": "sha512-2bL8qFW1TGBHozGGbVeyvnggRpMjibeZM2536AKNENLECutp2yfs44IL8Hmpn8qjFQ2K7A9PnYf3vc7aQq/cPA==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz",
"integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==",
"requires": {
"@babel/runtime": "^7.12.13"
}
},
"react-dom": {
"version": "16.12.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.12.0.tgz",
"integrity": "sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==",
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.18.0"
"scheduler": "^0.23.0"
}
},
"react-focus-lock": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.2.1.tgz",
"integrity": "sha512-47g0xYcCTZccdzKRGufepY8oZ3W1Qg+2hn6u9SHZ0zUB6uz/4K4xJe7yYFNZ1qT6m+2JDm82F6QgKeBTbjW4PQ==",
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.1.tgz",
"integrity": "sha512-pSWOQrUmiKLkffPO6BpMXN7SNKXMsuOakl652IBuALAu1esk+IcpJyM+ALcYzPTTFz1rD0R54aB9A4HuP5t1Wg==",
"requires": {
"@babel/runtime": "^7.0.0",
"focus-lock": "^0.6.6",
"focus-lock": "^0.11.2",
"prop-types": "^15.6.2",
"react-clientside-effect": "^1.2.2",
"use-callback-ref": "^1.2.1",
"use-sidecar": "^1.0.1"
"react-clientside-effect": "^1.2.6",
"use-callback-ref": "^1.3.0",
"use-sidecar": "^1.1.2"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
},
"readable-stream": {
"version": "3.6.0",
@ -22632,12 +22730,11 @@
}
},
"scheduler": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz",
"integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==",
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
"loose-envify": "^1.1.0"
}
},
"schema-utils": {
@ -23497,6 +23594,16 @@
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
"dev": true
},
"update-browserslist-db": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz",
"integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==",
"dev": true,
"requires": {
"escalade": "^3.1.1",
"picocolors": "^1.0.0"
}
},
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@ -23524,18 +23631,34 @@
}
},
"use-callback-ref": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz",
"integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==",
"requires": {}
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz",
"integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==",
"requires": {
"tslib": "^2.0.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"use-sidecar": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.0.5.tgz",
"integrity": "sha512-k9jnrjYNwN6xYLj1iaGhonDghfvmeTmYjAiGvOr7clwKfPjMXJf4/HOr7oT5tJwYafgp2tG2l3eZEOfoELiMcA==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
"integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
"requires": {
"detect-node-es": "^1.1.0",
"tslib": "^1.9.3"
"tslib": "^2.0.0"
},
"dependencies": {
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
}
}
},
"util-deprecate": {

View file

@ -15,7 +15,7 @@
"@babel/runtime": "7.16.0",
"@sentry/browser": "6.13.3",
"@stremio/stremio-colors": "4.0.1",
"@stremio/stremio-core-web": "0.43.0",
"@stremio/stremio-core-web": "0.44.0",
"@stremio/stremio-icons": "3.0.5",
"@stremio/stremio-video": "0.0.20-rc.4",
"a-color-picker": "1.2.1",
@ -25,13 +25,16 @@
"eventemitter3": "4.0.7",
"filter-invalid-dom-props": "2.1.0",
"lodash.debounce": "4.0.8",
"lodash.intersection": "4.4.0",
"lodash.isequal": "4.5.0",
"lodash.throttle": "4.1.1",
"prop-types": "15.7.2",
"react": "16.12.0",
"react-dom": "16.12.0",
"react-focus-lock": "2.2.1",
"spatial-navigation-polyfill": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6"
"react": "18.2.0",
"react-dom": "18.2.0",
"react-is": "18.2.0",
"react-focus-lock": "2.9.1",
"spatial-navigation-polyfill": "git+https://git@github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
"url": "0.11.0"
},
"devDependencies": {
"@babel/core": "7.16.0",

View file

@ -5,24 +5,21 @@ const React = require('react');
const { Router } = require('stremio-router');
const { Core, Shell, Chromecast, KeyboardShortcuts, ServicesProvider } = require('stremio/services');
const { NotFound } = require('stremio/routes');
const { ToastProvider, sanitizeLocationPath, CONSTANTS } = require('stremio/common');
const { ToastProvider, CONSTANTS } = require('stremio/common');
const CoreEventsToaster = require('./CoreEventsToaster');
const ErrorDialog = require('./ErrorDialog');
const routerViewsConfig = require('./routerViewsConfig');
const styles = require('./styles');
window.core_imports = {
app_version: process.env.VERSION,
shell_version: null,
sanitize_location_path: sanitizeLocationPath
};
const App = () => {
const onPathNotMatch = React.useCallback(() => {
return NotFound;
}, []);
const services = React.useMemo(() => ({
core: new Core(),
core: new Core({
appVersion: process.env.VERSION,
shellVersion: null
}),
shell: new Shell(),
chromecast: new Chromecast(),
keyboardShortcuts: new KeyboardShortcuts()
@ -93,6 +90,12 @@ const App = () => {
action: 'PullAddonsFromAPI'
}
});
services.core.transport.dispatch({
action: 'Ctx',
args: {
action: 'PullUserFromAPI'
}
});
}
}, [initialized]);
return (

View file

@ -5,6 +5,7 @@
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
:global {
@import (once, less) '~stremio/common/animations.less';
@import (once, less) '~stremio-router/styles.css';
}

View file

@ -3,6 +3,7 @@
const React = require('react');
const PropTypes = require('prop-types');
const ModalDialog = require('stremio/common/ModalDialog');
const { withCoreSuspender } = require('stremio/common/CoreSuspender');
const { useServices } = require('stremio/services');
const AddonDetailsWithRemoteAndLocalAddon = withRemoteAndLocalAddon(require('./AddonDetails'));
const useAddonDetails = require('./useAddonDetails');
@ -144,4 +145,18 @@ AddonDetailsModal.propTypes = {
onCloseRequest: PropTypes.func
};
module.exports = AddonDetailsModal;
const AddonDetailsModalFallback = ({ onCloseRequest }) => (
<ModalDialog
className={styles['addon-details-modal-container']}
title={'Stremio addon'}
onCloseRequest={onCloseRequest}
>
<div className={styles['addon-details-message-container']}>
Loading addon manifest
</div>
</ModalDialog>
);
AddonDetailsModalFallback.propTypes = AddonDetailsModal.propTypes;
module.exports = withCoreSuspender(AddonDetailsModal, AddonDetailsModalFallback);

View file

@ -3,12 +3,6 @@
const React = require('react');
const useModelState = require('stremio/common/useModelState');
const init = () => ({
selected: null,
localAddon: null,
remoteAddon: null
});
const useAddonDetails = (transportUrl) => {
const action = React.useMemo(() => {
if (typeof transportUrl === 'string') {
@ -27,7 +21,7 @@ const useAddonDetails = (transportUrl) => {
};
}
}, [transportUrl]);
return useModelState({ model: 'addon_details', action, init });
return useModelState({ model: 'addon_details', action });
};
module.exports = useAddonDetails;

View file

@ -0,0 +1,78 @@
// Copyright (C) 2017-2022 Smart code 203358507
const React = require('react');
const { useServices } = require('stremio/services');
const CoreSuspenderContext = React.createContext(null);
CoreSuspenderContext.displayName = 'CoreSuspenderContext';
function wrapPromise(promise) {
let status = 'pending';
let result;
const suspender = promise.then(
(resp) => {
status = 'success';
result = resp;
},
(error) => {
status = 'error';
result = error;
}
);
return {
read() {
if (status === 'pending') {
throw suspender;
} else if (status === 'error') {
throw result;
} else if (status === 'success') {
return result;
}
}
};
}
const useCoreSuspender = () => {
return React.useContext(CoreSuspenderContext);
};
const withCoreSuspender = (Component, Fallback = () => { }) => {
return function withCoreSuspender(props) {
const { core } = useServices();
const parentSuspender = useCoreSuspender();
const [render, setRender] = React.useState(parentSuspender === null);
const statesRef = React.useRef({});
const streamsRef = React.useRef({});
const getState = React.useCallback((model) => {
if (!statesRef.current[model]) {
statesRef.current[model] = wrapPromise(core.transport.getState(model));
}
return statesRef.current[model].read();
}, []);
const decodeStream = React.useCallback((stream) => {
if (!streamsRef.current[stream]) {
streamsRef.current[stream] = wrapPromise(core.transport.decodeStream(stream));
}
return streamsRef.current[stream].read();
}, []);
const suspender = React.useMemo(() => ({ getState, decodeStream }), []);
React.useLayoutEffect(() => {
if (!render) {
setRender(true);
}
}, []);
return render ?
<React.Suspense fallback={<Fallback {...props} />}>
<CoreSuspenderContext.Provider value={suspender}>
<Component {...props} />
</CoreSuspenderContext.Provider>
</React.Suspense>
:
null;
};
};
module.exports = { withCoreSuspender, useCoreSuspender };

View file

@ -0,0 +1,23 @@
// Copyright (C) 2017-2022 Smart code 203358507
const React = require('react');
const PropTypes = require('prop-types');
const DelayedRenderer = ({ children, delay }) => {
const [render, setRender] = React.useState(false);
React.useEffect(() => {
const timeout = setTimeout(() => {
setRender(true);
}, delay);
return () => {
clearTimeout(timeout);
};
}, []);
return render ? children : null;
};
DelayedRenderer.propTypes = {
children: PropTypes.node
};
module.exports = DelayedRenderer;

View file

@ -0,0 +1,5 @@
// Copyright (C) 2017-2022 Smart code 203358507
const DelayedRenderer = require('./DelayedRenderer');
module.exports = DelayedRenderer;

View file

@ -3,22 +3,14 @@
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const Icon = require('@stremio/stremio-icons/dom');
const { useRouteFocused } = require('stremio-router');
const { useServices } = require('stremio/services');
const Button = require('stremio/common/Button');
const Popup = require('stremio/common/Popup');
const useBinaryState = require('stremio/common/useBinaryState');
const useFullscreen = require('stremio/common/useFullscreen');
const useProfile = require('stremio/common/useProfile');
const styles = require('./styles');
const NavMenuContent = require('./NavMenuContent');
const NavMenu = (props) => {
const { core } = useServices();
const routeFocused = useRouteFocused();
const profile = useProfile();
const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false);
const [fullscreen, requestFullscreen, exitFullscreen] = useFullscreen();
const popupLabelOnClick = React.useCallback((event) => {
if (!event.nativeEvent.togglePopupPrevented) {
toggleMenu();
@ -27,14 +19,6 @@ const NavMenu = (props) => {
const popupMenuOnClick = React.useCallback((event) => {
event.nativeEvent.togglePopupPrevented = true;
}, []);
const logoutButtonOnClick = React.useCallback(() => {
core.transport.dispatch({
action: 'Ctx',
args: {
action: 'Logout'
}
});
}, []);
const renderLabel = React.useMemo(() => ({ ref, className, children }) => (
props.renderLabel({
ref,
@ -44,65 +28,8 @@ const NavMenu = (props) => {
})
), [menuOpen, popupLabelOnClick, props.renderLabel]);
const renderMenu = React.useCallback(() => (
<div className={styles['nav-menu-container']} onClick={popupMenuOnClick}>
<div className={styles['user-info-container']}>
<div
className={styles['avatar-container']}
style={{
backgroundImage: profile.auth === null ?
`url('${require('/images/anonymous.png')}')`
:
`url('${profile.auth.user.avatar}'), url('${require('/images/default_avatar.png')}')`
}}
/>
<div className={styles['email-container']}>
<div className={styles['email-label']}>{profile.auth === null ? 'Anonymous user' : profile.auth.user.email}</div>
</div>
<Button className={styles['logout-button-container']} title={profile.auth === null ? 'Log in / Sign up' : 'Log out'} href={'#/intro'} onClick={logoutButtonOnClick}>
<div className={styles['logout-label']}>{profile.auth === null ? 'Log in / Sign up' : 'Log out'}</div>
</Button>
</div>
<div className={styles['nav-menu-section']}>
<Button className={styles['nav-menu-option-container']} title={fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'} onClick={fullscreen ? exitFullscreen : requestFullscreen}>
<Icon className={styles['icon']} icon={fullscreen ? 'ic_exit_fullscreen' : 'ic_fullscreen'} />
<div className={styles['nav-menu-option-label']}>{fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'}</div>
</Button>
</div>
<div className={styles['nav-menu-section']}>
<Button className={styles['nav-menu-option-container']} title={'Settings'} href={'#/settings'}>
<Icon className={styles['icon']} icon={'ic_settings'} />
<div className={styles['nav-menu-option-label']}>Settings</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Addons'} href={'#/addons'}>
<Icon className={styles['icon']} icon={'ic_addons'} />
<div className={styles['nav-menu-option-label']}>Addons</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Remote Control'} disabled={true}>
<Icon className={styles['icon']} icon={'ic_remote'} />
<div className={styles['nav-menu-option-label']}>Remote Control</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Play Magnet Link'} disabled={true}>
<Icon className={styles['icon']} icon={'ic_magnet'} />
<div className={styles['nav-menu-option-label']}>Play Magnet Link</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Help & Feedback'} href={'https://stremio.zendesk.com/'} target={'_blank'}>
<Icon className={styles['icon']} icon={'ic_help'} />
<div className={styles['nav-menu-option-label']}>Help & Feedback</div>
</Button>
</div>
<div className={styles['nav-menu-section']}>
<Button className={styles['nav-menu-option-container']} title={'Terms of Service'} href={'https://www.stremio.com/tos'} target={'_blank'}>
<div className={styles['nav-menu-option-label']}>Terms of Service</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Privacy Policy'} href={'https://www.stremio.com/privacy'} target={'_blank'}>
<div className={styles['nav-menu-option-label']}>Privacy Policy</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'About Stremio'} href={'https://www.stremio.com/'} target={'_blank'}>
<div className={styles['nav-menu-option-label']}>About Stremio</div>
</Button>
</div>
</div>
), [profile, fullscreen]);
<NavMenuContent onClick={popupMenuOnClick} />
), []);
React.useEffect(() => {
if (!routeFocused) {
closeMenu();
@ -111,6 +38,7 @@ const NavMenu = (props) => {
return (
<Popup
open={menuOpen}
direction={'bottom-left'}
onCloseRequest={closeMenu}
renderLabel={renderLabel}
renderMenu={renderMenu}

View file

@ -0,0 +1,96 @@
// Copyright (C) 2017-2022 Smart code 203358507
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const Icon = require('@stremio/stremio-icons/dom');
const { useServices } = require('stremio/services');
const Button = require('stremio/common/Button');
const useFullscreen = require('stremio/common/useFullscreen');
const useProfile = require('stremio/common/useProfile');
const { withCoreSuspender } = require('stremio/common/CoreSuspender');
const styles = require('./styles');
const NavMenuContent = ({ onClick }) => {
const { core } = useServices();
const profile = useProfile();
const [fullscreen, requestFullscreen, exitFullscreen] = useFullscreen();
const logoutButtonOnClick = React.useCallback(() => {
core.transport.dispatch({
action: 'Ctx',
args: {
action: 'Logout'
}
});
}, []);
return (
<div className={classnames(styles['nav-menu-container'], 'animation-fade-in')} onClick={onClick}>
<div className={styles['user-info-container']}>
<div
className={styles['avatar-container']}
style={{
backgroundImage: profile.auth === null ?
`url('${require('/images/anonymous.png')}')`
:
`url('${profile.auth.user.avatar}'), url('${require('/images/default_avatar.png')}')`
}}
/>
<div className={styles['email-container']}>
<div className={styles['email-label']}>{profile.auth === null ? 'Anonymous user' : profile.auth.user.email}</div>
</div>
<Button className={styles['logout-button-container']} title={profile.auth === null ? 'Log in / Sign up' : 'Log out'} href={'#/intro'} onClick={logoutButtonOnClick}>
<div className={styles['logout-label']}>{profile.auth === null ? 'Log in / Sign up' : 'Log out'}</div>
</Button>
</div>
<div className={styles['nav-menu-section']}>
<Button className={styles['nav-menu-option-container']} title={fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'} onClick={fullscreen ? exitFullscreen : requestFullscreen}>
<Icon className={styles['icon']} icon={fullscreen ? 'ic_exit_fullscreen' : 'ic_fullscreen'} />
<div className={styles['nav-menu-option-label']}>{fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'}</div>
</Button>
</div>
<div className={styles['nav-menu-section']}>
<Button className={styles['nav-menu-option-container']} title={'Settings'} href={'#/settings'}>
<Icon className={styles['icon']} icon={'ic_settings'} />
<div className={styles['nav-menu-option-label']}>Settings</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Addons'} href={'#/addons'}>
<Icon className={styles['icon']} icon={'ic_addons'} />
<div className={styles['nav-menu-option-label']}>Addons</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Remote Control'} disabled={true}>
<Icon className={styles['icon']} icon={'ic_remote'} />
<div className={styles['nav-menu-option-label']}>Remote Control</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Play Magnet Link'} disabled={true}>
<Icon className={styles['icon']} icon={'ic_magnet'} />
<div className={styles['nav-menu-option-label']}>Play Magnet Link</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Help & Feedback'} href={'https://stremio.zendesk.com/'} target={'_blank'}>
<Icon className={styles['icon']} icon={'ic_help'} />
<div className={styles['nav-menu-option-label']}>Help & Feedback</div>
</Button>
</div>
<div className={styles['nav-menu-section']}>
<Button className={styles['nav-menu-option-container']} title={'Terms of Service'} href={'https://www.stremio.com/tos'} target={'_blank'}>
<div className={styles['nav-menu-option-label']}>Terms of Service</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'Privacy Policy'} href={'https://www.stremio.com/privacy'} target={'_blank'}>
<div className={styles['nav-menu-option-label']}>Privacy Policy</div>
</Button>
<Button className={styles['nav-menu-option-container']} title={'About Stremio'} href={'https://www.stremio.com/'} target={'_blank'}>
<div className={styles['nav-menu-option-label']}>About Stremio</div>
</Button>
</div>
</div>
);
};
NavMenuContent.propTypes = {
onClick: PropTypes.func
};
const NavMenuContentFallback = () => (
<div className={styles['nav-menu-container']} />
);
module.exports = withCoreSuspender(NavMenuContent, NavMenuContentFallback);

View file

@ -6,6 +6,7 @@ const PropTypes = require('prop-types');
const classnames = require('classnames');
const Button = require('stremio/common/Button');
const useProfile = require('stremio/common/useProfile');
const { withCoreSuspender } = require('stremio/common/CoreSuspender');
const styles = require('./styles');
const StreamingServerWarning = ({ className }) => {
@ -39,6 +40,12 @@ const StreamingServerWarning = ({ className }) => {
}
});
}, [profile.settings]);
if (!isNaN(profile.settings.streamingServerWarningDismissed.getTime()) &&
profile.settings.streamingServerWarningDismissed.getTime() > Date.now()) {
return null;
}
return (
<div className={classnames(className, styles['warning-container'])}>
<div className={styles['warning-statement']}>Streaming server is not available.</div>
@ -56,4 +63,4 @@ StreamingServerWarning.propTypes = {
className: PropTypes.string
};
module.exports = StreamingServerWarning;
module.exports = withCoreSuspender(StreamingServerWarning);

View file

@ -0,0 +1,22 @@
// Copyright (C) 2017-2022 Smart code 203358507
:global(.animation-fade-in) {
:local {
animation-name: fade-in;
}
animation-timing-function: ease-in-out;
animation-duration: 100ms;
}
@keyframes fade-in {
0% {
opacity: 0.6;
transform: translateY(0.2vh);
}
100% {
opacity: 1;
transform: translateY(0);
}
}

View file

@ -4,6 +4,7 @@ const AddonDetailsModal = require('./AddonDetailsModal');
const Button = require('./Button');
const Checkbox = require('./Checkbox');
const ColorInput = require('./ColorInput');
const DelayedRenderer = require('./DelayedRenderer');
const Image = require('./Image');
const LibItem = require('./LibItem');
const MainNavBars = require('./MainNavBars');
@ -24,15 +25,13 @@ const TextInput = require('./TextInput');
const { ToastProvider, useToast } = require('./Toast');
const comparatorWithPriorities = require('./comparatorWithPriorities');
const CONSTANTS = require('./CONSTANTS');
const { withCoreSuspender, useCoreSuspender } = require('./CoreSuspender');
const getVisibleChildrenRange = require('./getVisibleChildrenRange');
const languageNames = require('./languageNames');
const routesRegexp = require('./routesRegexp');
const sanitizeLocationPath = require('./sanitizeLocationPath');
const useAnimationFrame = require('./useAnimationFrame');
const useBinaryState = require('./useBinaryState');
const useDeepEqualEffect = require('./useDeepEqualEffect');
const useDeepEqualMemo = require('./useDeepEqualMemo');
const useDeepEqualState = require('./useDeepEqualState');
const useFullscreen = require('./useFullscreen');
const useLiveRef = require('./useLiveRef');
const useModelState = require('./useModelState');
@ -45,6 +44,7 @@ module.exports = {
Button,
Checkbox,
ColorInput,
DelayedRenderer,
Image,
LibItem,
MainNavBars,
@ -67,15 +67,14 @@ module.exports = {
useToast,
comparatorWithPriorities,
CONSTANTS,
withCoreSuspender,
useCoreSuspender,
getVisibleChildrenRange,
languageNames,
routesRegexp,
sanitizeLocationPath,
useAnimationFrame,
useBinaryState,
useDeepEqualEffect,
useDeepEqualMemo,
useDeepEqualState,
useFullscreen,
useLiveRef,
useModelState,

View file

@ -26,7 +26,7 @@ const routesRegexp = {
urlParamsNames: []
},
metadetails: {
regexp: /^\/metadetails\/([^/]*)\/([^/]*)(?:\/([^/]*))?$/,
regexp: /^\/(?:metadetails|detail)\/([^/]*)\/([^/]*)(?:\/([^/]*))?$/,
urlParamsNames: ['type', 'id', 'videoId']
},
addons: {

View file

@ -1,22 +0,0 @@
// Copyright (C) 2017-2022 Smart code 203358507
const UrlUtils = require('url');
const routesRegexp = require('stremio/common/routesRegexp');
const sanitizeLocationPath = (path) => {
const { href, pathname, search } = UrlUtils.parse(path);
if (typeof pathname === 'string') {
const matches = pathname.match(routesRegexp.player.regexp);
if (matches) {
if (typeof matches[2] === 'string') {
return `/player/***/***/${matches[3]}/${matches[4]}/${matches[5]}/${matches[6]}${typeof search === 'string' ? search : ''}`;
} else {
return `/player/***${typeof search === 'string' ? search : ''}`;
}
}
}
return href;
};
module.exports = sanitizeLocationPath;

View file

@ -1,10 +0,0 @@
// Copyright (C) 2017-2022 Smart code 203358507
const React = require('react');
const useDeepEqualMemo = require('stremio/common/useDeepEqualMemo');
const useDeepEqualEffect = (cb, deps) => {
React.useEffect(cb, [useDeepEqualMemo(() => ({}), deps)]);
};
module.exports = useDeepEqualEffect;

View file

@ -1,24 +0,0 @@
// Copyright (C) 2017-2022 Smart code 203358507
const React = require('react');
const isEqual = require('lodash.isequal');
const useDeepEqualState = (initialState) => {
return React.useReducer(
(prevState, nextState) => {
return isEqual(prevState, nextState) ?
prevState
:
nextState;
},
undefined,
() => {
return typeof initialState === 'function' ?
initialState()
:
initialState;
}
);
};
module.exports = useDeepEqualState;

View file

@ -3,16 +3,19 @@
const React = require('react');
const throttle = require('lodash.throttle');
const isEqual = require('lodash.isequal');
const intersection = require('lodash.intersection');
const { useCoreSuspender } = require('stremio/common/CoreSuspender');
const { useRouteFocused } = require('stremio-router');
const { useServices } = require('stremio/services');
const useModelState = ({ init, action, ...args }) => {
const useModelState = ({ action, ...args }) => {
const { core } = useServices();
const routeFocused = useRouteFocused();
const mountedRef = React.useRef(false);
const [model, timeout, map] = React.useMemo(() => {
return [args.model, args.timeout, args.map];
const [model, timeout, map, deps] = React.useMemo(() => {
return [args.model, args.timeout, args.map, args.deps];
}, []);
const { getState } = useCoreSuspender();
const [state, setState] = React.useReducer(
(prevState, nextState) => {
return Object.keys(prevState).reduce((result, key) => {
@ -22,35 +25,41 @@ const useModelState = ({ init, action, ...args }) => {
},
undefined,
() => {
return typeof init === 'function' ?
init()
:
init;
if (typeof map === 'function') {
return map(getState(model));
} else {
return getState(model);
}
}
);
React.useLayoutEffect(() => {
React.useInsertionEffect(() => {
if (action) {
core.transport.dispatch(action, model);
}
}, [action]);
React.useLayoutEffect(() => {
React.useInsertionEffect(() => {
return () => {
core.transport.dispatch({ action: 'Unload' }, model);
};
}, []);
React.useLayoutEffect(() => {
const onNewStateThrottled = throttle(() => {
const state = core.transport.getState(model);
React.useInsertionEffect(() => {
const onNewState = async (models) => {
if (models.indexOf(model) === -1 && (!Array.isArray(deps) || intersection(deps, models).length === 0)) {
return;
}
const state = await core.transport.getState(model);
if (typeof map === 'function') {
setState(map(state));
} else {
setState(state);
}
}, timeout);
};
const onNewStateThrottled = throttle(onNewState, timeout);
if (routeFocused) {
core.transport.on('NewState', onNewStateThrottled);
if (mountedRef.current) {
onNewStateThrottled.call();
onNewState([model]);
}
}
return () => {
@ -58,7 +67,7 @@ const useModelState = ({ init, action, ...args }) => {
core.transport.off('NewState', onNewStateThrottled);
};
}, [routeFocused]);
React.useLayoutEffect(() => {
React.useInsertionEffect(() => {
mountedRef.current = true;
}, []);
return state;

View file

@ -1,7 +1,5 @@
// Copyright (C) 2017-2022 Smart code 203358507
const React = require('react');
const { useServices } = require('stremio/services');
const useModelState = require('stremio/common/useModelState');
const map = (ctx) => ({
@ -18,12 +16,7 @@ const map = (ctx) => ({
});
const useProfile = () => {
const { core } = useServices();
const init = React.useCallback(() => {
const ctx = core.transport.getState('ctx');
return map(ctx);
}, []);
return useModelState({ model: 'ctx', init, map });
return useModelState({ model: 'ctx', map });
};
module.exports = useProfile;

View file

@ -1,15 +1,9 @@
// Copyright (C) 2017-2022 Smart code 203358507
const React = require('react');
const { useServices } = require('stremio/services');
const useModelState = require('stremio/common/useModelState');
const useStreamingServer = () => {
const { core } = useServices();
const init = React.useCallback(() => {
return core.transport.getState('streaming_server');
}, []);
return useModelState({ model: 'streaming_server', init });
return useModelState({ model: 'streaming_server' });
};
module.exports = useStreamingServer;

View file

@ -12,7 +12,8 @@ if (browser?.platform?.type === 'desktop') {
}
const React = require('react');
const ReactDOM = require('react-dom');
const ReactDOM = require('react-dom/client');
const App = require('./App');
ReactDOM.render(<App />, document.getElementById('app'));
const root = ReactDOM.createRoot(document.getElementById('app'));
root.render(<App />);

View file

@ -2,8 +2,9 @@
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const Icon = require('@stremio/stremio-icons/dom');
const { AddonDetailsModal, Button, Image, Multiselect, MainNavBars, TextInput, SearchBar, SharePrompt, ModalDialog, useBinaryState } = require('stremio/common');
const { AddonDetailsModal, Button, Image, Multiselect, MainNavBars, TextInput, SearchBar, SharePrompt, ModalDialog, useBinaryState, withCoreSuspender } = require('stremio/common');
const Addon = require('./Addon');
const useInstalledAddons = require('./useInstalledAddons');
const useRemoteAddons = require('./useRemoteAddons');
@ -118,7 +119,7 @@ const Addons = ({ urlParams, queryParams }) => {
.map((addon, index) => (
<Addon
key={index}
className={styles['addon']}
className={classnames(styles['addon'], 'animation-fade-in')}
id={addon.manifest.id}
name={addon.manifest.name}
version={addon.manifest.version}
@ -152,7 +153,7 @@ const Addons = ({ urlParams, queryParams }) => {
.map((addon, index) => (
<Addon
key={index}
className={styles['addon']}
className={classnames(styles['addon'], 'animation-fade-in')}
id={addon.manifest.id}
name={addon.manifest.name}
version={addon.manifest.version}
@ -261,4 +262,8 @@ Addons.propTypes = {
queryParams: PropTypes.instanceOf(URLSearchParams)
};
module.exports = Addons;
const AddonsFallback = () => (
<MainNavBars className={styles['addons-container']} route={'addons'} />
);
module.exports = withCoreSuspender(Addons, AddonsFallback);

View file

@ -1,14 +1,9 @@
// Copyright (C) 2017-2022 Smart code 203358507
const React = require('react');
const { useServices } = require('stremio/services');
const { useModelState } = require('stremio/common');
const useInstalledAddons = (urlParams) => {
const { core } = useServices();
const init = React.useMemo(() => {
return core.transport.getState('installed_addons');
}, []);
const action = React.useMemo(() => {
if (typeof urlParams.transportUrl !== 'string' && typeof urlParams.catalogId !== 'string') {
return {
@ -28,7 +23,7 @@ const useInstalledAddons = (urlParams) => {
};
}
}, [urlParams]);
return useModelState({ model: 'installed_addons', action, init });
return useModelState({ model: 'installed_addons', action });
};
module.exports = useInstalledAddons;

View file

@ -3,15 +3,6 @@
const React = require('react');
const { useModelState } = require('stremio/common');
const init = () => ({
selected: null,
selectable: {
catalogs: [],
types: []
},
catalog: null,
});
const useRemoteAddons = (urlParams) => {
const action = React.useMemo(() => {
if (typeof urlParams.type === 'string' && typeof urlParams.transportUrl === 'string' && typeof urlParams.catalogId === 'string') {
@ -38,7 +29,7 @@ const useRemoteAddons = (urlParams) => {
};
}
}, [urlParams]);
return useModelState({ model: 'remote_addons', action, init });
return useModelState({ model: 'remote_addons', action, deps: ['ctx'] });
};
module.exports = useRemoteAddons;

View file

@ -3,7 +3,7 @@
const React = require('react');
const classnames = require('classnames');
const debounce = require('lodash.debounce');
const { MainNavBars, MetaRow, LibItem, MetaItem, StreamingServerWarning, useProfile, useStreamingServer, getVisibleChildrenRange } = require('stremio/common');
const { MainNavBars, MetaRow, LibItem, MetaItem, StreamingServerWarning, useStreamingServer, withCoreSuspender, getVisibleChildrenRange } = require('stremio/common');
const useBoard = require('./useBoard');
const useContinueWatchingPreview = require('./useContinueWatchingPreview');
const styles = require('./styles');
@ -11,7 +11,6 @@ const styles = require('./styles');
const THRESHOLD = 5;
const Board = () => {
const profile = useProfile();
const streamingServer = useStreamingServer();
const continueWatchingPreview = useContinueWatchingPreview();
const [board, loadBoardRows] = useBoard();
@ -42,7 +41,7 @@ const Board = () => {
{
continueWatchingPreview.libraryItems.length > 0 ?
<MetaRow
className={classnames(styles['board-row'], styles['continue-watching-row'])}
className={classnames(styles['board-row'], styles['continue-watching-row'], 'animation-fade-in')}
title={'Continue Watching'}
items={continueWatchingPreview.libraryItems}
itemComponent={LibItem}
@ -57,7 +56,7 @@ const Board = () => {
return (
<MetaRow
key={index}
className={classnames(styles['board-row'], styles[`board-row-${catalog.content.content[0].posterShape}`])}
className={classnames(styles['board-row'], styles[`board-row-${catalog.content.content[0].posterShape}`], 'animation-fade-in')}
title={catalog.title}
items={catalog.content.content}
itemComponent={MetaItem}
@ -69,7 +68,7 @@ const Board = () => {
return (
<MetaRow
key={index}
className={styles['board-row']}
className={classnames(styles['board-row'], 'animation-fade-in')}
title={catalog.title}
message={catalog.content.content}
deepLinks={catalog.deepLinks}
@ -80,7 +79,7 @@ const Board = () => {
return (
<MetaRow.Placeholder
key={index}
className={classnames(styles['board-row'], styles['board-row-poster'])}
className={classnames(styles['board-row'], styles['board-row-poster'], 'animation-fade-in')}
title={catalog.title}
deepLinks={catalog.deepLinks}
/>
@ -91,8 +90,7 @@ const Board = () => {
</div>
</MainNavBars>
{
streamingServer.settings !== null && streamingServer.settings.type === 'Err' &&
(isNaN(profile.settings.streamingServerWarningDismissed.getTime()) || profile.settings.streamingServerWarningDismissed.getTime() < Date.now()) ?
streamingServer.settings !== null && streamingServer.settings.type === 'Err' ?
<StreamingServerWarning className={styles['board-warning-container']} />
:
null
@ -101,4 +99,10 @@ const Board = () => {
);
};
module.exports = Board;
const BoardFallback = () => (
<div className={styles['board-container']}>
<MainNavBars className={styles['board-content-container']} route={'board'} />
</div>
);
module.exports = withCoreSuspender(Board, BoardFallback);

View file

@ -4,11 +4,6 @@ const React = require('react');
const { useServices } = require('stremio/services');
const { useModelState } = require('stremio/common');
const init = () => ({
selected: null,
catalogs: []
});
const useBoard = () => {
const { core } = useServices();
const action = React.useMemo(() => ({
@ -27,7 +22,7 @@ const useBoard = () => {
}
}, 'board');
}, []);
const board = useModelState({ model: 'board', timeout: 1500, action, init });
const board = useModelState({ model: 'board', timeout: 1500, action });
return [board, loadRange];
};

View file

@ -1,15 +1,9 @@
// Copyright (C) 2017-2022 Smart code 203358507
const React = require('react');
const { useServices } = require('stremio/services');
const { useModelState } = require('stremio/common');
const useContinueWatchingPreview = () => {
const { core } = useServices();
const init = React.useMemo(() => {
return core.transport.getState('continue_watching_preview');
}, []);
return useModelState({ model: 'continue_watching_preview', init });
return useModelState({ model: 'continue_watching_preview' });
};
module.exports = useContinueWatchingPreview;

View file

@ -5,7 +5,7 @@ const PropTypes = require('prop-types');
const classnames = require('classnames');
const Icon = require('@stremio/stremio-icons/dom');
const { useServices } = require('stremio/services');
const { AddonDetailsModal, Button, MainNavBars, MetaItem, Image, MetaPreview, Multiselect, ModalDialog, CONSTANTS, useBinaryState, useOnScrollToBottom } = require('stremio/common');
const { AddonDetailsModal, DelayedRenderer, Button, MainNavBars, MetaItem, Image, MetaPreview, Multiselect, ModalDialog, CONSTANTS, useBinaryState, useOnScrollToBottom, withCoreSuspender } = require('stremio/common');
const useDiscover = require('./useDiscover');
const useSelectableInputs = require('./useSelectableInputs');
const styles = require('./styles');
@ -75,37 +75,26 @@ const Discover = ({ urlParams, queryParams }) => {
closeAddonModal();
setSelectedMetaItemIndex(0);
}, [discover.selected]);
const metaItemsContainerRef = React.useRef();
React.useEffect(() => {
if (discover.catalog?.content.type === 'Loading') {
metaItemsContainerRef.current.scrollTop = 0;
}
}, [discover.catalog]);
return (
<MainNavBars className={styles['discover-container']} route={'discover'}>
<div className={styles['discover-content']}>
<div className={styles['catalog-container']}>
{
discover.defaultRequest ?
<div className={styles['selectable-inputs-container']}>
{selectInputs.map(({ title, options, selected, renderLabelText, onSelect }, index) => (
<Multiselect
key={index}
className={styles['select-input']}
title={title}
options={options}
selected={selected}
renderLabelText={renderLabelText}
onSelect={onSelect}
/>
))}
<Button className={styles['filter-container']} title={'All filters'} onClick={openInputsModal}>
<Icon className={styles['filter-icon']} icon={'ic_filter'} />
</Button>
</div>
:
null
}
<div className={styles['selectable-inputs-container']}>
{selectInputs.map(({ title, options, selected, renderLabelText, onSelect }, index) => (
<Multiselect
key={index}
className={styles['select-input']}
title={title}
options={options}
selected={selected}
renderLabelText={renderLabelText}
onSelect={onSelect}
/>
))}
<Button className={styles['filter-container']} title={'All filters'} onClick={openInputsModal}>
<Icon className={styles['filter-icon']} icon={'ic_filter'} />
</Button>
</div>
{
discover.catalog !== null && !discover.catalog.installed ?
<div className={styles['missing-addon-warning-container']}>
@ -119,10 +108,12 @@ const Discover = ({ urlParams, queryParams }) => {
}
{
discover.catalog === null ?
<div className={styles['message-container']}>
<Image className={styles['image']} src={require('/images/empty.png')} alt={' '} />
<div className={styles['message-label']}>No catalog selected!</div>
</div>
<DelayedRenderer delay={500}>
<div className={styles['message-container']}>
<Image className={styles['image']} src={require('/images/empty.png')} alt={' '} />
<div className={styles['message-label']}>No catalog selected!</div>
</div>
</DelayedRenderer>
:
discover.catalog.content.type === 'Err' ?
<div className={styles['message-container']}>
@ -131,7 +122,7 @@ const Discover = ({ urlParams, queryParams }) => {
</div>
:
discover.catalog.content.type === 'Loading' ?
<div ref={metaItemsContainerRef} className={styles['meta-items-container']}>
<div className={classnames(styles['meta-items-container'], 'animation-fade-in')}>
{Array(CONSTANTS.CATALOG_PAGE_SIZE).fill(null).map((_, index) => (
<div key={index} className={styles['meta-item-placeholder']}>
<div className={styles['poster-container']} />
@ -142,7 +133,7 @@ const Discover = ({ urlParams, queryParams }) => {
))}
</div>
:
<div ref={metaItemsContainerRef} className={styles['meta-items-container']} onScroll={onScroll} onFocusCapture={metaItemsOnFocusCapture}>
<div className={classnames(styles['meta-items-container'], 'animation-fade-in')} onScroll={onScroll} onFocusCapture={metaItemsOnFocusCapture}>
{discover.catalog.content.content.map((metaItem, index) => (
<MetaItem
key={index}
@ -185,7 +176,7 @@ const Discover = ({ urlParams, queryParams }) => {
}
</div>
{
inputsModalOpen && discover.defaultRequest ?
inputsModalOpen ?
<ModalDialog title={'Catalog filters'} className={styles['selectable-inputs-modal']} onCloseRequest={closeInputsModal}>
{selectInputs.map(({ title, options, selected, renderLabelText, onSelect }, index) => (
<Multiselect
@ -221,4 +212,8 @@ Discover.propTypes = {
queryParams: PropTypes.instanceOf(URLSearchParams)
};
module.exports = Discover;
const DiscoverFallback = () => (
<MainNavBars className={styles['discover-container']} route={'discover'} />
);
module.exports = withCoreSuspender(Discover, DiscoverFallback);

View file

@ -5,18 +5,6 @@ const UrlUtils = require('url');
const { useServices } = require('stremio/services');
const { useModelState } = require('stremio/common');
const init = () => ({
selected: null,
selectable: {
types: [],
catalogs: [],
extra: [],
nextPage: false
},
catalog: null,
defaultRequest: null,
});
const map = (discover) => ({
...discover,
catalog: discover.catalog !== null && discover.catalog.content.type === 'Ready' ?
@ -67,25 +55,20 @@ const useDiscover = (urlParams, queryParams) => {
};
}
} else {
const discover = core.transport.getState('discover');
if (discover.defaultRequest !== null) {
return {
action: 'Load',
args: {
model: 'CatalogWithFilters',
args: {
request: discover.defaultRequest
}
}
};
}
return {
action: 'Load',
args: {
model: 'CatalogWithFilters',
args: null
}
};
}
return {
action: 'Unload'
};
}, [urlParams, queryParams]);
const discover = useModelState({ model: 'discover', action, map, init });
const discover = useModelState({ model: 'discover', action, map, deps: ['ctx'] });
return [discover, loadNextPage];
};

View file

@ -186,7 +186,6 @@ const Intro = ({ queryParams }) => {
tos: state.termsAccepted,
privacy: state.privacyPolicyAccepted,
marketing: state.marketingAccepted,
time: new Date(),
from: 'web'
}
}

View file

@ -5,7 +5,7 @@ const PropTypes = require('prop-types');
const classnames = require('classnames');
const Icon = require('@stremio/stremio-icons/dom');
const NotFound = require('stremio/routes/NotFound');
const { Button, Multiselect, MainNavBars, LibItem, Image, ModalDialog, PaginationInput, useProfile, routesRegexp, useBinaryState } = require('stremio/common');
const { Button, DelayedRenderer, Multiselect, MainNavBars, LibItem, Image, ModalDialog, PaginationInput, useProfile, routesRegexp, useBinaryState, withCoreSuspender } = require('stremio/common');
const useLibrary = require('./useLibrary');
const useSelectableInputs = require('./useSelectableInputs');
const styles = require('./styles');
@ -85,14 +85,16 @@ const Library = ({ model, urlParams, queryParams }) => {
</div>
:
library.selected === null ?
<div className={styles['message-container']}>
<Image
className={styles['image']}
src={require('/images/empty.png')}
alt={' '}
/>
<div className={styles['message-label']}>{model === 'library' ? 'Library' : 'Continue Watching'} not loaded!</div>
</div>
<DelayedRenderer delay={500}>
<div className={styles['message-container']}>
<Image
className={styles['image']}
src={require('/images/empty.png')}
alt={' '}
/>
<div className={styles['message-label']}>{model === 'library' ? 'Library' : 'Continue Watching'} not loaded!</div>
</div>
</DelayedRenderer>
:
library.catalog.length === 0 ?
<div className={styles['message-container']}>
@ -104,7 +106,7 @@ const Library = ({ model, urlParams, queryParams }) => {
<div className={styles['message-label']}>Empty {model === 'library' ? 'Library' : 'Continue Watching'}</div>
</div>
:
<div className={styles['meta-items-container']}>
<div className={classnames(styles['meta-items-container'], 'animation-fade-in')}>
{library.catalog.map((libItem, index) => (
<LibItem {...libItem} removable={model === 'library'} key={index} />
))}
@ -132,4 +134,10 @@ Library.propTypes = {
queryParams: PropTypes.instanceOf(URLSearchParams)
};
module.exports = withModel(Library);
const LibraryFallback = ({ model }) => (
<MainNavBars className={styles['library-container']} route={model} />
);
LibraryFallback.propTypes = Library.propTypes;
module.exports = withModel(withCoreSuspender(Library, LibraryFallback));

View file

@ -3,17 +3,6 @@
const React = require('react');
const { useModelState } = require('stremio/common');
const init = () => ({
selected: null,
selectable: {
types: [],
sorts: [],
prevPage: null,
nextPage: null
},
catalog: []
});
const useLibrary = (model, urlParams, queryParams) => {
const action = React.useMemo(() => ({
action: 'Load',
@ -28,7 +17,7 @@ const useLibrary = (model, urlParams, queryParams) => {
}
}
}), [urlParams, queryParams]);
return useModelState({ model, action, init });
return useModelState({ model, action });
};
module.exports = useLibrary;

View file

@ -2,8 +2,9 @@
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const { useServices } = require('stremio/services');
const { VerticalNavBar, HorizontalNavBar, MetaPreview, ModalDialog, Image } = require('stremio/common');
const { VerticalNavBar, HorizontalNavBar, MetaPreview, ModalDialog, Image, DelayedRenderer, withCoreSuspender } = require('stremio/common');
const StreamsList = require('./StreamsList');
const VideosList = require('./VideosList');
const useMetaDetails = require('./useMetaDetails');
@ -86,10 +87,12 @@ const MetaDetails = ({ urlParams, queryParams }) => {
}
{
metaPath === null ?
<div className={styles['meta-message-container']}>
<Image className={styles['image']} src={require('/images/empty.png')} alt={' '} />
<div className={styles['message-label']}>No meta was selected!</div>
</div>
<DelayedRenderer delay={500}>
<div className={styles['meta-message-container']}>
<Image className={styles['image']} src={require('/images/empty.png')} alt={' '} />
<div className={styles['message-label']}>No meta was selected!</div>
</div>
</DelayedRenderer>
:
metaDetails.metaItem === null ?
<div className={styles['meta-message-container']}>
@ -122,7 +125,7 @@ const MetaDetails = ({ urlParams, queryParams }) => {
null
}
<MetaPreview
className={styles['meta-preview']}
className={classnames(styles['meta-preview'], 'animation-fade-in')}
name={metaDetails.metaItem.content.content.name}
logo={metaDetails.metaItem.content.content.logo}
runtime={metaDetails.metaItem.content.content.runtime}
@ -188,4 +191,16 @@ MetaDetails.propTypes = {
queryParams: PropTypes.instanceOf(URLSearchParams)
};
module.exports = MetaDetails;
const MetaDetailsFallback = () => (
<div className={styles['metadetails-container']}>
<HorizontalNavBar
className={styles['nav-bar']}
backButton={true}
addonsButton={true}
fullscreenButton={true}
navMenu={true}
/>
</div>
);
module.exports = withCoreSuspender(MetaDetails, MetaDetailsFallback);

View file

@ -88,7 +88,7 @@ const Video = ({ className, id, title, thumbnail, episode, released, upcoming, w
{
released instanceof Date && !isNaN(released.getTime()) ?
<div className={styles['released-container']}>
{released.toLocaleString(undefined, { year: '2-digit', month: 'short', day: 'numeric' })}
{released.toLocaleString(undefined, { year: 'numeric', month: 'short', day: 'numeric' })}
</div>
:
scheduled ?

View file

@ -3,14 +3,6 @@
const React = require('react');
const { useModelState } = require('stremio/common');
const init = () => ({
selected: null,
metaItem: null,
streams: [],
metaExtensions: [],
title: null
});
const map = (metaDetails) => ({
...metaDetails,
metaItem: metaDetails.metaItem !== null && metaDetails.metaItem.content.type === 'Ready' ?
@ -74,7 +66,7 @@ const useMetaDetails = (urlParams) => {
};
}
}, [urlParams]);
return useModelState({ model: 'meta_details', action, map, init });
return useModelState({ model: 'meta_details', action, map });
};
module.exports = useMetaDetails;

View file

@ -6,7 +6,7 @@ const classnames = require('classnames');
const debounce = require('lodash.debounce');
const { useRouteFocused } = require('stremio-router');
const { useServices } = require('stremio/services');
const { HorizontalNavBar, Button, useFullscreen, useBinaryState, useToast, useStreamingServer } = require('stremio/common');
const { HorizontalNavBar, Button, useFullscreen, useBinaryState, useToast, useStreamingServer, withCoreSuspender } = require('stremio/common');
const Icon = require('@stremio/stremio-icons/dom');
const BufferingLoader = require('./BufferingLoader');
const ControlBar = require('./ControlBar');
@ -18,14 +18,14 @@ const useSettings = require('./useSettings');
const styles = require('./styles');
const Player = ({ urlParams, queryParams }) => {
const { core, chromecast, shell } = useServices();
const { chromecast, shell } = useServices();
const [forceTranscoding, maxAudioChannels] = React.useMemo(() => {
return [
queryParams.has('forceTranscoding'),
queryParams.has('maxAudioChannels') ? parseInt(queryParams.get('maxAudioChannels'), 10) : null
];
}, [queryParams]);
const [player, updateLibraryItemState, pushToLibrary] = usePlayer(urlParams);
const [player, timeChanged, pausedChanged, ended, pushToLibrary] = usePlayer(urlParams);
const [settings, updateSettings] = useSettings();
const streamingServer = useStreamingServer();
const routeFocused = useRouteFocused();
@ -42,6 +42,7 @@ const Player = ({ urlParams, queryParams }) => {
const [videoState, setVideoState] = React.useReducer(
(videoState, nextVideoState) => ({ ...videoState, ...nextVideoState }),
{
manifest: null,
stream: null,
paused: null,
time: null,
@ -75,6 +76,7 @@ const Player = ({ urlParams, queryParams }) => {
}
}, []);
const onImplementationChanged = React.useCallback((manifest) => {
setVideoState({ manifest });
manifest.props.forEach((propName) => {
dispatch({ type: 'observeProp', propName });
});
@ -93,16 +95,8 @@ const Player = ({ urlParams, queryParams }) => {
setVideoState({ [propName]: propValue });
}, []);
const onEnded = React.useCallback(() => {
ended();
pushToLibrary();
if (player.libraryItem !== null) {
core.transport.dispatch({
action: 'Ctx',
args: {
action: 'RewindLibraryItem',
args: player.libraryItem._id
}
});
}
if (player.nextVideo !== null) {
window.location.replace(
typeof player.nextVideo.deepLinks.player === 'string' ?
@ -224,7 +218,8 @@ const Player = ({ urlParams, queryParams }) => {
setError(null);
if (player.selected === null) {
dispatch({ type: 'command', commandName: 'unload' });
} else if (streamingServer.baseUrl !== null && streamingServer.baseUrl.type !== 'Loading' && player.metaItem !== null && player.metaItem.type !== 'Loading') {
} else if (streamingServer.baseUrl !== null && streamingServer.baseUrl.type !== 'Loading' &&
(player.selected.metaRequest === null || (player.metaItem !== null && player.metaItem.type !== 'Loading'))) {
dispatch({
type: 'command',
commandName: 'load',
@ -302,10 +297,17 @@ const Player = ({ urlParams, queryParams }) => {
dispatch({ type: 'setProp', propName: 'extraSubtitlesOutlineColor', propValue: settings.subtitlesOutlineColor });
}, [settings.subtitlesOutlineColor]);
React.useEffect(() => {
if (videoState.time !== null && !isNaN(videoState.time) && videoState.duration !== null && !isNaN(videoState.duration)) {
updateLibraryItemState(videoState.time, videoState.duration);
if (videoState.time !== null && !isNaN(videoState.time) &&
videoState.duration !== null && !isNaN(videoState.duration) &&
videoState.manifest !== null && typeof videoState.manifest.name === 'string') {
timeChanged(videoState.time, videoState.duration, videoState.manifest.name);
}
}, [videoState.time, videoState.duration]);
}, [videoState.time, videoState.duration, videoState.manifest]);
React.useEffect(() => {
if (videoState.paused !== null) {
pausedChanged(videoState.paused);
}
}, [videoState.paused]);
React.useEffect(() => {
if ((!Array.isArray(videoState.subtitlesTracks) || videoState.subtitlesTracks.length === 0) &&
(!Array.isArray(videoState.extraSubtitlesTracks) || videoState.extraSubtitlesTracks.length === 0) &&
@ -568,4 +570,8 @@ Player.propTypes = {
queryParams: PropTypes.instanceOf(URLSearchParams)
};
module.exports = Player;
const PlayerFallback = () => (
<div className={classnames(styles['player-container'])} />
);
module.exports = withCoreSuspender(Player, PlayerFallback);

View file

@ -2,18 +2,7 @@
const React = require('react');
const { useServices } = require('stremio/services');
const { useModelState } = require('stremio/common');
const init = () => ({
selected: null,
metaItem: null,
subtitles: [],
nextVideo: null,
seriesInfo: null,
libraryItem: null,
title: null,
addon: null,
});
const { useModelState, useCoreSuspender } = require('stremio/common');
const map = (player) => ({
...player,
@ -45,8 +34,9 @@ const map = (player) => ({
const usePlayer = (urlParams) => {
const { core } = useServices();
const { decodeStream } = useCoreSuspender();
const stream = decodeStream(urlParams.stream);
const action = React.useMemo(() => {
const stream = core.transport.decodeStream(urlParams.stream);
if (stream !== null) {
return {
action: 'Load',
@ -96,12 +86,12 @@ const usePlayer = (urlParams) => {
};
}
}, [urlParams]);
const updateLibraryItemState = React.useCallback((time, duration) => {
const timeChanged = React.useCallback((time, duration, device) => {
core.transport.dispatch({
action: 'Player',
args: {
action: 'UpdateLibraryItemState',
args: { time, duration }
action: 'TimeChanged',
args: { time, duration, device }
}
}, 'player');
}, []);
@ -113,8 +103,25 @@ const usePlayer = (urlParams) => {
}
}, 'player');
}, []);
const player = useModelState({ model: 'player', action, init, map });
return [player, updateLibraryItemState, pushToLibrary];
const ended = React.useCallback(() => {
core.transport.dispatch({
action: 'Player',
args: {
action: 'Ended'
}
}, 'player');
}, []);
const pausedChanged = React.useCallback((paused) => {
core.transport.dispatch({
action: 'Player',
args: {
action: 'PausedChanged',
args: { paused }
}
}, 'player');
}, []);
const player = useModelState({ model: 'player', action, map });
return [player, timeChanged, pausedChanged, ended, pushToLibrary];
};
module.exports = usePlayer;

View file

@ -5,7 +5,7 @@ const PropTypes = require('prop-types');
const classnames = require('classnames');
const debounce = require('lodash.debounce');
const Icon = require('@stremio/stremio-icons/dom');
const { Image, MainNavBars, MetaRow, MetaItem, useDeepEqualMemo, getVisibleChildrenRange } = require('stremio/common');
const { Image, MainNavBars, MetaRow, MetaItem, useDeepEqualMemo, withCoreSuspender, getVisibleChildrenRange } = require('stremio/common');
const useSearch = require('./useSearch');
const styles = require('./styles');
@ -47,7 +47,7 @@ const Search = ({ queryParams }) => {
<div ref={scrollContainerRef} className={styles['search-content']} onScroll={onScroll}>
{
query === null ?
<div className={styles['search-hints-container']}>
<div className={classnames(styles['search-hints-container'], 'animation-fade-in')}>
<div className={styles['search-hint-container']}>
<Icon className={styles['icon']} icon={'ic_movies'} />
<div className={styles['label']}>Search for movies, series, YouTube and TV channels</div>
@ -74,7 +74,7 @@ const Search = ({ queryParams }) => {
return (
<MetaRow
key={index}
className={classnames(styles['search-row'], styles[`search-row-${catalog.content.content[0].posterShape}`])}
className={classnames(styles['search-row'], styles[`search-row-${catalog.content.content[0].posterShape}`], 'animation-fade-in')}
title={catalog.title}
items={catalog.content.content}
itemComponent={MetaItem}
@ -86,7 +86,7 @@ const Search = ({ queryParams }) => {
return (
<MetaRow
key={index}
className={styles['search-row']}
className={classnames(styles['search-row'], 'animation-fade-in')}
title={catalog.title}
message={catalog.content.content}
deepLinks={catalog.deepLinks}
@ -97,7 +97,7 @@ const Search = ({ queryParams }) => {
return (
<MetaRow.Placeholder
key={index}
className={classnames(styles['search-row'], styles['search-row-poster'])}
className={classnames(styles['search-row'], styles['search-row-poster'], 'animation-fade-in')}
title={catalog.title}
deepLinks={catalog.deepLinks}
/>
@ -115,4 +115,10 @@ Search.propTypes = {
queryParams: PropTypes.instanceOf(URLSearchParams)
};
module.exports = Search;
const SearchFallback = ({ queryParams }) => (
<MainNavBars className={styles['search-container']} route={'search'} query={queryParams.get('search')} />
);
SearchFallback.propTypes = Search.propTypes;
module.exports = withCoreSuspender(Search, SearchFallback);

View file

@ -4,37 +4,33 @@ const React = require('react');
const { useModelState } = require('stremio/common');
const { useServices } = require('stremio/services');
const init = () => ({
selected: null,
catalogs: []
});
const useSearch = (queryParams) => {
const { core } = useServices();
React.useEffect(() => {
let timerId = setTimeout(emitSearchEvent, 500);
function emitSearchEvent() {
timerId = null;
const state = core.transport.getState('search');
if (state.selected !== null) {
const [, query] = state.selected.extra.find(([name]) => name === 'search');
const responses = state.catalogs.filter((catalog) => catalog.content?.type === 'Ready');
core.transport.analytics({
event: 'Search',
args: {
query,
responsesCount: responses.length
}
});
}
}
return () => {
if (timerId !== null) {
clearTimeout(timerId);
emitSearchEvent();
}
};
}, [queryParams.get('search')]);
// TODO: refactor this to be in stremio-core-web
// React.useEffect(() => {
// let timerId = setTimeout(emitSearchEvent, 500);
// function emitSearchEvent() {
// timerId = null;
// const state = core.transport.getState('search');
// if (state.selected !== null) {
// const [, query] = state.selected.extra.find(([name]) => name === 'search');
// const responses = state.catalogs.filter((catalog) => catalog.content?.type === 'Ready');
// core.transport.analytics({
// event: 'Search',
// args: {
// query,
// responsesCount: responses.length
// }
// });
// }
// }
// return () => {
// if (timerId !== null) {
// clearTimeout(timerId);
// emitSearchEvent();
// }
// };
// }, [queryParams.get('search')]);
const action = React.useMemo(() => {
if (queryParams.has('search') && queryParams.get('search').length > 0) {
return {
@ -63,7 +59,7 @@ const useSearch = (queryParams) => {
}
}, 'search');
}, []);
const search = useModelState({ model: 'search', action, init });
const search = useModelState({ model: 'search', action });
return [search, loadRange];
};

View file

@ -6,7 +6,7 @@ const throttle = require('lodash.throttle');
const Icon = require('@stremio/stremio-icons/dom');
const { useRouteFocused } = require('stremio-router');
const { useServices } = require('stremio/services');
const { Button, Checkbox, MainNavBars, Multiselect, ColorInput, TextInput, ModalDialog, useProfile, useStreamingServer, useBinaryState } = require('stremio/common');
const { Button, Checkbox, MainNavBars, Multiselect, ColorInput, TextInput, ModalDialog, useProfile, useStreamingServer, useBinaryState, withCoreSuspender } = require('stremio/common');
const useProfileSettingsInputs = require('./useProfileSettingsInputs');
const useStreamingServerSettingsInputs = require('./useStreamingServerSettingsInputs');
const styles = require('./styles');
@ -131,7 +131,7 @@ const Settings = () => {
}, [routeFocused]);
return (
<MainNavBars className={styles['settings-container']} route={'settings'}>
<div className={styles['settings-content']}>
<div className={classnames(styles['settings-content'], 'animation-fade-in')}>
<div className={styles['side-menu-container']}>
<Button className={classnames(styles['side-menu-button'], { [styles['selected']]: selectedSectionId === GENERAL_SECTION })} title={'General'} data-section={GENERAL_SECTION} onClick={sideMenuButtonOnClick}>
General
@ -449,4 +449,8 @@ const Settings = () => {
);
};
module.exports = Settings;
const SettingsFallback = () => (
<MainNavBars className={styles['settings-container']} route={'settings'} />
);
module.exports = withCoreSuspender(Settings, SettingsFallback);

View file

@ -5,6 +5,7 @@ const { CONSTANTS, languageNames, useDeepEqualMemo } = require('stremio/common')
const useProfileSettingsInputs = (profile) => {
const { core } = useServices();
// TODO combine those useDeepEqualMemo in one
const interfaceLanguageSelect = useDeepEqualMemo(() => ({
options: Object.keys(languageNames).map((code) => ({
value: code,

View file

@ -45,6 +45,7 @@ const TORRENT_PROFILES = {
const useStreamingServerSettingsInputs = (streamingServer) => {
const { core } = useServices();
// TODO combine those useDeepEqualMemo in one
const cacheSizeSelect = useDeepEqualMemo(() => {
if (streamingServer.settings === null || streamingServer.settings.type !== 'Ready') {
return null;

View file

@ -3,7 +3,7 @@
const EventEmitter = require('eventemitter3');
const CoreTransport = require('./CoreTransport');
function Core() {
function Core(args) {
let active = false;
let error = null;
let starting = false;
@ -66,7 +66,7 @@ function Core() {
}
starting = true;
transport = new CoreTransport();
transport = new CoreTransport(args);
transport.on('init', onTransportInit);
transport.on('error', onTransportError);
onStateChanged();

View file

@ -1,19 +1,22 @@
// Copyright (C) 2017-2022 Smart code 203358507
const EventEmitter = require('eventemitter3');
const { default: initialize_api, initialize_runtime, get_state, get_debug_state, dispatch, analytics, decode_stream } = require('@stremio/stremio-core-web');
const Bridge = require('@stremio/stremio-core-web/bridge');
function CoreTransport() {
function CoreTransport(args) {
const events = new EventEmitter();
const worker = new Worker(`${process.env.COMMIT_HASH}/scripts/worker.js`);
const bridge = new Bridge(window, worker);
initialize_api(require('@stremio/stremio-core-web/stremio_core_web_bg.wasm'))
.then(() => initialize_runtime(({ name, args }) => {
try {
events.emit(name, args);
} catch (error) {
console.error('CoreTransport', error);
}
}))
window.onCoreEvent = ({ name, args }) => {
try {
events.emit(name, args);
} catch (error) {
console.error('CoreTransport', error);
}
};
bridge.call(['init'], [args])
.then(() => {
try {
events.emit('init');
@ -34,28 +37,20 @@ function CoreTransport() {
this.removeAllListeners = function() {
events.removeAllListeners();
};
this.getState = function(field) {
return get_state(field);
this.getState = async function(field) {
return bridge.call(['getState'], [field]);
};
this.getDebugState = function() {
return get_debug_state();
this.getDebugState = async function() {
return bridge.call(['getDebugState'], []);
};
this.dispatch = function(action, field) {
try {
dispatch(action, field);
} catch (error) {
console.error('CoreTransport', error);
}
this.dispatch = async function(action, field) {
return bridge.call(['dispatch'], [action, field, location.hash]);
};
this.analytics = function(event) {
try {
analytics(event);
} catch (error) {
console.error('CoreTransport', error);
}
this.analytics = async function(event) {
return bridge.call(['analytics'], [event, location.hash]);
};
this.decodeStream = function(stream) {
return decode_stream(stream);
this.decodeStream = async function(stream) {
return bridge.call(['decodeStream'], [stream]);
};
}

View file

@ -15,7 +15,10 @@ const COMMIT_HASH = execSync('git rev-parse HEAD').toString().trim();
module.exports = (env, argv) => ({
mode: argv.mode,
devtool: argv.mode === 'production' ? 'source-map' : 'eval-source-map',
entry: './src/index.js',
entry: {
main: './src/index.js',
worker: './node_modules/@stremio/stremio-core-web/worker.js'
},
output: {
path: path.join(__dirname, 'build'),
filename: `${COMMIT_HASH}/scripts/[name].js`