mirror of
https://github.com/FreeTubeApp/FreeTube
synced 2024-11-25 11:19:59 +01:00
Compare commits
18 Commits
3309a29421
...
d66e54f3f8
Author | SHA1 | Date | |
---|---|---|---|
|
d66e54f3f8 | ||
|
a70a5c6ab2 | ||
|
997fbdbbba | ||
|
fa35784522 | ||
|
687ef1e889 | ||
|
b548532ea7 | ||
|
f71e9cefd6 | ||
|
bb3be09122 | ||
|
5a54142c70 | ||
|
d57111aea6 | ||
|
5ca47a6349 | ||
|
937935f06a | ||
|
0347336647 | ||
|
db27c7b923 | ||
|
2f1986ca53 | ||
|
b64d9896d4 | ||
|
e95426d193 | ||
|
b8c6a40a36 |
@ -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',
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -203,7 +203,6 @@ export default defineComponent({
|
||||
'updateHideLiveChat',
|
||||
'updateHideActiveSubscriptions',
|
||||
'updatePlayNextVideo',
|
||||
'updateDefaultTheatreMode',
|
||||
'updateHideVideoDescription',
|
||||
'updateHideComments',
|
||||
'updateHideCommentPhotos',
|
||||
|
@ -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
|
||||
|
@ -14,7 +14,8 @@
|
||||
<router-link
|
||||
class="thumbnailLink"
|
||||
tabindex="-1"
|
||||
:to="watchPageLinkTo"
|
||||
:to="watchVideoRouterLink"
|
||||
@click.native="handleWatchPageLinkClick"
|
||||
>
|
||||
<img
|
||||
:src="thumbnail"
|
||||
@ -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"
|
||||
@ -112,7 +113,8 @@
|
||||
<div class="info">
|
||||
<router-link
|
||||
class="title"
|
||||
:to="watchPageLinkTo"
|
||||
:to="watchVideoRouterLink"
|
||||
@click.native="handleWatchPageLinkClick"
|
||||
>
|
||||
<h3 class="h3Title">
|
||||
{{ displayTitle }}
|
||||
|
@ -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
|
||||
@ -1645,7 +1671,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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1730,7 +1756,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)
|
||||
@ -2525,6 +2551,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(
|
||||
@ -2741,6 +2773,12 @@ export default defineComponent({
|
||||
ignoreErrors = true
|
||||
|
||||
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
|
||||
@ -2793,6 +2831,7 @@ export default defineComponent({
|
||||
|
||||
handlePlay,
|
||||
handlePause,
|
||||
handleCanPlay,
|
||||
handleEnded,
|
||||
updateVolume,
|
||||
handleTimeupdate,
|
||||
|
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
id="videoContainer"
|
||||
ref="container"
|
||||
class="ftVideoPlayer shaka-video-container"
|
||||
:class="{
|
||||
@ -9,6 +10,7 @@
|
||||
>
|
||||
<!-- eslint-disable-next-line vuejs-accessibility/media-has-caption -->
|
||||
<video
|
||||
id="video"
|
||||
ref="video"
|
||||
class="player"
|
||||
preload="auto"
|
||||
@ -19,6 +21,7 @@
|
||||
@play="handlePlay"
|
||||
@pause="handlePause"
|
||||
@ended="handleEnded"
|
||||
@canplay="handleCanPlay"
|
||||
@volumechange="updateVolume"
|
||||
@timeupdate="handleTimeupdate"
|
||||
/>
|
||||
|
@ -5,11 +5,12 @@ import i18n from '../../../i18n/index'
|
||||
export class FullWindowButton extends shaka.ui.Element {
|
||||
/**
|
||||
* @param {boolean} fullWindowEnabled
|
||||
* @param {boolean} startInFullWindow
|
||||
* @param {EventTarget} events
|
||||
* @param {HTMLElement} parent
|
||||
* @param {shaka.ui.Controls} controls
|
||||
*/
|
||||
constructor(fullWindowEnabled, events, parent, controls) {
|
||||
constructor(fullWindowEnabled, startInFullWindow, events, parent, controls) {
|
||||
super(parent, controls)
|
||||
|
||||
/** @private */
|
||||
@ -62,6 +63,12 @@ export class FullWindowButton extends shaka.ui.Element {
|
||||
this.updateLocalisedStrings_()
|
||||
})
|
||||
|
||||
if (startInFullWindow) {
|
||||
events.dispatchEvent(new CustomEvent('setFullWindow', {
|
||||
detail: true
|
||||
}))
|
||||
}
|
||||
|
||||
this.updateLocalisedStrings_()
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,13 @@ export default defineComponent({
|
||||
screenshotFolderPlaceholder: '',
|
||||
screenshotFilenameExample: '',
|
||||
screenshotDefaultPattern: '%Y%M%D-%H%N%S',
|
||||
viewingModeValues: [
|
||||
'default',
|
||||
...(process.env.IS_ELECTRON ? ['fullscreen'] : []),
|
||||
'fullwindow',
|
||||
...(process.env.IS_ELECTRON ? ['pip'] : []),
|
||||
'external_player'
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -119,10 +126,24 @@ export default defineComponent({
|
||||
return this.$store.getters.getDefaultQuality
|
||||
},
|
||||
|
||||
defaultViewingMode: function () {
|
||||
const defaultViewingMode = this.$store.getters.getDefaultViewingMode
|
||||
if ((defaultViewingMode === 'external_player' && this.externalPlayer === '') ||
|
||||
(!process.env.IS_ELECTRON && (defaultViewingMode === 'fullscreen' || defaultViewingMode === 'pip'))) {
|
||||
return 'default'
|
||||
}
|
||||
|
||||
return defaultViewingMode
|
||||
},
|
||||
|
||||
defaultTheatreMode: function () {
|
||||
return this.$store.getters.getDefaultTheatreMode
|
||||
},
|
||||
|
||||
externalPlayer: function () {
|
||||
return this.$store.getters.getExternalPlayer
|
||||
},
|
||||
|
||||
hideRecommendedVideos: function () {
|
||||
return this.$store.getters.getHideRecommendedVideos
|
||||
},
|
||||
@ -177,6 +198,23 @@ export default defineComponent({
|
||||
]
|
||||
},
|
||||
|
||||
viewingModeNames: function () {
|
||||
const viewingModeNames = [
|
||||
this.$t('Settings.General Settings.Thumbnail Preference.Default'),
|
||||
...(process.env.IS_ELECTRON ? [this.$t('Settings.Player Settings.Default Viewing Mode.Full Screen')] : []),
|
||||
this.$t('Video.Player.Full Window'),
|
||||
...(process.env.IS_ELECTRON ? [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
|
||||
},
|
||||
|
||||
enableScreenshot: function() {
|
||||
return this.$store.getters.getEnableScreenshot
|
||||
},
|
||||
@ -289,11 +327,12 @@ export default defineComponent({
|
||||
'updatePlayNextVideo',
|
||||
'updateEnableSubtitlesByDefault',
|
||||
'updateProxyVideos',
|
||||
'updateDefaultTheatreMode',
|
||||
'updateDefaultViewingMode',
|
||||
'updateDefaultSkipInterval',
|
||||
'updateDefaultInterval',
|
||||
'updateDefaultVolume',
|
||||
'updateDefaultPlayback',
|
||||
'updateDefaultTheatreMode',
|
||||
'updateDefaultVideoFormat',
|
||||
'updateDefaultQuality',
|
||||
'updateVideoVolumeMouseScroll',
|
||||
|
@ -81,6 +81,41 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ft-flex-box>
|
||||
<ft-select
|
||||
:placeholder="$t('Settings.Player Settings.Default Viewing Mode.Default Viewing Mode')"
|
||||
:value="defaultViewingMode"
|
||||
:select-names="viewingModeNames"
|
||||
:select-values="viewingModeValues"
|
||||
:icon="['fas', 'expand']"
|
||||
@change="updateDefaultViewingMode"
|
||||
/>
|
||||
<ft-select
|
||||
:placeholder="$t('Settings.Player Settings.Default Video Format.Default Video Format')"
|
||||
:value="defaultVideoFormat"
|
||||
:select-names="formatNames"
|
||||
:select-values="formatValues"
|
||||
:tooltip="$t('Tooltips.Player Settings.Default Video Format')"
|
||||
:icon="['fas', 'file-video']"
|
||||
@change="updateDefaultVideoFormat"
|
||||
/>
|
||||
<ft-select
|
||||
:placeholder="$t('Settings.Player Settings.Default Quality.Default Quality')"
|
||||
:value="defaultQuality"
|
||||
:select-names="qualityNames"
|
||||
:select-values="qualityValues"
|
||||
:icon="['fas', 'photo-film']"
|
||||
@change="updateDefaultQuality"
|
||||
/>
|
||||
<ft-select
|
||||
:placeholder="$t('Settings.Player Settings.Video Playback Rate Interval')"
|
||||
:value="videoPlaybackRateInterval"
|
||||
:select-names="playbackRateIntervalValues"
|
||||
:select-values="playbackRateIntervalValues"
|
||||
:icon="['fas', 'gauge']"
|
||||
@change="updateVideoPlaybackRateInterval"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<ft-flex-box>
|
||||
<ft-slider
|
||||
:label="$t('Settings.Player Settings.Fast-Forward / Rewind Interval')"
|
||||
@ -127,33 +162,6 @@
|
||||
value-extension="x"
|
||||
@change="updateMaxVideoPlaybackRate"
|
||||
/>
|
||||
<ft-select
|
||||
:placeholder="$t('Settings.Player Settings.Video Playback Rate Interval')"
|
||||
:value="videoPlaybackRateInterval"
|
||||
:select-names="playbackRateIntervalValues"
|
||||
:select-values="playbackRateIntervalValues"
|
||||
:icon="['fas', 'gauge']"
|
||||
@change="updateVideoPlaybackRateInterval"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<ft-flex-box>
|
||||
<ft-select
|
||||
:placeholder="$t('Settings.Player Settings.Default Video Format.Default Video Format')"
|
||||
:value="defaultVideoFormat"
|
||||
:select-names="formatNames"
|
||||
:select-values="formatValues"
|
||||
:tooltip="$t('Tooltips.Player Settings.Default Video Format')"
|
||||
:icon="['fas', 'file-video']"
|
||||
@change="updateDefaultVideoFormat"
|
||||
/>
|
||||
<ft-select
|
||||
:placeholder="$t('Settings.Player Settings.Default Quality.Default Quality')"
|
||||
:value="defaultQuality"
|
||||
:select-names="qualityNames"
|
||||
:select-values="qualityValues"
|
||||
:icon="['fas', 'photo-film']"
|
||||
@change="updateDefaultQuality"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<br>
|
||||
<ft-flex-box
|
||||
|
@ -45,6 +45,7 @@ import {
|
||||
faEnvelope,
|
||||
faExchangeAlt,
|
||||
faExclamationCircle,
|
||||
faExpand,
|
||||
faExternalLinkAlt,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
@ -158,6 +159,7 @@ library.add(
|
||||
faEnvelope,
|
||||
faExchangeAlt,
|
||||
faExclamationCircle,
|
||||
faExpand,
|
||||
faExternalLinkAlt,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
|
@ -179,6 +179,7 @@ const state = {
|
||||
defaultQuality: '720',
|
||||
defaultSkipInterval: 5,
|
||||
defaultTheatreMode: false,
|
||||
defaultViewingMode: 'default',
|
||||
defaultVideoFormat: 'dash',
|
||||
disableSmoothScrolling: false,
|
||||
displayVideoPlayButton: false,
|
||||
|
@ -66,6 +66,9 @@ export default defineComponent({
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
startNextVideoInFullscreen: false,
|
||||
startNextVideoInFullwindow: false,
|
||||
startNextVideoInPip: false,
|
||||
isLoading: true,
|
||||
firstLoad: true,
|
||||
useTheatreMode: false,
|
||||
@ -166,6 +169,12 @@ export default defineComponent({
|
||||
defaultTheatreMode: function () {
|
||||
return this.$store.getters.getDefaultTheatreMode
|
||||
},
|
||||
defaultViewingMode: function () {
|
||||
return this.$store.getters.getDefaultViewingMode
|
||||
},
|
||||
externalPlayer: function () {
|
||||
return this.$store.getters.getExternalPlayer
|
||||
},
|
||||
defaultVideoFormat: function () {
|
||||
return this.$store.getters.getDefaultVideoFormat
|
||||
},
|
||||
@ -313,7 +322,7 @@ export default defineComponent({
|
||||
this.checkIfPlaylist()
|
||||
|
||||
// this has to be below checkIfPlaylist() as theatrePossible needs to know if there is a playlist or not
|
||||
this.useTheatreMode = this.defaultTheatreMode && this.theatrePossible
|
||||
this.setViewingModeOnFirstLoad()
|
||||
|
||||
if (!process.env.SUPPORTS_LOCAL_API || this.backendPreference === 'invidious') {
|
||||
this.getVideoInformationInvidious()
|
||||
@ -324,6 +333,21 @@ export default defineComponent({
|
||||
window.addEventListener('beforeunload', this.handleWatchProgress)
|
||||
},
|
||||
|
||||
setViewingModeOnFirstLoad: function () {
|
||||
this.useTheatreMode = this.defaultTheatreMode && this.theatrePossible
|
||||
|
||||
switch (this.defaultViewingMode) {
|
||||
case 'fullscreen':
|
||||
this.startNextVideoInFullscreen = true
|
||||
return
|
||||
case 'fullwindow':
|
||||
this.startNextVideoInFullwindow = true
|
||||
return
|
||||
case 'pip':
|
||||
this.startNextVideoInPip = true
|
||||
}
|
||||
},
|
||||
|
||||
changeTimestamp: function (timestamp) {
|
||||
const player = this.$refs.player
|
||||
|
||||
@ -1228,6 +1252,12 @@ export default defineComponent({
|
||||
this.activeFormat = 'audio'
|
||||
},
|
||||
|
||||
handlePlayerDestroyed: function(startNextVideoInFullscreen = false, startNextVideoInFullwindow = false, startNextVideoInPip = false) {
|
||||
this.startNextVideoInFullscreen = startNextVideoInFullscreen
|
||||
this.startNextVideoInFullwindow = startNextVideoInFullwindow
|
||||
this.startNextVideoInPip = startNextVideoInPip
|
||||
},
|
||||
|
||||
handleVideoEnded: function () {
|
||||
if ((!this.watchingPlaylist || !this.autoplayPlaylists) && !this.playNextVideo) {
|
||||
return
|
||||
|
@ -34,10 +34,14 @@
|
||||
:theatre-possible="theatrePossible"
|
||||
:use-theatre-mode="useTheatreMode"
|
||||
:vr-projection="vrProjection"
|
||||
:start-in-fullscreen="startNextVideoInFullscreen"
|
||||
:start-in-fullwindow="startNextVideoInFullwindow"
|
||||
:start-in-pip="startNextVideoInPip"
|
||||
class="videoPlayer"
|
||||
@error="handlePlayerError"
|
||||
@loaded="handleVideoLoaded"
|
||||
@timeupdate="updateCurrentChapter"
|
||||
@player-destroyed="handlePlayerDestroyed"
|
||||
@ended="handleVideoEnded"
|
||||
@toggle-theatre-mode="useTheatreMode = !useTheatreMode"
|
||||
/>
|
||||
|
@ -686,20 +686,23 @@ Channel:
|
||||
Shorts:
|
||||
This channel does not currently have any shorts: ''
|
||||
Live:
|
||||
Live: ''
|
||||
This channel does not currently have any live streams: ''
|
||||
Live: 'Regstreeks'
|
||||
This channel does not currently have any live streams: 'Hierdie kanaal het tans
|
||||
geen rekstreekse stromings nie'
|
||||
Playlists:
|
||||
Playlists: ''
|
||||
This channel does not currently have any playlists: ''
|
||||
Playlists: 'Afspeellyste'
|
||||
This channel does not currently have any playlists: 'Hierdie kanaal het tans geen
|
||||
afspeellyste nie'
|
||||
Sort Types:
|
||||
Last Video Added: ''
|
||||
Newest: ''
|
||||
Oldest: ''
|
||||
Last Video Added: 'Laaste toegevoegde video'
|
||||
Newest: 'Nuutste'
|
||||
Oldest: 'Oudste'
|
||||
Podcasts:
|
||||
Podcasts: ''
|
||||
This channel does not currently have any podcasts: ''
|
||||
Podcasts: 'Podsendings'
|
||||
This channel does not currently have any podcasts: 'Hierdie kanaal het tans geen
|
||||
podsendings nie'
|
||||
Releases:
|
||||
Releases: ''
|
||||
Releases: 'Vrystellings'
|
||||
This channel does not currently have any releases: 'Hierdie kanaal het nie tans
|
||||
enige vrystellings nie'
|
||||
About:
|
||||
|
@ -392,11 +392,16 @@ Settings:
|
||||
Player Settings:
|
||||
Player Settings: Player
|
||||
Play Next Video: Play Next Video
|
||||
Turn on Subtitles by Default: Turn on Subtitles by Default
|
||||
Turn on Subtitles by Default: Enable Subtitles by Default
|
||||
Autoplay Videos: Autoplay Videos
|
||||
Proxy Videos Through Invidious: Proxy Videos Through Invidious
|
||||
Autoplay Playlists: Autoplay Playlists
|
||||
Enable Theatre Mode by Default: Enable Theatre Mode by Default
|
||||
Enable Theatre Mode by Default: Enable Theater Mode by Default
|
||||
Default Viewing Mode:
|
||||
Default Viewing Mode: Default Viewing Mode
|
||||
Full Screen: Full Screen
|
||||
Picture in Picture: Picture in Picture
|
||||
External Player: External Player ({externalPlayerName})
|
||||
Scroll Volume Over Video Player: Scroll Volume Over Video Player
|
||||
Scroll Playback Rate Over Video Player: Scroll Playback Rate Over Video Player
|
||||
Skip by Scrolling Over Video Player: Skip by Scrolling Over Video Player
|
||||
|
Loading…
Reference in New Issue
Block a user