Languages
@@ -182,7 +182,7 @@ const SubtitlesPicker = (props) => {
);
};
-SubtitlesPicker.propTypes = {
+SubtitlesMenu.propTypes = {
className: PropTypes.string,
tracks: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string.isRequired,
@@ -202,4 +202,4 @@ SubtitlesPicker.propTypes = {
onOffsetChanged: PropTypes.func
};
-module.exports = SubtitlesPicker;
+module.exports = SubtitlesMenu;
diff --git a/src/routes/Player/SubtitlesMenu/index.js b/src/routes/Player/SubtitlesMenu/index.js
new file mode 100644
index 000000000..17f9571d4
--- /dev/null
+++ b/src/routes/Player/SubtitlesMenu/index.js
@@ -0,0 +1,3 @@
+const SubtitlesMenu = require('./SubtitlesMenu');
+
+module.exports = SubtitlesMenu;
diff --git a/src/routes/Player/SubtitlesPicker/styles.less b/src/routes/Player/SubtitlesMenu/styles.less
similarity index 93%
rename from src/routes/Player/SubtitlesPicker/styles.less
rename to src/routes/Player/SubtitlesMenu/styles.less
index eac05c0aa..8b60354ab 100644
--- a/src/routes/Player/SubtitlesPicker/styles.less
+++ b/src/routes/Player/SubtitlesMenu/styles.less
@@ -1,10 +1,9 @@
@import (reference) '~stremio-colors/dist/less/stremio-colors.less';
-.subtitles-picker-container {
+.subtitles-menu-container {
height: 23rem;
display: flex;
flex-direction: row;
- background-color: @color-background-dark1;
.languages-container, .variants-container, .subtitles-settings-container {
flex: none;
@@ -49,7 +48,7 @@
height: 0.5rem;
border-radius: 100%;
margin-left: 1rem;
- background-color: @color-accent3;
+ background-color: @color-accent3-90;
}
}
}
@@ -100,7 +99,7 @@
}
&:global(.disabled) {
- color: @color-surface;
+ color: @color-surface-90;
}
}
}
diff --git a/src/routes/Player/SubtitlesPicker/index.js b/src/routes/Player/SubtitlesPicker/index.js
deleted file mode 100644
index 0eb5e540e..000000000
--- a/src/routes/Player/SubtitlesPicker/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const SubtitlesPicker = require('./SubtitlesPicker');
-
-module.exports = SubtitlesPicker;
diff --git a/src/routes/Player/styles.less b/src/routes/Player/styles.less
index 9c3c6ce50..01aad2eef 100644
--- a/src/routes/Player/styles.less
+++ b/src/routes/Player/styles.less
@@ -26,7 +26,7 @@ html:not(.active-slider-within) {
z-index: 0;
width: 100%;
height: 100%;
- background-color: @color-background-dark2;
+ background-color: @color-background-dark5;
.layer {
position: absolute;
@@ -104,6 +104,7 @@ html:not(.active-slider-within) {
bottom: 8rem;
max-height: calc(100% - 13.5rem);
max-width: calc(100% - 4rem);
+ background-color: @color-background-dark1;
box-shadow: 0 1.35rem 2.7rem @color-background-dark5-40,
0 1.1rem 0.85rem @color-background-dark5-20;
overflow: auto;
diff --git a/src/routes/Player/useInfo.js b/src/routes/Player/useInfo.js
new file mode 100644
index 000000000..08f85b7c6
--- /dev/null
+++ b/src/routes/Player/useInfo.js
@@ -0,0 +1,41 @@
+const React = require('react');
+
+const useInfo = (player, profile) => {
+ const info = React.useMemo(() => {
+ if (player.selected === null) {
+ return null;
+ }
+
+ const stream = player.selected.stream;
+ const addon = stream.addon;
+ const metaItem = player.meta_resource !== null && player.meta_resource.content.type === 'Ready' ?
+ player.meta_resource.content.content
+ :
+ null;
+ const video = metaItem !== null ?
+ metaItem.videos.reduce((result, video) => {
+ if (video.id === player.selected.video_id) {
+ return video;
+ }
+
+ return result;
+ }, null)
+ :
+ null;
+ const streamTitle = typeof stream.title === 'string' ? stream.title : '';
+ const metaItemTitle = metaItem !== null ? metaItem.name : '';
+ const videoTitle = video !== null && typeof video.title === 'string' && video.title.length > 0 ? video.title : '';
+ const seriesInfo = video !== null && !isNaN(video.season) && !isNaN(video.episode) ? `${video.season}x${video.episode}` : '';
+ const title = metaItemTitle.length > 0 ?
+ metaItemTitle
+ .concat(videoTitle.length > 0 || seriesInfo.length > 0 ? ' -' : '')
+ .concat(videoTitle.length > 0 ? ` ${videoTitle}` : '')
+ .concat(seriesInfo.length > 0 ? ` (${seriesInfo})` : '')
+ :
+ streamTitle;
+ return { stream, addon, metaItem, title };
+ }, [player, profile]);
+ return info;
+};
+
+module.exports = useInfo;
diff --git a/src/routes/Player/usePlayer.js b/src/routes/Player/usePlayer.js
index 526d95278..b7bb38b2f 100644
--- a/src/routes/Player/usePlayer.js
+++ b/src/routes/Player/usePlayer.js
@@ -1,5 +1,6 @@
const React = require('react');
const pako = require('pako');
+const { useServices } = require('stremio/services');
const { useModelState } = require('stremio/common');
const initPlayerState = () => ({
@@ -15,23 +16,13 @@ const mapPlayerStateWithCtx = (player, ctx) => {
{
stream: {
...player.selected.stream,
- subtitles: Array.isArray(player.selected.stream.subtitles) ?
- player.selected.stream.subtitles.map(({ url, lang }) => ({
- url,
- lang,
- origin: ctx.profile.addons.reduce((origin, addon) => {
- if (player.selected.stream_resource_request !== null && addon.transportUrl === player.selected.stream_resource_request.base) {
- return typeof addon.manifest.name === 'string' && addon.manifest.name.length > 0 ?
- addon.manifest.name
- :
- addon.manifest.id;
- }
+ addon: ctx.profile.addons.reduce((result, addon) => {
+ if (player.selected.stream_resource_request !== null && addon.transportUrl === player.selected.stream_resource_request.base) {
+ return addon;
+ }
- return origin;
- }, player.selected.stream_resource_request !== null ? player.selected.stream_resource_request.base : 'Stream')
- }))
- :
- []
+ return result;
+ }, null)
},
stream_resource_request: player.selected.stream_resource_request,
meta_resource_request: player.selected.meta_resource_request,
@@ -40,48 +31,54 @@ const mapPlayerStateWithCtx = (player, ctx) => {
}
:
null;
- const meta_resource = player.meta_resource;
+ const meta_resource = player.meta_resource !== null && player.meta_resource.content.type === 'Ready' ?
+ {
+ request: player.meta_resource.request,
+ content: {
+ type: 'Ready',
+ content: {
+ ...player.meta_resource.content.content,
+ released: new Date(
+ typeof player.meta_resource.content.content.released === 'string' ?
+ player.meta_resource.content.content.released
+ :
+ NaN
+ ),
+ videos: player.meta_resource.content.content.videos.map((video) => ({
+ ...video,
+ released: new Date(
+ typeof video.released === 'string' ?
+ video.released
+ :
+ NaN
+ ),
+ // TODO add watched and progress
+ href: `#/metadetails/${player.meta_resource.content.content.type}/${player.meta_resource.content.content.id}/${video.id}`
+ }))
+ }
+ }
+ }
+ :
+ player.meta_resource;
const subtitles_resources = player.subtitles_resources.map((subtitles_resource) => {
const request = subtitles_resource.request;
- const origin = ctx.profile.addons.reduce((origin, addon) => {
+ const addon = ctx.profile.addons.reduce((result, addon) => {
if (addon.transportUrl === subtitles_resource.request.base) {
- return typeof addon.manifest.name === 'string' && addon.manifest.name.length > 0 ?
- addon.manifest.name
- :
- addon.manifest.id;
+ return addon;
}
- return origin;
- }, subtitles_resource.request.base);
- const content = subtitles_resource.content.type === 'Ready' ?
- {
- type: 'Ready',
- content: subtitles_resource.content.content.map(({ url, lang }) => ({
- url,
- lang,
- origin
- }))
- }
- :
- subtitles_resource.content;
- return {
- request,
- origin,
- content
- };
+ return result;
+ }, null);
+ const content = subtitles_resource.content;
+ return { request, addon, content };
});
const next_video = player.next_video;
const lib_item = player.lib_item;
- return {
- selected,
- meta_resource,
- subtitles_resources,
- next_video,
- lib_item
- };
+ return { selected, meta_resource, subtitles_resources, next_video, lib_item };
};
const usePlayer = (urlParams) => {
+ const { core } = useServices();
const loadPlayerAction = React.useMemo(() => {
try {
return {
@@ -123,7 +120,10 @@ const usePlayer = (urlParams) => {
}
:
null,
- video_id: urlParams.videoId
+ video_id: typeof urlParams.videoId === 'string' ?
+ urlParams.videoId
+ :
+ null
}
}
};
@@ -133,12 +133,30 @@ const usePlayer = (urlParams) => {
};
}
}, [urlParams]);
- return useModelState({
+ const updateLibraryItemState = React.useCallback((time, duration) => {
+ core.dispatch({
+ action: 'Player',
+ args: {
+ action: 'UpdateLibraryItemState',
+ args: { time, duration }
+ }
+ }, 'player');
+ }, []);
+ const pushToLibrary = React.useCallback(() => {
+ core.dispatch({
+ action: 'Player',
+ args: {
+ action: 'PushToLibrary'
+ }
+ }, 'player');
+ }, []);
+ const player = useModelState({
model: 'player',
action: loadPlayerAction,
init: initPlayerState,
mapWithCtx: mapPlayerStateWithCtx
});
+ return [player, updateLibraryItemState, pushToLibrary];
};
module.exports = usePlayer;
diff --git a/src/routes/Player/useSettings.js b/src/routes/Player/useSettings.js
index 8ad45791c..be82d6034 100644
--- a/src/routes/Player/useSettings.js
+++ b/src/routes/Player/useSettings.js
@@ -1,10 +1,8 @@
const React = require('react');
-const { useProfile } = require('stremio/common');
const { useServices } = require('stremio/services');
-const useSettings = () => {
+const useSettings = (profile) => {
const { core } = useServices();
- const profile = useProfile();
const updateSettings = React.useCallback((settings) => {
core.dispatch({
action: 'Ctx',
@@ -16,7 +14,7 @@ const useSettings = () => {
}
}
});
- }, [profile.settings]);
+ }, [profile]);
return [profile.settings, updateSettings];
};
diff --git a/src/video/HTMLSubtitles.js b/src/video/HTMLSubtitles.js
index a9764557e..d4b6d73d5 100644
--- a/src/video/HTMLSubtitles.js
+++ b/src/video/HTMLSubtitles.js
@@ -55,7 +55,7 @@ function HTMLSubtitles(options) {
track.url.length > 0 &&
typeof track.origin === 'string' &&
track.origin.length > 0 &&
- track.origin !== 'EMBEDDED';
+ track.origin !== 'EMBEDDED IN VIDEO';
})
.map(function(track) {
return Object.freeze(Object.assign({}, track, {
diff --git a/src/video/YouTubeVideo.js b/src/video/YouTubeVideo.js
index 1ef9bc466..e679e0a5e 100644
--- a/src/video/YouTubeVideo.js
+++ b/src/video/YouTubeVideo.js
@@ -84,7 +84,7 @@ function YouTubeVideo(options) {
.map(function(track) {
return Object.freeze({
id: track.languageCode,
- origin: 'EMBEDDED',
+ origin: 'EMBEDDED IN VIDEO',
label: track.languageName
});
});
@@ -482,7 +482,7 @@ function YouTubeVideo(options) {
embeddedSubtitlesSelectedTrackId = null;
var tracks = getSubtitlesTracks();
for (var i = 0; i < tracks.length; i++) {
- if (tracks[i].id === arguments[2] && tracks[i].origin === 'EMBEDDED') {
+ if (tracks[i].id === arguments[2] && tracks[i].origin === 'EMBEDDED IN VIDEO') {
embeddedSubtitlesSelectedTrackId = tracks[i].id;
break;
}