From 65b077c72f05cb36a7451b8ab52dde7be396789e Mon Sep 17 00:00:00 2001 From: hackrush Date: Sat, 2 Dec 2017 21:05:13 +0530 Subject: [PATCH 1/9] Base commit --- src/renderer/js/component/video/index.js | 7 +++- .../component/video/internal/play-button.jsx | 12 +----- .../js/component/video/internal/player.jsx | 40 ++++++++++++------- src/renderer/js/component/video/view.jsx | 2 + src/renderer/js/constants/action_types.js | 1 + src/renderer/js/redux/actions/content.js | 9 +++++ src/renderer/js/redux/reducers/content.js | 6 +++ src/renderer/js/redux/selectors/content.js | 5 +++ 8 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/renderer/js/component/video/index.js b/src/renderer/js/component/video/index.js index a9b0ed62d1e..684e657687d 100644 --- a/src/renderer/js/component/video/index.js +++ b/src/renderer/js/component/video/index.js @@ -2,7 +2,11 @@ import React from "react"; import { connect } from "react-redux"; import { doChangeVolume } from "redux/actions/app"; import { selectVolume } from "redux/selectors/app"; -import { doPlayUri, doSetPlayingUri } from "redux/actions/content"; +import { + doPlayUri, + doSetPlayingUri, + doEnableOverlay, +} from "redux/actions/content"; import { makeSelectMetadataForUri, makeSelectContentTypeForUri, @@ -33,6 +37,7 @@ const perform = dispatch => ({ play: uri => dispatch(doPlayUri(uri)), cancelPlay: () => dispatch(doSetPlayingUri(null)), changeVolume: volume => dispatch(doChangeVolume(volume)), + enableOverlay: canBeOverlayed => dispatch(doEnableOverlay(canBeOverlayed)), }); export default connect(select, perform)(Video); diff --git a/src/renderer/js/component/video/internal/play-button.jsx b/src/renderer/js/component/video/internal/play-button.jsx index ca49cb33141..5d8c436337a 100644 --- a/src/renderer/js/component/video/internal/play-button.jsx +++ b/src/renderer/js/component/video/internal/play-button.jsx @@ -26,15 +26,7 @@ class VideoPlayButton extends React.PureComponent { } render() { - const { button, label, isLoading, fileInfo, mediaType } = this.props; - - /* - title={ - isLoading ? "Video is Loading" : - !costInfo ? "Waiting on cost info..." : - fileInfo === undefined ? "Waiting on file info..." : "" - } - */ + const { isLoading, fileInfo, mediaType } = this.props; const disabled = isLoading || fileInfo === undefined; const icon = @@ -44,9 +36,7 @@ class VideoPlayButton extends React.PureComponent { return ( this.watch()} diff --git a/src/renderer/js/component/video/internal/player.jsx b/src/renderer/js/component/video/internal/player.jsx index 9d7b6608ff0..676e2a56029 100644 --- a/src/renderer/js/component/video/internal/player.jsx +++ b/src/renderer/js/component/video/internal/player.jsx @@ -21,20 +21,24 @@ class VideoPlayer extends React.PureComponent { } componentDidMount() { - const container = this.refs.media; + const container = this.media; const { contentType, downloadPath, mediaType, changeVolume, volume, + enableOverlay, } = this.props; const loadedMetadata = e => { this.setState({ hasMetadata: true, startedPlaying: true }); - this.refs.media.children[0].play(); + this.media.children[0].play(); }; const renderMediaCallback = err => { - if (err) this.setState({ unplayable: true }); + if (err) { + this.setState({ unplayable: true }); + enableOverlay(false); + } }; // Handle fullscreen change for the Windows platform const win32FullScreenChange = e => { @@ -46,6 +50,14 @@ class VideoPlayer extends React.PureComponent { } }; + // not all media is "overlayable" so this has to manually set/unset for such media + // by default it is true for A/V, but it is set to false if the player errs + if (["video", "audio"].indexOf(mediaType) !== -1) { + enableOverlay(true); + } else { + enableOverlay(false); + } + // use renderAudio override for mp3 if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) { this.renderAudio(container, null, false); @@ -59,7 +71,7 @@ class VideoPlayer extends React.PureComponent { } document.addEventListener("keydown", this.togglePlayListener); - const mediaElement = this.refs.media.children[0]; + const mediaElement = this.media.children[0]; if (mediaElement) { mediaElement.addEventListener("click", this.togglePlayListener); mediaElement.addEventListener( @@ -82,7 +94,7 @@ class VideoPlayer extends React.PureComponent { componentWillUnmount() { document.removeEventListener("keydown", this.togglePlayListener); - const mediaElement = this.refs.media.children[0]; + const mediaElement = this.media.children[0]; if (mediaElement) { mediaElement.removeEventListener("click", this.togglePlayListener); } @@ -111,7 +123,7 @@ class VideoPlayer extends React.PureComponent { return; } event.preventDefault(); - const mediaElement = this.refs.media.children[0]; + const mediaElement = this.media.children[0]; if (mediaElement) { if (!mediaElement.paused) { mediaElement.pause(); @@ -125,11 +137,11 @@ class VideoPlayer extends React.PureComponent { const { contentType, downloadCompleted } = this.props; const { startedPlaying } = this.state; - if (this.playableType() && !startedPlaying && downloadCompleted) { - const container = this.refs.media.children[0]; + if (this.isPlayableType() && !startedPlaying && downloadCompleted) { + const container = this.media.children[0]; if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) { - this.renderAudio(this.refs.media, true); + this.renderAudio(this.media, true); } else { player.render(this.file(), container, { autoplay: true, @@ -150,7 +162,7 @@ class VideoPlayer extends React.PureComponent { }; } - playableType() { + isPlayableType() { const { mediaType } = this.props; return ["audio", "video"].indexOf(mediaType) !== -1; @@ -162,20 +174,20 @@ class VideoPlayer extends React.PureComponent { const noMetadataMessage = "Waiting for metadata."; const unplayableMessage = "Sorry, looks like we can't play this file."; - const needsMetadata = this.playableType(); + const needsMetadata = this.isPlayableType(); return (
{["audio", "application"].indexOf(mediaType) !== -1 && - (!this.playableType() || hasMetadata) && + (!this.isPlayableType() || hasMetadata) && !unplayable && } - {this.playableType() && + {this.isPlayableType() && !hasMetadata && !unplayable && } {unplayable && ( )} -
+
(this.media = ref)} className="media" />
); } diff --git a/src/renderer/js/component/video/view.jsx b/src/renderer/js/component/video/view.jsx index c42994a177c..ae651ed8460 100644 --- a/src/renderer/js/component/video/view.jsx +++ b/src/renderer/js/component/video/view.jsx @@ -56,6 +56,7 @@ class Video extends React.PureComponent { changeVolume, volume, uri, + enableOverlay, } = this.props; const isPlaying = playingUri === uri; @@ -110,6 +111,7 @@ class Video extends React.PureComponent { downloadCompleted={fileInfo.completed} changeVolume={changeVolume} volume={volume} + enableOverlay={enableOverlay} /> ))} {!isPlaying && ( diff --git a/src/renderer/js/constants/action_types.js b/src/renderer/js/constants/action_types.js index 3670912fd64..1e1e3ca78e4 100644 --- a/src/renderer/js/constants/action_types.js +++ b/src/renderer/js/constants/action_types.js @@ -7,6 +7,7 @@ export const DAEMON_READY = "DAEMON_READY"; export const DAEMON_VERSION_MATCH = "DAEMON_VERSION_MATCH"; export const DAEMON_VERSION_MISMATCH = "DAEMON_VERSION_MISMATCH"; export const VOLUME_CHANGED = "VOLUME_CHANGED"; +export const ENABLE_OVERLAY = "ENABLE_OVERLAY"; // Navigation export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH"; diff --git a/src/renderer/js/redux/actions/content.js b/src/renderer/js/redux/actions/content.js index ab3b630a83d..9c9b233fd04 100644 --- a/src/renderer/js/redux/actions/content.js +++ b/src/renderer/js/redux/actions/content.js @@ -538,3 +538,12 @@ export function doAbandonClaim(txid, nout) { .then(successCallback, errorCallback); }; } + +export function doEnableOverlay(canBeOverlayed) { + return function(dispatch, getState) { + dispatch({ + type: types.ENABLE_OVERLAY, + data: { canBeOverlayed }, + }); + }; +} diff --git a/src/renderer/js/redux/reducers/content.js b/src/renderer/js/redux/reducers/content.js index 0d2b8cd09be..c71451238a4 100644 --- a/src/renderer/js/redux/reducers/content.js +++ b/src/renderer/js/redux/reducers/content.js @@ -83,6 +83,12 @@ reducers[types.FETCH_CHANNEL_CLAIM_COUNT_COMPLETED] = function(state, action) { }); }; +reducers[types.ENABLE_OVERLAY] = function(state, action) { + return Object.assign({}, state, { + overlayable: action.data.canBeOverlayed, + }); +}; + export default function reducer(state = defaultState, action) { const handler = reducers[action.type]; if (handler) return handler(state, action); diff --git a/src/renderer/js/redux/selectors/content.js b/src/renderer/js/redux/selectors/content.js index a1c98f049ec..a0206eb7f4c 100644 --- a/src/renderer/js/redux/selectors/content.js +++ b/src/renderer/js/redux/selectors/content.js @@ -49,3 +49,8 @@ export const selectRewardContentClaimIds = createSelector( _selectState, state => state.rewardedContentClaimIds ); + +export const selectIsOverlayable = createSelector( + _selectState, + state => state.overlayable +); From d12bc9d27487e86eb65fd6900f47ed64e708b6d2 Mon Sep 17 00:00:00 2001 From: hackrush Date: Sun, 3 Dec 2017 19:40:47 +0530 Subject: [PATCH 2/9] Video displays on all pages except different show page --- src/renderer/js/component/app/index.js | 3 + src/renderer/js/component/app/view.jsx | 19 ++ src/renderer/js/component/video/index.js | 3 - src/renderer/js/component/video/view.jsx | 42 ++-- .../js/component/videoPlayer/index.js | 27 +++ .../js/component/videoPlayer/view.jsx | 208 ++++++++++++++++++ src/renderer/scss/component/_video.scss | 31 +++ 7 files changed, 304 insertions(+), 29 deletions(-) create mode 100644 src/renderer/js/component/videoPlayer/index.js create mode 100644 src/renderer/js/component/videoPlayer/view.jsx diff --git a/src/renderer/js/component/app/index.js b/src/renderer/js/component/app/index.js index 66b50bb7186..5765cd30f9c 100644 --- a/src/renderer/js/component/app/index.js +++ b/src/renderer/js/component/app/index.js @@ -8,6 +8,7 @@ import { import { selectUser } from "redux/selectors/user"; import { doAlertError } from "redux/actions/app"; import { doRecordScroll } from "redux/actions/navigation"; +import { selectPlayingUri, selectIsOverlayable } from "redux/selectors/content"; import App from "./view"; const select = (state, props) => ({ @@ -15,6 +16,8 @@ const select = (state, props) => ({ user: selectUser(state), currentStackIndex: selectHistoryIndex(state), currentPageAttributes: selectActiveHistoryEntry(state), + playingUri: selectPlayingUri(state), + overlayable: selectIsOverlayable(state), }); const perform = dispatch => ({ diff --git a/src/renderer/js/component/app/view.jsx b/src/renderer/js/component/app/view.jsx index edc9fa2dcb9..ef28174887f 100644 --- a/src/renderer/js/component/app/view.jsx +++ b/src/renderer/js/component/app/view.jsx @@ -5,6 +5,8 @@ import Theme from "component/theme"; import ModalRouter from "modal/modalRouter"; import lbry from "lbry"; import throttle from "util/throttle"; +import VideoPlayer from "component/videoPlayer"; +import { Icon } from "component/common"; class App extends React.PureComponent { constructor() { @@ -51,12 +53,28 @@ class App extends React.PureComponent { window.document.title = props.pageTitle || "LBRY"; } + renderVideo() { + const { playingUri, overlayable } = this.props; + + if (overlayable && playingUri !== null) { + return ( +
+
+ +
+ +
+ ); + } + } + render() { return (
+ {this.renderVideo()}
@@ -66,3 +84,4 @@ class App extends React.PureComponent { } export default App; +// onClick={() => closeOverlayMedia()}> diff --git a/src/renderer/js/component/video/index.js b/src/renderer/js/component/video/index.js index 684e657687d..e96b6b11a0b 100644 --- a/src/renderer/js/component/video/index.js +++ b/src/renderer/js/component/video/index.js @@ -30,14 +30,11 @@ const select = (state, props) => ({ isDownloading: makeSelectDownloadingForUri(props.uri)(state), playingUri: selectPlayingUri(state), contentType: makeSelectContentTypeForUri(props.uri)(state), - volume: selectVolume(state), }); const perform = dispatch => ({ play: uri => dispatch(doPlayUri(uri)), cancelPlay: () => dispatch(doSetPlayingUri(null)), - changeVolume: volume => dispatch(doChangeVolume(volume)), - enableOverlay: canBeOverlayed => dispatch(doEnableOverlay(canBeOverlayed)), }); export default connect(select, perform)(Video); diff --git a/src/renderer/js/component/video/view.jsx b/src/renderer/js/component/video/view.jsx index ae651ed8460..4a45a9a09de 100644 --- a/src/renderer/js/component/video/view.jsx +++ b/src/renderer/js/component/video/view.jsx @@ -1,8 +1,8 @@ import React from "react"; import lbry from "lbry"; -import VideoPlayer from "./internal/player"; import VideoPlayButton from "./internal/play-button"; import LoadingScreen from "./internal/loading-screen"; +import VideoPlayer from "component/videoPlayer"; import NsfwOverlay from "component/nsfwOverlay"; class Video extends React.PureComponent { @@ -13,8 +13,12 @@ class Video extends React.PureComponent { }; } - componentWillUnmount() { - this.props.cancelPlay(); + componentWillMount() { + const { uri, playingUri } = this.props; + + if (uri !== playingUri) { + this.props.cancelPlay(); + } } isMediaSame(nextProps) { @@ -53,10 +57,7 @@ class Video extends React.PureComponent { playingUri, fileInfo, contentType, - changeVolume, - volume, uri, - enableOverlay, } = this.props; const isPlaying = playingUri === uri; @@ -79,22 +80,21 @@ class Video extends React.PureComponent { loadStatusMessage = __("Downloading stream... not long left now!"); } - let klasses = []; - klasses.push(obscureNsfw ? "video--obscured " : ""); - if (isLoading || isDownloading) klasses.push("video-embedded", "video"); + let classes = []; + classes.push(obscureNsfw ? "video--obscured " : ""); + if (isLoading || isDownloading) classes.push("video-embedded", "video"); if (mediaType === "video") { - klasses.push("video-embedded", "video"); - klasses.push(isPlaying ? "video--active" : "video--hidden"); + classes.push("video-embedded", "video"); + classes.push(isPlaying ? "video--active" : "video--hidden"); } else if (mediaType === "application") { - klasses.push("video-embedded"); + classes.push("video-embedded"); } else { - if (!isPlaying) klasses.push("video-embedded"); + if (!isPlaying) classes.push("video-embedded"); } - const poster = metadata.thumbnail; return (
@@ -102,17 +102,7 @@ class Video extends React.PureComponent { (!isReadyToPlay ? ( ) : ( - + ))} {!isPlaying && (
({ + fileInfo: makeSelectFileInfoForUri(props.uri)(state), + metadata: makeSelectMetadataForUri(props.uri)(state), + playingUri: selectPlayingUri(state), + contentType: makeSelectContentTypeForUri(props.uri)(state), + volume: selectVolume(state), +}); + +const perform = dispatch => ({ + changeVolume: volume => dispatch(doChangeVolume(volume)), + enableOverlay: canBeOverlayed => dispatch(doEnableOverlay(canBeOverlayed)), +}); + +export default connect(select, perform)(VideoPlayer); diff --git a/src/renderer/js/component/videoPlayer/view.jsx b/src/renderer/js/component/videoPlayer/view.jsx new file mode 100644 index 00000000000..9aca9ff52e8 --- /dev/null +++ b/src/renderer/js/component/videoPlayer/view.jsx @@ -0,0 +1,208 @@ +const { remote } = require("electron"); +import React from "react"; +import player from "render-media"; +import fs from "fs"; +import lbry from "lbry"; +import LoadingScreen from "component/video/internal/loading-screen"; +import { Thumbnail } from "component/common"; + +class VideoPlayer extends React.PureComponent { + static MP3_CONTENT_TYPES = ["audio/mpeg3", "audio/mpeg"]; + + constructor(props) { + super(props); + + const { + file_name: _filename, + download_path: _downloadPath, + completed: _completed, + } = this.props.fileInfo; + + const media_type = lbry.getMediaType( + this.props.contentType, + this.props.fileInfo && _filename + ); + + this.state = { + hasMetadata: false, + startedPlaying: false, + unplayable: false, + mediaType: media_type, + filename: _filename, + downloadPath: _downloadPath, + completed: _completed, + }; + + this.togglePlayListener = this.togglePlay.bind(this); + } + + componentDidMount() { + const container = this.media; + const { contentType, changeVolume, volume, enableOverlay } = this.props; + const { downloadPath, mediaType } = this.state; + const loadedMetadata = e => { + this.setState({ hasMetadata: true, startedPlaying: true }); + this.media.children[0].play(); + }; + const renderMediaCallback = err => { + if (err) { + this.setState({ unplayable: true }); + enableOverlay(false); + } + }; + // Handle fullscreen change for the Windows platform + const win32FullScreenChange = e => { + const win = remote.BrowserWindow.getFocusedWindow(); + if ("win32" === process.platform) { + win.setMenu( + document.webkitIsFullScreen ? null : remote.Menu.getApplicationMenu() + ); + } + }; + + // not all media is "overlayable" so this has to manually set/unset for such media + // by default it is true for A/V, but it is set to false if the player errs + if (["video", "audio"].indexOf(mediaType) !== -1) { + enableOverlay(true); + } else { + enableOverlay(false); + } + + // use renderAudio override for mp3 + if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) { + this.renderAudio(container, null, false); + } else { + player.append( + this.file(), + container, + { autoplay: false, controls: true }, + renderMediaCallback.bind(this) + ); + } + + document.addEventListener("keydown", this.togglePlayListener); + const mediaElement = this.media.children[0]; + if (mediaElement) { + mediaElement.addEventListener("click", this.togglePlayListener); + mediaElement.addEventListener( + "loadedmetadata", + loadedMetadata.bind(this), + { + once: true, + } + ); + mediaElement.addEventListener( + "webkitfullscreenchange", + win32FullScreenChange.bind(this) + ); + mediaElement.addEventListener("volumechange", () => { + changeVolume(mediaElement.volume); + }); + mediaElement.volume = volume; + } + } + + componentWillUnmount() { + document.removeEventListener("keydown", this.togglePlayListener); + const mediaElement = this.media.children[0]; + if (mediaElement) { + mediaElement.removeEventListener("click", this.togglePlayListener); + } + } + + renderAudio(container, autoplay) { + if (container.firstChild) { + container.firstChild.remove(); + } + + // clear the container + const { downloadPath } = this.state; + const audio = document.createElement("audio"); + audio.autoplay = autoplay; + audio.controls = true; + audio.src = downloadPath; + container.appendChild(audio); + } + + togglePlay(event) { + // ignore all events except click and spacebar keydown, or input events in a form control + if ( + "keydown" === event.type && + ("Space" !== event.code || "input" === event.target.tagName.toLowerCase()) + ) { + return; + } + event.preventDefault(); + const mediaElement = this.media.children[0]; + if (mediaElement) { + if (!mediaElement.paused) { + mediaElement.pause(); + } else { + mediaElement.play(); + } + } + } + + componentDidUpdate() { + const { contentType } = this.props; + const { startedPlaying, downloadCompleted } = this.state; + + if (this.isPlayableType() && !startedPlaying && downloadCompleted) { + const container = this.media.children[0]; + + if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) { + this.renderAudio(this.media, true); + } else { + player.render(this.file(), container, { + autoplay: true, + controls: true, + }); + } + } + } + + file() { + const { downloadPath, filename } = this.state; + + return { + name: filename, + createReadStream: opts => { + return fs.createReadStream(downloadPath, opts); + }, + }; + } + + isPlayableType() { + const { mediaType } = this.state; + + return ["audio", "video"].indexOf(mediaType) !== -1; + } + + render() { + const { contentType, fileInfo, metadata } = this.props; + const { mediaType } = this.state; + const { hasMetadata, unplayable } = this.state; + const noMetadataMessage = "Waiting for metadata."; + const unplayableMessage = "Sorry, looks like we can't play this file."; + + const poster = metadata.thumbnail; + const needsMetadata = this.isPlayableType(); + + return ( +
+ {["audio", "application"].indexOf(mediaType) !== -1 && + (!this.isPlayableType() || hasMetadata) && + !unplayable && } + {this.isPlayableType() && + !hasMetadata && + !unplayable && } + {unplayable && ( + + )} +
(this.media = ref)} className="media" /> +
+ ); + } +} + +export default VideoPlayer; diff --git a/src/renderer/scss/component/_video.scss b/src/renderer/scss/component/_video.scss index d6021849e73..44de3e1c50c 100644 --- a/src/renderer/scss/component/_video.scss +++ b/src/renderer/scss/component/_video.scss @@ -84,3 +84,34 @@ video { transition: opacity var(--transition-duration) var(--transition-type); } } + +.overlay { + position: fixed; + max-height: 50%; + max-width: 50%; + width: 20%; + height: inherit; + bottom: 1%; + right: 1%; + z-index: 3; + box-shadow: var(--box-shadow-layer); + + + &:hover .button-close { + display: inline-block; + background: rgba(0, 0, 0, 0.5); + position: absolute; + height: 22px; + width: 22px; + top: 0px; + right: 0px; + color: #000; + text-align: center; + cursor: pointer; + z-index: 4; + } +} + +.overlay .button-close { + display: none; +} From d329e397739d9852dc48c9cf94d2713f1dd7595c Mon Sep 17 00:00:00 2001 From: hackrush Date: Sun, 3 Dec 2017 20:20:44 +0530 Subject: [PATCH 3/9] Overlays the video properly --- src/renderer/js/component/app/index.js | 2 ++ src/renderer/js/component/app/view.jsx | 19 ++++++------ src/renderer/js/component/video/view.jsx | 2 +- .../js/component/videoPlayer/index.js | 4 ++- .../js/component/videoPlayer/view.jsx | 29 ++++++++++++++++--- 5 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/renderer/js/component/app/index.js b/src/renderer/js/component/app/index.js index 5765cd30f9c..944197388b4 100644 --- a/src/renderer/js/component/app/index.js +++ b/src/renderer/js/component/app/index.js @@ -4,6 +4,7 @@ import { selectPageTitle, selectHistoryIndex, selectActiveHistoryEntry, + selectCurrentPage, } from "redux/selectors/navigation"; import { selectUser } from "redux/selectors/user"; import { doAlertError } from "redux/actions/app"; @@ -18,6 +19,7 @@ const select = (state, props) => ({ currentPageAttributes: selectActiveHistoryEntry(state), playingUri: selectPlayingUri(state), overlayable: selectIsOverlayable(state), + currentPage: selectCurrentPage(state), }); const perform = dispatch => ({ diff --git a/src/renderer/js/component/app/view.jsx b/src/renderer/js/component/app/view.jsx index ef28174887f..bc86cd419e9 100644 --- a/src/renderer/js/component/app/view.jsx +++ b/src/renderer/js/component/app/view.jsx @@ -54,21 +54,20 @@ class App extends React.PureComponent { } renderVideo() { - const { playingUri, overlayable } = this.props; + //
+ //
+ // + //
+ //
+ const { playingUri, overlayable, currentPage } = this.props; - if (overlayable && playingUri !== null) { - return ( -
-
- -
- -
- ); + if (overlayable && playingUri !== null && currentPage !== "show") { + return ; } } render() { + // return (
diff --git a/src/renderer/js/component/video/view.jsx b/src/renderer/js/component/video/view.jsx index 4a45a9a09de..41d96eeba3a 100644 --- a/src/renderer/js/component/video/view.jsx +++ b/src/renderer/js/component/video/view.jsx @@ -102,7 +102,7 @@ class Video extends React.PureComponent { (!isReadyToPlay ? ( ) : ( - + ))} {!isPlaying && (
({ + contentType: makeSelectContentTypeForUri(props.uri)(state), fileInfo: makeSelectFileInfoForUri(props.uri)(state), metadata: makeSelectMetadataForUri(props.uri)(state), + currentPage: selectCurrentPage(state), playingUri: selectPlayingUri(state), - contentType: makeSelectContentTypeForUri(props.uri)(state), volume: selectVolume(state), }); diff --git a/src/renderer/js/component/videoPlayer/view.jsx b/src/renderer/js/component/videoPlayer/view.jsx index 9aca9ff52e8..40169fe53c8 100644 --- a/src/renderer/js/component/videoPlayer/view.jsx +++ b/src/renderer/js/component/videoPlayer/view.jsx @@ -4,7 +4,7 @@ import player from "render-media"; import fs from "fs"; import lbry from "lbry"; import LoadingScreen from "component/video/internal/loading-screen"; -import { Thumbnail } from "component/common"; +import { Thumbnail, Icon } from "component/common"; class VideoPlayer extends React.PureComponent { static MP3_CONTENT_TYPES = ["audio/mpeg3", "audio/mpeg"]; @@ -31,6 +31,7 @@ class VideoPlayer extends React.PureComponent { filename: _filename, downloadPath: _downloadPath, completed: _completed, + overlayable: false, }; this.togglePlayListener = this.togglePlay.bind(this); @@ -47,6 +48,7 @@ class VideoPlayer extends React.PureComponent { const renderMediaCallback = err => { if (err) { this.setState({ unplayable: true }); + this.setState({ overlayable: true }); enableOverlay(false); } }; @@ -63,8 +65,10 @@ class VideoPlayer extends React.PureComponent { // not all media is "overlayable" so this has to manually set/unset for such media // by default it is true for A/V, but it is set to false if the player errs if (["video", "audio"].indexOf(mediaType) !== -1) { + this.setState({ overlayable: true }); enableOverlay(true); } else { + this.setState({ overlayable: false }); enableOverlay(false); } @@ -179,17 +183,26 @@ class VideoPlayer extends React.PureComponent { } render() { - const { contentType, fileInfo, metadata } = this.props; + const { + contentType, + fileInfo, + metadata, + overlay, + currentPage, + playingUri, + } = this.props; const { mediaType } = this.state; - const { hasMetadata, unplayable } = this.state; + const { hasMetadata, unplayable, overlayable } = this.state; const noMetadataMessage = "Waiting for metadata."; const unplayableMessage = "Sorry, looks like we can't play this file."; const poster = metadata.thumbnail; const needsMetadata = this.isPlayableType(); + const displayOverlay = overlay && currentPage !== "show"; + console.log(displayOverlay); return ( -
+
{["audio", "application"].indexOf(mediaType) !== -1 && (!this.isPlayableType() || hasMetadata) && !unplayable && } @@ -199,6 +212,14 @@ class VideoPlayer extends React.PureComponent { {unplayable && ( )} + + {displayOverlay && + overlayable && ( +
+ +
+ )} +
(this.media = ref)} className="media" />
); From ffaeae64d166220d4c310833107122c25e33b016 Mon Sep 17 00:00:00 2001 From: hackrush Date: Sun, 3 Dec 2017 23:39:57 +0530 Subject: [PATCH 4/9] Working Video Overlay --- src/renderer/js/component/app/view.jsx | 4 +-- src/renderer/js/component/video/index.js | 8 +----- .../js/component/videoPlayer/index.js | 3 ++- .../js/component/videoPlayer/view.jsx | 26 ++++++++++++------- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/renderer/js/component/app/view.jsx b/src/renderer/js/component/app/view.jsx index bc86cd419e9..6a0005f6fcb 100644 --- a/src/renderer/js/component/app/view.jsx +++ b/src/renderer/js/component/app/view.jsx @@ -59,9 +59,9 @@ class App extends React.PureComponent { // //
//
- const { playingUri, overlayable, currentPage } = this.props; + const { playingUri, currentPage } = this.props; - if (overlayable && playingUri !== null && currentPage !== "show") { + if (playingUri !== null && currentPage !== "show") { return ; } } diff --git a/src/renderer/js/component/video/index.js b/src/renderer/js/component/video/index.js index e96b6b11a0b..c883292aedc 100644 --- a/src/renderer/js/component/video/index.js +++ b/src/renderer/js/component/video/index.js @@ -1,12 +1,6 @@ import React from "react"; import { connect } from "react-redux"; -import { doChangeVolume } from "redux/actions/app"; -import { selectVolume } from "redux/selectors/app"; -import { - doPlayUri, - doSetPlayingUri, - doEnableOverlay, -} from "redux/actions/content"; +import { doPlayUri, doSetPlayingUri } from "redux/actions/content"; import { makeSelectMetadataForUri, makeSelectContentTypeForUri, diff --git a/src/renderer/js/component/videoPlayer/index.js b/src/renderer/js/component/videoPlayer/index.js index 6af75e24afe..0a3be84d544 100644 --- a/src/renderer/js/component/videoPlayer/index.js +++ b/src/renderer/js/component/videoPlayer/index.js @@ -2,7 +2,7 @@ import React from "react"; import { connect } from "react-redux"; import { doChangeVolume } from "redux/actions/app"; import { selectVolume } from "redux/selectors/app"; -import { doEnableOverlay } from "redux/actions/content"; +import { doEnableOverlay, doSetPlayingUri } from "redux/actions/content"; import { makeSelectMetadataForUri, makeSelectContentTypeForUri, @@ -24,6 +24,7 @@ const select = (state, props) => ({ const perform = dispatch => ({ changeVolume: volume => dispatch(doChangeVolume(volume)), enableOverlay: canBeOverlayed => dispatch(doEnableOverlay(canBeOverlayed)), + cancelPlay: () => dispatch(doSetPlayingUri(null)), }); export default connect(select, perform)(VideoPlayer); diff --git a/src/renderer/js/component/videoPlayer/view.jsx b/src/renderer/js/component/videoPlayer/view.jsx index 40169fe53c8..b024cc61ab2 100644 --- a/src/renderer/js/component/videoPlayer/view.jsx +++ b/src/renderer/js/component/videoPlayer/view.jsx @@ -4,7 +4,8 @@ import player from "render-media"; import fs from "fs"; import lbry from "lbry"; import LoadingScreen from "component/video/internal/loading-screen"; -import { Thumbnail, Icon } from "component/common"; +import { Thumbnail } from "component/common"; +import Link from "component/link"; class VideoPlayer extends React.PureComponent { static MP3_CONTENT_TYPES = ["audio/mpeg3", "audio/mpeg"]; @@ -182,6 +183,11 @@ class VideoPlayer extends React.PureComponent { return ["audio", "video"].indexOf(mediaType) !== -1; } + cancel_play() { + console.log("yahan tak toh pahunch gaya saala"); + this.props.cancelPlay(); + } + render() { const { contentType, @@ -198,8 +204,7 @@ class VideoPlayer extends React.PureComponent { const poster = metadata.thumbnail; const needsMetadata = this.isPlayableType(); - const displayOverlay = overlay && currentPage !== "show"; - console.log(displayOverlay); + const displayOverlay = overlay && overlayable; return (
@@ -213,12 +218,15 @@ class VideoPlayer extends React.PureComponent { )} - {displayOverlay && - overlayable && ( -
- -
- )} + {displayOverlay && ( +
+ this.cancel_play()} + /> +
+ )}
(this.media = ref)} className="media" />
From 5e8c0c5ccb488ecc1e2c33269aa0cbd703972a72 Mon Sep 17 00:00:00 2001 From: hackrush Date: Mon, 4 Dec 2017 01:57:09 +0530 Subject: [PATCH 5/9] Clean-up --- src/renderer/js/component/app/index.js | 3 +- src/renderer/js/component/app/view.jsx | 7 - src/renderer/js/component/common.js | 19 ++ .../video/internal/loading-screen.jsx | 14 -- .../js/component/video/internal/player.jsx | 196 ------------------ src/renderer/js/component/video/view.jsx | 2 +- .../js/component/videoPlayer/index.js | 3 +- .../js/component/videoPlayer/view.jsx | 32 ++- src/renderer/js/constants/action_types.js | 1 - 9 files changed, 34 insertions(+), 243 deletions(-) delete mode 100644 src/renderer/js/component/video/internal/loading-screen.jsx delete mode 100644 src/renderer/js/component/video/internal/player.jsx diff --git a/src/renderer/js/component/app/index.js b/src/renderer/js/component/app/index.js index 944197388b4..fd13464b4cf 100644 --- a/src/renderer/js/component/app/index.js +++ b/src/renderer/js/component/app/index.js @@ -9,7 +9,7 @@ import { import { selectUser } from "redux/selectors/user"; import { doAlertError } from "redux/actions/app"; import { doRecordScroll } from "redux/actions/navigation"; -import { selectPlayingUri, selectIsOverlayable } from "redux/selectors/content"; +import { selectPlayingUri } from "redux/selectors/content"; import App from "./view"; const select = (state, props) => ({ @@ -18,7 +18,6 @@ const select = (state, props) => ({ currentStackIndex: selectHistoryIndex(state), currentPageAttributes: selectActiveHistoryEntry(state), playingUri: selectPlayingUri(state), - overlayable: selectIsOverlayable(state), currentPage: selectCurrentPage(state), }); diff --git a/src/renderer/js/component/app/view.jsx b/src/renderer/js/component/app/view.jsx index 6a0005f6fcb..108eac3d0d7 100644 --- a/src/renderer/js/component/app/view.jsx +++ b/src/renderer/js/component/app/view.jsx @@ -54,11 +54,6 @@ class App extends React.PureComponent { } renderVideo() { - //
- //
- // - //
- //
const { playingUri, currentPage } = this.props; if (playingUri !== null && currentPage !== "show") { @@ -67,7 +62,6 @@ class App extends React.PureComponent { } render() { - // return (
@@ -83,4 +77,3 @@ class App extends React.PureComponent { } export default App; -// onClick={() => closeOverlayMedia()}> diff --git a/src/renderer/js/component/common.js b/src/renderer/js/component/common.js index 2f8ae5489c6..b498bf837de 100644 --- a/src/renderer/js/component/common.js +++ b/src/renderer/js/component/common.js @@ -194,3 +194,22 @@ export class Thumbnail extends React.PureComponent { ); } } + +export class LoadingScreen extends React.PureComponent { + static defaultProps = { + spinner: true, + }; + + render() { + const { status, spinner } = this.props; + return ( +
+
+ {spinner &&
} + +
{status}
+
+
+ ); + } +} diff --git a/src/renderer/js/component/video/internal/loading-screen.jsx b/src/renderer/js/component/video/internal/loading-screen.jsx deleted file mode 100644 index 94cc7ce4d48..00000000000 --- a/src/renderer/js/component/video/internal/loading-screen.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from "react"; -import Spinner from "component/common/spinner"; - -const LoadingScreen = ({ status, spinner = true }) => ( -
-
- {spinner && } - -
{status}
-
-
-); - -export default LoadingScreen; diff --git a/src/renderer/js/component/video/internal/player.jsx b/src/renderer/js/component/video/internal/player.jsx deleted file mode 100644 index 676e2a56029..00000000000 --- a/src/renderer/js/component/video/internal/player.jsx +++ /dev/null @@ -1,196 +0,0 @@ -const { remote } = require("electron"); -import React from "react"; -import { Thumbnail } from "component/common"; -import player from "render-media"; -import fs from "fs"; -import LoadingScreen from "./loading-screen"; - -class VideoPlayer extends React.PureComponent { - static MP3_CONTENT_TYPES = ["audio/mpeg3", "audio/mpeg"]; - - constructor(props) { - super(props); - - this.state = { - hasMetadata: false, - startedPlaying: false, - unplayable: false, - }; - - this.togglePlayListener = this.togglePlay.bind(this); - } - - componentDidMount() { - const container = this.media; - const { - contentType, - downloadPath, - mediaType, - changeVolume, - volume, - enableOverlay, - } = this.props; - const loadedMetadata = e => { - this.setState({ hasMetadata: true, startedPlaying: true }); - this.media.children[0].play(); - }; - const renderMediaCallback = err => { - if (err) { - this.setState({ unplayable: true }); - enableOverlay(false); - } - }; - // Handle fullscreen change for the Windows platform - const win32FullScreenChange = e => { - const win = remote.BrowserWindow.getFocusedWindow(); - if ("win32" === process.platform) { - win.setMenu( - document.webkitIsFullScreen ? null : remote.Menu.getApplicationMenu() - ); - } - }; - - // not all media is "overlayable" so this has to manually set/unset for such media - // by default it is true for A/V, but it is set to false if the player errs - if (["video", "audio"].indexOf(mediaType) !== -1) { - enableOverlay(true); - } else { - enableOverlay(false); - } - - // use renderAudio override for mp3 - if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) { - this.renderAudio(container, null, false); - } else { - player.append( - this.file(), - container, - { autoplay: false, controls: true }, - renderMediaCallback.bind(this) - ); - } - - document.addEventListener("keydown", this.togglePlayListener); - const mediaElement = this.media.children[0]; - if (mediaElement) { - mediaElement.addEventListener("click", this.togglePlayListener); - mediaElement.addEventListener( - "loadedmetadata", - loadedMetadata.bind(this), - { - once: true, - } - ); - mediaElement.addEventListener( - "webkitfullscreenchange", - win32FullScreenChange.bind(this) - ); - mediaElement.addEventListener("volumechange", () => { - changeVolume(mediaElement.volume); - }); - mediaElement.volume = volume; - } - } - - componentWillUnmount() { - document.removeEventListener("keydown", this.togglePlayListener); - const mediaElement = this.media.children[0]; - if (mediaElement) { - mediaElement.removeEventListener("click", this.togglePlayListener); - } - } - - renderAudio(container, autoplay) { - if (container.firstChild) { - container.firstChild.remove(); - } - - // clear the container - const { downloadPath } = this.props; - const audio = document.createElement("audio"); - audio.autoplay = autoplay; - audio.controls = true; - audio.src = downloadPath; - container.appendChild(audio); - } - - togglePlay(event) { - // ignore all events except click and spacebar keydown, or input events in a form control - if ( - "keydown" === event.type && - ("Space" !== event.code || "input" === event.target.tagName.toLowerCase()) - ) { - return; - } - event.preventDefault(); - const mediaElement = this.media.children[0]; - if (mediaElement) { - if (!mediaElement.paused) { - mediaElement.pause(); - } else { - mediaElement.play(); - } - } - } - - componentDidUpdate() { - const { contentType, downloadCompleted } = this.props; - const { startedPlaying } = this.state; - - if (this.isPlayableType() && !startedPlaying && downloadCompleted) { - const container = this.media.children[0]; - - if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) { - this.renderAudio(this.media, true); - } else { - player.render(this.file(), container, { - autoplay: true, - controls: true, - }); - } - } - } - - file() { - const { downloadPath, filename } = this.props; - - return { - name: filename, - createReadStream: opts => { - return fs.createReadStream(downloadPath, opts); - }, - }; - } - - isPlayableType() { - const { mediaType } = this.props; - - return ["audio", "video"].indexOf(mediaType) !== -1; - } - - render() { - const { mediaType, poster } = this.props; - const { hasMetadata, unplayable } = this.state; - const noMetadataMessage = "Waiting for metadata."; - const unplayableMessage = "Sorry, looks like we can't play this file."; - - const needsMetadata = this.isPlayableType(); - - return ( -
- {["audio", "application"].indexOf(mediaType) !== -1 && - (!this.isPlayableType() || hasMetadata) && - !unplayable && } - {this.isPlayableType() && - !hasMetadata && - !unplayable && } - {unplayable && ( - - )} -
(this.media = ref)} className="media" /> -
- ); - } -} - -export default VideoPlayer; diff --git a/src/renderer/js/component/video/view.jsx b/src/renderer/js/component/video/view.jsx index 41d96eeba3a..6264b322345 100644 --- a/src/renderer/js/component/video/view.jsx +++ b/src/renderer/js/component/video/view.jsx @@ -1,7 +1,7 @@ import React from "react"; import lbry from "lbry"; import VideoPlayButton from "./internal/play-button"; -import LoadingScreen from "./internal/loading-screen"; +import { LoadingScreen } from "component/common"; import VideoPlayer from "component/videoPlayer"; import NsfwOverlay from "component/nsfwOverlay"; diff --git a/src/renderer/js/component/videoPlayer/index.js b/src/renderer/js/component/videoPlayer/index.js index 0a3be84d544..e141e0cb9fd 100644 --- a/src/renderer/js/component/videoPlayer/index.js +++ b/src/renderer/js/component/videoPlayer/index.js @@ -2,7 +2,7 @@ import React from "react"; import { connect } from "react-redux"; import { doChangeVolume } from "redux/actions/app"; import { selectVolume } from "redux/selectors/app"; -import { doEnableOverlay, doSetPlayingUri } from "redux/actions/content"; +import { doSetPlayingUri } from "redux/actions/content"; import { makeSelectMetadataForUri, makeSelectContentTypeForUri, @@ -23,7 +23,6 @@ const select = (state, props) => ({ const perform = dispatch => ({ changeVolume: volume => dispatch(doChangeVolume(volume)), - enableOverlay: canBeOverlayed => dispatch(doEnableOverlay(canBeOverlayed)), cancelPlay: () => dispatch(doSetPlayingUri(null)), }); diff --git a/src/renderer/js/component/videoPlayer/view.jsx b/src/renderer/js/component/videoPlayer/view.jsx index b024cc61ab2..3d03fac11a1 100644 --- a/src/renderer/js/component/videoPlayer/view.jsx +++ b/src/renderer/js/component/videoPlayer/view.jsx @@ -3,8 +3,7 @@ import React from "react"; import player from "render-media"; import fs from "fs"; import lbry from "lbry"; -import LoadingScreen from "component/video/internal/loading-screen"; -import { Thumbnail } from "component/common"; +import { Thumbnail, LoadingScreen } from "component/common"; import Link from "component/link"; class VideoPlayer extends React.PureComponent { @@ -40,7 +39,7 @@ class VideoPlayer extends React.PureComponent { componentDidMount() { const container = this.media; - const { contentType, changeVolume, volume, enableOverlay } = this.props; + const { contentType, changeVolume, volume } = this.props; const { downloadPath, mediaType } = this.state; const loadedMetadata = e => { this.setState({ hasMetadata: true, startedPlaying: true }); @@ -48,9 +47,10 @@ class VideoPlayer extends React.PureComponent { }; const renderMediaCallback = err => { if (err) { - this.setState({ unplayable: true }); - this.setState({ overlayable: true }); - enableOverlay(false); + this.setState({ + unplayable: true, + overlayable: true, + }); } }; // Handle fullscreen change for the Windows platform @@ -67,10 +67,8 @@ class VideoPlayer extends React.PureComponent { // by default it is true for A/V, but it is set to false if the player errs if (["video", "audio"].indexOf(mediaType) !== -1) { this.setState({ overlayable: true }); - enableOverlay(true); } else { this.setState({ overlayable: false }); - enableOverlay(false); } // use renderAudio override for mp3 @@ -183,11 +181,6 @@ class VideoPlayer extends React.PureComponent { return ["audio", "video"].indexOf(mediaType) !== -1; } - cancel_play() { - console.log("yahan tak toh pahunch gaya saala"); - this.props.cancelPlay(); - } - render() { const { contentType, @@ -196,6 +189,7 @@ class VideoPlayer extends React.PureComponent { overlay, currentPage, playingUri, + cancelPlay, } = this.props; const { mediaType } = this.state; const { hasMetadata, unplayable, overlayable } = this.state; @@ -219,13 +213,11 @@ class VideoPlayer extends React.PureComponent { )} {displayOverlay && ( -
- this.cancel_play()} - /> -
+ cancelPlay()} + /> )}
(this.media = ref)} className="media" /> diff --git a/src/renderer/js/constants/action_types.js b/src/renderer/js/constants/action_types.js index 1e1e3ca78e4..3670912fd64 100644 --- a/src/renderer/js/constants/action_types.js +++ b/src/renderer/js/constants/action_types.js @@ -7,7 +7,6 @@ export const DAEMON_READY = "DAEMON_READY"; export const DAEMON_VERSION_MATCH = "DAEMON_VERSION_MATCH"; export const DAEMON_VERSION_MISMATCH = "DAEMON_VERSION_MISMATCH"; export const VOLUME_CHANGED = "VOLUME_CHANGED"; -export const ENABLE_OVERLAY = "ENABLE_OVERLAY"; // Navigation export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH"; From 5d7a69cda97687cc27e8890f8d558ca50820cc90 Mon Sep 17 00:00:00 2001 From: hackrush Date: Mon, 4 Dec 2017 17:36:30 +0530 Subject: [PATCH 6/9] Made new component for overlay and other clean-up --- src/renderer/js/component/app/index.js | 4 ---- src/renderer/js/component/app/view.jsx | 15 +++--------- src/renderer/js/component/video/view.jsx | 9 +------ .../js/component/videoOverlay/index.js | 24 +++++++++++++++++++ .../js/component/videoOverlay/view.jsx | 23 ++++++++++++++++++ .../js/component/videoPlayer/view.jsx | 2 +- src/renderer/scss/component/_video.scss | 2 +- 7 files changed, 53 insertions(+), 26 deletions(-) create mode 100644 src/renderer/js/component/videoOverlay/index.js create mode 100644 src/renderer/js/component/videoOverlay/view.jsx diff --git a/src/renderer/js/component/app/index.js b/src/renderer/js/component/app/index.js index fd13464b4cf..66b50bb7186 100644 --- a/src/renderer/js/component/app/index.js +++ b/src/renderer/js/component/app/index.js @@ -4,12 +4,10 @@ import { selectPageTitle, selectHistoryIndex, selectActiveHistoryEntry, - selectCurrentPage, } from "redux/selectors/navigation"; import { selectUser } from "redux/selectors/user"; import { doAlertError } from "redux/actions/app"; import { doRecordScroll } from "redux/actions/navigation"; -import { selectPlayingUri } from "redux/selectors/content"; import App from "./view"; const select = (state, props) => ({ @@ -17,8 +15,6 @@ const select = (state, props) => ({ user: selectUser(state), currentStackIndex: selectHistoryIndex(state), currentPageAttributes: selectActiveHistoryEntry(state), - playingUri: selectPlayingUri(state), - currentPage: selectCurrentPage(state), }); const perform = dispatch => ({ diff --git a/src/renderer/js/component/app/view.jsx b/src/renderer/js/component/app/view.jsx index 108eac3d0d7..ac2f6d372c9 100644 --- a/src/renderer/js/component/app/view.jsx +++ b/src/renderer/js/component/app/view.jsx @@ -1,12 +1,11 @@ import React from "react"; -import Router from "component/router/index"; +import Router from "component/router"; import Header from "component/header"; import Theme from "component/theme"; +import VideoOverlay from "component/videoOverlay"; import ModalRouter from "modal/modalRouter"; import lbry from "lbry"; import throttle from "util/throttle"; -import VideoPlayer from "component/videoPlayer"; -import { Icon } from "component/common"; class App extends React.PureComponent { constructor() { @@ -53,21 +52,13 @@ class App extends React.PureComponent { window.document.title = props.pageTitle || "LBRY"; } - renderVideo() { - const { playingUri, currentPage } = this.props; - - if (playingUri !== null && currentPage !== "show") { - return ; - } - } - render() { return (
- {this.renderVideo()} +
diff --git a/src/renderer/js/component/video/view.jsx b/src/renderer/js/component/video/view.jsx index 6264b322345..7a21109be04 100644 --- a/src/renderer/js/component/video/view.jsx +++ b/src/renderer/js/component/video/view.jsx @@ -16,19 +16,12 @@ class Video extends React.PureComponent { componentWillMount() { const { uri, playingUri } = this.props; + // If not on the same uri cancel playback if (uri !== playingUri) { this.props.cancelPlay(); } } - isMediaSame(nextProps) { - return ( - this.props.fileInfo && - nextProps.fileInfo && - this.props.fileInfo.outpoint === nextProps.fileInfo.outpoint - ); - } - handleMouseOver() { if ( this.props.obscureNsfw && diff --git a/src/renderer/js/component/videoOverlay/index.js b/src/renderer/js/component/videoOverlay/index.js new file mode 100644 index 00000000000..8d7a6576de0 --- /dev/null +++ b/src/renderer/js/component/videoOverlay/index.js @@ -0,0 +1,24 @@ +import { connect } from "react-redux"; +import { doSetPlayingUri } from "redux/actions/content"; +import { + makeSelectFileInfoForUri, + makeSelectDownloadingForUri, + makeSelectLoadingForUri, +} from "redux/selectors/file_info"; +import { selectCurrentPage } from "redux/selectors/navigation"; +import { selectPlayingUri } from "redux/selectors/content"; +import VideoOverlay from "./view"; + +const select = (state, props) => ({ + playingUri: selectPlayingUri(state), + currentPage: selectCurrentPage(state), + isDownloading: uri => makeSelectDownloadingForUri(uri)(state), + isLoading: uri => makeSelectLoadingForUri(uri)(state), + fileInfo: uri => makeSelectFileInfoForUri(uri)(state), +}); + +const perform = dispatch => ({ + cancelPlay: () => dispatch(doSetPlayingUri(null)), +}); + +export default connect(select, perform)(VideoOverlay); diff --git a/src/renderer/js/component/videoOverlay/view.jsx b/src/renderer/js/component/videoOverlay/view.jsx new file mode 100644 index 00000000000..94eb8de7e70 --- /dev/null +++ b/src/renderer/js/component/videoOverlay/view.jsx @@ -0,0 +1,23 @@ +import React from "react"; +import VideoPlayer from "component/videoPlayer"; + +class VideoOverlay extends React.PureComponent { + render() { + const { playingUri, currentPage, cancelPlay } = this.props; + + const isDownloading = this.props.isDownloading(playingUri); + const isLoading = this.props.isLoading(playingUri); + const fileInfo = this.props.fileInfo(playingUri); + const haveFileInfo = fileInfo && fileInfo.written_bytes > 0; + + const isReadyToPlay = (haveFileInfo || isDownloading) && !isLoading; + + if (isReadyToPlay && playingUri !== null && currentPage !== "show") { + return ; + } + + return null; + } +} + +export default VideoOverlay; diff --git a/src/renderer/js/component/videoPlayer/view.jsx b/src/renderer/js/component/videoPlayer/view.jsx index 3d03fac11a1..68be43e03f1 100644 --- a/src/renderer/js/component/videoPlayer/view.jsx +++ b/src/renderer/js/component/videoPlayer/view.jsx @@ -49,7 +49,7 @@ class VideoPlayer extends React.PureComponent { if (err) { this.setState({ unplayable: true, - overlayable: true, + overlayable: false, }); } }; diff --git a/src/renderer/scss/component/_video.scss b/src/renderer/scss/component/_video.scss index 44de3e1c50c..f8647642e3a 100644 --- a/src/renderer/scss/component/_video.scss +++ b/src/renderer/scss/component/_video.scss @@ -105,7 +105,7 @@ video { width: 22px; top: 0px; right: 0px; - color: #000; + color: #FFF; text-align: center; cursor: pointer; z-index: 4; From b75f41eb43cc47936b412e873c4bab68dd77c73b Mon Sep 17 00:00:00 2001 From: hackrush Date: Mon, 4 Dec 2017 20:09:05 +0530 Subject: [PATCH 7/9] Attempt at resumable playback --- src/renderer/js/component/videoPlayer/index.js | 6 ++++-- src/renderer/js/component/videoPlayer/view.jsx | 9 ++++++++- src/renderer/js/constants/action_types.js | 1 + src/renderer/js/redux/actions/content.js | 6 +++--- src/renderer/js/redux/reducers/content.js | 4 ++-- src/renderer/js/redux/selectors/content.js | 4 ++-- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/renderer/js/component/videoPlayer/index.js b/src/renderer/js/component/videoPlayer/index.js index e141e0cb9fd..ff462f11c93 100644 --- a/src/renderer/js/component/videoPlayer/index.js +++ b/src/renderer/js/component/videoPlayer/index.js @@ -2,13 +2,13 @@ import React from "react"; import { connect } from "react-redux"; import { doChangeVolume } from "redux/actions/app"; import { selectVolume } from "redux/selectors/app"; -import { doSetPlayingUri } from "redux/actions/content"; +import { doSetPlayingUri, doSetTime } from "redux/actions/content"; import { makeSelectMetadataForUri, makeSelectContentTypeForUri, } from "redux/selectors/claims"; import { makeSelectFileInfoForUri } from "redux/selectors/file_info"; -import { selectPlayingUri } from "redux/selectors/content"; +import { selectPlayingUri, selectCurrentTime } from "redux/selectors/content"; import { selectCurrentPage } from "redux/selectors/navigation"; import VideoPlayer from "./view"; @@ -16,12 +16,14 @@ const select = (state, props) => ({ contentType: makeSelectContentTypeForUri(props.uri)(state), fileInfo: makeSelectFileInfoForUri(props.uri)(state), metadata: makeSelectMetadataForUri(props.uri)(state), + currentTime: selectCurrentTime(state), currentPage: selectCurrentPage(state), playingUri: selectPlayingUri(state), volume: selectVolume(state), }); const perform = dispatch => ({ + setTime: currentTime => dispatch(doSetTime(currentTime)), changeVolume: volume => dispatch(doChangeVolume(volume)), cancelPlay: () => dispatch(doSetPlayingUri(null)), }); diff --git a/src/renderer/js/component/videoPlayer/view.jsx b/src/renderer/js/component/videoPlayer/view.jsx index 68be43e03f1..9c13be46ef8 100644 --- a/src/renderer/js/component/videoPlayer/view.jsx +++ b/src/renderer/js/component/videoPlayer/view.jsx @@ -39,7 +39,7 @@ class VideoPlayer extends React.PureComponent { componentDidMount() { const container = this.media; - const { contentType, changeVolume, volume } = this.props; + const { contentType, changeVolume, volume, currentTime } = this.props; const { downloadPath, mediaType } = this.state; const loadedMetadata = e => { this.setState({ hasMetadata: true, startedPlaying: true }); @@ -101,6 +101,9 @@ class VideoPlayer extends React.PureComponent { mediaElement.addEventListener("volumechange", () => { changeVolume(mediaElement.volume); }); + if (currentTime && currentTime > 0) { + mediaElement.currentTime = currentTime; + } mediaElement.volume = volume; } } @@ -110,6 +113,10 @@ class VideoPlayer extends React.PureComponent { const mediaElement = this.media.children[0]; if (mediaElement) { mediaElement.removeEventListener("click", this.togglePlayListener); + const currentTime = mediaElement.currentTime; + if (currentTime) { + this.props.setTime(mediaElement.currentTime); + } } } diff --git a/src/renderer/js/constants/action_types.js b/src/renderer/js/constants/action_types.js index 3670912fd64..37004e4d27c 100644 --- a/src/renderer/js/constants/action_types.js +++ b/src/renderer/js/constants/action_types.js @@ -7,6 +7,7 @@ export const DAEMON_READY = "DAEMON_READY"; export const DAEMON_VERSION_MATCH = "DAEMON_VERSION_MATCH"; export const DAEMON_VERSION_MISMATCH = "DAEMON_VERSION_MISMATCH"; export const VOLUME_CHANGED = "VOLUME_CHANGED"; +export const SET_CURRENT_TIME = "SET_CURRENT_TIME"; // Navigation export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH"; diff --git a/src/renderer/js/redux/actions/content.js b/src/renderer/js/redux/actions/content.js index 9c9b233fd04..c3bba57123c 100644 --- a/src/renderer/js/redux/actions/content.js +++ b/src/renderer/js/redux/actions/content.js @@ -539,11 +539,11 @@ export function doAbandonClaim(txid, nout) { }; } -export function doEnableOverlay(canBeOverlayed) { +export function doSetTime(currentTime) { return function(dispatch, getState) { dispatch({ - type: types.ENABLE_OVERLAY, - data: { canBeOverlayed }, + type: types.SET_CURRENT_TIME, + data: { currentTime }, }); }; } diff --git a/src/renderer/js/redux/reducers/content.js b/src/renderer/js/redux/reducers/content.js index c71451238a4..362ac208782 100644 --- a/src/renderer/js/redux/reducers/content.js +++ b/src/renderer/js/redux/reducers/content.js @@ -83,9 +83,9 @@ reducers[types.FETCH_CHANNEL_CLAIM_COUNT_COMPLETED] = function(state, action) { }); }; -reducers[types.ENABLE_OVERLAY] = function(state, action) { +reducers[types.SET_CURRENT_TIME] = function(state, action) { return Object.assign({}, state, { - overlayable: action.data.canBeOverlayed, + currentTime: action.data.currentTime, }); }; diff --git a/src/renderer/js/redux/selectors/content.js b/src/renderer/js/redux/selectors/content.js index a0206eb7f4c..08ad21adddb 100644 --- a/src/renderer/js/redux/selectors/content.js +++ b/src/renderer/js/redux/selectors/content.js @@ -50,7 +50,7 @@ export const selectRewardContentClaimIds = createSelector( state => state.rewardedContentClaimIds ); -export const selectIsOverlayable = createSelector( +export const selectCurrentTime = createSelector( _selectState, - state => state.overlayable + state => state.currentTime ); From d8b9fb7695fb14fc126bb7821cea10d1ba3e3306 Mon Sep 17 00:00:00 2001 From: hackrush Date: Thu, 7 Dec 2017 15:40:16 +0530 Subject: [PATCH 8/9] Resumable playback working --- .../js/component/{video => media}/index.js | 4 +- .../{video => media}/internal/play-button.jsx | 6 +- .../js/component/{video => media}/view.jsx | 72 +++++++++---------- .../js/component/videoOverlay/index.js | 7 +- .../js/component/videoOverlay/view.jsx | 2 +- .../js/component/videoPlayer/index.js | 9 ++- .../js/component/videoPlayer/view.jsx | 43 ++++++----- src/renderer/js/page/file/view.jsx | 4 +- src/renderer/scss/component/_video.scss | 19 ++++- 9 files changed, 93 insertions(+), 73 deletions(-) rename src/renderer/js/component/{video => media}/index.js (93%) rename src/renderer/js/component/{video => media}/internal/play-button.jsx (91%) rename src/renderer/js/component/{video => media}/view.jsx (73%) diff --git a/src/renderer/js/component/video/index.js b/src/renderer/js/component/media/index.js similarity index 93% rename from src/renderer/js/component/video/index.js rename to src/renderer/js/component/media/index.js index c883292aedc..a23d35fda9f 100644 --- a/src/renderer/js/component/video/index.js +++ b/src/renderer/js/component/media/index.js @@ -12,7 +12,7 @@ import { } from "redux/selectors/file_info"; import { makeSelectCostInfoForUri } from "redux/selectors/cost_info"; import { selectShowNsfw } from "redux/selectors/settings"; -import Video from "./view"; +import Media from "./view"; import { selectPlayingUri } from "redux/selectors/content"; const select = (state, props) => ({ @@ -31,4 +31,4 @@ const perform = dispatch => ({ cancelPlay: () => dispatch(doSetPlayingUri(null)), }); -export default connect(select, perform)(Video); +export default connect(select, perform)(Media); diff --git a/src/renderer/js/component/video/internal/play-button.jsx b/src/renderer/js/component/media/internal/play-button.jsx similarity index 91% rename from src/renderer/js/component/video/internal/play-button.jsx rename to src/renderer/js/component/media/internal/play-button.jsx index 5d8c436337a..9ff39104700 100644 --- a/src/renderer/js/component/video/internal/play-button.jsx +++ b/src/renderer/js/component/media/internal/play-button.jsx @@ -17,11 +17,11 @@ class VideoPlayButton extends React.PureComponent { "Space" === event.code ) { event.preventDefault(); - this.watch(); + this.renderMedia(); } } - watch() { + renderMedia() { this.props.play(this.props.uri); } @@ -39,7 +39,7 @@ class VideoPlayButton extends React.PureComponent { disabled={disabled} className="video__play-button" icon={icon} - onClick={() => this.watch()} + onClick={() => this.renderMedia()} /> ); } diff --git a/src/renderer/js/component/video/view.jsx b/src/renderer/js/component/media/view.jsx similarity index 73% rename from src/renderer/js/component/video/view.jsx rename to src/renderer/js/component/media/view.jsx index 7a21109be04..2453e7748ea 100644 --- a/src/renderer/js/component/video/view.jsx +++ b/src/renderer/js/component/media/view.jsx @@ -5,7 +5,7 @@ import { LoadingScreen } from "component/common"; import VideoPlayer from "component/videoPlayer"; import NsfwOverlay from "component/nsfwOverlay"; -class Video extends React.PureComponent { +class Media extends React.PureComponent { constructor(props) { super(props); this.state = { @@ -22,44 +22,21 @@ class Video extends React.PureComponent { } } + isNSFW() { + const { obscureNsfw, metadata } = this.props; + return obscureNsfw && metadata && metadata.nsfw; + } + handleMouseOver() { - if ( - this.props.obscureNsfw && - this.props.metadata && - this.props.metadata.nsfw - ) { - this.setState({ - showNsfwHelp: true, - }); - } + if (this.isNSFW()) this.setState({ showNsfwHelp: true }); } handleMouseOut() { - if (this.state.showNsfwHelp) { - this.setState({ - showNsfwHelp: false, - }); - } + this.setState({ showNsfwHelp: false }); } - render() { - const { - metadata, - isLoading, - isDownloading, - playingUri, - fileInfo, - contentType, - uri, - } = this.props; - - const isPlaying = playingUri === uri; - const isReadyToPlay = fileInfo && fileInfo.written_bytes > 0; - const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw; - const mediaType = lbry.getMediaType( - contentType, - fileInfo && fileInfo.file_name - ); + determineLoadStatusMessage() { + const { fileInfo, isLoading, isDownloading } = this.props; let loadStatusMessage = ""; @@ -73,9 +50,17 @@ class Video extends React.PureComponent { loadStatusMessage = __("Downloading stream... not long left now!"); } + return loadStatusMessage; + } + + determineClasses(isPlaying, mediaType) { + const { isLoading, isDownloading } = this.props; + let classes = []; - classes.push(obscureNsfw ? "video--obscured " : ""); + classes.push(this.isNSFW() ? "video--obscured " : ""); + if (isLoading || isDownloading) classes.push("video-embedded", "video"); + if (mediaType === "video") { classes.push("video-embedded", "video"); classes.push(isPlaying ? "video--active" : "video--hidden"); @@ -85,15 +70,28 @@ class Video extends React.PureComponent { if (!isPlaying) classes.push("video-embedded"); } + return classes.join(" "); + } + + render() { + const { metadata, playingUri, fileInfo, contentType, uri } = this.props; + + const isPlaying = playingUri === uri; + const isReadyToPlay = fileInfo && fileInfo.written_bytes > 0; + const mediaType = lbry.getMediaType( + contentType, + fileInfo && fileInfo.file_name + ); + return (
{isPlaying && (!isReadyToPlay ? ( - + ) : ( ))} @@ -111,4 +109,4 @@ class Video extends React.PureComponent { } } -export default Video; +export default Media; diff --git a/src/renderer/js/component/videoOverlay/index.js b/src/renderer/js/component/videoOverlay/index.js index 8d7a6576de0..2e25f2cdd30 100644 --- a/src/renderer/js/component/videoOverlay/index.js +++ b/src/renderer/js/component/videoOverlay/index.js @@ -1,5 +1,4 @@ import { connect } from "react-redux"; -import { doSetPlayingUri } from "redux/actions/content"; import { makeSelectFileInfoForUri, makeSelectDownloadingForUri, @@ -17,8 +16,4 @@ const select = (state, props) => ({ fileInfo: uri => makeSelectFileInfoForUri(uri)(state), }); -const perform = dispatch => ({ - cancelPlay: () => dispatch(doSetPlayingUri(null)), -}); - -export default connect(select, perform)(VideoOverlay); +export default connect(select, null)(VideoOverlay); diff --git a/src/renderer/js/component/videoOverlay/view.jsx b/src/renderer/js/component/videoOverlay/view.jsx index 94eb8de7e70..069e21a43d9 100644 --- a/src/renderer/js/component/videoOverlay/view.jsx +++ b/src/renderer/js/component/videoOverlay/view.jsx @@ -3,7 +3,7 @@ import VideoPlayer from "component/videoPlayer"; class VideoOverlay extends React.PureComponent { render() { - const { playingUri, currentPage, cancelPlay } = this.props; + const { playingUri, currentPage } = this.props; const isDownloading = this.props.isDownloading(playingUri); const isLoading = this.props.isLoading(playingUri); diff --git a/src/renderer/js/component/videoPlayer/index.js b/src/renderer/js/component/videoPlayer/index.js index ff462f11c93..3487bc81248 100644 --- a/src/renderer/js/component/videoPlayer/index.js +++ b/src/renderer/js/component/videoPlayer/index.js @@ -1,15 +1,15 @@ import React from "react"; import { connect } from "react-redux"; import { doChangeVolume } from "redux/actions/app"; -import { selectVolume } from "redux/selectors/app"; +import { doNavigate } from "redux/actions/navigation"; import { doSetPlayingUri, doSetTime } from "redux/actions/content"; import { makeSelectMetadataForUri, makeSelectContentTypeForUri, } from "redux/selectors/claims"; +import { selectVolume } from "redux/selectors/app"; import { makeSelectFileInfoForUri } from "redux/selectors/file_info"; -import { selectPlayingUri, selectCurrentTime } from "redux/selectors/content"; -import { selectCurrentPage } from "redux/selectors/navigation"; +import { selectCurrentTime } from "redux/selectors/content"; import VideoPlayer from "./view"; const select = (state, props) => ({ @@ -17,12 +17,11 @@ const select = (state, props) => ({ fileInfo: makeSelectFileInfoForUri(props.uri)(state), metadata: makeSelectMetadataForUri(props.uri)(state), currentTime: selectCurrentTime(state), - currentPage: selectCurrentPage(state), - playingUri: selectPlayingUri(state), volume: selectVolume(state), }); const perform = dispatch => ({ + navigate: (path, params) => dispatch(doNavigate(path, params)), setTime: currentTime => dispatch(doSetTime(currentTime)), changeVolume: volume => dispatch(doChangeVolume(volume)), cancelPlay: () => dispatch(doSetPlayingUri(null)), diff --git a/src/renderer/js/component/videoPlayer/view.jsx b/src/renderer/js/component/videoPlayer/view.jsx index 9c13be46ef8..50a42788e25 100644 --- a/src/renderer/js/component/videoPlayer/view.jsx +++ b/src/renderer/js/component/videoPlayer/view.jsx @@ -39,7 +39,7 @@ class VideoPlayer extends React.PureComponent { componentDidMount() { const container = this.media; - const { contentType, changeVolume, volume, currentTime } = this.props; + const { contentType, changeVolume, volume } = this.props; const { downloadPath, mediaType } = this.state; const loadedMetadata = e => { this.setState({ hasMetadata: true, startedPlaying: true }); @@ -101,13 +101,22 @@ class VideoPlayer extends React.PureComponent { mediaElement.addEventListener("volumechange", () => { changeVolume(mediaElement.volume); }); - if (currentTime && currentTime > 0) { - mediaElement.currentTime = currentTime; - } mediaElement.volume = volume; } } + componentWillReceiveProps(nextProps) { + const mediaElement = this.media.children[0]; + const { currentTime } = nextProps; + const shouldSeek = + currentTime !== this.props.currentTime && currentTime && currentTime > 0; + + if (mediaElement && shouldSeek) { + mediaElement.currentTime = nextProps.currentTime; + this.props.setTime(null); + } + } + componentWillUnmount() { document.removeEventListener("keydown", this.togglePlayListener); const mediaElement = this.media.children[0]; @@ -189,15 +198,7 @@ class VideoPlayer extends React.PureComponent { } render() { - const { - contentType, - fileInfo, - metadata, - overlay, - currentPage, - playingUri, - cancelPlay, - } = this.props; + const { metadata, overlay, uri, cancelPlay, navigate } = this.props; const { mediaType } = this.state; const { hasMetadata, unplayable, overlayable } = this.state; const noMetadataMessage = "Waiting for metadata."; @@ -210,14 +211,17 @@ class VideoPlayer extends React.PureComponent { return (
{["audio", "application"].indexOf(mediaType) !== -1 && + !displayOverlay && (!this.isPlayableType() || hasMetadata) && !unplayable && } {this.isPlayableType() && + !displayOverlay && !hasMetadata && !unplayable && } - {unplayable && ( - - )} + {unplayable && + (!displayOverlay && ( + + ))} {displayOverlay && ( cancelPlay()} /> )} + {displayOverlay && ( + navigate("/show", { uri })} + /> + )}
(this.media = ref)} className="media" />
diff --git a/src/renderer/js/page/file/view.jsx b/src/renderer/js/page/file/view.jsx index abe538fd84a..131b726a2e7 100644 --- a/src/renderer/js/page/file/view.jsx +++ b/src/renderer/js/page/file/view.jsx @@ -1,7 +1,7 @@ import React from "react"; import lbry from "lbry.js"; import lbryuri from "lbryuri.js"; -import Video from "component/video"; +import Media from "component/media"; import { Thumbnail } from "component/common"; import FilePrice from "component/filePrice"; import FileDetails from "component/fileDetails"; @@ -66,7 +66,7 @@ class FilePage extends React.PureComponent {
{isPlayable ? ( -