diff --git a/src/constants.js b/src/constants.js index f45e30fb8..8065f0905 100644 --- a/src/constants.js +++ b/src/constants.js @@ -15,6 +15,9 @@ const IpcChannels = { APP_READY: 'app-ready', RELAUNCH_REQUEST: 'relaunch-request', + REQUEST_FULLSCREEN: 'request-fullscreen', + REQUEST_PIP: 'request-pip', + SEARCH_INPUT_HANDLING_READY: 'search-input-handling-ready', UPDATE_SEARCH_INPUT_TEXT: 'update-search-input-text', diff --git a/src/main/index.js b/src/main/index.js index 3e671abcc..fa300a90c 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -928,6 +928,14 @@ function runApp() { return app.getPath('pictures') }) + ipcMain.on(IpcChannels.REQUEST_FULLSCREEN, ({ sender }) => { + sender.executeJavaScript('document.getElementById("videoContainer").requestFullscreen({navigationUI: "hide"})', true) + }) + + ipcMain.on(IpcChannels.REQUEST_PIP, ({ sender }) => { + sender.executeJavaScript('document.getElementById("video").requestPictureInPicture()', true) + }) + ipcMain.handle(IpcChannels.SHOW_OPEN_DIALOG, async ({ sender }, options) => { const senderWindow = findSenderWindow(sender) if (senderWindow) { diff --git a/src/renderer/components/distraction-settings/distraction-settings.js b/src/renderer/components/distraction-settings/distraction-settings.js index cdb0f4f01..002631739 100644 --- a/src/renderer/components/distraction-settings/distraction-settings.js +++ b/src/renderer/components/distraction-settings/distraction-settings.js @@ -203,7 +203,6 @@ export default defineComponent({ 'updateHideLiveChat', 'updateHideActiveSubscriptions', 'updatePlayNextVideo', - 'updateDefaultTheatreMode', 'updateHideVideoDescription', 'updateHideComments', 'updateHideCommentPhotos', diff --git a/src/renderer/components/ft-list-video/ft-list-video.js b/src/renderer/components/ft-list-video/ft-list-video.js index 2356c816e..e65b772e7 100644 --- a/src/renderer/components/ft-list-video/ft-list-video.js +++ b/src/renderer/components/ft-list-video/ft-list-video.js @@ -377,6 +377,10 @@ export default defineComponent({ return this.$store.getters.getExternalPlayer }, + externalPlayerIsDefaultViewingMode: function () { + return this.externalPlayer !== '' && this.$store.getters.getDefaultViewingMode === 'external_player' + }, + defaultPlayback: function () { return this.$store.getters.getDefaultPlayback }, @@ -479,13 +483,18 @@ export default defineComponent({ return this.isInQuickBookmarkPlaylist ? 'base favorite' : 'base' }, - watchPageLinkTo() { - // For `router-link` attribute `to` + watchVideoRoute() { return { path: `/watch/${this.id}`, query: this.watchPageLinkQuery, } }, + + // For `router-link` attribute `to` + watchVideoRouterLink() { + return !this.externalPlayerIsDefaultViewingMode ? this.watchVideoRoute : {} + }, + watchPageLinkQuery() { const query = {} if (this.playlistIdFinal) { query.playlistId = this.playlistIdFinal } @@ -531,6 +540,11 @@ export default defineComponent({ } }, methods: { + handleWatchPageLinkClick: function() { + if (this.externalPlayerIsDefaultViewingMode) { + this.handleExternalPlayer() + } + }, fetchDeArrowThumbnail: async function() { if (this.thumbnailPreference === 'hidden') { return } const videoId = this.id diff --git a/src/renderer/components/ft-list-video/ft-list-video.vue b/src/renderer/components/ft-list-video/ft-list-video.vue index 7d63dcf98..0f1ccd322 100644 --- a/src/renderer/components/ft-list-video/ft-list-video.vue +++ b/src/renderer/components/ft-list-video/ft-list-video.vue @@ -14,7 +14,8 @@

{{ displayTitle }} diff --git a/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js b/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js index 074739caf..1f5fcf271 100644 --- a/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js +++ b/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js @@ -104,12 +104,25 @@ export default defineComponent({ vrProjection: { type: String, default: null + }, + startInFullscreen: { + type: Boolean, + default: false + }, + startInFullwindow: { + type: Boolean, + default: false + }, + startInPip: { + type: Boolean, + default: false } }, emits: [ 'error', 'loaded', 'ended', + 'player-destroyed', 'timeupdate', 'toggle-theatre-mode' ], @@ -139,11 +152,15 @@ export default defineComponent({ const isLive = ref(false) const useOverFlowMenu = ref(false) - const fullWindowEnabled = ref(false) const forceAspectRatio = ref(false) const activeLegacyFormat = shallowRef(null) + const fullWindowEnabled = ref(false) + const startInFullwindow = props.startInFullwindow + let startInFullscreen = props.startInFullscreen + let startInPip = props.startInPip + /** * @type {{ * url: string, @@ -1046,6 +1063,15 @@ export default defineComponent({ emit('ended') } + function handleCanPlay() { + // PiP can only be activated once the video's readState and video track are populated + if (startInPip && ui.getControls().isPiPAllowed() && process.env.IS_ELECTRON) { + startInPip = false + const { ipcRenderer } = require('electron') + ipcRenderer.send(IpcChannels.REQUEST_PIP) + } + } + function updateVolume() { const video_ = video.value // https://docs.videojs.com/html5#volume @@ -1648,7 +1674,7 @@ export default defineComponent({ */ class FullWindowButtonFactory { create(rootElement, controls) { - return new FullWindowButton(fullWindowEnabled.value, events, rootElement, controls) + return new FullWindowButton(fullWindowEnabled.value, startInFullwindow, events, rootElement, controls) } } @@ -1733,7 +1759,7 @@ export default defineComponent({ /** * As shaka-player doesn't let you unregister custom control factories, * overwrite them with `null` instead so the referenced objects - * (e.g. {@linkcode events}, {@linkcode fullWindowEnabled}) can get gargabe collected + * (e.g. {@linkcode events}, {@linkcode fullWindowEnabled}) can get garbage collected */ function cleanUpCustomPlayerControls() { shakaControls.registerElement('ft_audio_tracks', null) @@ -2517,6 +2543,12 @@ export default defineComponent({ if (props.chapters.length > 0) { createChapterMarkers() } + + if (startInFullscreen && process.env.IS_ELECTRON) { + startInFullscreen = false + const { ipcRenderer } = require('electron') + ipcRenderer.send(IpcChannels.REQUEST_FULLSCREEN) + } } watch( @@ -2703,6 +2735,12 @@ export default defineComponent({ */ async function destroyPlayer() { if (ui) { + if (ui.getControls()) { + // save the state of player settings to reinitialize them upon next creation + const controls = ui.getControls() + emit('player-destroyed', controls.isFullScreenEnabled(), fullWindowEnabled.value, controls.isPiPEnabled()) + } + // destroying the ui also destroys the player await ui.destroy() ui = null @@ -2755,6 +2793,7 @@ export default defineComponent({ handlePlay, handlePause, + handleCanPlay, handleEnded, updateVolume, handleTimeupdate, diff --git a/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.vue b/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.vue index 6b2762e4e..7fc433912 100644 --- a/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.vue +++ b/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.vue @@ -1,5 +1,6 @@