Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stay in fullscreen/fullwindow/PiP + default viewing mode setting #5903

Open
wants to merge 34 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b8c6a40
MHave fullscreen persist when videos autoplay
kommunarr Oct 20, 2024
e95426d
Have fullscreen and PiP be re-requested on autoplay when they are open
kommunarr Oct 20, 2024
b64d989
Implement Default Viewing Mode setting
kommunarr Oct 20, 2024
2f1986c
Implement external player default viewing mode
kommunarr Oct 20, 2024
db27c7b
Disable & hide 'External Player' default viewing mode when no externa…
kommunarr Oct 20, 2024
0347336
Fix fullscreen issue with icons by calling requestFullscreen on video…
kommunarr Oct 20, 2024
937935f
Revert Enable Theater Mode by Default removal
kommunarr Oct 20, 2024
5ca47a6
Update src/renderer/components/player-settings/player-settings.js
kommunarr Oct 23, 2024
d57111a
Update to trigger setFullWindow event when starting in fullwindow
kommunarr Oct 23, 2024
5a54142
Update to use expand icon instead
kommunarr Oct 24, 2024
bb3be09
Clean up External Player Default Viewing Mode link template logic
kommunarr Oct 24, 2024
f71e9ce
Remove PiP and fullscreen default viewing mode options in settings wh…
kommunarr Oct 28, 2024
b548532
Improve stay-in-mode handling to save state values on player destroy
kommunarr Oct 28, 2024
687ef1e
Merge branch 'development' of github.com:FreeTubeApp/FreeTube into fe…
kommunarr Oct 28, 2024
fa35784
Fix linting
kommunarr Oct 28, 2024
997fbdb
Update values to check IS_ELECTRON
kommunarr Oct 28, 2024
9fa1ed9
Add clarifying code comments
kommunarr Dec 2, 2024
de596ca
Merge branch 'development' of github.com:FreeTubeApp/FreeTube into fe…
kommunarr Dec 6, 2024
e60b9aa
Revert "Revert Enable Theater Mode by Default removal"
kommunarr Dec 7, 2024
05858be
Implement theatre mode setting migration
kommunarr Dec 7, 2024
bf92924
Add 'Theater' label in lieu of reusing 'Theater Mode' label
kommunarr Dec 8, 2024
9edd920
Use native Shaka functions for toggling FS and PiP
kommunarr Dec 12, 2024
66ed004
Merge branch 'development' of github.com:FreeTubeApp/FreeTube into fe…
kommunarr Dec 28, 2024
2efef70
Implement code review suggestions
kommunarr Jan 1, 2025
ebbf672
Remove entries of removed theatre mode key in all other locales
kommunarr Jan 1, 2025
cafd511
Apply suggestions from code review
kommunarr Jan 2, 2025
6b69db1
Implement changes from review
kommunarr Jan 2, 2025
3f619a5
Remove key from additional languages
kommunarr Jan 2, 2025
b82daca
Merge branch 'development' of github.com:FreeTubeApp/FreeTube into fe…
kommunarr Jan 9, 2025
5aa0900
Merge branch 'development' of github.com:FreeTubeApp/FreeTube into fe…
kommunarr Jan 14, 2025
b1f0e5f
Remove label from two more languages
kommunarr Jan 14, 2025
f20d1b2
Fix language-related merge conflicts
kommunarr Jan 15, 2025
4eefa33
Revert changes in other locales to prevent merge conflicts
kommunarr Jan 15, 2025
eb21d87
Merge branch 'development' of github.com:FreeTubeApp/FreeTube into fe…
kommunarr Jan 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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',

Expand Down
13 changes: 13 additions & 0 deletions src/datastores/handlers/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ class Settings {
await this.upsert('externalPlayerCustomArgs', newValue)
}

// In FreeTube 0.23.0, the "Enable Theatre Mode by Default" setting was incoporated as an option
// of the "Default Viewing Mode" setting. This is a one time migration to preserve users'
// Theater Mode preference through this change.
const defaultTheatreMode = await db.settings.findOneAsync({ _id: 'defaultTheatreMode' })

if (defaultTheatreMode) {
if (defaultTheatreMode.value) {
await this.upsert('defaultViewingMode', 'theatre')
}

await db.settings.removeAsync({ _id: 'defaultTheatreMode' })
}

return db.settings.findAsync({ _id: { $ne: 'bounds' } })
}

Expand Down
12 changes: 12 additions & 0 deletions src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,18 @@ function runApp() {
return app.getPath('pictures')
})

