Skip to content

Commit

Permalink
Add autoplay toggle to the video player (FreeTubeApp#5866)
Browse files Browse the repository at this point in the history
* Add autoplay toggle to the video player

* Fix (pre-existing) bug where mobile Shaka overflow menu overflows into top nav bar

* Update confusing autoplay labels to use consistent language

* Update src/renderer/components/ft-shaka-video-player/player-components/AutoplayToggle.js

Co-authored-by: absidue <48293849+absidue@users.noreply.github.com>

* Have autoplay toggle value update when setting updates elsewhere, e.g., in another window

* Apply suggestions from code review

Co-authored-by: absidue <48293849+absidue@users.noreply.github.com>

* Remove now-redundant playlist pause feature

* Update overflow width threshold

---------

Co-authored-by: absidue <48293849+absidue@users.noreply.github.com>
  • Loading branch information
2 people authored and Alban Dumas committed Jan 24, 2025
1 parent 89d163e commit 67102f1
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@
grid-gap: 8px;
}

.autoPlayToggle {
display: flex;
justify-content: flex-end;
align-items: center;
}

.VideoRecommendationsTopBar {
display: flex;
justify-content: space-between;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@
<h3>
{{ $t("Up Next") }}
</h3>
<FtToggleSwitch
v-if="showAutoplay"
class="autoPlayToggle"
:label="$t('Video.Autoplay')"
:compact="true"
:default-value="playNextVideo"
@change="updatePlayNextVideo"
/>
</div>
<FtListVideoLazy
v-for="(video, index) in data"
Expand All @@ -27,36 +19,16 @@
</template>

<script setup>
import { computed } from 'vue'
import FtCard from '../ft-card/ft-card.vue'
import FtListVideoLazy from '../ft-list-video-lazy/ft-list-video-lazy.vue'
import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue'
import store from '../../store/index'
defineProps({
data: {
type: Array,
required: true
},
showAutoplay: {
type: Boolean,
default: false
}
})
/** @type {import('vue').ComputedRef<boolean>} */
const playNextVideo = computed(() => {
return store.getters.getPlayNextVideo
})
/**
* @param {boolean} value
*/
function updatePlayNextVideo(value) {
store.dispatch('updatePlayNextVideo', value)
}
</script>

<style scoped src="./WatchVideoRecommendations.css" />
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
:deep(.shaka-overflow-menu),
:deep(.shaka-settings-menu) {
background: var(--card-bg-color) !important;
max-height: 200px;

}

:deep(.shaka-overflow-menu button),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { LegacyQualitySelection } from './player-components/LegacyQualitySelecti
import { ScreenshotButton } from './player-components/ScreenshotButton'
import { StatsButton } from './player-components/StatsButton'
import { TheatreModeButton } from './player-components/TheatreModeButton'
import { AutoplayToggle } from './player-components/AutoplayToggle'
import {
findMostSimilarAudioBandwidth,
getSponsorBlockSegments,
Expand All @@ -34,7 +35,7 @@ import { pathExists } from '../../helpers/filesystem'
// The UTF-8 characters "h", "t", "t", and "p".
const HTTP_IN_HEX = 0x68747470

const USE_OVERFLOW_MENU_WIDTH_THRESHOLD = 600
const USE_OVERFLOW_MENU_WIDTH_THRESHOLD = 634

const RequestType = shaka.net.NetworkingEngine.RequestType
const AdvancedRequestType = shaka.net.NetworkingEngine.AdvancedRequestType
Expand Down Expand Up @@ -119,6 +120,14 @@ export default defineComponent({
type: Boolean,
default: false
},
autoplayPossible: {
type: Boolean,
default: false
},
autoplayEnabled: {
type: Boolean,
default: false
},
vrProjection: {
type: String,
default: null
Expand All @@ -133,6 +142,7 @@ export default defineComponent({
'loaded',
'ended',
'timeupdate',
'toggle-autoplay',
'toggle-theatre-mode',
'playback-rate-updated'
],
Expand Down Expand Up @@ -751,6 +761,7 @@ export default defineComponent({
if (useOverFlowMenu.value) {
uiConfig.overflowMenuButtons = [
'ft_screenshot',
'ft_autoplay_toggle',
'playback_rate',
'loop',
'ft_audio_tracks',
Expand All @@ -770,6 +781,7 @@ export default defineComponent({
'recenter_vr',
'toggle_stereoscopic',
'ft_screenshot',
'ft_autoplay_toggle',
'playback_rate',
'loop',
'ft_audio_tracks',
Expand Down Expand Up @@ -798,6 +810,11 @@ export default defineComponent({
}
}

if (!props.autoplayPossible) {
const index = elementList.indexOf('ft_autoplay_toggle')
elementList.splice(index, 1)
}

if (props.format === 'audio') {
const index = elementList.indexOf('picture_in_picture')
elementList.splice(index, 1)
Expand Down Expand Up @@ -975,6 +992,14 @@ export default defineComponent({
}
})

watch(() => props.autoplayEnabled, (newValue, oldValue) => {
if (newValue !== oldValue) {
events.dispatchEvent(new CustomEvent('setAutoplay', {
detail: newValue
}))
}
})

/** @type {ResizeObserver|null} */
let resizeObserver = null

Expand Down Expand Up @@ -1643,6 +1668,24 @@ export default defineComponent({
shakaOverflowMenu.registerElement('ft_audio_tracks', new AudioTrackSelectionFactory())
}

function registerAutoplayToggle() {
events.addEventListener('toggleAutoplay', () => {
emit('toggle-autoplay')
})

/**
* @implements {shaka.extern.IUIElement.Factory}
*/
class AutoplayToggleFactory {
create(rootElement, controls) {
return new AutoplayToggle(props.autoplayEnabled, events, rootElement, controls)
}
}

shakaControls.registerElement('ft_autoplay_toggle', new AutoplayToggleFactory())
shakaOverflowMenu.registerElement('ft_autoplay_toggle', new AutoplayToggleFactory())
}

function registerTheatreModeButton() {
events.addEventListener('toggleTheatreMode', () => {
emit('toggle-theatre-mode')
Expand Down Expand Up @@ -1772,6 +1815,9 @@ export default defineComponent({
shakaControls.registerElement('ft_audio_tracks', null)
shakaOverflowMenu.registerElement('ft_audio_tracks', null)

shakaControls.registerElement('ft_autoplay_toggle', null)
shakaOverflowMenu.registerElement('ft_autoplay_toggle', null)

shakaControls.registerElement('ft_theatre_mode', null)
shakaOverflowMenu.registerElement('ft_theatre_mode', null)

Expand Down Expand Up @@ -2394,6 +2440,8 @@ export default defineComponent({

registerScreenshotButton()
registerAudioTrackSelection()
registerAutoplayToggle()

registerTheatreModeButton()
registerFullWindowButton()
registerLegacyQualitySelection()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import shaka from 'shaka-player'

import i18n from '../../../i18n/index'

export class AutoplayToggle extends shaka.ui.Element {
/**
* @param {boolean} autoplayEnabled
* @param {EventTarget} events
* @param {HTMLElement} parent
* @param {shaka.ui.Controls} controls
*/
constructor(autoplayEnabled, events, parent, controls) {
super(parent, controls)

/** @private */
this.button_ = document.createElement('button')
this.button_.classList.add('autoplay-toggle')
this.button_.classList.add('shaka-tooltip')

/** @private */
this.icon_ = document.createElement('i')
this.icon_.classList.add('material-icons-round')
this.icon_.textContent = 'pause_circle'

this.button_.appendChild(this.icon_)

const label = document.createElement('label')
label.classList.add('shaka-overflow-button-label')
label.classList.add('shaka-overflow-menu-only')

/** @private */
this.nameSpan_ = document.createElement('span')
label.appendChild(this.nameSpan_)

/** @private */
this.currentState_ = document.createElement('span')
this.currentState_.classList.add('shaka-current-selection-span')
label.appendChild(this.currentState_)

this.button_.appendChild(label)

this.parent.appendChild(this.button_)

/** @private */
this.autoplayEnabled_ = autoplayEnabled

// listeners

this.eventManager.listen(this.button_, 'click', () => {
events.dispatchEvent(new CustomEvent('toggleAutoplay', {
detail: !this.autoplayEnabled_
}))
})

const handleAutoplayValueChange = (/** @type {CustomEvent} */ event) => {
this.autoplayEnabled_ = event.detail
this.updateLocalisedStrings_()
}

this.eventManager.listen(events, 'setAutoplay', handleAutoplayValueChange)

this.eventManager.listen(events, 'localeChanged', () => {
this.updateLocalisedStrings_()
})

this.updateLocalisedStrings_()
}

/** @private */
updateLocalisedStrings_() {
this.nameSpan_.textContent = i18n.t('Video.Autoplay')

this.icon_.textContent = this.autoplayEnabled_ ? 'play_circle' : 'pause_circle'

this.currentState_.textContent = this.localization.resolve(this.autoplayEnabled_ ? 'ON' : 'OFF')

this.button_.ariaLabel = this.autoplayEnabled_ ? i18n.t('Video.Player.Autoplay is on') : i18n.t('Video.Player.Autoplay is off')
}
}
14 changes: 7 additions & 7 deletions src/renderer/components/player-settings/player-settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@
</div>
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Player Settings.Autoplay Videos')"
:label="$t('Settings.Player Settings.Play Next Video')"
:compact="true"
:default-value="autoplayVideos"
@change="updateAutoplayVideos"
:disabled="hideRecommendedVideos"
:default-value="playNextVideo"
@change="updatePlayNextVideo"
/>
<ft-toggle-switch
:label="$t('Settings.Player Settings.Autoplay Playlists')"
Expand All @@ -61,11 +62,10 @@
@change="updateAutoplayPlaylists"
/>
<ft-toggle-switch
:label="$t('Settings.Player Settings.Play Next Video')"
:label="$t('Settings.Player Settings.Autoplay Videos')"
:compact="true"
:disabled="hideRecommendedVideos"
:default-value="playNextVideo"
@change="updatePlayNextVideo"
:default-value="autoplayVideos"
@change="updateAutoplayVideos"
/>
<ft-toggle-switch
:label="$t('Settings.Player Settings.Display Play Button In Video Player')"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export default defineComponent({
shuffleEnabled: false,
loopEnabled: false,
reversePlaylist: false,
pauseOnCurrentVideo: false,
prevVideoBeforeDeletion: null,
channelName: '',
channelId: '',
Expand Down Expand Up @@ -294,16 +293,6 @@ export default defineComponent({
})
},

togglePauseOnCurrentVideo: function () {
if (this.pauseOnCurrentVideo) {
this.pauseOnCurrentVideo = false
showToast(this.$t('Playlist will not pause when current video is finished'))
} else {
this.pauseOnCurrentVideo = true
showToast(this.$t('Playlist will pause when current video is finished'))
}
},

playNextVideo: function () {
const playlistInfo = {
playlistId: this.playlistId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,6 @@
@keydown.enter.prevent="playNextVideo"
@keydown.space.prevent="playNextVideo"
/>
<font-awesome-icon
class="playlistIcon"
:class="{ playlistIconActive: pauseOnCurrentVideo }"
:icon="['fas', 'pause']"
:title="$t('Video.Pause on Current Video')"
role="button"
tabindex="0"
:aria-pressed="pauseOnCurrentVideo"
@click="togglePauseOnCurrentVideo"
@keydown.enter.prevent="togglePauseOnCurrentVideo"
@keydown.space.prevent="togglePauseOnCurrentVideo"
/>
</p>
<div
v-if="!isLoading"
Expand Down
2 changes: 0 additions & 2 deletions src/renderer/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ import {
faNetworkWired,
faNewspaper,
faPalette,
faPause,
faPhotoFilm,
faPlay,
faPlus,
Expand Down Expand Up @@ -204,7 +203,6 @@ library.add(
faNetworkWired,
faNewspaper,
faPalette,
faPause,
faPhotoFilm,
faPlay,
faPlus,
Expand Down
Loading

0 comments on commit 67102f1

Please sign in to comment.