Merge branch 'master' of github.com:Stremio/stremio-web into addons-screen

This commit is contained in:
svetlagasheva 2019-01-17 10:49:19 +02:00
commit a61697ce38
20 changed files with 736 additions and 143 deletions

View file

@ -1,4 +1,4 @@
import { Calendar, Discover, Addons, Settings, Board, Player } from 'stremio-routes';
import { Calendar, Discover, Addons, Settings, Board, Player, Detail } from 'stremio-routes';
const config = {
views: [
@ -27,6 +27,10 @@ const config = {
{
path: '/settings',
component: Settings
},
{
path: '/detail',
component: Detail
}
]
},

View file

@ -15,13 +15,15 @@ class Popup extends Component {
this.labelBorderRightRef = React.createRef();
this.labelBorderBottomRef = React.createRef();
this.labelBorderLeftRef = React.createRef();
this.menuRef = React.createRef();
this.menuContainerRef = React.createRef();
this.menuScrollRef = React.createRef();
this.menuChildrenRef = React.createRef();
this.menuBorderTopRef = React.createRef();
this.menuBorderRightRef = React.createRef();
this.menuBorderBottomRef = React.createRef();
this.menuBorderLeftRef = React.createRef();
this.hiddenBorderRef = React.createRef();
this.popupMutationObserver = this.createPopupMutationObserver();
this.state = {
open: false
@ -38,6 +40,7 @@ class Popup extends Component {
window.removeEventListener('blur', this.close);
window.removeEventListener('resize', this.close);
window.removeEventListener('keyup', this.onKeyUp);
this.popupMutationObserver.disconnect();
}
shouldComponentUpdate(nextProps, nextState) {
@ -48,19 +51,65 @@ class Popup extends Component {
componentDidUpdate(prevProps, prevState) {
if (this.state.open && !prevState.open) {
this.updateStyles();
this.popupMutationObserver.observe(document.documentElement, {
childList: true,
attributes: true,
subtree: true
});
if (typeof this.props.onOpen === 'function') {
this.props.onOpen();
}
} else if (!this.state.open && prevState.open && typeof this.props.onClose === 'function') {
this.props.onClose();
} else if (!this.state.open && prevState.open) {
this.popupMutationObserver.disconnect();
if (typeof this.props.onClose === 'function') {
this.props.onClose();
}
}
}
createPopupMutationObserver = () => {
let prevLabelRect = {};
let prevMenuChildrenRect = {};
return new MutationObserver(() => {
if (this.state.open) {
const labelRect = this.labelRef.current.getBoundingClientRect();
const menuChildrenRect = this.menuChildrenRef.current.getBoundingClientRect();
if (labelRect.x !== prevLabelRect.x ||
labelRect.y !== prevLabelRect.y ||
labelRect.width !== prevLabelRect.width ||
labelRect.height !== prevLabelRect.height ||
menuChildrenRect.x !== prevMenuChildrenRect.x ||
menuChildrenRect.y !== prevMenuChildrenRect.y ||
menuChildrenRect.width !== prevMenuChildrenRect.width ||
menuChildrenRect.height !== prevMenuChildrenRect.height) {
this.updateStyles();
}
prevLabelRect = labelRect;
prevMenuChildrenRect = menuChildrenRect;
} else {
prevLabelRect = {};
prevMenuChildrenRect = {};
}
});
}
updateStyles = () => {
this.menuContainerRef.current.removeAttribute('style');
this.menuScrollRef.current.removeAttribute('style');
this.menuBorderTopRef.current.removeAttribute('style');
this.menuBorderRightRef.current.removeAttribute('style');
this.menuBorderBottomRef.current.removeAttribute('style');
this.menuBorderLeftRef.current.removeAttribute('style');
this.labelBorderTopRef.current.removeAttribute('style');
this.labelBorderRightRef.current.removeAttribute('style');
this.labelBorderBottomRef.current.removeAttribute('style');
this.labelBorderLeftRef.current.removeAttribute('style');
const menuDirections = {};
const bodyRect = document.body.getBoundingClientRect();
const menuRect = this.menuRef.current.getBoundingClientRect();
const labelRect = this.labelRef.current.getBoundingClientRect();
const menuChildredRect = this.menuChildrenRef.current.getBoundingClientRect();
const borderSize = parseFloat(window.getComputedStyle(this.hiddenBorderRef.current).getPropertyValue('border-top-width'));
const labelPosition = {
left: labelRect.x - bodyRect.x,
@ -69,38 +118,38 @@ class Popup extends Component {
bottom: (bodyRect.height + bodyRect.y) - (labelRect.y + labelRect.height)
};
if (menuRect.height <= labelPosition.bottom) {
this.menuRef.current.style.top = `${labelPosition.top + labelRect.height}px`;
if (menuChildredRect.height <= labelPosition.bottom) {
this.menuContainerRef.current.style.top = `${labelPosition.top + labelRect.height}px`;
this.menuScrollRef.current.style.maxHeight = `${labelPosition.bottom}px`;
menuDirections.bottom = true;
} else if (menuRect.height <= labelPosition.top) {
this.menuRef.current.style.bottom = `${labelPosition.bottom + labelRect.height}px`;
} else if (menuChildredRect.height <= labelPosition.top) {
this.menuContainerRef.current.style.bottom = `${labelPosition.bottom + labelRect.height}px`;
this.menuScrollRef.current.style.maxHeight = `${labelPosition.top}px`;
menuDirections.top = true;
} else if (labelPosition.bottom >= labelPosition.top) {
this.menuRef.current.style.top = `${labelPosition.top + labelRect.height}px`;
this.menuContainerRef.current.style.top = `${labelPosition.top + labelRect.height}px`;
this.menuScrollRef.current.style.maxHeight = `${labelPosition.bottom}px`;
menuDirections.bottom = true;
} else {
this.menuRef.current.style.bottom = `${labelPosition.bottom + labelRect.height}px`;
this.menuContainerRef.current.style.bottom = `${labelPosition.bottom + labelRect.height}px`;
this.menuScrollRef.current.style.maxHeight = `${labelPosition.top}px`;
menuDirections.top = true;
}
if (menuRect.width <= (labelPosition.right + labelRect.width)) {
this.menuRef.current.style.left = `${labelPosition.left}px`;
if (menuChildredRect.width <= (labelPosition.right + labelRect.width)) {
this.menuContainerRef.current.style.left = `${labelPosition.left}px`;
this.menuScrollRef.current.style.maxWidth = `${labelPosition.right + labelRect.width}px`;
menuDirections.right = true;
} else if (menuRect.width <= (labelPosition.left + labelRect.width)) {
this.menuRef.current.style.right = `${labelPosition.right}px`;
} else if (menuChildredRect.width <= (labelPosition.left + labelRect.width)) {
this.menuContainerRef.current.style.right = `${labelPosition.right}px`;
this.menuScrollRef.current.style.maxWidth = `${labelPosition.left + labelRect.width}px`;
menuDirections.left = true;
} else if (labelPosition.right > labelPosition.left) {
this.menuRef.current.style.left = `${labelPosition.left}px`;
this.menuContainerRef.current.style.left = `${labelPosition.left}px`;
this.menuScrollRef.current.style.maxWidth = `${labelPosition.right + labelRect.width}px`;
menuDirections.right = true;
} else {
this.menuRef.current.style.right = `${labelPosition.right}px`;
this.menuContainerRef.current.style.right = `${labelPosition.right}px`;
this.menuScrollRef.current.style.maxWidth = `${labelPosition.left + labelRect.width}px`;
menuDirections.left = true;
}
@ -128,14 +177,14 @@ class Popup extends Component {
this.labelBorderLeftRef.current.style.left = `${labelPosition.left}px`;
if (menuDirections.top) {
this.labelBorderTopRef.current.style.display = 'none';
this.labelBorderTopRef.current.style.left = `${labelPosition.left + menuChildredRect.width}px`;
if (menuDirections.left) {
this.menuBorderBottomRef.current.style.right = `${labelRect.width - borderSize}px`;
} else {
this.menuBorderBottomRef.current.style.left = `${labelRect.width - borderSize}px`;
}
} else {
this.labelBorderBottomRef.current.style.display = 'none';
this.labelBorderBottomRef.current.style.left = `${labelPosition.left + menuChildredRect.width}px`;
if (menuDirections.left) {
this.menuBorderTopRef.current.style.right = `${labelRect.width - borderSize}px`;
} else {
@ -144,7 +193,7 @@ class Popup extends Component {
}
}
this.menuRef.current.style.visibility = 'visible';
this.menuContainerRef.current.style.visibility = 'visible';
}
onKeyUp = (event) => {
@ -177,9 +226,11 @@ class Popup extends Component {
return (
<Modal className={classnames('modal-container', this.props.className)} onClick={this.close}>
<div ref={this.menuRef} className={styles['menu-container']} onClick={this.menuContainerOnClick}>
<div ref={this.menuScrollRef} className={styles['scroll-container']}>
{children}
<div ref={this.menuContainerRef} className={styles['menu-container']} onClick={this.menuContainerOnClick}>
<div ref={this.menuScrollRef} className={styles['menu-scroll-container']}>
<div ref={this.menuChildrenRef}>
{children}
</div>
</div>
<div ref={this.menuBorderTopRef} className={classnames(styles['border'], styles['border-top'])} />
<div ref={this.menuBorderRightRef} className={classnames(styles['border'], styles['border-right'])} />

View file

@ -2,7 +2,7 @@
position: absolute;
visibility: hidden;
.scroll-container {
.menu-scroll-container {
overflow: auto;
}
}

View file

@ -1,42 +1,10 @@
import React, { PureComponent } from 'react';
import { Catalogs } from 'stremio-aggregators';
import { addons } from 'stremio-services';
import { Stream } from 'stremio-common';
import { Video } from 'stremio-common';
import { LibraryItemList } from 'stremio-common';
import { MetaItem } from 'stremio-common';
import { Addon } from 'stremio-common';
import { ShareAddon } from 'stremio-common';
import { UserPanel } from 'stremio-common';
class Board extends PureComponent {
constructor(props) {
super(props);
// this.aggregator = new Catalogs(addons.addons);
this.state = {
catalogs: []
};
}
componentDidMount() {
// this.aggregator.evs.addListener('updated', this.onCatalogsUpdated);
// this.aggregator.run();
}
componentWillUnmount() {
// this.aggregator.evs.removeListener('updated', this.onCatalogsUpdated);
}
onCatalogsUpdated = () => {
// this.setState({ catalogs: this.aggregator.results.slice() });
}
render() {
return (
<div style={{ paddingTop: 40, color: 'yellow' }}>
<UserPanel photo={'https://image.freepik.com/free-vector/wild-animals-cartoon_1196-361.jpg'} email={'animals@mail.com'}></UserPanel>
Board
</div>
);
}

View file

@ -1,9 +1,161 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import VideosList from './VideosList';
import Icon from 'stremio-icons/dom';
import styles from './styles';
class Detail extends Component {
constructor(props) {
super(props);
this.state = {
logoLoaded: true
};
}
shouldComponentUpdate(nextProps, nextState) {
return nextState.logoLoaded !== this.state.logoLoaded;
}
renderSection({ title, links }) {
return (
<div className={styles['section-container']}>
{
title ?
<div className={styles['title']}>{title}</div>
:
null
}
{links.map(link => <a key={link} className={styles['link']}>{link}</a>)}
</div>
);
}
render() {
return null;
return (
<div style={this.props.metaItem.background.length > 0 ? { backgroundImage: 'url(' + this.props.metaItem.background + ')' } : { backgroundColor: colors.backgrounddarker }} className={styles['detail-container']}>
<div className={styles['overlay-container']} />
<div className={styles['info-container']}>
{
this.state.logoLoaded ?
<img className={styles['logo']} src={this.props.metaItem.logo} onError={() => this.setState({ logoLoaded: false })} />
:
null
}
<div className={styles['duration']}>{this.props.metaItem.duration}</div>
<div className={styles['release-info']}>
{
this.props.metaItem.releaseInfo.length > 0 ?
this.props.metaItem.releaseInfo
:
this.props.metaItem.released.getFullYear()
}
</div>
<div className={styles['name']}>{this.props.metaItem.name}</div>
<div className={styles['description']}>{this.props.metaItem.description}</div>
{this.renderSection({ title: 'GENRES', links: this.props.metaItem.genres })}
{this.renderSection({ title: 'WRITTEN BY', links: this.props.metaItem.writers })}
{this.renderSection({ title: 'DIRECTED BY', links: this.props.metaItem.directors })}
{this.renderSection({ title: 'CAST', links: this.props.metaItem.cast })}
<div className={styles['action-buttons-container']}>
<a href={this.props.metaItem.links.youtube} className={styles['action-button-container']}>
<Icon className={styles['icon']} icon={'ic_movies'} />
<div className={styles['label']}>Trailer</div>
</a>
<a href={this.props.metaItem.links.imdb} target={'_blank'} className={styles['action-button-container']}>
<Icon className={styles['icon']} icon={'ic_imdb'} />
<div className={styles['label']}>{this.props.metaItem.imdbRating} / 10</div>
</a>
<div className={styles['action-button-container']} onClick={this.props.toggleLibraryButton}>
<Icon className={styles['icon']} icon={this.props.inLibrary ? 'ic_removelib' : 'ic_addlib'} />
<div className={styles['label']}>{this.props.inLibrary ? 'Remove from Library' : 'Add to library'}</div>
</div>
<div className={styles['action-button-container']}>
<Icon className={styles['icon']} icon={'ic_share'} />
<div className={styles['label']}>Share</div>
</div>
</div>
</div>
<VideosList className={styles['videos-list']} videos={this.props.metaItem.videos}></VideosList>
</div>
);
}
}
Detail.propTypes = {
inLibrary: PropTypes.bool.isRequired,
metaItem: PropTypes.object.isRequired,
toggleLibraryButton: PropTypes.func
};
Detail.defaultProps = {
inLibrary: false,
metaItem: {
logo: 'https://images.metahub.space/logo/medium/tt4123430/img',
background: 'https://images.metahub.space/background/medium/tt4123430/img',
duration: '134 min',
releaseInfo: '2018',
released: new Date(2018, 4, 23),
imdbRating: '7.4',
name: 'Fantastic Beasts and Where to Find Them: The Original Screenplay',
description: 'In an effort to thwart Grindelwald' + 's plans ofraisingpurebloodwizardstoansofraisingpurebloodwizardstoansofraisingpurebloodwizardstoansofraising pure-blood wizards toans of raising pure-blood wizards toans of raising pure-blood wizards toans of raising pure-blood wizards toans of raising pure-blood wizards toans of raising pure-blood wizards toans of raising pure-blood wizards toans of raising pure-blood wizards toans of raising pure-blood wizards toans of raising pure-blood wizards toans of raising pure-blood wizards toans of raising pure-blood wizards toans of raising pure-blood wizards toans of raising pure-blood wizards to rule over all non-magical beings, Albus Dumbledore enlists his former student Newt Scamander, who agrees to help, unaware of the dangers that lie ahead. Lines are drawn as love and loyalty are tested, even among the truest friends and family, in an increasingly divided wizarding world.',
genres: ['Adventure', 'Family', 'Fantasy'],
writers: ['J. K. Rowling'],
directors: ['David Yates'],
cast: ['Johny Depp', 'Kevin Guthrie', 'Carmen Ejogo', 'Wolf Roth'],
videos: [
{ id: '1', poster: 'https://www.stremio.com/websiste/home-stremio.png', episode: 1, name: 'The Bing BranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, isWatched: true, season: 1 },
{ id: '2', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 2, name: 'The Bing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isWatched: true, season: 3 },
{ id: '3', episode: 4, name: 'The Luminous Fish Effect', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 5 },
{ id: '4', poster: 'https://www.stremiocom/website/home-stremio.png', episode: 5, name: 'The Dumpling Paradox', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 4 },
{ id: '5', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 5 },
{ id: '6', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 1, name: 'The Bing BranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, isWatched: true, season: 1 },
{ id: '7', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 2, name: 'The Bing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isWatched: true, season: 1 },
{ id: '8', episode: 4, name: 'The Luminous Fish Effect', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 3 },
{ id: '9', poster: 'https://www.stremiocom/website/home-stremio.png', episode: 5, name: 'The Dumpling Paradox', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 2 },
{ id: '10', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 5 },
{ id: '11', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 1 },
{ id: '12', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 1, name: 'The Bing BranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, isWatched: true, season: 1 },
{ id: '13', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 2, name: 'The Bing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isWatched: true, season: 1 },
{ id: '14', episode: 4, name: 'The Luminous Fish Effect', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 3 },
{ id: '15', poster: 'https://www.stremiocom/website/home-stremio.png', episode: 5, name: 'The Dumpling Paradox', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 2 },
{ id: '16', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 5 },
{ id: '17', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 1 },
{ id: '18', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 1, name: 'The Bing BranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, isWatched: true, season: 1 },
{ id: '19', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 2, name: 'The Bing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isWatched: true, season: 1 },
{ id: '20', episode: 4, name: 'The Luminous Fish Effect', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 3 },
{ id: '21', poster: 'https://www.stremiocom/wsebsite/home-stremio.png', episode: 5, name: 'The Dumpling Paradox', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 2 },
{ id: '22', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 5 },
{ id: '23', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 1 },
{ id: '24', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 1, name: 'The Bing BranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, isWatched: true, season: 1 },
{ id: '25', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 2, name: 'The Bing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isWatched: true, season: 1 },
{ id: '26', episode: 4, name: 'The Luminous Fish Effect', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 3 },
{ id: '27', poster: 'https://www.stremiocom/website/home-stremio.png', episode: 5, name: 'The Dumpling Paradox', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 2 },
{ id: '28', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 5 },
{ id: '29', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 1 },
{ id: '30', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 1, name: 'The Bing BranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, isWatched: true, season: 1 },
{ id: '31', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 2, name: 'The Bing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isWatched: true, season: 1 },
{ id: '32', episode: 4, name: 'The Luminous Fish Effect', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 1 },
{ id: '33', poster: 'https://www.stremiocom/website/home-stremio.png', episode: 5, name: 'The Dumpling Paradox', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 2 },
{ id: '34', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 1 },
{ id: '35', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 1 },
{ id: '36', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 1, name: 'The Bing BranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, isWatched: true, season: 1 },
{ id: '37', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 2, name: 'The Bing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isWatched: true, season: 1 },
{ id: '38', episode: 4, name: 'The Luminous Fish Effect', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 1 },
{ id: '39', poster: 'https://www.stremiocom/website/home-stremio.png', episode: 5, name: 'The Dumpling Paradox', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 2 },
{ id: '40', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 1 },
{ id: '41', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 1 },
{ id: '42', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 1, name: 'The Bing BranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBranHypothesiingBran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesiing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, isWatched: true, season: 1 },
{ id: '43', poster: 'https://www.stremio.com/website/home-stremio.png', episode: 2, name: 'The Bing Bran Hypothesis', description: 'dasdasda', released: new Date(2018, 4, 23), isWatched: true, season: 1 },
{ id: '44', episode: 4, name: 'The Luminous Fish Effect', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 1 },
{ id: '45', poster: 'https://www.stremiocom/website/home-stremio.png', episode: 5, name: 'The Dumpling Paradox', description: 'dasdasda', released: new Date(2018, 4, 23), progress: 50, season: 2 },
{ id: '46', episode: 8, name: 'The Loobendfeld Decay', description: 'dasdasda', released: new Date(2018, 4, 23), isUpcoming: true, season: 1 }
],
links: {
share: '',
imdb: 'https://www.imdb.com/title/tt4123430/?ref_=fn_al_tt_3',
youtube: '#/player'
}
}
};
export default Detail;

View file

@ -87,7 +87,7 @@ const renderProgress = (progress) => {
const Video = (props) => {
return (
<div onClick={props.onVideoClicked} className={classnames(styles['video-container'], props.className)}>
<div className={classnames(styles['video-container'], props.className)} data-video-id={props.id} onClick={props.onClick}>
<div className={styles['flex-row-container']}>
{renderPoster(props.poster)}
<div className={styles['info-container']}>
@ -106,14 +106,16 @@ const Video = (props) => {
Video.propTypes = {
className: PropTypes.string,
id: PropTypes.string.isRequired,
poster: PropTypes.string.isRequired,
episode: PropTypes.number.isRequired,
season: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
released: PropTypes.instanceOf(Date).isRequired,
isWatched: PropTypes.bool.isRequired,
isUpcoming: PropTypes.bool.isRequired,
progress: PropTypes.number.isRequired,
onVideoClicked: PropTypes.func
onClick: PropTypes.func
};
Video.defaultProps = {
poster: '',

View file

@ -1,15 +1,6 @@
.video-container {
--video-width: 360px;
--spacing: 8px;
--title-font-size: 12px;
--released-date-font-size: 11px;
--label-font-size: 10px;
--label-border-width: 2px;
}
.video-container {
width: var(--video-width);
background-color: var(--color-backgroundlight);
background-color: var(--color-surfacedarker60);
.flex-row-container {
display: flex;
@ -40,21 +31,21 @@
.info-container {
flex: 3;
min-height: calc(0.2 * var(--video-width));
min-height: calc(var(--video-width) * 0.2);
padding: var(--spacing);
display: flex;
flex-direction: column;
justify-content: center;
.title {
font-size: var(--title-font-size);
color: var(--color-surfacelighter);
line-height: 1.2em;
color: var(--color-surfacelight);
word-break: break-all; //Firefox doesn't support { break-word }
word-break: break-word;
}
.released-date {
font-size: var(--released-date-font-size);
font-size: 0.9em;
color: var(--color-surface);
}
@ -62,13 +53,13 @@
display: flex;
.upcoming-label, .watched-label {
font-size: var(--label-font-size);
font-weight: 600;
font-size: 0.8em;
font-weight: 500;
line-height: 1.5;
border-width: var(--label-border-width);
border-width: calc(var(--spacing) * 0.25);
border-style: solid;
padding: 0 0.6em;
color: var(--color-surfacelighter);
color: var(--color-surfacelight);
}
.upcoming-label {
@ -82,25 +73,25 @@
}
>:not(:last-child) {
margin-bottom: calc(0.5 * var(--spacing));
margin-bottom: calc(var(--spacing) * 0.5);
}
}
.arrow-container {
width: calc(0.07 * var(--video-width));
width: calc(var(--video-width) * 0.07);
display: flex;
align-items: center;
padding: var(--spacing) var(--spacing) var(--spacing) 0;
.arrow {
width: 100%;
fill: var(--color-surfacelighter);
fill: var(--color-surfacelight);
}
}
}
.progress-container {
height: calc(0.5 * var(--spacing));
height: calc(var(--spacing) * 0.5);
background-color: var(--color-primarydark);
.progress {
@ -111,6 +102,24 @@
&:hover {
cursor: pointer;
background-color: var(--color-surfacelighter20);
background-color: var(--color-surfacedarker);
.info-container {
.title {
color: var(--color-surfacelighter);
}
.label-container {
.upcoming-label, .watched-label {
color: var(--color-surfacelighter);
}
}
}
.arrow-container {
.arrow {
fill: var(--color-surfacelighter);
}
}
}
}

View file

@ -1,6 +1,8 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Icon from 'stremio-icons/dom';
import { Popup } from 'stremio-common';
import Video from './Video';
import styles from './styles';
@ -8,20 +10,25 @@ class VideosList extends Component {
constructor(props) {
super(props);
this.seasonsPopupRef = React.createRef();
this.seasons = this.props.videos.map((video) => video.season)
.filter((season, index, seasons) => seasons.indexOf(season) === index);
this.state = {
selectedSeason: this.seasons[0]
selectedSeason: this.seasons[0],
selectedVideoId: 0,
seasonsPopupOpen: false
}
}
changeSeason = (event) => {
this.setState({ selectedSeason: parseInt(event.target.value) });
this.setState({ selectedSeason: parseInt(event.currentTarget.dataset.season) });
this.seasonsPopupRef.current && this.seasonsPopupRef.current.close();
}
shouldComponentUpdate(nextProps, nextState) {
return nextState.selectedSeason !== this.state.selectedSeason;
return nextState.selectedSeason !== this.state.selectedSeason ||
nextState.seasonsPopupOpen !== this.state.seasonsPopupOpen;
}
onPrevButtonClicked = () => {
@ -34,20 +41,45 @@ class VideosList extends Component {
this.setState({ selectedSeason: this.seasons[nextSeasonIndex] });
}
onSeasonsPopupOpen = () => {
this.setState({ seasonsPopupOpen: true });
}
onSeasonsPopupClose = () => {
this.setState({ seasonsPopupOpen: false });
}
onClick = (event) => {
this.setState({ selectedVideoId: event.currentTarget.dataset.videoId });
console.log(event.currentTarget.dataset.videoId);
}
render() {
return (
<div className={styles['videos-list-container']}>
<div className={classnames(styles['videos-list-container'], this.props.className)}>
<div className={styles['seasons-bar']}>
<div className={styles['button-container']} onClick={this.onPrevButtonClicked}>
<Icon className={styles['button-icon']} icon={'ic_arrow_left'} />
</div>
<select value={this.state.selectedSeason} onChange={this.changeSeason}>
{this.seasons.map((season) =>
<option key={season} value={season}>
{season}
</option>
)}
</select>
<Popup ref={this.seasonsPopupRef} className={'detail-popup-container'} onOpen={this.onSeasonsPopupOpen} onClose={this.onSeasonsPopupClose}>
<Popup.Label>
<div className={classnames(styles['season-bar-button'], { 'active': this.state.seasonsPopupOpen })}>
<div className={styles['season-label']}>Season</div>
<div className={styles['season-number']}>{this.state.selectedSeason}</div>
<Icon className={styles['icon']} icon={'ic_arrow_down'} />
</div>
</Popup.Label>
<Popup.Menu>
<div className={styles['popup-content']}>
{this.seasons.map((season) =>
<div className={classnames(styles['season'], { [styles['selected-season']]: this.state.selectedSeason === season })} key={season} data-season={season} onClick={this.changeSeason}>
<div className={styles['season-label']}>Season</div>
<div className={styles['season-number']}>{season}</div>
</div>
)}
</div>
</Popup.Menu>
</Popup>
<div className={styles['button-container']} onClick={this.onNextButtonClicked} >
<Icon className={styles['button-icon']} icon={'ic_arrow_left'} />
</div>
@ -58,13 +90,16 @@ class VideosList extends Component {
.map((video) =>
<Video key={video.id}
className={styles['video']}
id={video.id}
poster={video.poster}
episode={video.episode}
season={video.season}
title={video.name}
released={video.released}
isWatched={video.isWatched}
isUpcoming={video.isUpcoming}
progress={video.progress}
onClick={this.onClick}
/>
)}
</div>
@ -74,6 +109,7 @@ class VideosList extends Component {
}
VideosList.propTypes = {
className: PropTypes.string,
videos: PropTypes.arrayOf(PropTypes.object).isRequired
};
VideosList.defaultProps = {

View file

@ -1,24 +1,69 @@
.videos-list-container {
--scroll-container-width: 392px;
--seasons-bar-height: 50px;
--spacing: 8px;
}
.videos-list-container {
height: 100%;
display: inline-flex;
width: calc(var(--video-width) + var(--spacing) * 6);
display: flex;
flex-direction: column;
background: var(--color-background);
align-items: center;
background: var(--color-backgrounddarker40);
.seasons-bar {
height: var(--seasons-bar-height);
height: calc(var(--video-width) * 0.14);
width: 100%;
display: flex;
justify-content: space-between;
margin-bottom: var(--spacing);
.button-container {
width: calc(1.5 * var(--seasons-bar-height));
.season-bar-button {
cursor: pointer;
width: calc(var(--video-width) * 0.6);
display: flex;
align-items: center;
justify-content: center;
padding: var(--spacing);
color: var(--color-surfacelight);
.season-label {
max-width: 8em;
max-height: 2.4em;
font-size: 1.2em;
line-height: 1.2em;
text-align: end;
overflow: hidden;
}
.season-number {
margin: 0 calc(var(--spacing) * 1.5) 0 var(--spacing);
font-size: 1.2em;
line-height: 1.2em;
}
.icon {
width: calc(var(--video-width) * 0.04);
height: calc(var(--video-width) * 0.04);
fill: var(--color-surfacelight);
}
&:hover {
color: var(--color-surfacelighter);
background-color: var(--color-surfacedarker60);
.icon {
fill: var(--color-surfacelighter);
}
}
&:global(.active) {
color: var(--color-backgrounddarker);
background-color: var(--color-surfacelighter);
.icon {
fill: var(--color-backgrounddarker);
}
}
}
.button-container {
cursor: pointer;
width: calc(var(--video-width) * 0.2);
display: flex;
align-items: center;
justify-content: center;
@ -26,18 +71,22 @@
.button-icon {
width: 60%;
height: 60%;
fill: var(--color-surfacelighter);
fill: var(--color-surfacelight);
}
&:hover {
background-color: var(--color-surfacelighter20);
background-color: var(--color-surfacedarker60);
.button-icon {
fill: var(--color-surfacelighter);
}
}
}
}
.scroll-container {
flex: 1;
width: var(--scroll-container-width);
width: calc(var(--video-width) + var(--spacing) * 4);
padding: 0 calc(2 * var(--spacing));
margin: 0 var(--spacing);
overflow-y: auto;
@ -49,14 +98,54 @@
}
.scroll-container::-webkit-scrollbar {
width: var(--spacing);
width: var(--spacing) !important;
}
.scroll-container::-webkit-scrollbar-thumb {
background-color: var(--color-secondarylighter80);
background-color: var(--color-secondarylighter);
}
.scroll-container::-webkit-scrollbar-track {
background-color: var(--color-backgroundlight);
background-color: var(--color-backgroundlighter);
}
}
:global(.detail-popup-container) {
--border-color: var(--color-backgrounddarker80);
.popup-content {
width: calc(var(--video-width) * 0.6);
background-color: var(--color-surfacelighter);
.season {
cursor: pointer;
display: flex;
align-items: center;
width: 100%;
padding: calc(var(--spacing) * 1.5);
font-size: 1.2em;
color: var(--color-backgrounddark);
.season-label {
max-height: 2.4em;
line-height: 1.2em;
overflow: hidden;
}
.season-number {
margin-left: var(--spacing);
line-height: 1.2em;
}
&.selected-season {
color: var(--color-surfacelighter);
background-color: var(--color-primarydark);
}
&:hover {
color: var(--color-surfacelighter);
background-color: var(--color-primarylight);
}
}
}
}

View file

@ -0,0 +1,175 @@
.detail-container, :global(.detail-popup-container) {
--spacing: 8px;
--action-button-width: 80px;
--video-width: 360px;
--stream-width: 360px;
font-size: 12px;
}
.detail-container {
position: relative;
z-index: 0;
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
.overlay-container {
position: absolute;
z-index: 0;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--color-backgrounddarker60);
}
.info-container {
position: absolute;
z-index: 1;
top: 0;
left: 0;
right: 40%;
bottom: 0;
padding: calc(var(--spacing) * 3);
.logo {
display: block;
height: calc(var(--action-button-width) * 1.2);
object-fit: contain;
}
.logo-error {
display: none;
}
.duration {
display: inline-block;
max-width: 45%;
margin-right: 1.2em;
font-size: 1.15em;
overflow: hidden;
white-space: pre;
text-overflow: ellipsis;
color: var(--color-surfacelight);
}
.release-info {
display: inline-block;
max-width: 45%;
font-size: 1.15em;
overflow: hidden;
white-space: pre;
text-overflow: ellipsis;
color: var(--color-surfacelight);
}
.name {
max-height: 3em;
overflow: hidden;
overflow-wrap: break-word;
font-size: 1.5em;
line-height: 1.5;
color: var(--color-surfacelight);
}
.description {
max-height: 10.5em;
overflow: hidden;
overflow-wrap: break-word;
line-height: 1.5;
color: var(--color-surfacelight);
}
.section-container {
max-height: 3.2em;
overflow: hidden;
.title {
margin-bottom: 0.3em;
font-size: 1.15em;
color: var(--color-surface);
}
.link {
display: none;
max-width: 100%;
padding: 0.3em 0.6em;
font-size: 1.15em;
overflow: hidden;
white-space: pre;
text-overflow: ellipsis;
color: var(--color-surfacelight);
cursor: pointer;
&:hover {
color: var(--color-surfacelighter);
background-color: var(--color-surface40);
}
&:nth-child(-n+6) {
display: inline-block;
}
}
}
.action-buttons-container {
position: absolute;
left: calc(var(--spacing) * 3);
bottom: calc(var(--spacing) * 3);
.action-button-container {
cursor: pointer;
width: var(--action-button-width);
height: var(--action-button-width);
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
.icon {
height: 30%;
margin: 10% 0;
fill: var(--color-surfacelight);
}
.label {
height: 2.4em;
padding: 0 1em;
overflow-wrap: break-word;
overflow: hidden;
font-size: 1.05em;
line-height: 1.2em;
color: var(--color-surfacelight);
}
&:hover {
background-color: var(--color-surfacedarker60);
.icon {
fill: var(--color-surfacelighter);
}
.label {
color: var(--color-surfacelighter);
}
}
}
}
>:not(:last-child) {
margin-bottom: calc(var(--spacing) * 1.5);
}
}
.videos-list {
position: absolute;
z-index: 2;
top: 0;
right: 0;
bottom: 0;
padding: calc(3 * var(--spacing)) 0;
}
}

View file

@ -0,0 +1,25 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import styles from './styles';
class BufferingLoader extends PureComponent {
render() {
if (!this.props.buffering) {
return null;
}
return (
<div className={classnames(this.props.className, styles['buffering-loader-container'])}>
<div className={styles['bufferring-loader']} />
</div>
);
}
}
BufferingLoader.propTypes = {
className: PropTypes.string,
buffering: PropTypes.bool
};
export default BufferingLoader;

View file

@ -0,0 +1,3 @@
import BufferingLoader from './BufferingLoader';
export default BufferingLoader;

View file

@ -0,0 +1,24 @@
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.buffering-loader-container {
display: flex;
align-items: center;
justify-content: center;
.bufferring-loader {
width: 150px;
height: 150px;
border-radius: 50%;
border: 10px solid #f3f3f3;
border-top: 10px solid #3498db;
animation: spin 2s linear infinite;
}
}

View file

@ -9,8 +9,8 @@ import VolumeBar from './VolumeBar';
import SubtitlesPicker from './SubtitlesPicker';
import styles from './styles';
const ControlBarButton = React.forwardRef(({ icon, active, onClick }, ref) => (
<div ref={ref} className={classnames(styles['control-bar-button'], { 'active': active })} onClick={onClick}>
const ControlBarButton = React.forwardRef(({ icon, active, disabled, onClick }, ref) => (
<div ref={ref} className={classnames(styles['control-bar-button'], { 'active': active }, { 'disabled': disabled })} onClick={!disabled ? onClick : null}>
<Icon className={styles['icon']} icon={icon} />
</div>
));
@ -109,15 +109,12 @@ class ControlBar extends Component {
}
renderSubtitlesButton() {
if (this.props.subtitleTracks.length === 0) {
return null;
}
return (
<Popup className={'player-popup-container'} border={true} onOpen={this.onSubtitlesPopupOpen} onClose={this.onSubtitlesPopupClose}>
<Popup.Label>
<ControlBarButton
icon={'ic_sub'}
disabled={this.props.subtitleTracks.length === 0}
active={this.state.subtitlesPopupOpen}
/>
</Popup.Label>

View file

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class PlayPauseButton extends Component {
shouldComponentUpdate(nextProps, nextState) {
@ -20,4 +21,14 @@ class PlayPauseButton extends Component {
}
}
PlayPauseButton.propTypes = {
paused: PropTypes.bool,
dispatch: PropTypes.func.isRequired,
toggleButtonComponent: PropTypes.oneOfType([
PropTypes.func,
PropTypes.string,
PropTypes.shape({ render: PropTypes.func.isRequired }),
]).isRequired
};
export default PlayPauseButton;

View file

@ -81,7 +81,11 @@ class VolumeBar extends Component {
VolumeBar.propTypes = {
className: PropTypes.string,
volume: PropTypes.number,
toggleButtonComponent: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired,
toggleButtonComponent: PropTypes.oneOfType([
PropTypes.func,
PropTypes.string,
PropTypes.shape({ render: PropTypes.func.isRequired }),
]).isRequired,
dispatch: PropTypes.func.isRequired
};

View file

@ -41,7 +41,15 @@
}
}
&:hover {
&:global(.disabled) {
cursor: default;
.icon {
fill: var(--color-surfacedark);
}
}
&:hover:not(:global(.disabled)) {
.icon {
fill: var(--color-surfacelighter);
}
@ -80,7 +88,7 @@
}
:global(.player-popup-container) {
--border-color: var(--color-primarylight);
--border-color: var(--color-surfacelighter);
.popup-content {
background-color: var(--color-backgrounddark);

View file

@ -2,6 +2,7 @@ import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Video from './Video';
import BufferingLoader from './BufferingLoader';
import ControlBar from './ControlBar';
import styles from './styles';
@ -15,6 +16,7 @@ class Player extends Component {
paused: null,
time: null,
duration: null,
buffering: null,
volume: null,
subtitleTracks: [],
selectedSubtitleTrackId: null,
@ -28,6 +30,7 @@ class Player extends Component {
return nextState.paused !== this.state.paused ||
nextState.time !== this.state.time ||
nextState.duration !== this.state.duration ||
nextState.buffering !== this.state.buffering ||
nextState.volume !== this.state.volume ||
nextState.subtitleTracks !== this.state.subtitleTracks ||
nextState.selectedSubtitleTrackId !== this.state.selectedSubtitleTrackId ||
@ -43,6 +46,7 @@ class Player extends Component {
label: 'English'
}]);
this.dispatch('setProp', 'selectedSubtitleTrackId', 'https://raw.githubusercontent.com/caitp/ng-media/master/example/assets/captions/bunny-en.vtt');
this.dispatch('command', 'load', this.props.stream, {});
}
onEnded = () => {
@ -71,7 +75,6 @@ class Player extends Component {
<Video
ref={this.videoRef}
className={styles['layer']}
stream={this.props.stream}
onEnded={this.onEnded}
onError={this.onError}
onPropValue={this.onPropValue}
@ -82,6 +85,15 @@ class Player extends Component {
);
}
renderBufferingLoader() {
return (
<BufferingLoader
className={styles['layer']}
buffering={this.state.buffering}
/>
);
}
renderControlBar() {
return (
<ControlBar
@ -104,6 +116,7 @@ class Player extends Component {
return (
<div className={styles['player-container']}>
{this.renderVideo()}
{this.renderBufferingLoader()}
{this.renderControlBar()}
</div>
);

View file

@ -13,19 +13,6 @@ class Video extends Component {
this.video = null;
}
componentDidMount() {
const Video = this.selectVideoImplementation();
this.video = new Video(this.containerRef.current);
this.video.on('ended', this.props.onEnded);
this.video.on('error', this.props.onError);
this.video.on('propValue', this.props.onPropValue);
this.video.on('propChanged', this.props.onPropChanged);
this.video.constructor.manifest.props.forEach((propName) => {
this.dispatch('observeProp', propName);
});
this.dispatch('command', 'load', this.props.stream, this.props.extra);
}
shouldComponentUpdate() {
return false;
}
@ -34,8 +21,8 @@ class Video extends Component {
this.dispatch('command', 'destroy');
}
selectVideoImplementation = () => {
if (this.props.stream.ytId) {
selectVideoImplementation = (stream, extra) => {
if (stream.ytId) {
return YouTubeVideo;
} else {
return HTMLVideo;
@ -43,6 +30,21 @@ class Video extends Component {
}
dispatch = (...args) => {
if (args[0] === 'command' && args[1] === 'load') {
const Video = this.selectVideoImplementation(args[2], args[3]);
if (this.video === null || this.video.constructor !== Video) {
this.dispatch('command', 'destroy');
this.video = new Video(this.containerRef.current);
this.video.on('ended', this.props.onEnded);
this.video.on('error', this.props.onError);
this.video.on('propValue', this.props.onPropValue);
this.video.on('propChanged', this.props.onPropChanged);
this.video.constructor.manifest.props.forEach((propName) => {
this.dispatch('observeProp', propName);
});
}
}
try {
this.video && this.video.dispatch(...args);
} catch (e) {
@ -59,15 +61,10 @@ class Video extends Component {
Video.propTypes = {
className: PropTypes.string,
stream: PropTypes.object.isRequired,
extra: PropTypes.object.isRequired,
onEnded: PropTypes.func.isRequired,
onError: PropTypes.func.isRequired,
onPropValue: PropTypes.func.isRequired,
onPropChanged: PropTypes.func.isRequired
};
Video.defaultProps = {
extra: Object.freeze({})
};
export default Video;

View file

@ -51,6 +51,13 @@ var HTMLVideo = function(containerElement) {
return Math.floor(videoElement.duration * 1000);
}
function getBuffering() {
if (!loaded) {
return null;
}
return videoElement.readyState < videoElement.HAVE_FUTURE_DATA;
}
function getVolume() {
if (destroyed) {
return null;
@ -140,6 +147,9 @@ var HTMLVideo = function(containerElement) {
function onDurationChanged() {
events.emit('propChanged', 'duration', getDuration());
}
function onBufferingChanged() {
events.emit('propChanged', 'buffering', getBuffering());
}
function onVolumeChanged() {
events.emit('propChanged', 'volume', getVolume());
}
@ -216,6 +226,15 @@ var HTMLVideo = function(containerElement) {
videoElement.removeEventListener('durationchange', onDurationChanged);
videoElement.addEventListener('durationchange', onDurationChanged);
return;
case 'buffering':
events.emit('propValue', 'buffering', getBuffering());
videoElement.removeEventListener('waiting', onBufferingChanged);
videoElement.addEventListener('waiting', onBufferingChanged);
videoElement.removeEventListener('playing', onBufferingChanged);
videoElement.addEventListener('playing', onBufferingChanged);
videoElement.removeEventListener('loadeddata', onBufferingChanged);
videoElement.addEventListener('loadeddata', onBufferingChanged);
return;
case 'volume':
events.emit('propValue', 'volume', getVolume());
videoElement.removeEventListener('volumechange', onVolumeChanged);
@ -383,6 +402,7 @@ var HTMLVideo = function(containerElement) {
onPausedChanged();
onTimeChanged();
onDurationChanged();
onBufferingChanged();
onSubtitleTracksChanged();
onSelectedSubtitleTrackIdChanged();
onSubtitleDelayChanged();
@ -402,6 +422,8 @@ var HTMLVideo = function(containerElement) {
onPausedChanged();
onTimeChanged();
onDurationChanged();
onBufferingChanged();
onSubtitleDelayChanged();
updateSubtitleText();
flushArgsQueue();
return;
@ -417,6 +439,9 @@ var HTMLVideo = function(containerElement) {
videoElement.removeEventListener('timeupdate', onTimeChanged);
videoElement.removeEventListener('durationchange', onDurationChanged);
videoElement.removeEventListener('volumechange', onVolumeChanged);
videoElement.removeEventListener('waiting', onBufferingChanged);
videoElement.removeEventListener('playing', onBufferingChanged);
videoElement.removeEventListener('loadeddata', onBufferingChanged);
containerElement.removeChild(videoElement);
containerElement.removeChild(stylesElement);
containerElement.removeChild(subtitlesElement);
@ -438,7 +463,7 @@ var HTMLVideo = function(containerElement) {
HTMLVideo.manifest = {
name: 'HTMLVideo',
embedded: true,
props: ['paused', 'time', 'duration', 'volume', 'subtitleTracks', 'selectedSubtitleTrackId', 'subtitleSize', 'subtitleDelay', 'subtitleDarkBackground']
props: ['paused', 'time', 'duration', 'volume', 'buffering', 'subtitleTracks', 'selectedSubtitleTrackId', 'subtitleSize', 'subtitleDelay', 'subtitleDarkBackground']
};
module.exports = HTMLVideo;