// Allows programmatic toggling of fullscreen without accompanying user interaction.
// See: https://developer.mozilla.org/en-US/docs/Web/Security/User_activation#transient_activation
ipcMain.on(IpcChannels.REQUEST_FULLSCREEN, ({ sender }) => {
sender.executeJavaScript('document.querySelector("video.player").ui.getControls().toggleFullScreen()', true)
})

// Allows programmatic toggling of picture-in-picture mode without accompanying user interaction.
// See: https://developer.mozilla.org/en-US/docs/Web/Security/User_activation#transient_activation
ipcMain.on(IpcChannels.REQUEST_PIP, ({ sender }) => {
sender.executeJavaScript('document.querySelector("video.player").ui.getControls().togglePiP()', true)
})

ipcMain.handle(IpcChannels.SHOW_OPEN_DIALOG, async ({ sender }, options) => {
const senderWindow = findSenderWindow(sender)
if (senderWindow) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,6 @@ export default defineComponent({
'updateHideLiveChat',
'updateHideActiveSubscriptions',
'updatePlayNextVideo',
'updateDefaultTheatreMode',
'updateHideVideoDescription',
'updateHideComments',
'updateHideCommentPhotos',
Expand Down
24 changes: 19 additions & 5 deletions src/renderer/components/ft-list-video/ft-list-video.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,10 @@ export default defineComponent({
return this.$store.getters.getExternalPlayer
},

externalPlayerIsDefaultViewingMode: function () {
return process.env.IS_ELECTRON && this.externalPlayer !== '' && this.$store.getters.getDefaultViewingMode === 'external_player'
},

defaultPlayback: function () {
return this.$store.getters.getDefaultPlayback
},
Expand Down Expand Up @@ -482,13 +486,18 @@ export default defineComponent({
return this.isInQuickBookmarkPlaylist ? 'base favorite' : 'base'
},

watchPageLinkTo() {
// For `router-link` attribute `to`
return {
path: `/watch/${this.id}`,
query: this.watchPageLinkQuery,
watchVideoRouterLink() {
// For `router-link` attribute `to`
if (!this.externalPlayerIsDefaultViewingMode) {
return {
path: `/watch/${this.id}`,
query: this.watchPageLinkQuery,
}
} else {
return {}
}
},

watchPageLinkQuery() {
const query = {}
if (this.playlistIdFinal) { query.playlistId = this.playlistIdFinal }
Expand Down Expand Up @@ -547,6 +556,11 @@ export default defineComponent({
}
},
methods: {
handleWatchPageLinkClick: function() {
if (this.externalPlayerIsDefaultViewingMode) {
this.handleExternalPlayer()
}
},
fetchDeArrowThumbnail: async function() {
if (this.thumbnailPreference === 'hidden') { return }
const videoId = this.id
Expand Down
8 changes: 5 additions & 3 deletions src/renderer/components/ft-list-video/ft-list-video.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
<router-link
class="thumbnailLink"
tabindex="-1"
:to="watchPageLinkTo"
:to="watchVideoRouterLink"
@click.native="handleWatchPageLinkClick"
>
<img
:src="thumbnail"
Expand All @@ -34,7 +35,7 @@
{{ isLive ? $t("Video.Live") : (isUpcoming ? $t("Video.Upcoming") : displayDuration) }}
</div>
<ft-icon-button
v-if="externalPlayer !== ''"
v-if="externalPlayer !== '' && !externalPlayerIsDefaultViewingMode"
:title="$t('Video.External Player.OpenInTemplate', { externalPlayer })"
:icon="['fas', 'external-link-alt']"
class="externalPlayerIcon"
Expand Down Expand Up @@ -112,7 +113,8 @@
<div class="info">
<router-link
class="title"
:to="watchPageLinkTo"
:to="watchVideoRouterLink"
@click.native="handleWatchPageLinkClick"
>
<h3 class="h3Title">
{{ displayTitle }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,18 @@ export default defineComponent({
type: String,
default: null
},
startInFullscreen: {
type: Boolean,
default: false
},
startInFullwindow: {
type: Boolean,
default: false
},
startInPip: {
type: Boolean,
default: false
},
currentPlaybackRate: {
type: Number,
default: 1
Expand Down Expand Up @@ -172,11 +184,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,
Expand Down Expand Up @@ -1117,6 +1133,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 && props.format !== 'audio' && 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
Expand Down Expand Up @@ -1719,6 +1744,12 @@ export default defineComponent({
}
})

if (startInFullwindow) {
events.dispatchEvent(new CustomEvent('setFullWindow', {
detail: true
}))
}

/**
* @implements {shaka.extern.IUIElement.Factory}
*/
Expand Down Expand Up @@ -1809,7 +1840,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)
Expand Down Expand Up @@ -2633,6 +2664,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(
Expand Down Expand Up @@ -2844,11 +2881,25 @@ export default defineComponent({
* Vue's lifecycle hooks are synchonous, so if we destroy the player in {@linkcode onBeforeUnmount},
* it won't be finished in time, as the player destruction is asynchronous.
* To workaround that we destroy the player first and wait for it to finish before we unmount this component.
*
* @returns {Promise<{ startNextVideoInFullscreen: boolean, startNextVideoInFullwindow: boolean, startNextVideoInPip: boolean }>}
*/
async function destroyPlayer() {
ignoreErrors = true

let uiState = { startNextVideoInFullscreen: false, startNextVideoInFullwindow: false, startNextVideoInPip: false }

if (ui) {
if (ui.getControls()) {
// save the state of player settings to reinitialize them upon next creation
const controls = ui.getControls()
uiState = {
startNextVideoInFullscreen: controls.isFullScreenEnabled(),
startNextVideoInFullwindow: fullWindowEnabled.value,
startNextVideoInPip: controls.isPiPEnabled()
}
}

// destroying the ui also destroys the player
await ui.destroy()
ui = null
Expand All @@ -2867,6 +2918,8 @@ export default defineComponent({
if (video.value) {
video.value.ui = null
}

return uiState
}

expose({
Expand Down Expand Up @@ -2920,6 +2973,7 @@ export default defineComponent({

handlePlay,
handlePause,
handleCanPlay,
handleEnded,
updateVolume,
handleTimeupdate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
@play="handlePlay"
@pause="handlePause"
@ended="handleEnded"
@canplay="handleCanPlay"
@volumechange="updateVolume"
@timeupdate="handleTimeupdate"
/>
Expand Down
56 changes: 53 additions & 3 deletions src/renderer/components/player-settings/player-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,18 @@ export default defineComponent({
return this.$store.getters.getDefaultQuality
},

defaultTheatreMode: function () {
return this.$store.getters.getDefaultTheatreMode
defaultViewingMode: function () {
const defaultViewingMode = this.$store.getters.getDefaultViewingMode
if ((defaultViewingMode === 'external_player' && (!process.env.IS_ELECTRON || this.externalPlayer === '')) ||
(!process.env.IS_ELECTRON && (defaultViewingMode === 'fullscreen' || defaultViewingMode === 'pip'))) {
return 'default'
}

return defaultViewingMode
},

externalPlayer: function () {
return this.$store.getters.getExternalPlayer
},

hideRecommendedVideos: function () {
Expand Down Expand Up @@ -183,6 +193,46 @@ export default defineComponent({
]
},

viewingModeNames: function () {
const viewingModeNames = [
this.$t('Settings.General Settings.Thumbnail Preference.Default'),
this.$t('Settings.Player Settings.Default Viewing Mode.Theater'),
this.$t('Video.Player.Full Window'),
]

if (process.env.IS_ELECTRON) {
viewingModeNames.push(
this.$t('Settings.Player Settings.Default Viewing Mode.Full Screen'),
this.$t('Settings.Player Settings.Default Viewing Mode.Picture in Picture')
)
if (this.externalPlayer !== '') {
viewingModeNames.push(
this.$t('Settings.Player Settings.Default Viewing Mode.External Player', { externalPlayerName: this.externalPlayer })
)
}
}

return viewingModeNames
},

viewingModeValues: function () {
const viewingModeValues = [
'default',
'theatre',
'fullwindow'
]

if (process.env.IS_ELECTRON) {
viewingModeValues.push('fullscreen', 'pip')

if (this.externalPlayer !== '') {
viewingModeValues.push('external_player')
}
}

return viewingModeValues
},

enableScreenshot: function() {
return this.$store.getters.getEnableScreenshot
},
Expand Down Expand Up @@ -296,7 +346,7 @@ export default defineComponent({
'updatePlayNextVideo',
'updateEnableSubtitlesByDefault',
'updateProxyVideos',
'updateDefaultTheatreMode',
'updateDefaultViewingMode',
'updateDefaultSkipInterval',
'updateDefaultInterval',
'updateDefaultVolume',
Expand Down
Loading
Loading