diff --git a/package-lock.json b/package-lock.json index f1990498f..5752a5875 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "stremio", - "version": "5.0.0-beta.16", + "version": "5.0.0-beta.18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "stremio", - "version": "5.0.0-beta.16", + "version": "5.0.0-beta.18", "license": "gpl-2.0", "dependencies": { "@babel/runtime": "7.26.0", @@ -14,7 +14,7 @@ "@stremio/stremio-colors": "5.2.0", "@stremio/stremio-core-web": "https://stremio.github.io/stremio-core/stremio-core-web/feat/stream-rar-and-zip-stream-creation/dev/stremio-stremio-core-web-0.48.5.tgz", "@stremio/stremio-icons": "5.4.1", - "@stremio/stremio-video": "0.0.48", + "@stremio/stremio-video": "0.0.53", "a-color-picker": "1.2.1", "bowser": "2.11.0", "buffer": "6.0.3", @@ -36,7 +36,7 @@ "react-i18next": "^15.1.3", "react-is": "18.3.1", "spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", - "stremio-translations": "github:Stremio/stremio-translations#a0f50634202f748a57907b645d2cd92fbaa479dd", + "stremio-translations": "github:Stremio/stremio-translations#62bcc6e8f44258203c7375af59210771efb6f634", "url": "0.11.4", "use-long-press": "^3.2.0" }, @@ -3409,10 +3409,9 @@ ] }, "node_modules/@stremio/stremio-video": { - "version": "0.0.48", - "resolved": "https://registry.npmjs.org/@stremio/stremio-video/-/stremio-video-0.0.48.tgz", - "integrity": "sha512-6ALGXCZC4NPsfhPcrwFWQzvH6UMMRsgSkHetnOhv9WmZ5ubiyUdbBzj9atGiGuuQz8pRcze66ztrub+dsaQbpw==", - "license": "MIT", + "version": "0.0.53", + "resolved": "https://registry.npmjs.org/@stremio/stremio-video/-/stremio-video-0.0.53.tgz", + "integrity": "sha512-hSlk8GqMdk4N8VbcdvduYqWVZsQLgHyU7GfFmd1k+t0pSpDKAhI3C6dohG5Sr09CKCjHa8D1rls+CwMNPXLSGw==", "dependencies": { "buffer": "6.0.3", "color": "4.2.3", @@ -13374,8 +13373,8 @@ }, "node_modules/stremio-translations": { "version": "1.44.9", - "resolved": "git+ssh://git@github.com/Stremio/stremio-translations.git#a0f50634202f748a57907b645d2cd92fbaa479dd", - "integrity": "sha512-JJpd1JJet3T6/VTNdZ2NZ7uvHJ4zkuyqo5BnTcDGqLVNO/OpicGqKhZjE4WGSgmuhsfPBU8T0ICCfzKu2xpvKg==", + "resolved": "git+ssh://git@github.com/Stremio/stremio-translations.git#62bcc6e8f44258203c7375af59210771efb6f634", + "integrity": "sha512-8Sc5Qvd4IiObwGXkmj1XFXFavUc15My5po6G48HHDBbp42SVc5I/t7h+1yxW1A81byyBCXbL23a9iU9v49vpQA==", "license": "MIT" }, "node_modules/string_decoder": { diff --git a/package.json b/package.json index a8a9a917e..1260c7a10 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "stremio", "displayName": "Stremio", - "version": "5.0.0-beta.16", + "version": "5.0.0-beta.18", "author": "Smart Code OOD", "private": true, "license": "gpl-2.0", @@ -18,7 +18,7 @@ "@stremio/stremio-colors": "5.2.0", "@stremio/stremio-core-web": "https://stremio.github.io/stremio-core/stremio-core-web/feat/stream-rar-and-zip-stream-creation/dev/stremio-stremio-core-web-0.48.5.tgz", "@stremio/stremio-icons": "5.4.1", - "@stremio/stremio-video": "0.0.48", + "@stremio/stremio-video": "0.0.53", "a-color-picker": "1.2.1", "bowser": "2.11.0", "buffer": "6.0.3", @@ -40,7 +40,7 @@ "react-i18next": "^15.1.3", "react-is": "18.3.1", "spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6", - "stremio-translations": "github:Stremio/stremio-translations#a0f50634202f748a57907b645d2cd92fbaa479dd", + "stremio-translations": "github:Stremio/stremio-translations#62bcc6e8f44258203c7375af59210771efb6f634", "url": "0.11.4", "use-long-press": "^3.2.0" }, diff --git a/src/App/App.js b/src/App/App.js index 730e75f28..6dc2d6e0b 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -6,10 +6,11 @@ const { useTranslation } = require('react-i18next'); const { Router } = require('stremio-router'); const { Core, Shell, Chromecast, DragAndDrop, KeyboardShortcuts, ServicesProvider } = require('stremio/services'); const { NotFound } = require('stremio/routes'); -const { PlatformProvider, ToastProvider, TooltipProvider, CONSTANTS, withCoreSuspender } = require('stremio/common'); +const { FileDropProvider, PlatformProvider, ToastProvider, TooltipProvider, CONSTANTS, withCoreSuspender } = require('stremio/common'); const ServicesToaster = require('./ServicesToaster'); const DeepLinkHandler = require('./DeepLinkHandler'); const SearchParamsHandler = require('./SearchParamsHandler'); +const { default: UpdaterBanner } = require('./UpdaterBanner'); const ErrorDialog = require('./ErrorDialog'); const withProtectedRoutes = require('./withProtectedRoutes'); const routerViewsConfig = require('./routerViewsConfig'); @@ -165,14 +166,17 @@ const App = () => { - - - - + + + + + + + diff --git a/src/App/UpdaterBanner/UpdaterBanner.less b/src/App/UpdaterBanner/UpdaterBanner.less new file mode 100644 index 000000000..9928fb493 --- /dev/null +++ b/src/App/UpdaterBanner/UpdaterBanner.less @@ -0,0 +1,46 @@ +.updater-banner { + height: 4rem; + display: flex; + align-items: center; + justify-content: center; + gap: 1rem; + padding: 0 1rem; + font-size: 1rem; + font-weight: bold; + color: var(--primary-foreground-color); + background-color: var(--primary-accent-color); + + .button { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + height: 2.5rem; + padding: 0 1rem; + border-radius: var(--border-radius); + color: var(--primary-background-color); + background-color: var(--primary-foreground-color); + transition: all 0.1s ease-out; + + &:hover { + color: var(--primary-foreground-color); + background-color: transparent; + box-shadow: inset 0 0 0 0.15rem var(--primary-foreground-color); + } + } + + .close { + position: absolute; + right: 0; + height: 4rem; + width: 4rem; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + + .icon { + height: 2rem; + } + } +} \ No newline at end of file diff --git a/src/App/UpdaterBanner/UpdaterBanner.tsx b/src/App/UpdaterBanner/UpdaterBanner.tsx new file mode 100644 index 000000000..3d421deb5 --- /dev/null +++ b/src/App/UpdaterBanner/UpdaterBanner.tsx @@ -0,0 +1,50 @@ +import React, { useEffect } from 'react'; +import Icon from '@stremio/stremio-icons/react'; +import { useTranslation } from 'react-i18next'; +import { useServices } from 'stremio/services'; +import { useBinaryState, useShell } from 'stremio/common'; +import { Button, Transition } from 'stremio/components'; +import styles from './UpdaterBanner.less'; + +type Props = { + className: string, +}; + +const UpdaterBanner = ({ className }: Props) => { + const { t } = useTranslation(); + const { shell } = useServices(); + const shellTransport = useShell(); + const [visible, show, hide] = useBinaryState(false); + + const onInstallClick = () => { + shellTransport.send('autoupdater-notif-clicked'); + }; + + useEffect(() => { + shell.transport && shell.transport.on('autoupdater-show-notif', show); + + return () => { + shell.transport && shell.transport.off('autoupdater-show-notif', show); + }; + }, []); + + return ( +
+ +
+
+ { t('UPDATER_TITLE') } +
+ + +
+
+
+ ); +}; + +export default UpdaterBanner; diff --git a/src/App/UpdaterBanner/index.ts b/src/App/UpdaterBanner/index.ts new file mode 100644 index 000000000..e4306ecb2 --- /dev/null +++ b/src/App/UpdaterBanner/index.ts @@ -0,0 +1,2 @@ +import UpdaterBanner from './UpdaterBanner'; +export default UpdaterBanner; diff --git a/src/App/styles.less b/src/App/styles.less index a413ed163..77ba398f1 100644 --- a/src/App/styles.less +++ b/src/App/styles.less @@ -202,12 +202,32 @@ html { background-color: var(--modal-background-color); box-shadow: var(--outer-glow); transition: opacity 0.1s ease-out; + } + + .file-drop-container { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + border-radius: 1rem; + border: 0.5rem dashed transparent; + pointer-events: none; + transition: border-color 0.25s ease-out; &:global(.active) { - transition-delay: 0.25s; + border-color: var(--primary-accent-color); } } + .updater-banner-container { + z-index: 1; + position: absolute; + left: 0; + right: 0; + bottom: 0; + } + .router { width: 100%; height: 100%; diff --git a/src/common/CONSTANTS.js b/src/common/CONSTANTS.js index 727c152fa..8e4e3efdc 100644 --- a/src/common/CONSTANTS.js +++ b/src/common/CONSTANTS.js @@ -41,6 +41,16 @@ const ICON_FOR_TYPE = new Map([ ['other', 'movies'], ]); +const MIME_SIGNATURES = { + 'application/x-subrip': ['310D0A', '310A'], + 'text/vtt': ['574542565454'], +}; + +const SUPPORTED_LOCAL_SUBTITLES = [ + 'application/x-subrip', + 'text/vtt', +]; + const EXTERNAL_PLAYERS = [ { label: 'EXTERNAL_PLAYER_DISABLED', @@ -113,6 +123,8 @@ module.exports = { WRITERS_LINK_CATEGORY, TYPE_PRIORITIES, ICON_FOR_TYPE, + MIME_SIGNATURES, + SUPPORTED_LOCAL_SUBTITLES, EXTERNAL_PLAYERS, WHITELISTED_HOSTS, }; diff --git a/src/common/FileDrop/FileDrop.tsx b/src/common/FileDrop/FileDrop.tsx new file mode 100644 index 000000000..aae4e146b --- /dev/null +++ b/src/common/FileDrop/FileDrop.tsx @@ -0,0 +1,91 @@ +import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'; +import classNames from 'classnames'; +import { isFileType } from './utils'; + +export type FileType = string; +export type FileDropListener = (filename: string, buffer: ArrayBuffer) => void; + +type FileDropContext = { + on: (type: FileType, listener: FileDropListener) => void, + off: (type: FileType, listener: FileDropListener) => void, +}; + +const FileDropContext = createContext({} as FileDropContext); + +type Props = { + className: string, + children: JSX.Element, +}; + +const FileDropProvider = ({ className, children }: Props) => { + const [listeners, setListeners] = useState<[FileType, FileDropListener][]>([]); + const [active, setActive] = useState(false); + + const onDragOver = (event: DragEvent) => { + event.preventDefault(); + setActive(true); + }; + + const onDragLeave = () => { + setActive(false); + }; + + const onDrop = useCallback((event: DragEvent) => { + event.preventDefault(); + const { dataTransfer } = event; + + if (dataTransfer && dataTransfer?.files.length > 0) { + const file = dataTransfer.files[0]; + + file + .arrayBuffer() + .then((buffer) => { + listeners + .filter(([type]) => file.type ? type === file.type : isFileType(buffer, type)) + .forEach(([, listerner]) => listerner(file.name, buffer)); + }); + } + + setActive(false); + }, [listeners]); + + const on = (type: FileType, listener: FileDropListener) => { + setListeners((listeners) => { + return [...listeners, [type, listener]]; + }); + }; + + const off = (type: FileType, listener: FileDropListener) => { + setListeners((listeners) => { + return listeners.filter(([key, value]) => key !== type && value !== listener); + }); + }; + + useEffect(() => { + window.addEventListener('dragover', onDragOver); + window.addEventListener('dragleave', onDragLeave); + window.addEventListener('drop', onDrop); + + return () => { + window.removeEventListener('dragover', onDragOver); + window.removeEventListener('dragleave', onDragLeave); + window.removeEventListener('drop', onDrop); + }; + }, [onDrop]); + + return ( + + { children } +
+ + ); +}; + +const useFileDrop = () => { + return useContext(FileDropContext); +}; + +export { + FileDropProvider, + useFileDrop, +}; diff --git a/src/common/FileDrop/index.ts b/src/common/FileDrop/index.ts new file mode 100644 index 000000000..41acfbfd7 --- /dev/null +++ b/src/common/FileDrop/index.ts @@ -0,0 +1,8 @@ +import { FileDropProvider, useFileDrop } from './FileDrop'; +import onFileDrop from './onFileDrop'; + +export { + FileDropProvider, + useFileDrop, + onFileDrop, +}; diff --git a/src/common/FileDrop/onFileDrop.ts b/src/common/FileDrop/onFileDrop.ts new file mode 100644 index 000000000..339b0219b --- /dev/null +++ b/src/common/FileDrop/onFileDrop.ts @@ -0,0 +1,14 @@ +import { useEffect } from 'react'; +import { type FileType, type FileDropListener, useFileDrop } from './FileDrop'; + +const onFileDrop = (types: FileType[], listener: FileDropListener) => { + const { on, off } = useFileDrop(); + + useEffect(() => { + types.forEach((type) => on(type, listener)); + + return () => types.forEach((type) => off(type, listener)); + }, []); +}; + +export default onFileDrop; diff --git a/src/common/FileDrop/utils.ts b/src/common/FileDrop/utils.ts new file mode 100644 index 000000000..f9996aed3 --- /dev/null +++ b/src/common/FileDrop/utils.ts @@ -0,0 +1,19 @@ +import { MIME_SIGNATURES } from 'stremio/common/CONSTANTS'; + +const SIGNATURES = MIME_SIGNATURES as Record; + +const isFileType = (buffer: ArrayBuffer, type: string) => { + const signatures = SIGNATURES[type]; + + return signatures.some((signature) => { + const array = new Uint8Array(buffer); + const signatureBuffer = Buffer.from(signature, 'hex'); + const bufferToCompare = array.subarray(0, signatureBuffer.length); + + return Buffer.compare(signatureBuffer, bufferToCompare) === 0; + }); +}; + +export { + isFileType, +}; diff --git a/src/common/Platform/Platform.tsx b/src/common/Platform/Platform.tsx index 41e74b24f..2212303e4 100644 --- a/src/common/Platform/Platform.tsx +++ b/src/common/Platform/Platform.tsx @@ -1,6 +1,6 @@ import React, { createContext, useContext } from 'react'; import { WHITELISTED_HOSTS } from 'stremio/common/CONSTANTS'; -import useShell from './useShell'; +import useShell from 'stremio/common/useShell'; import { name, isMobile } from './device'; interface PlatformContext { diff --git a/src/common/animations.less b/src/common/animations.less index c7a30d2fb..91dbe386d 100644 --- a/src/common/animations.less +++ b/src/common/animations.less @@ -69,6 +69,19 @@ transform: translateX(100%); } +.slide-up-enter { + transform: translateY(100%); +} + +.slide-up-active { + transform: translateY(0%); + transition: transform 0.3s cubic-bezier(0.32, 0, 0.67, 0); +} + +.slide-up-exit { + transform: translateY(100%); +} + @keyframes fade-in-no-motion { 0% { opacity: 0; diff --git a/src/common/index.js b/src/common/index.js index 4c514dfbe..4acf8b056 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -1,5 +1,6 @@ // Copyright (C) 2017-2023 Smart code 203358507 +const { FileDropProvider, onFileDrop } = require('./FileDrop'); const { PlatformProvider, usePlatform } = require('./Platform'); const { ToastProvider, useToast } = require('./Toast'); const { TooltipProvider, Tooltip } = require('./Tooltips'); @@ -19,11 +20,14 @@ const useModelState = require('./useModelState'); const useNotifications = require('./useNotifications'); const useOnScrollToBottom = require('./useOnScrollToBottom'); const useProfile = require('./useProfile'); +const { default: useShell } = require('./useShell'); const useStreamingServer = require('./useStreamingServer'); const useTorrent = require('./useTorrent'); const useTranslate = require('./useTranslate'); module.exports = { + FileDropProvider, + onFileDrop, PlatformProvider, usePlatform, ToastProvider, @@ -47,6 +51,7 @@ module.exports = { useNotifications, useOnScrollToBottom, useProfile, + useShell, useStreamingServer, useTorrent, useTranslate, diff --git a/src/common/Platform/useShell.ts b/src/common/useShell.ts similarity index 100% rename from src/common/Platform/useShell.ts rename to src/common/useShell.ts diff --git a/src/components/ColorInput/ColorInput.js b/src/components/ColorInput/ColorInput.js index 88e9189ef..afa411f63 100644 --- a/src/components/ColorInput/ColorInput.js +++ b/src/components/ColorInput/ColorInput.js @@ -5,7 +5,8 @@ const PropTypes = require('prop-types'); const classnames = require('classnames'); const AColorPicker = require('a-color-picker'); const { useTranslation } = require('react-i18next'); -const { Button, ModalDialog } = require('stremio/components'); +const { Button } = require('stremio/components'); +const ModalDialog = require('stremio/components/ModalDialog'); const useBinaryState = require('stremio/common/useBinaryState'); const ColorPicker = require('./ColorPicker'); const styles = require('./styles'); diff --git a/src/components/EventModal/EventModal.js b/src/components/EventModal/EventModal.js index 917190ba1..f4e46b199 100644 --- a/src/components/EventModal/EventModal.js +++ b/src/components/EventModal/EventModal.js @@ -2,7 +2,8 @@ const React = require('react'); const { useTranslation } = require('react-i18next'); -const { Button, ModalDialog } = require('stremio/components'); +const { default: Button } = require('stremio/components/Button'); +const ModalDialog = require('stremio/components/ModalDialog'); const useEvents = require('./useEvents'); const styles = require('./styles'); const { default: Icon } = require('@stremio/stremio-icons/react'); diff --git a/src/routes/MetaDetails/StreamsList/Stream/Stream.js b/src/routes/MetaDetails/StreamsList/Stream/Stream.js index 768b8d40a..41f81b386 100644 --- a/src/routes/MetaDetails/StreamsList/Stream/Stream.js +++ b/src/routes/MetaDetails/StreamsList/Stream/Stream.js @@ -181,12 +181,17 @@ const Stream = ({ className, videoId, videoReleased, addonName, name, descriptio const renderMenu = React.useMemo(() => function renderMenu() { return (
+
+ {description} +
{ streamLink && } diff --git a/src/routes/MetaDetails/StreamsList/Stream/styles.less b/src/routes/MetaDetails/StreamsList/Stream/styles.less index 0461c77ee..06720a879 100644 --- a/src/routes/MetaDetails/StreamsList/Stream/styles.less +++ b/src/routes/MetaDetails/StreamsList/Stream/styles.less @@ -111,12 +111,29 @@ background-color: var(--secondary-accent-color); } + .menu-icon { + flex: none; + width: 1.7rem; + height: 1.7rem; + margin-right: 1rem; + color: var(--color-placeholder); + } + .context-menu-container { max-width: calc(90% - 1.5rem); z-index: 2; .context-menu-content { --spatial-navigation-contain: contain; + + .context-menu-title { + font-size: 0.9rem; + padding: 1rem 1.5rem; + font-weight: 100; + border-bottom: 1px solid var(--color-placeholder); + color: var(--primary-foreground-color); + white-space: break-spaces; + } .context-menu-option-container { display: flex; @@ -131,8 +148,9 @@ .context-menu-option-label { font-size: 1rem; - font-weight: 500; - color:var(--primary-foreground-color); + font-weight: 300; + color: var(--primary-foreground-color); + text-transform: capitalize; } } } diff --git a/src/routes/Player/ControlBar/ControlBar.js b/src/routes/Player/ControlBar/ControlBar.js index 0fa6de1a4..745e2bd49 100644 --- a/src/routes/Player/ControlBar/ControlBar.js +++ b/src/routes/Player/ControlBar/ControlBar.js @@ -138,6 +138,7 @@ const ControlBar = ({
diff --git a/src/routes/Player/ControlBar/VolumeSlider/VolumeSlider.js b/src/routes/Player/ControlBar/VolumeSlider/VolumeSlider.js index 420f4ba41..a65ebfe9c 100644 --- a/src/routes/Player/ControlBar/VolumeSlider/VolumeSlider.js +++ b/src/routes/Player/ControlBar/VolumeSlider/VolumeSlider.js @@ -8,7 +8,7 @@ const { useRouteFocused } = require('stremio-router'); const { Slider } = require('stremio/components'); const styles = require('./styles'); -const VolumeSlider = ({ className, volume, onVolumeChangeRequested }) => { +const VolumeSlider = ({ className, volume, onVolumeChangeRequested, muted }) => { const disabled = volume === null || isNaN(volume); const routeFocused = useRouteFocused(); const [slidingVolume, setSlidingVolume] = React.useState(null); @@ -45,7 +45,9 @@ const VolumeSlider = ({ className, volume, onVolumeChangeRequested }) => { className={classnames(className, styles['volume-slider'], { 'active': slidingVolume !== null })} value={ !disabled ? - slidingVolume !== null ? slidingVolume : volume + !muted ? + slidingVolume !== null ? slidingVolume : volume + : 0 : 100 } @@ -61,7 +63,8 @@ const VolumeSlider = ({ className, volume, onVolumeChangeRequested }) => { VolumeSlider.propTypes = { className: PropTypes.string, volume: PropTypes.number, - onVolumeChangeRequested: PropTypes.func + onVolumeChangeRequested: PropTypes.func, + muted: PropTypes.bool, }; module.exports = VolumeSlider; diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 60604faf0..d6934e904 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -8,7 +8,7 @@ const langs = require('langs'); const { useTranslation } = require('react-i18next'); const { useRouteFocused } = require('stremio-router'); const { useServices } = require('stremio/services'); -const { useFullscreen, useBinaryState, useToast, useStreamingServer, withCoreSuspender } = require('stremio/common'); +const { onFileDrop, useFullscreen, useBinaryState, useToast, useStreamingServer, withCoreSuspender, CONSTANTS } = require('stremio/common'); const { HorizontalNavBar, Transition } = require('stremio/components'); const BufferingLoader = require('./BufferingLoader'); const VolumeChangeIndicator = require('./VolumeChangeIndicator'); @@ -133,11 +133,20 @@ const Player = ({ urlParams, queryParams }) => { toast.show({ type: 'success', title: t('PLAYER_SUBTITLES_LOADED'), - message: track.exclusive ? t('PLAYER_SUBTITLES_LOADED_EXCLUSIVE') : t('PLAYER_SUBTITLES_LOADED_ORIGIN', { origin: track.origin }), + message: + track.exclusive ? t('PLAYER_SUBTITLES_LOADED_EXCLUSIVE') : + track.local ? t('PLAYER_SUBTITLES_LOADED_LOCAL') : + t('PLAYER_SUBTITLES_LOADED_ORIGIN', { origin: track.origin }), timeout: 3000 }); }, []); + const onExtraSubtitlesTrackAdded = React.useCallback((track) => { + if (track.local) { + video.setExtraSubtitlesTrack(track.id); + } + }, []); + const onPlayRequested = React.useCallback(() => { video.setProp('paused', false); setSeeking(false); @@ -172,13 +181,11 @@ const Player = ({ urlParams, queryParams }) => { }, []); const onSubtitlesTrackSelected = React.useCallback((id) => { - video.setProp('selectedSubtitlesTrackId', id); - video.setProp('selectedExtraSubtitlesTrackId', null); + video.setSubtitlesTrack(id); }, []); const onExtraSubtitlesTrackSelected = React.useCallback((id) => { - video.setProp('selectedSubtitlesTrackId', null); - video.setProp('selectedExtraSubtitlesTrackId', id); + video.setExtraSubtitlesTrack(id); }, []); const onAudioTrackSelected = React.useCallback((id) => { @@ -270,6 +277,10 @@ const Player = ({ urlParams, queryParams }) => { event.nativeEvent.immersePrevented = true; }, []); + onFileDrop(CONSTANTS.SUPPORTED_LOCAL_SUBTITLES, async (filename, buffer) => { + video.addLocalSubtitles(filename, buffer); + }); + React.useEffect(() => { setError(null); video.unload(); @@ -295,6 +306,7 @@ const Player = ({ urlParams, queryParams }) => { 0, forceTranscoding: forceTranscoding || casting, maxAudioChannels: settings.surroundSound ? 32 : 2, + hardwareDecoding: settings.hardwareDecoding, streamingServerURL: streamingServer.baseUrl ? casting ? streamingServer.baseUrl @@ -302,7 +314,7 @@ const Player = ({ urlParams, queryParams }) => { streamingServer.selected.transportUrl : null, - seriesInfo: player.seriesInfo + seriesInfo: player.seriesInfo, }, { chromecastTransport: chromecast.active ? chromecast.transport : null, shellTransport: shell.active ? shell.transport : null, @@ -585,6 +597,7 @@ const Player = ({ urlParams, queryParams }) => { video.events.on('ended', onEnded); video.events.on('subtitlesTrackLoaded', onSubtitlesTrackLoaded); video.events.on('extraSubtitlesTrackLoaded', onExtraSubtitlesTrackLoaded); + video.events.on('extraSubtitlesTrackAdded', onExtraSubtitlesTrackAdded); video.events.on('implementationChanged', onImplementationChanged); return () => { @@ -592,6 +605,7 @@ const Player = ({ urlParams, queryParams }) => { video.events.off('ended', onEnded); video.events.off('subtitlesTrackLoaded', onSubtitlesTrackLoaded); video.events.off('extraSubtitlesTrackLoaded', onExtraSubtitlesTrackLoaded); + video.events.off('extraSubtitlesTrackAdded', onExtraSubtitlesTrackAdded); video.events.off('implementationChanged', onImplementationChanged); }; }, []); diff --git a/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js b/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js index 44cd20864..b1e15a52f 100644 --- a/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js +++ b/src/routes/Player/SubtitlesMenu/SubtitlesMenu.js @@ -10,11 +10,13 @@ const styles = require('./styles'); const { t } = require('i18next'); const ORIGIN_PRIORITIES = { + 'LOCAL': 3, 'EMBEDDED': 2, - 'EXCLUSIVE': 1 + 'EXCLUSIVE': 1, }; const LANGUAGE_PRIORITIES = { - 'eng': 1 + 'local': 2, + 'eng': 1, }; const SubtitlesMenu = React.memo((props) => { @@ -161,7 +163,11 @@ const SubtitlesMenu = React.memo((props) => { {subtitlesLanguages.map((lang, index) => (
diff --git a/src/services/DragAndDrop/DragAndDrop.js b/src/services/DragAndDrop/DragAndDrop.js index bc54e2f35..1538c328e 100644 --- a/src/services/DragAndDrop/DragAndDrop.js +++ b/src/services/DragAndDrop/DragAndDrop.js @@ -36,6 +36,12 @@ function DragAndDrop({ core }) { } break; } + case 'application/x-subrip': + break; + case 'text/vtt': + break; + case '': + break; default: { events.emit('error', { message: 'Unsupported file', diff --git a/webpack.config.js b/webpack.config.js index 878d52f97..36f6b6e50 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -163,7 +163,7 @@ module.exports = (env, argv) => ({ exclude: /node_modules/, type: 'asset/resource', generator: { - filename: `${COMMIT_HASH}/images/[name][ext][query]` + filename: 'images/[name][ext][query]' } }, { @@ -231,9 +231,9 @@ module.exports = (env, argv) => ({ }), new CopyWebpackPlugin({ patterns: [ - { from: 'favicons', to: `${COMMIT_HASH}/favicons` }, - { from: 'images', to: `${COMMIT_HASH}/images` }, - { from: 'screenshots/*.webp', to: `${COMMIT_HASH}` }, + { from: 'favicons', to: 'favicons' }, + { from: 'images', to: 'images' }, + { from: 'screenshots/*.webp', to: './' }, ] }), new MiniCssExtractPlugin({ @@ -243,8 +243,8 @@ module.exports = (env, argv) => ({ template: './src/index.html', inject: false, scriptLoading: 'blocking', - faviconsPath: `${COMMIT_HASH}/favicons`, - imagesPath: `${COMMIT_HASH}/images`, + faviconsPath: 'favicons', + imagesPath: 'images', }), new WebpackPwaManifest({ name: 'Stremio Web', @@ -261,33 +261,33 @@ module.exports = (env, argv) => ({ icons: [ { src: 'images/icon.png', - destination: `${COMMIT_HASH}/icons`, + destination: 'icons', sizes: [196, 512], purpose: 'any' }, { src: 'images/maskable_icon.png', - destination: `${COMMIT_HASH}/maskable_icons`, + destination: 'maskable_icons', sizes: [196, 512], purpose: 'maskable', ios: true }, { src: 'favicons/favicon.ico', - destination: `${COMMIT_HASH}/favicons`, + destination: 'favicons', sizes: [256], } ], screenshots : [ { - src: `${COMMIT_HASH}/screenshots/board_wide.webp`, + src: 'screenshots/board_wide.webp', sizes: '1440x900', type: 'image/webp', form_factor: 'wide', label: 'Homescreen of Stremio' }, { - src: `${COMMIT_HASH}/screenshots/board_narrow.webp`, + src: 'screenshots/board_narrow.webp', sizes: '414x896', type: 'image/webp', form_factor: 'narrow',