mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-04-21 11:42:05 +00:00
Revert "Revert "stream info design update""
This reverts commit 22b8a54c4a.
This commit is contained in:
parent
22b8a54c4a
commit
b669fe08fc
9 changed files with 279 additions and 54 deletions
|
|
@ -194,34 +194,50 @@ const MetaPreview = ({ className, compact, name, logo, background, runtime, rele
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles['action-buttons-container']}>
|
<div className={styles['action-buttons-container']}>
|
||||||
{
|
<div className={styles['action-button-wrapper']}>
|
||||||
typeof toggleInLibrary === 'function' ?
|
{
|
||||||
<ActionButton
|
typeof toggleInLibrary === 'function' ?
|
||||||
className={styles['action-button']}
|
<ActionButton
|
||||||
icon={inLibrary ? 'remove-from-library' : 'add-to-library'}
|
className={styles['action-button']}
|
||||||
label={compact ? null : inLibrary ? t('REMOVE_FROM_LIB') : t('ADD_TO_LIB')}
|
icon={inLibrary ? 'remove-from-library' : 'add-to-library'}
|
||||||
tabIndex={compact ? -1 : 0}
|
label={compact ? null : inLibrary ? t('REMOVE_FROM_LIB') : t('ADD_TO_LIB')}
|
||||||
onClick={toggleInLibrary}
|
tabIndex={compact ? -1 : 0}
|
||||||
/>
|
onClick={toggleInLibrary}
|
||||||
:
|
/>
|
||||||
null
|
:
|
||||||
}
|
null
|
||||||
{
|
}
|
||||||
typeof trailerHref === 'string' ?
|
{
|
||||||
<ActionButton
|
typeof toggleInLibrary === 'function' && compact ?
|
||||||
className={styles['action-button']}
|
<div className={styles['label']}>{inLibrary ? t('REMOVE_FROM_LIB') : t('ADD_TO_LIB')}</div>
|
||||||
icon={'trailer'}
|
:
|
||||||
label={compact ? null : t('TRAILER')}
|
null
|
||||||
tabIndex={compact ? -1 : 0}
|
}
|
||||||
href={trailerHref}
|
</div>
|
||||||
/>
|
<div className={styles['action-button-wrapper']}>
|
||||||
:
|
{
|
||||||
null
|
typeof trailerHref === 'string' ?
|
||||||
}
|
<ActionButton
|
||||||
|
className={styles['action-button']}
|
||||||
|
icon={'trailer'}
|
||||||
|
label={compact ? null : t('TRAILER')}
|
||||||
|
tabIndex={compact ? -1 : 0}
|
||||||
|
href={trailerHref}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
|
{
|
||||||
|
typeof trailerHref === 'string' && compact ?
|
||||||
|
<div className={styles['label']}>{t('WATCH_TRAILER')}</div>
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
|
</div>
|
||||||
{
|
{
|
||||||
typeof showHref === 'string' && compact ?
|
typeof showHref === 'string' && compact ?
|
||||||
<ActionButton
|
<ActionButton
|
||||||
className={styles['action-button']}
|
className={`${styles['action-button']} ${styles['show-button']}`}
|
||||||
icon={'play'}
|
icon={'play'}
|
||||||
label={t('SHOW')}
|
label={t('SHOW')}
|
||||||
tabIndex={compact ? -1 : 0}
|
tabIndex={compact ? -1 : 0}
|
||||||
|
|
|
||||||
|
|
@ -156,9 +156,35 @@
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
|
||||||
max-height: 10rem;
|
max-height: 10rem;
|
||||||
|
padding-top: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
overflow-x: visible;
|
||||||
|
|
||||||
|
.action-button-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
max-width: 6rem;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .label {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.action-button {
|
.action-button {
|
||||||
flex: none;
|
flex: none;
|
||||||
width: 4rem;
|
width: 4rem;
|
||||||
|
|
@ -174,6 +200,12 @@
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
}
|
}
|
||||||
|
&.show-button {
|
||||||
|
&:hover, &:focus {
|
||||||
|
background-color: var( --secondary-accent-color);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -198,16 +230,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-buttons-container {
|
.action-buttons-container {
|
||||||
flex-wrap: nowrap;
|
flex-shrink: 0;
|
||||||
margin-top: 3rem;
|
margin-top: 3rem;
|
||||||
overflow-x: visible;
|
overflow: visible;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.share-prompt {
|
.share-prompt {
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,20 @@ const React = require('react');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const classnames = require('classnames');
|
const classnames = require('classnames');
|
||||||
const { default: Icon } = require('@stremio/stremio-icons/react');
|
const { default: Icon } = require('@stremio/stremio-icons/react');
|
||||||
const { Button, Image, PlayIconCircleCentered, useProfile, platform, useStreamingServer, useToast } = require('stremio/common');
|
const { Button, Image, useProfile, platform, useStreamingServer, useToast } = require('stremio/common');
|
||||||
const { useServices } = require('stremio/services');
|
const { useServices } = require('stremio/services');
|
||||||
const StreamPlaceholder = require('./StreamPlaceholder');
|
const StreamPlaceholder = require('./StreamPlaceholder');
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
|
const parseTorrentInfo = require('./parseTorrentInfo');
|
||||||
|
const StreamInfo = require('./StreamInfo');
|
||||||
|
|
||||||
const Stream = ({ className, videoId, videoReleased, addonName, name, description, thumbnail, progress, deepLinks, ...props }) => {
|
const Stream = ({ className, videoId, videoReleased, addonName, name, description, thumbnail, progress, deepLinks, ...props }) => {
|
||||||
const profile = useProfile();
|
const profile = useProfile();
|
||||||
const streamingServer = useStreamingServer();
|
const streamingServer = useStreamingServer();
|
||||||
const { core } = useServices();
|
const { core } = useServices();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
const torrentInfo = parseTorrentInfo(description);
|
||||||
|
const {streamName, streamSeeders, streamSize, streamProvider, streamFlags } = torrentInfo;
|
||||||
const href = React.useMemo(() => {
|
const href = React.useMemo(() => {
|
||||||
const haveStreamingServer = streamingServer.settings !== null && streamingServer.settings.type === 'Ready';
|
const haveStreamingServer = streamingServer.settings !== null && streamingServer.settings.type === 'Ready';
|
||||||
return deepLinks ?
|
return deepLinks ?
|
||||||
|
|
@ -79,7 +83,7 @@ const Stream = ({ className, videoId, videoReleased, addonName, name, descriptio
|
||||||
<Button href={href} download={forceDownload} {...props} onClick={onClick} className={classnames(className, styles['stream-container'])} title={addonName}>
|
<Button href={href} download={forceDownload} {...props} onClick={onClick} className={classnames(className, styles['stream-container'])} title={addonName}>
|
||||||
{
|
{
|
||||||
typeof thumbnail === 'string' && thumbnail.length > 0 ?
|
typeof thumbnail === 'string' && thumbnail.length > 0 ?
|
||||||
<div className={styles['thumbnail-container']} title={name || addonName}>
|
<div className={styles['thumbnail-container']} title={name?.replace(/\|/g, '') || addonName?.replace(/\|/g, '')}>
|
||||||
<Image
|
<Image
|
||||||
className={styles['thumbnail']}
|
className={styles['thumbnail']}
|
||||||
src={thumbnail}
|
src={thumbnail}
|
||||||
|
|
@ -88,12 +92,23 @@ const Stream = ({ className, videoId, videoReleased, addonName, name, descriptio
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
<div className={styles['addon-name-container']} title={name || addonName}>
|
<div className={styles['addon-name-container']} title={name?.replace(/\|/g, '') || addonName?.replace(/\|/g, '')}>
|
||||||
<div className={styles['addon-name']}>{name || addonName}</div>
|
<div className={styles['addon-name']}>{name?.replace(/\|/g, '') || addonName?.replace(/\|/g, '')}</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div className={styles['info-container']} title={description}>{description}</div>
|
{
|
||||||
<PlayIconCircleCentered className={styles['play-icon']} />
|
typeof description === 'string' && description.length > 0 ?
|
||||||
|
<StreamInfo
|
||||||
|
streamName={streamName}
|
||||||
|
streamProvider={streamProvider}
|
||||||
|
streamSeeders={streamSeeders}
|
||||||
|
streamSize={streamSize}
|
||||||
|
streamFlags={streamFlags}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
|
<Icon className={styles['icon']} name={'play'} />
|
||||||
{
|
{
|
||||||
progress !== null && !isNaN(progress) && progress > 0 ?
|
progress !== null && !isNaN(progress) && progress > 0 ?
|
||||||
<div className={styles['progress-bar-container']}>
|
<div className={styles['progress-bar-container']}>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
const React = require('react');
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const { default: Icon } = require('@stremio/stremio-icons/react');
|
||||||
|
const styles = require('./styles');
|
||||||
|
|
||||||
|
const StreamInfo = ({ streamSize, streamProvider, streamName, streamFlags, streamSeeders }) => {
|
||||||
|
return (
|
||||||
|
<div className={styles['info-container']}>
|
||||||
|
<div className={styles['stream-name']}>{streamName}</div>
|
||||||
|
<div className={styles['description-container']}>
|
||||||
|
<div className={styles['stream-properties']}>
|
||||||
|
{
|
||||||
|
typeof streamSeeders === 'number' && streamSeeders > 0 ?
|
||||||
|
<div className={styles['property-container']}>
|
||||||
|
<Icon className={styles['icon']} name={'person'} />
|
||||||
|
<div className={styles['property']}>{streamSeeders}</div>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
|
{
|
||||||
|
typeof streamSize === 'string' && streamSize.length > 0 ?
|
||||||
|
<div className={styles['property-container']}>
|
||||||
|
<Icon className={styles['icon']} name={'memory'} />
|
||||||
|
<div className={styles['property']}>{streamSize}</div>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
|
{
|
||||||
|
typeof streamProvider === 'string' && streamProvider.length > 0 ?
|
||||||
|
<div className={styles['property-container']}>
|
||||||
|
<Icon className={styles['icon']} name={'settings-outline'} />
|
||||||
|
<div className={styles['property overflow']}>{streamProvider}</div>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
|
{
|
||||||
|
typeof streamFlags === 'string' && streamFlags.length > 0 ?
|
||||||
|
<div className={styles['property-container']}>
|
||||||
|
<div className={styles['property']}>{streamFlags}</div>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
null
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
StreamInfo.propTypes = {
|
||||||
|
streamFlags: PropTypes.string,
|
||||||
|
streamSize: PropTypes.string,
|
||||||
|
streamProvider: PropTypes.string,
|
||||||
|
streamSeeders: PropTypes.number,
|
||||||
|
streamName: PropTypes.string
|
||||||
|
};
|
||||||
|
module.exports = StreamInfo;
|
||||||
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Copyright (C) 2017-2023 Smart code 203358507
|
||||||
|
|
||||||
|
const StreamInfo = require('./StreamInfo');
|
||||||
|
|
||||||
|
module.exports = StreamInfo;
|
||||||
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
.info-container {
|
||||||
|
flex: 1;
|
||||||
|
max-height: 3.6em;
|
||||||
|
padding: 0 0.5rem 0 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
white-space: pre;
|
||||||
|
|
||||||
|
.stream-name {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.stream-properties {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 0 0.5em;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
.property-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
.icon {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.property {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
.overflow {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
const parseTorrent = (description) => {
|
||||||
|
const isEmoji = (str) => {
|
||||||
|
const emojiRegex = /[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]/g;
|
||||||
|
return [...str.matchAll(emojiRegex)];
|
||||||
|
};
|
||||||
|
const streamName = description.split(/\u{1F464}/u)[0].trim();
|
||||||
|
|
||||||
|
const seedersMatch = description.match(/\u{1F464}\s(\d+)/u);
|
||||||
|
const streamSeeders = seedersMatch ? parseInt(seedersMatch[1]) : null;
|
||||||
|
|
||||||
|
const sizeMatch = description.match(/(?<=\uD83D\uDCBE).*?(?=\u2699\uFE0F?)/);
|
||||||
|
const streamSize = sizeMatch ? sizeMatch[0] : null;
|
||||||
|
|
||||||
|
const providerMatch = description.match(/\u2699\uFE0F\s(\S+?)\s/);
|
||||||
|
|
||||||
|
const streamProvider = providerMatch ? providerMatch[1].trim() : null;
|
||||||
|
|
||||||
|
const flagMatches = isEmoji(description);
|
||||||
|
let streamFlags = '';
|
||||||
|
for (const match of flagMatches) {
|
||||||
|
streamFlags += match[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const torrentInfo = {
|
||||||
|
streamName,
|
||||||
|
streamSeeders,
|
||||||
|
streamSize,
|
||||||
|
streamProvider,
|
||||||
|
streamFlags
|
||||||
|
};
|
||||||
|
|
||||||
|
return torrentInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = parseTorrent;
|
||||||
|
|
||||||
|
|
@ -70,26 +70,39 @@
|
||||||
flex: 1;
|
flex: 1;
|
||||||
max-height: 3.6em;
|
max-height: 3.6em;
|
||||||
padding: 0 0.5rem 0 1.5rem;
|
padding: 0 0.5rem 0 1.5rem;
|
||||||
display: -webkit-box;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
-webkit-line-clamp: 3;
|
-webkit-line-clamp: 3;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
color: var(--primary-foreground-color);
|
color: var(--primary-foreground-color);
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
|
|
||||||
|
.stream-name {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.stream-properties {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
.icon {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--primary-foreground-color);
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.play-icon {
|
.icon {
|
||||||
flex: none;
|
flex: none;
|
||||||
width: 3.5rem;
|
width: 3rem;
|
||||||
height: 5rem;
|
height: 3rem;
|
||||||
opacity: 0;
|
padding: 0.7rem;
|
||||||
|
border-radius: 50%;
|
||||||
.play-icon-circle-centered-background {
|
color: var(--primary-foreground-color);
|
||||||
fill: none;
|
background-color: var(--secondary-accent-color);
|
||||||
}
|
|
||||||
|
|
||||||
.play-icon-circle-centered-icon {
|
|
||||||
fill: var(--primary-foreground-color);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar-container {
|
.progress-bar-container {
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,7 @@
|
||||||
padding: 1.5rem 1rem;
|
padding: 1.5rem 1rem;
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
background-color: var(--secondary-accent-color);
|
background-color: var(--secondary-accent-color);
|
||||||
|
border-radius: 3rem;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
outline: var(--focus-outline-size) solid var(--secondary-accent-color);
|
outline: var(--focus-outline-size) solid var(--secondary-accent-color);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue