diff --git a/src/common/CONSTANTS.js b/src/common/CONSTANTS.js index 2bbf49f94..f5702fd4e 100644 --- a/src/common/CONSTANTS.js +++ b/src/common/CONSTANTS.js @@ -7,6 +7,7 @@ const CATALOG_PREVIEW_SIZE = 10; const CATALOG_PAGE_SIZE = 100; const NONE_EXTRA_VALUE = 'None'; const SKIP_EXTRA_NAME = 'skip'; +const META_LINK_CATEGORY = 'meta'; const IMDB_LINK_CATEGORY = 'imdb'; const SHARE_LINK_CATEGORY = 'share'; const TYPE_PRIORITIES = { @@ -31,6 +32,7 @@ module.exports = { CATALOG_PAGE_SIZE, NONE_EXTRA_VALUE, SKIP_EXTRA_NAME, + META_LINK_CATEGORY, IMDB_LINK_CATEGORY, SHARE_LINK_CATEGORY, TYPE_PRIORITIES diff --git a/src/common/MetaPreview/MetaPreview.js b/src/common/MetaPreview/MetaPreview.js index a1cae3a77..b5f96c0a5 100644 --- a/src/common/MetaPreview/MetaPreview.js +++ b/src/common/MetaPreview/MetaPreview.js @@ -27,43 +27,44 @@ const ALLOWED_LINK_REDIRECTS = [ const MetaPreview = ({ className, compact, name, logo, background, runtime, releaseInfo, released, description, links, trailer, inLibrary, toggleInLibrary }) => { const [shareModalOpen, openShareModal, closeShareModal] = useBinaryState(false); const linksGroups = React.useMemo(() => { - return Array.isArray(links) ? - links - .filter((link) => link && typeof link.category === 'string' && typeof link.url === 'string') - .reduce((linksGroups, { category, name, url }) => { - if (category === CONSTANTS.IMDB_LINK_CATEGORY) { - linksGroups[category] = { - label: name, - href: `https://www.stremio.com/warning#${encodeURIComponent(`https://www.imdb.com/title/${encodeURIComponent(url)}`)}` - }; - } else if (category === CONSTANTS.SHARE_LINK_CATEGORY) { - linksGroups[category] = { - label: name, - href: url - }; - } else { - const { protocol, host, path, pathname } = UrlUtils.parse(url); - if (protocol === 'stremio:') { - if (ALLOWED_LINK_REDIRECTS.some((regexp) => pathname.match(regexp))) { - linksGroups[category] = linksGroups[category] || []; - linksGroups[category].push({ - label: name, - href: `#${path}` - }); + return links + .filter((link) => link && typeof link.category === 'string' && typeof link.url === 'string') + .reduce((linksGroups, { category, name, url }) => { + if (category === CONSTANTS.IMDB_LINK_CATEGORY) { + linksGroups.set(category, { + label: name, + href: `https://www.stremio.com/warning#${encodeURIComponent(`https://www.imdb.com/title/${encodeURIComponent(url)}`)}` + }); + } else if (category === CONSTANTS.SHARE_LINK_CATEGORY) { + linksGroups.set(category, { + label: name, + href: url + }); + } else { + const { protocol, host, path, pathname } = UrlUtils.parse(url); + if (protocol === 'stremio:') { + if (pathname !== null && ALLOWED_LINK_REDIRECTS.some((regexp) => pathname.match(regexp))) { + if (!linksGroups.has(category)) { + linksGroups.set(category, []); } - } else if (typeof host === 'string' && host.length > 0) { - linksGroups[category] = linksGroups[category] || []; - linksGroups[category].push({ + linksGroups.get(category).push({ label: name, - href: `https://www.stremio.com/warning#${encodeURIComponent(url)}` + href: `#${path}` }); } + } else if (typeof host === 'string' && host.length > 0) { + if (!linksGroups.has(category)) { + linksGroups.set(category, []); + } + linksGroups.get(category).push({ + label: name, + href: `https://www.stremio.com/warning#${encodeURIComponent(url)}` + }); } + } - return linksGroups; - }, {}) - : - []; + return linksGroups; + }, new Map()); }, [links]); const trailerHref = React.useMemo(() => { if (typeof trailer !== 'object' || trailer === null) { @@ -99,7 +100,7 @@ const MetaPreview = ({ className, compact, name, logo, background, runtime, rele null } { - (typeof releaseInfo === 'string' && releaseInfo.length > 0) || (released instanceof Date && !isNaN(released.getTime())) || (typeof runtime === 'string' && runtime.length > 0) || typeof linksGroups[CONSTANTS.IMDB_LINK_CATEGORY] === 'object' ? + (typeof releaseInfo === 'string' && releaseInfo.length > 0) || (released instanceof Date && !isNaN(released.getTime())) || (typeof runtime === 'string' && runtime.length > 0) || linksGroups.has(CONSTANTS.IMDB_LINK_CATEGORY) ?
{ typeof runtime === 'string' && runtime.length > 0 ? @@ -117,16 +118,16 @@ const MetaPreview = ({ className, compact, name, logo, background, runtime, rele null } { - typeof linksGroups[CONSTANTS.IMDB_LINK_CATEGORY] === 'object' ? + linksGroups.has(CONSTANTS.IMDB_LINK_CATEGORY) ? : null @@ -150,7 +151,7 @@ const MetaPreview = ({ className, compact, name, logo, background, runtime, rele null } { - Object.keys(linksGroups) + Array.from(linksGroups.keys()) .filter((category) => { return category !== CONSTANTS.IMDB_LINK_CATEGORY && category !== CONSTANTS.SHARE_LINK_CATEGORY; @@ -160,7 +161,7 @@ const MetaPreview = ({ className, compact, name, logo, background, runtime, rele key={index} className={styles['meta-links']} label={category} - links={linksGroups[category]} + links={linksGroups.get(category)} /> )) } @@ -191,7 +192,7 @@ const MetaPreview = ({ className, compact, name, logo, background, runtime, rele null } { - typeof linksGroups[CONSTANTS.SHARE_LINK_CATEGORY] === 'object' ? + linksGroups.has(CONSTANTS.SHARE_LINK_CATEGORY) ? : diff --git a/src/common/deepLinking.js b/src/common/deepLinking.js index f90bc1436..beff1431a 100644 --- a/src/common/deepLinking.js +++ b/src/common/deepLinking.js @@ -23,17 +23,6 @@ const withMetaItem = ({ metaItem }) => { }; }; -const withMetaResource = ({ metaResource, type, id, videoId }) => { - const queryParams = new URLSearchParams([['metaTransportUrl', metaResource.request.base]]); - return { - meta_details_videos: `#/metadetails/${encodeURIComponent(type)}/${encodeURIComponent(id)}?${queryParams.toString()}`, - meta_details_streams: typeof videoId === 'string' ? - `#/metadetails/${encodeURIComponent(type)}/${encodeURIComponent(id)}/${encodeURIComponent(videoId)}?${queryParams.toString()}` - : - null - }; -}; - const withLibItem = ({ libItem, streams = {} }) => { const [stream, streamTransportUrl, metaTransportUrl] = typeof libItem.state.video_id === 'string' && typeof streams[`${encodeURIComponent(libItem._id)}/${encodeURIComponent(libItem.state.video_id)}`] === 'object' ? streams[`${encodeURIComponent(libItem._id)}/${encodeURIComponent(libItem.state.video_id)}`] @@ -60,13 +49,12 @@ const withLibItem = ({ libItem, streams = {} }) => { }; const withVideo = ({ video, metaTransportUrl, metaItem, streams = {} }) => { - const queryParams = new URLSearchParams([['metaTransportUrl', metaTransportUrl]]); const [stream, streamTransportUrl] = typeof streams[`${encodeURIComponent(metaItem.id)}/${encodeURIComponent(video.id)}`] === 'object' ? streams[`${encodeURIComponent(metaItem.id)}/${encodeURIComponent(video.id)}`] : []; return { - meta_details_streams: `#/metadetails/${encodeURIComponent(metaItem.type)}/${encodeURIComponent(metaItem.id)}/${encodeURIComponent(video.id)}?${queryParams.toString()}`, + meta_details_streams: `#/metadetails/${encodeURIComponent(metaItem.type)}/${encodeURIComponent(metaItem.id)}/${encodeURIComponent(video.id)}`, // TODO check if stream is external player: typeof stream === 'object' && typeof streamTransportUrl === 'string' ? `#/player/${encodeURIComponent(serializeStream(stream))}/${encodeURIComponent(streamTransportUrl)}/${encodeURIComponent(metaTransportUrl)}/${encodeURIComponent(metaItem.type)}/${encodeURIComponent(metaItem.id)}/${encodeURIComponent(video.id)}` @@ -96,7 +84,6 @@ const withCatalog = ({ request }) => { module.exports = { withCatalog, withMetaItem, - withMetaResource, withLibItem, withVideo, withStream, diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 11404fa02..23d058142 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -277,23 +277,20 @@ const Intro = ({ queryParams }) => { }; }, [routeFocused]); React.useEffect(() => { - var initScriptElement = document.createElement('script'); - var sdkScriptElement = document.createElement('script'); - initScriptElement.innerHTML = `window.fbAsyncInit = function() { - FB.init({ - appId: '1537119779906825', - autoLogAppEvents: false, - xfbml: false, - version: 'v2.5' - }); - };`; + window.fbAsyncInit = function() { + FB.init({ + appId: '1537119779906825', + autoLogAppEvents: false, + xfbml: false, + version: 'v2.5' + }); + }; + const sdkScriptElement = document.createElement('script'); sdkScriptElement.src = 'https://connect.facebook.net/en_US/sdk.js'; sdkScriptElement.async = true; sdkScriptElement.defer = true; - document.body.appendChild(initScriptElement); document.body.appendChild(sdkScriptElement); return () => { - document.body.removeChild(initScriptElement); document.body.removeChild(sdkScriptElement); }; }, []); diff --git a/src/routes/MetaDetails/MetaDetails.js b/src/routes/MetaDetails/MetaDetails.js index 5c8c25789..ecda0105a 100644 --- a/src/routes/MetaDetails/MetaDetails.js +++ b/src/routes/MetaDetails/MetaDetails.js @@ -2,35 +2,41 @@ const React = require('react'); const PropTypes = require('prop-types'); -const { VerticalNavBar, HorizontalNavBar, MetaPreview, Image, useInLibrary } = require('stremio/common'); +const { VerticalNavBar, HorizontalNavBar, MetaPreview, ModalDialog, Image, useInLibrary } = require('stremio/common'); const StreamsList = require('./StreamsList'); const VideosList = require('./VideosList'); const useMetaDetails = require('./useMetaDetails'); +const useMetaExtensions = require('./useMetaExtensions'); const styles = require('./styles'); const MetaDetails = ({ urlParams, queryParams }) => { const metaDetails = useMetaDetails(urlParams); + const { tabs, selectedMetaExtension, clearSelectedMetaExtension } = useMetaExtensions(metaDetails.meta_resources); const metaResourceRef = React.useMemo(() => { return metaDetails.selected !== null ? metaDetails.selected.meta_resources_ref : null; }, [metaDetails.selected]); - const selectedAddon = queryParams.get('metaTransportUrl'); const selectedMetaResource = React.useMemo(() => { return metaDetails.meta_resources.reduceRight((result, metaResource) => { - if (typeof selectedAddon === 'string') { - if (metaResource.request.base === selectedAddon) { - return metaResource; - } - } else if (metaResource.content.type === 'Ready') { + if (metaResource.content.type === 'Ready') { return metaResource; } return result; }, null); - }, [metaDetails, selectedAddon]); + }, [metaDetails]); const streamsResourceRef = metaDetails.selected !== null ? metaDetails.selected.streams_resource_ref : null; const streamsResources = metaDetails.streams_resources; + const seasonQueryParam = React.useMemo(() => { + return queryParams.has('season') && !isNaN(queryParams.get('season')) ? + parseInt(queryParams.get('season')) + : + null; + }, [queryParams]); + const seasonOnSelect = React.useCallback((event) => { + window.location.replace(`#/metadetails/${selectedMetaResource.request.path.type_name}/${selectedMetaResource.request.path.id}?season=${event.value}`); + }, [selectedMetaResource]); const selectedVideo = React.useMemo(() => { - return streamsResourceRef !== null && selectedMetaResource !== null && selectedMetaResource.content.type === 'Ready' ? + return streamsResourceRef !== null && selectedMetaResource !== null ? selectedMetaResource.content.content.videos.reduce((result, video) => { if (video.id === streamsResourceRef.id) { return video; @@ -41,30 +47,21 @@ const MetaDetails = ({ urlParams, queryParams }) => { : null; }, [selectedMetaResource, streamsResourceRef]); - const tabs = React.useMemo(() => { - return metaDetails.meta_resources.map((metaResource) => ({ - id: metaResource.addon.transportUrl, - label: metaResource.addon.manifest.name, - logo: metaResource.addon.manifest.logo, - icon: 'ic_addons', - href: metaResource.deepLinks.meta_details_streams !== null ? metaResource.deepLinks.meta_details_streams : metaResource.deepLinks.meta_details_videos - })); - }, [metaDetails]); - const [inLibrary, toggleInLibrary] = useInLibrary(selectedMetaResource !== null && selectedMetaResource.content.type === 'Ready' ? selectedMetaResource.content.content : null); + const [inLibrary, toggleInLibrary] = useInLibrary(selectedMetaResource !== null ? selectedMetaResource.content.content : null); return (
{ - metaDetails.meta_resources.length > 0 ? + tabs.length > 0 ? : null @@ -88,7 +85,7 @@ const MetaDetails = ({ urlParams, queryParams }) => {
No metadata was found!
: - selectedMetaResource !== null && selectedMetaResource.content.type === 'Ready' ? + selectedMetaResource !== null ? { typeof selectedMetaResource.content.content.background === 'string' && @@ -137,11 +134,28 @@ const MetaDetails = ({ urlParams, queryParams }) => { : null }
+ { + selectedMetaExtension !== null ? + +