diff --git a/package-lock.json b/package-lock.json index b5c856635..7bd2bd6ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,20 @@ { "name": "stremio", - "version": "5.0.0-beta.14", + "version": "5.0.0-beta.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "stremio", - "version": "5.0.0-beta.14", + "version": "5.0.0-beta.15", "license": "gpl-2.0", "dependencies": { "@babel/runtime": "7.26.0", "@sentry/browser": "8.42.0", "@stremio/stremio-colors": "5.2.0", - "@stremio/stremio-core-web": "0.48.2", + "@stremio/stremio-core-web": "0.48.3", "@stremio/stremio-icons": "5.4.1", - "@stremio/stremio-video": "0.0.46", + "@stremio/stremio-video": "0.0.48", "a-color-picker": "1.2.1", "bowser": "2.11.0", "buffer": "6.0.3", @@ -67,6 +67,7 @@ "postcss-loader": "8.1.1", "readdirp": "4.0.2", "terser-webpack-plugin": "5.3.10", + "thread-loader": "^4.0.4", "ts-loader": "^9.5.1", "typescript": "^5.7.2", "typescript-eslint": "^8.17.0", @@ -3370,9 +3371,9 @@ "integrity": "sha512-dYlPgu9W/H7c9s1zmW5tiDnRenaUa4Hg1QCyOg1lhOcgSfM/bVTi5nnqX+IfvGTTUNA0zgzh8hI3o3miwnZxTg==" }, "node_modules/@stremio/stremio-core-web": { - "version": "0.48.2", - "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.48.2.tgz", - "integrity": "sha512-zkz4ftGMoK9RmIDlGLd/DYLtaXuf4HxnMEN1NduKNXDlYPSJ4Q/b1hCbXrVqVK/nx6s+8X4XyYr9IOwFLaT5ew==", + "version": "0.48.3", + "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.48.3.tgz", + "integrity": "sha512-JL8pOLOEVACYG+33Dtp/mrB2/vuc7RoYZdxX1BQa5MPR8EzsODjpvL5uETmdxo/swgtMZyx2A6/e1B53eKA4oQ==", "dependencies": { "@babel/runtime": "7.24.1" } @@ -3407,9 +3408,10 @@ ] }, "node_modules/@stremio/stremio-video": { - "version": "0.0.46", - "resolved": "https://registry.npmjs.org/@stremio/stremio-video/-/stremio-video-0.0.46.tgz", - "integrity": "sha512-U15CGB6CrUZKq3IKcEouAEH2RQoLy2+BI/hDStEYEACxlRlFaavKPI2opl37muh9TY089RnZVBYAM3yDidBZdg==", + "version": "0.0.48", + "resolved": "https://registry.npmjs.org/@stremio/stremio-video/-/stremio-video-0.0.48.tgz", + "integrity": "sha512-6ALGXCZC4NPsfhPcrwFWQzvH6UMMRsgSkHetnOhv9WmZ5ubiyUdbBzj9atGiGuuQz8pRcze66ztrub+dsaQbpw==", + "license": "MIT", "dependencies": { "buffer": "6.0.3", "color": "4.2.3", @@ -10152,6 +10154,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -13929,6 +13938,29 @@ "node": ">=0.2.6" } }, + "node_modules/thread-loader": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/thread-loader/-/thread-loader-4.0.4.tgz", + "integrity": "sha512-tXagu6Hivd03wB2tiS1bqvw345sc7mKei32EgpYpq31ZLes9FN0mEK2nKzXLRFgwt3PsBB0E/MZDp159rDoqwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.1.0", + "neo-async": "^2.6.2", + "schema-utils": "^4.2.0" + }, + "engines": { + "node": ">= 16.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", diff --git a/package.json b/package.json old mode 100755 new mode 100644 index 2dd83b86d..d1709095e --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "stremio", "displayName": "Stremio", - "version": "5.0.0-beta.14", + "version": "5.0.0-beta.15", "author": "Smart Code OOD", "private": true, "license": "gpl-2.0", @@ -16,9 +16,9 @@ "@babel/runtime": "7.26.0", "@sentry/browser": "8.42.0", "@stremio/stremio-colors": "5.2.0", - "@stremio/stremio-core-web": "0.48.2", + "@stremio/stremio-core-web": "0.48.3", "@stremio/stremio-icons": "5.4.1", - "@stremio/stremio-video": "0.0.46", + "@stremio/stremio-video": "0.0.48", "a-color-picker": "1.2.1", "bowser": "2.11.0", "buffer": "6.0.3", @@ -71,6 +71,7 @@ "postcss-loader": "8.1.1", "readdirp": "4.0.2", "terser-webpack-plugin": "5.3.10", + "thread-loader": "^4.0.4", "ts-loader": "^9.5.1", "typescript": "^5.7.2", "typescript-eslint": "^8.17.0", diff --git a/src/common/EventModal/styles.less b/src/common/EventModal/styles.less index 24c1d053d..b251175e5 100644 --- a/src/common/EventModal/styles.less +++ b/src/common/EventModal/styles.less @@ -102,7 +102,7 @@ } } -@media (orientation: landscape) and (max-height: @xsmall) { +@media (orientation: landscape) and (max-height: @minimum) { .event-modal { .modal-dialog-container { .modal-dialog-content { diff --git a/src/common/MetaItem/styles.less b/src/common/MetaItem/styles.less index c2d094ac9..17c7c4884 100644 --- a/src/common/MetaItem/styles.less +++ b/src/common/MetaItem/styles.less @@ -166,6 +166,7 @@ object-position: center; object-fit: cover; opacity: 0.9; + overflow-clip-margin: unset; } .placeholder-icon { diff --git a/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js b/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js index 40cfbed06..f9e4176bd 100644 --- a/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js +++ b/src/common/NavBar/HorizontalNavBar/SearchBar/SearchBar.js @@ -61,7 +61,7 @@ const SearchBar = React.memo(({ className, query, active }) => { const queryInputOnSubmit = React.useCallback((event) => { event.preventDefault(); - const searchValue = `/search?search=${event.target.value}`; + const searchValue = `/search?search=${encodeURIComponent(event.target.value)}`; setCurrentQuery(searchValue); if (searchInputRef.current && searchValue) { window.location.hash = searchValue; diff --git a/src/routes/MetaDetails/StreamsList/StreamsList.js b/src/routes/MetaDetails/StreamsList/StreamsList.js index 840664c8b..56f9c0d08 100644 --- a/src/routes/MetaDetails/StreamsList/StreamsList.js +++ b/src/routes/MetaDetails/StreamsList/StreamsList.js @@ -9,16 +9,20 @@ const { Button, Image, Multiselect } = require('stremio/common'); const { useServices } = require('stremio/services'); const Stream = require('./Stream'); const styles = require('./styles'); +const { usePlatform } = require('stremio/common'); const ALL_ADDONS_KEY = 'ALL'; const StreamsList = ({ className, video, ...props }) => { const { t } = useTranslation(); const { core } = useServices(); + const platform = usePlatform(); + const streamsContainerRef = React.useRef(null); const [selectedAddon, setSelectedAddon] = React.useState(ALL_ADDONS_KEY); const onAddonSelected = React.useCallback((event) => { + streamsContainerRef.current.scrollTo({ top: 0, left: 0, behavior: platform.name === 'ios' ? 'smooth' : 'instant' }); setSelectedAddon(event.value); - }, []); + }, [platform]); const backButtonOnClick = React.useCallback(() => { if (video.deepLinks && typeof video.deepLinks.metaDetailsVideos === 'string') { window.location.replace(video.deepLinks.metaDetailsVideos + ( @@ -142,7 +146,7 @@ const StreamsList = ({ className, video, ...props }) => { : null } -
+
{filteredStreams.map((stream, index) => ( { const onSubtitlesOffsetChanged = React.useCallback((event) => { const delta = event.value === 'increment' ? 1 : -1; if (typeof props.selectedSubtitlesTrackId === 'string') { - if (props.extraSubtitlesOffset !== null && !isNaN(props.extraSubtitlesOffset)) { - const offset = Math.max(0, Math.min(100, Math.floor(props.extraSubtitlesOffset + delta))); - if (typeof props.onExtraSubtitlesOffsetChanged === 'function') { - props.onExtraSubtitlesOffsetChanged(offset); - } - } - } else if (typeof props.selectedExtraSubtitlesTrackId === 'string') { if (props.subtitlesOffset !== null && !isNaN(props.subtitlesOffset)) { const offset = Math.max(0, Math.min(100, Math.floor(props.subtitlesOffset + delta))); if (typeof props.onSubtitlesOffsetChanged === 'function') { props.onSubtitlesOffsetChanged(offset); } } + } else if (typeof props.selectedExtraSubtitlesTrackId === 'string') { + if (props.extraSubtitlesOffset !== null && !isNaN(props.extraSubtitlesOffset)) { + const offset = Math.max(0, Math.min(100, Math.floor(props.extraSubtitlesOffset + delta))); + if (typeof props.onExtraSubtitlesOffsetChanged === 'function') { + props.onExtraSubtitlesOffsetChanged(offset); + } + } } }, [props.selectedSubtitlesTrackId, props.selectedExtraSubtitlesTrackId, props.subtitlesOffset, props.extraSubtitlesOffset, props.onSubtitlesOffsetChanged, props.onExtraSubtitlesOffsetChanged]); const audioTrackOnClick = React.useCallback((event) => { diff --git a/src/routes/Settings/URLsManager/AddItem/AddItem.less b/src/routes/Settings/URLsManager/AddItem/AddItem.less index b31ce9ac8..e8de1ecf6 100644 --- a/src/routes/Settings/URLsManager/AddItem/AddItem.less +++ b/src/routes/Settings/URLsManager/AddItem/AddItem.less @@ -4,11 +4,10 @@ .add-item { display: flex; - padding: 0.5rem 1.5rem; - gap: 1rem; - border-radius: var(--border-radius); + padding: 0.35rem 1.5rem; + border-radius: 2.5rem; transition: 0.3s all ease-in-out; - background-color: transparent; + background-color: var(--overlay-color); border: 2px solid transparent; justify-content: space-between; position: relative; @@ -77,13 +76,12 @@ } &:hover { - border: 2px solid transparent; - background-color: var(--overlay-color); + border: 2px solid var(--overlay-color); } } @media only screen and (max-width: @minimum) { .add-item { - padding: 0.5rem; + padding: 0.35rem 0.5rem; } } \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 1b6c6601b..b23ee4c15 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,8 +1,10 @@ // Copyright (C) 2017-2023 Smart code 203358507 const path = require('path'); +const os = require('os'); const { execSync } = require('child_process'); const webpack = require('webpack'); +const threadLoader = require('thread-loader'); const HtmlWebPackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); @@ -14,6 +16,25 @@ const pachageJson = require('./package.json'); const COMMIT_HASH = execSync('git rev-parse HEAD').toString().trim(); +const THREAD_LOADER = { + loader: 'thread-loader', + options: { + name: 'shared-pool', + workers: os.cpus().length, + }, +}; + +threadLoader.warmup( + THREAD_LOADER.options, + [ + 'babel-loader', + 'ts-loader', + 'css-loader', + 'postcss-loader', + 'less-loader', + ], +); + module.exports = (env, argv) => ({ mode: argv.mode, devtool: argv.mode === 'production' ? 'source-map' : 'eval-source-map', @@ -30,20 +51,31 @@ module.exports = (env, argv) => ({ { test: /\.js$/, exclude: /node_modules/, - use: { - loader: 'babel-loader', - options: { - presets: [ - '@babel/preset-env', - '@babel/preset-react' - ], + use: [ + THREAD_LOADER, + { + loader: 'babel-loader', + options: { + presets: [ + '@babel/preset-env', + '@babel/preset-react' + ], + } } - } + ] }, { test: /\.(ts|tsx)$/, exclude: /node_modules/, - use: 'ts-loader', + use: [ + THREAD_LOADER, + { + loader: 'ts-loader', + options: { + happyPackMode: true, + } + } + ] }, { test: /\.less$/, @@ -55,6 +87,7 @@ module.exports = (env, argv) => ({ esModule: false } }, + THREAD_LOADER, { loader: 'css-loader', options: {