Add Parental Controls (#1456)

* start to implement some parental controls

* Hide share + Hide Unsubscribe

* Hide live streams

* fix hide live streams

* Add "parental-control-settings"

* Implement Hide Live Streams & Hide "Age Restricted"

* Hide live streams from Subscriptions + fix hide live streams from search

* enable safe search on showFamilyFriendlyOnly

* Move some settings from parental control to distraction free

* fix channel loading

* make parental control settings collapsible

* fix lint

* dont show age restricted on videos that are loading

* improve hide live videos

* code refactor

* grammar

* nvm im dumb

* use named placeholder for age restricted message

* improve readability

* change Hide Description to Hide Video Description

* update translated strings

* fix age restricted component

Co-authored-by: Preston <freetubeapp@protonmail.com>
Co-authored-by: peepopoggers <72892531+peepopoggers@users.noreply.github.com>
This commit is contained in:
ChunkyProgrammer 2022-06-21 02:14:15 -04:00 committed by GitHub
parent ca2799e999
commit 3321fa91e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 368 additions and 65 deletions

View File

@ -45,6 +45,18 @@ export default Vue.extend({
}, },
hideActiveSubscriptions: function () { hideActiveSubscriptions: function () {
return this.$store.getters.getHideActiveSubscriptions return this.$store.getters.getHideActiveSubscriptions
},
hideVideoDescription: function () {
return this.$store.getters.getHideVideoDescription
},
hideComments: function () {
return this.$store.getters.getHideComments
},
hideLiveStreams: function() {
return this.$store.getters.getHideLiveStreams
},
hideSharingActions: function() {
return this.$store.getters.getHideSharingActions
} }
}, },
methods: { methods: {
@ -68,7 +80,11 @@ export default Vue.extend({
'updateHideLiveChat', 'updateHideLiveChat',
'updateHideActiveSubscriptions', 'updateHideActiveSubscriptions',
'updatePlayNextVideo', 'updatePlayNextVideo',
'updateDefaultTheatreMode' 'updateDefaultTheatreMode',
'updateHideVideoDescription',
'updateHideComments',
'updateHideLiveStreams',
'updateHideSharingActions'
]) ])
} }
}) })

View File

@ -38,6 +38,18 @@
:default-value="hideActiveSubscriptions" :default-value="hideActiveSubscriptions"
@change="updateHideActiveSubscriptions" @change="updateHideActiveSubscriptions"
/> />
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Video Description')"
:compact="true"
:default-value="hideVideoDescription"
@change="updateHideVideoDescription"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Sharing Actions')"
:compact="true"
:default-value="hideSharingActions"
@change="updateHideSharingActions"
/>
</div> </div>
<div class="switchColumn"> <div class="switchColumn">
<ft-toggle-switch <ft-toggle-switch
@ -70,6 +82,18 @@
:default-value="hideLiveChat" :default-value="hideLiveChat"
@change="updateHideLiveChat" @change="updateHideLiveChat"
/> />
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Live Streams')"
:compact="true"
:default-value="hideLiveStreams"
@change="updateHideLiveStreams"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Comments')"
:compact="true"
:default-value="hideComments"
@change="updateHideComments"
/>
</div> </div>
</div> </div>
<br> <br>

View File

@ -0,0 +1,22 @@
import Vue from 'vue'
export default Vue.extend({
name: 'FtAgeRestricted',
props: {
contentTypeString: {
type: String,
required: true
}
},
computed: {
emoji: function () {
const emojis = ['😵', '😦', '🙁', '☹️', '😦', '🤫', '😕']
return emojis[Math.floor(Math.random() * emojis.length)]
},
restrictedMessage: function () {
const contentType = this.$t('Age Restricted.Type.' + this.contentTypeString)
return this.$t('Age Restricted.This $contentType is age restricted').replace('$contentType', contentType)
}
}
})

View File

@ -0,0 +1,14 @@
.ft-age-restricted
color: var(--primary-text-color)
h2
width: 100%
text-align: center
background-color: var(--card-bg-color)
padding: 10px 0
.frown
width: 100%
text-align: center
background-color: var(--card-bg-color)
font-size: 10em
padding: 20px 0
height: 100%

View File

@ -0,0 +1,15 @@
<template>
<div
class="ft-age-restricted"
>
<h2>
{{ restrictedMessage }}
</h2>
<div class="frown">
{{ emoji }}
</div>
</div>
</template>
<script src="./ft-age-restricted.js" />
<style scoped lang="sass" src="./ft-age-restricted.sass" />

View File

@ -33,6 +33,11 @@ export default Vue.extend({
visible: this.firstScreen visible: this.firstScreen
} }
}, },
computed: {
hideLiveStreams: function() {
return this.$store.getters.getHideLiveStreams
}
},
methods: { methods: {
onVisibilityChanged: function (visible) { onVisibilityChanged: function (visible) {
this.visible = visible this.visible = visible

View File

@ -1,5 +1,6 @@
<template> <template>
<div <div
v-if="data.type !== undefined && (data.type === 'video' ? ((!data.liveNow && (data.lengthSeconds != null)) || (!hideLiveStreams)) : true)"
v-observe-visibility="firstScreen ? false : { v-observe-visibility="firstScreen ? false : {
callback: onVisibilityChanged, callback: onVisibilityChanged,
once: true, once: true,

View File

@ -117,69 +117,76 @@ export default Vue.extend({
return (this.watchProgress / this.data.lengthSeconds) * 100 return (this.watchProgress / this.data.lengthSeconds) * 100
}, },
hideSharingActions: function() {
return this.$store.getters.getHideSharingActions
},
dropdownOptions: function () { dropdownOptions: function () {
const options = [] const options = []
options.push( options.push(
{ {
label: this.watched label: this.watched
? this.$t('Video.Remove From History') ? this.$t('Video.Remove From History')
: this.$t('Video.Mark As Watched'), : this.$t('Video.Mark As Watched'),
value: 'history' value: 'history'
},
{
type: 'divider'
},
{
label: this.$t('Video.Copy YouTube Link'),
value: 'copyYoutube'
},
{
label: this.$t('Video.Copy YouTube Embedded Player Link'),
value: 'copyYoutubeEmbed'
},
{
label: this.$t('Video.Copy Invidious Link'),
value: 'copyInvidious'
},
{
type: 'divider'
},
{
label: this.$t('Video.Open in YouTube'),
value: 'openYoutube'
},
{
label: this.$t('Video.Open YouTube Embedded Player'),
value: 'openYoutubeEmbed'
},
{
label: this.$t('Video.Open in Invidious'),
value: 'openInvidious'
},
{
type: 'divider'
},
{
label: this.$t('Video.Copy YouTube Channel Link'),
value: 'copyYoutubeChannel'
},
{
label: this.$t('Video.Copy Invidious Channel Link'),
value: 'copyInvidiousChannel'
},
{
type: 'divider'
},
{
label: this.$t('Video.Open Channel in YouTube'),
value: 'openYoutubeChannel'
},
{
label: this.$t('Video.Open Channel in Invidious'),
value: 'openInvidiousChannel'
} }
) )
if (!this.hideSharingActions) {
options.push(
{
type: 'divider'
},
{
label: this.$t('Video.Copy YouTube Link'),
value: 'copyYoutube'
},
{
label: this.$t('Video.Copy YouTube Embedded Player Link'),
value: 'copyYoutubeEmbed'
},
{
label: this.$t('Video.Copy Invidious Link'),
value: 'copyInvidious'
},
{
type: 'divider'
},
{
label: this.$t('Video.Open in YouTube'),
value: 'openYoutube'
},
{
label: this.$t('Video.Open YouTube Embedded Player'),
value: 'openYoutubeEmbed'
},
{
label: this.$t('Video.Open in Invidious'),
value: 'openInvidious'
},
{
type: 'divider'
},
{
label: this.$t('Video.Copy YouTube Channel Link'),
value: 'copyYoutubeChannel'
},
{
label: this.$t('Video.Copy Invidious Channel Link'),
value: 'copyInvidiousChannel'
},
{
type: 'divider'
},
{
label: this.$t('Video.Open Channel in YouTube'),
value: 'openYoutubeChannel'
},
{
label: this.$t('Video.Open Channel in Invidious'),
value: 'openInvidiousChannel'
}
)
}
return options return options
}, },
@ -203,6 +210,11 @@ export default Vue.extend({
return `${baseUrl}/vi/${this.id}/mqdefault.jpg` return `${baseUrl}/vi/${this.id}/mqdefault.jpg`
} }
}, },
hideLiveStreams: function() {
return this.$store.getters.getHideLiveStreams
},
hideVideoViews: function () { hideVideoViews: function () {
return this.$store.getters.getHideVideoViews return this.$store.getters.getHideVideoViews
}, },

View File

@ -0,0 +1,36 @@
import Vue from 'vue'
import { mapActions } from 'vuex'
import FtCard from '../ft-card/ft-card.vue'
import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue'
import FtButton from '../ft-button/ft-button.vue'
import FtSelect from '../ft-select/ft-select.vue'
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
export default Vue.extend({
name: 'ParentalControlSettings',
components: {
'ft-card': FtCard,
'ft-toggle-switch': FtToggleSwitch,
'ft-button': FtButton,
'ft-select': FtSelect,
'ft-flex-box': FtFlexBox
},
computed: {
hideSearchBar: function () {
return this.$store.getters.getHideSearchBar
},
hideUnsubscribeButton: function() {
return this.$store.getters.getHideUnsubscribeButton
},
showFamilyFriendlyOnly: function() {
return this.$store.getters.getShowFamilyFriendlyOnly
}
},
methods: {
...mapActions([
'updateHideSearchBar',
'updateHideUnsubscribeButton',
'updateShowFamilyFriendlyOnly'
])
}
})

View File

@ -0,0 +1 @@
@use "../../sass-partials/settings"

View File

@ -0,0 +1,37 @@
<template>
<details>
<summary>
<h3>
{{ $t("Settings.Parental Control Settings.Parental Control Settings") }}
</h3>
</summary>
<hr>
<div class="switchColumnGrid">
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Parental Control Settings.Hide Unsubscribe Button')"
:compact="true"
:default-value="hideUnsubscribeButton"
@change="updateHideUnsubscribeButton"
/>
<ft-toggle-switch
:label="$t('Settings.Parental Control Settings.Show Family Friendly Only')"
:compact="true"
:default-value="showFamilyFriendlyOnly"
@change="updateShowFamilyFriendlyOnly"
/>
</div>
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Parental Control Settings.Hide Search Bar')"
:compact="true"
:default-value="hideSearchBar"
@change="updateHideSearchBar"
/>
</div>
</div>
</details>
</template>
<script src="./parental-control-settings.js" />
<style scoped lang="sass" src="./parental-control-settings.sass" />

View File

@ -35,6 +35,10 @@ export default Vue.extend({
} }
}, },
computed: { computed: {
hideSharingActions: function() {
return this.$store.getters.getHideSharingActions
},
currentInvidiousInstance: function () { currentInvidiousInstance: function () {
return this.$store.getters.getCurrentInvidiousInstance return this.$store.getters.getCurrentInvidiousInstance
}, },

View File

@ -47,6 +47,7 @@
<br> <br>
<ft-list-dropdown <ft-list-dropdown
v-if="!hideSharingActions"
:title="$t('Playlist.Share Playlist.Share Playlist')" :title="$t('Playlist.Share Playlist.Share Playlist')"
:label-names="shareHeaders" :label-names="shareHeaders"
:label-values="shareValues" :label-values="shareValues"

View File

@ -32,6 +32,10 @@ export default Vue.extend({
return this.$store.getters.getUsingElectron return this.$store.getters.getUsingElectron
}, },
hideSearchBar: function () {
return this.$store.getters.getHideSearchBar
},
enableSearchSuggestions: function () { enableSearchSuggestions: function () {
return this.$store.getters.getEnableSearchSuggestions return this.$store.getters.getEnableSearchSuggestions
}, },

View File

@ -33,6 +33,7 @@
@keypress="historyForward" @keypress="historyForward"
/> />
<font-awesome-icon <font-awesome-icon
v-if="!hideSearchBar"
class="navSearchIcon navIcon" class="navSearchIcon navIcon"
icon="search" icon="search"
role="button" role="button"
@ -66,6 +67,7 @@
<div class="middle"> <div class="middle">
<div class="searchContainer"> <div class="searchContainer">
<ft-input <ft-input
v-if="!hideSearchBar"
ref="searchInput" ref="searchInput"
:placeholder="$t('Search / Go to URL')" :placeholder="$t('Search / Go to URL')"
class="searchInput" class="searchInput"
@ -78,6 +80,7 @@
@click="goToSearch" @click="goToSearch"
/> />
<font-awesome-icon <font-awesome-icon
v-if="!hideSearchBar"
class="navFilterIcon navIcon" class="navFilterIcon navIcon"
:class="{ filterChanged: searchFilterValueChanged }" :class="{ filterChanged: searchFilterValueChanged }"
icon="filter" icon="filter"
@ -88,6 +91,7 @@
/> />
</div> </div>
<ft-search-filters <ft-search-filters
v-if="!hideSearchBar"
v-show="showFilters" v-show="showFilters"
class="searchFilters" class="searchFilters"
@filterValueUpdated="handleSearchFilterValueChanged" @filterValueUpdated="handleSearchFilterValueChanged"

View File

@ -126,6 +126,14 @@ export default Vue.extend({
return this.$store.getters.getCurrentInvidiousInstance return this.$store.getters.getCurrentInvidiousInstance
}, },
hideSharingActions: function() {
return this.$store.getters.getHideSharingActions
},
hideUnsubscribeButton: function() {
return this.$store.getters.getHideUnsubscribeButton
},
currentLocale: function () { currentLocale: function () {
return this.$store.getters.getCurrentLocale return this.$store.getters.getCurrentLocale
}, },

View File

@ -27,6 +27,7 @@
{{ channelName }} {{ channelName }}
</div> </div>
<ft-button <ft-button
v-if="!hideUnsubscribeButton"
:label="subscribedText" :label="subscribedText"
class="subscribeButton" class="subscribeButton"
background-color="var(--primary-color)" background-color="var(--primary-color)"
@ -113,6 +114,7 @@
@click="handleFormatChange" @click="handleFormatChange"
/> />
<ft-share-button <ft-share-button
v-if="!hideSharingActions"
:id="id" :id="id"
:get-timestamp="getTimestamp" :get-timestamp="getTimestamp"
:playlist-id="playlistId" :playlist-id="playlistId"

View File

@ -192,11 +192,17 @@ const state = {
hideActiveSubscriptions: false, hideActiveSubscriptions: false,
hideChannelSubscriptions: false, hideChannelSubscriptions: false,
hideCommentLikes: false, hideCommentLikes: false,
hideComments: false,
hideVideoDescription: false,
hideLiveChat: false, hideLiveChat: false,
hideLiveStreams: false,
hidePlaylists: false, hidePlaylists: false,
hidePopularVideos: false, hidePopularVideos: false,
hideRecommendedVideos: false, hideRecommendedVideos: false,
hideSearchBar: false,
hideSharingActions: false,
hideTrendingVideos: false, hideTrendingVideos: false,
hideUnsubscribeButton: false,
hideVideoLikesAndDislikes: false, hideVideoLikesAndDislikes: false,
hideVideoViews: false, hideVideoViews: false,
hideWatchedSubs: false, hideWatchedSubs: false,
@ -213,6 +219,7 @@ const state = {
rememberHistory: true, rememberHistory: true,
removeVideoMetaFiles: true, removeVideoMetaFiles: true,
saveWatchedProgress: true, saveWatchedProgress: true,
showFamilyFriendlyOnly: false,
sponsorBlockShowSkippedToast: true, sponsorBlockShowSkippedToast: true,
sponsorBlockUrl: 'https://sponsor.ajay.app', sponsorBlockUrl: 'https://sponsor.ajay.app',
sponsorBlockSponsor: { sponsorBlockSponsor: {

View File

@ -8,6 +8,7 @@ import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
import FtChannelBubble from '../../components/ft-channel-bubble/ft-channel-bubble.vue' import FtChannelBubble from '../../components/ft-channel-bubble/ft-channel-bubble.vue'
import FtLoader from '../../components/ft-loader/ft-loader.vue' import FtLoader from '../../components/ft-loader/ft-loader.vue'
import FtElementList from '../../components/ft-element-list/ft-element-list.vue' import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
import FtAgeRestricted from '../../components/ft-age-restricted/ft-age-restricted.vue'
import ytch from 'yt-channel-info' import ytch from 'yt-channel-info'
import autolinker from 'autolinker' import autolinker from 'autolinker'
@ -23,7 +24,8 @@ export default Vue.extend({
'ft-flex-box': FtFlexBox, 'ft-flex-box': FtFlexBox,
'ft-channel-bubble': FtChannelBubble, 'ft-channel-bubble': FtChannelBubble,
'ft-loader': FtLoader, 'ft-loader': FtLoader,
'ft-element-list': FtElementList 'ft-element-list': FtElementList,
'ft-age-restricted': FtAgeRestricted
}, },
data: function () { data: function () {
return { return {
@ -50,6 +52,7 @@ export default Vue.extend({
searchResults: [], searchResults: [],
shownElementList: [], shownElementList: [],
apiUsed: '', apiUsed: '',
isFamilyFriendly: false,
errorMessage: '', errorMessage: '',
videoSelectValues: [ videoSelectValues: [
'newest', 'newest',
@ -75,6 +78,14 @@ export default Vue.extend({
return this.$store.getters.getBackendFallback return this.$store.getters.getBackendFallback
}, },
hideUnsubscribeButton: function() {
return this.$store.getters.getHideUnsubscribeButton
},
showFamilyFriendlyOnly: function() {
return this.$store.getters.getShowFamilyFriendlyOnly
},
currentInvidiousInstance: function () { currentInvidiousInstance: function () {
return this.$store.getters.getCurrentInvidiousInstance return this.$store.getters.getCurrentInvidiousInstance
}, },
@ -264,6 +275,7 @@ export default Vue.extend({
const channelThumbnailUrl = response.authorThumbnails[2].url const channelThumbnailUrl = response.authorThumbnails[2].url
this.id = channelId this.id = channelId
this.channelName = channelName this.channelName = channelName
this.isFamilyFriendly = response.isFamilyFriendly
document.title = `${this.channelName} - ${process.env.PRODUCT_NAME}` document.title = `${this.channelName} - ${process.env.PRODUCT_NAME}`
if (this.hideChannelSubscriptions || response.subscriberCount === 0) { if (this.hideChannelSubscriptions || response.subscriberCount === 0) {
this.subCount = null this.subCount = null
@ -383,6 +395,7 @@ export default Vue.extend({
this.channelName = channelName this.channelName = channelName
document.title = `${this.channelName} - ${process.env.PRODUCT_NAME}` document.title = `${this.channelName} - ${process.env.PRODUCT_NAME}`
this.id = channelId this.id = channelId
this.isFamilyFriendly = response.isFamilyFriendly
if (this.hideChannelSubscriptions) { if (this.hideChannelSubscriptions) {
this.subCount = null this.subCount = null
} else { } else {

View File

@ -7,7 +7,7 @@
:fullscreen="true" :fullscreen="true"
/> />
<ft-card <ft-card
v-else v-else-if="(isFamilyFriendly || !showFamilyFriendlyOnly)"
class="card channelDetails" class="card channelDetails"
> >
<div <div
@ -52,6 +52,7 @@
</div> </div>
<ft-button <ft-button
v-if="!hideUnsubscribeButton"
:label="subscribedText" :label="subscribedText"
background-color="var(--primary-color)" background-color="var(--primary-color)"
text-color="var(--text-with-main-color)" text-color="var(--text-with-main-color)"
@ -113,7 +114,7 @@
</div> </div>
</ft-card> </ft-card>
<ft-card <ft-card
v-if="!isLoading && !errorMessage" v-if="!isLoading && !errorMessage && (isFamilyFriendly || !showFamilyFriendlyOnly)"
class="card" class="card"
> >
<div <div
@ -203,6 +204,11 @@
{{ errorMessage }} {{ errorMessage }}
</p> </p>
</ft-card> </ft-card>
<ft-age-restricted
v-else-if="!isLoading && (!isFamilyFriendly && showFamilyFriendlyOnly)"
class="ageRestricted"
:content-type-string="'Channel'"
/>
</div> </div>
</template> </template>

View File

@ -37,6 +37,13 @@ export default Vue.extend({
backendFallback: function () { backendFallback: function () {
return this.$store.getters.getBackendFallback return this.$store.getters.getBackendFallback
},
hideLiveStreams: function() {
return this.$store.getters.getHideLiveStreams
},
showFamilyFriendlyOnly: function() {
return this.$store.getters.getShowFamilyFriendlyOnly
} }
}, },
watch: { watch: {
@ -94,6 +101,7 @@ export default Vue.extend({
if (sameSearch.length > 0) { if (sameSearch.length > 0) {
console.log(sameSearch) console.log(sameSearch)
// Replacing the data right away causes a strange error where the data // Replacing the data right away causes a strange error where the data
// Shown is mixed from 2 different search results. So we'll wait a moment // Shown is mixed from 2 different search results. So we'll wait a moment
// Before showing the results. // Before showing the results.
@ -118,6 +126,8 @@ export default Vue.extend({
payload.options.pages = 1 payload.options.pages = 1
} }
payload.options.safeSearch = this.showFamilyFriendlyOnly
this.ytSearch(payload).then((result) => { this.ytSearch(payload).then((result) => {
console.log(result) console.log(result)
if (!result) { if (!result) {

View File

@ -12,6 +12,7 @@ import DataSettings from '../../components/data-settings/data-settings.vue'
import DistractionSettings from '../../components/distraction-settings/distraction-settings.vue' import DistractionSettings from '../../components/distraction-settings/distraction-settings.vue'
import ProxySettings from '../../components/proxy-settings/proxy-settings.vue' import ProxySettings from '../../components/proxy-settings/proxy-settings.vue'
import SponsorBlockSettings from '../../components/sponsor-block-settings/sponsor-block-settings.vue' import SponsorBlockSettings from '../../components/sponsor-block-settings/sponsor-block-settings.vue'
import ParentControlSettings from '../../components/parental-control-settings/parental-control-settings.vue'
export default Vue.extend({ export default Vue.extend({
name: 'Settings', name: 'Settings',
@ -28,7 +29,8 @@ export default Vue.extend({
'distraction-settings': DistractionSettings, 'distraction-settings': DistractionSettings,
'proxy-settings': ProxySettings, 'proxy-settings': ProxySettings,
'sponsor-block-settings': SponsorBlockSettings, 'sponsor-block-settings': SponsorBlockSettings,
'download-settings': DownloadSettings 'download-settings': DownloadSettings,
'parental-control-settings': ParentControlSettings
}, },
computed: { computed: {
usingElectron: function () { usingElectron: function () {

View File

@ -20,6 +20,8 @@
<hr> <hr>
<download-settings v-if="usingElectron" /> <download-settings v-if="usingElectron" />
<hr> <hr>
<parental-control-settings />
<hr>
<sponsor-block-settings /> <sponsor-block-settings />
</div> </div>
</template> </template>

View File

@ -86,6 +86,10 @@ export default Vue.extend({
activeSubscriptionList: function () { activeSubscriptionList: function () {
return this.activeProfile.subscriptions return this.activeProfile.subscriptions
},
hideLiveStreams: function() {
return this.$store.getters.getHideLiveStreams
} }
}, },
watch: { watch: {
@ -178,7 +182,11 @@ export default Vue.extend({
videoList = await Promise.all(videoList.sort((a, b) => { videoList = await Promise.all(videoList.sort((a, b) => {
return b.publishedDate - a.publishedDate return b.publishedDate - a.publishedDate
})) }))
if (this.hideLiveStreams) {
videoList = videoList.filter(item => {
return (!item.liveNow && !item.isUpcoming)
})
}
const profileSubscriptions = { const profileSubscriptions = {
activeProfile: this.activeProfile._id, activeProfile: this.activeProfile._id,
videoList: videoList, videoList: videoList,

View File

@ -13,6 +13,7 @@ import WatchVideoComments from '../../components/watch-video-comments/watch-vide
import WatchVideoLiveChat from '../../components/watch-video-live-chat/watch-video-live-chat.vue' import WatchVideoLiveChat from '../../components/watch-video-live-chat/watch-video-live-chat.vue'
import WatchVideoPlaylist from '../../components/watch-video-playlist/watch-video-playlist.vue' import WatchVideoPlaylist from '../../components/watch-video-playlist/watch-video-playlist.vue'
import WatchVideoRecommendations from '../../components/watch-video-recommendations/watch-video-recommendations.vue' import WatchVideoRecommendations from '../../components/watch-video-recommendations/watch-video-recommendations.vue'
import FtAgeRestricted from '../../components/ft-age-restricted/ft-age-restricted.vue'
export default Vue.extend({ export default Vue.extend({
name: 'Watch', name: 'Watch',
@ -26,7 +27,8 @@ export default Vue.extend({
'watch-video-comments': WatchVideoComments, 'watch-video-comments': WatchVideoComments,
'watch-video-live-chat': WatchVideoLiveChat, 'watch-video-live-chat': WatchVideoLiveChat,
'watch-video-playlist': WatchVideoPlaylist, 'watch-video-playlist': WatchVideoPlaylist,
'watch-video-recommendations': WatchVideoRecommendations 'watch-video-recommendations': WatchVideoRecommendations,
'ft-age-restricted': FtAgeRestricted
}, },
beforeRouteLeave: function (to, from, next) { beforeRouteLeave: function (to, from, next) {
this.handleRouteChange(this.videoId) this.handleRouteChange(this.videoId)
@ -42,6 +44,7 @@ export default Vue.extend({
showLegacyPlayer: false, showLegacyPlayer: false,
showYouTubeNoCookieEmbed: false, showYouTubeNoCookieEmbed: false,
hidePlayer: false, hidePlayer: false,
isFamilyFriendly: false,
isLive: false, isLive: false,
isLiveContent: false, isLiveContent: false,
isUpcoming: false, isUpcoming: false,
@ -134,6 +137,15 @@ export default Vue.extend({
hideLiveChat: function () { hideLiveChat: function () {
return this.$store.getters.getHideLiveChat return this.$store.getters.getHideLiveChat
}, },
hideComments: function () {
return this.$store.getters.getHideComments
},
hideVideoDescription: function () {
return this.$store.getters.getHideVideoDescription
},
showFamilyFriendlyOnly: function() {
return this.$store.getters.getShowFamilyFriendlyOnly
},
youtubeNoCookieEmbeddedFrame: function () { youtubeNoCookieEmbeddedFrame: function () {
return `<iframe width='560' height='315' src='https://www.youtube-nocookie.com/embed/${this.videoId}?rel=0' frameborder='0' allow='autoplay; encrypted-media' allowfullscreen></iframe>` return `<iframe width='560' height='315' src='https://www.youtube-nocookie.com/embed/${this.videoId}?rel=0' frameborder='0' allow='autoplay; encrypted-media' allowfullscreen></iframe>`
@ -300,6 +312,7 @@ export default Vue.extend({
break break
} }
this.isFamilyFriendly = result.videoDetails.isFamilySafe
this.recommendedVideos = result.related_videos.map((video) => { this.recommendedVideos = result.related_videos.map((video) => {
video.videoId = video.id video.videoId = video.id
video.authorId = video.author.id video.authorId = video.author.id
@ -586,6 +599,7 @@ export default Vue.extend({
return format return format
}) })
this.isLive = result.liveNow this.isLive = result.liveNow
this.isFamilyFriendly = result.isFamilyFriendly
this.captionHybridList = result.captions.map(caption => { this.captionHybridList = result.captions.map(caption => {
caption.url = this.currentInvidiousInstance + caption.url caption.url = this.currentInvidiousInstance + caption.url
caption.type = '' caption.type = ''

View File

@ -6,6 +6,12 @@
=single-column-template =single-column-template
grid-template: "video" auto "info" auto "sidebar" auto / auto grid-template: "video" auto "info" auto "sidebar" auto / auto
.ageRestricted
max-width: calc(80vh * 1.78)
display: inline-block
+single-column-template
@media only screen and (min-width: 901px)
width: 300%
.videoLayout .videoLayout
display: grid display: grid

View File

@ -11,7 +11,10 @@
v-if="isLoading" v-if="isLoading"
:fullscreen="true" :fullscreen="true"
/> />
<div class="videoArea"> <div
v-if="(isFamilyFriendly || !showFamilyFriendlyOnly)"
class="videoArea"
>
<div class="videoAreaMargin"> <div class="videoAreaMargin">
<ft-video-player <ft-video-player
v-if="!isLoading && !hidePlayer && !isUpcoming" v-if="!isLoading && !hidePlayer && !isUpcoming"
@ -64,7 +67,15 @@
</div> </div>
</div> </div>
</div> </div>
<div class="infoArea"> <ft-age-restricted
v-if="(!isLoading && !isFamilyFriendly && showFamilyFriendlyOnly)"
class="ageRestricted"
:content-type-string="'Video'"
/>
<div
v-if="(isFamilyFriendly || !showFamilyFriendlyOnly)"
class="infoArea"
>
<watch-video-info <watch-video-info
v-if="!isLoading" v-if="!isLoading"
:id="videoId" :id="videoId"
@ -96,7 +107,7 @@
@pause-player="pausePlayer" @pause-player="pausePlayer"
/> />
<watch-video-description <watch-video-description
v-if="!isLoading" v-if="!isLoading && !hideVideoDescription"
:published="videoPublished" :published="videoPublished"
:description="videoDescription" :description="videoDescription"
:description-html="videoDescriptionHtml" :description-html="videoDescriptionHtml"
@ -105,7 +116,7 @@
@timestamp-event="changeTimestamp" @timestamp-event="changeTimestamp"
/> />
<watch-video-comments <watch-video-comments
v-if="!isLoading && !isLive" v-if="!isLoading && !isLive && !hideComments"
:id="videoId" :id="videoId"
class="watchVideo" class="watchVideo"
:class="{ theatreWatchVideo: useTheatreMode }" :class="{ theatreWatchVideo: useTheatreMode }"
@ -114,7 +125,10 @@
@timestamp-event="changeTimestamp" @timestamp-event="changeTimestamp"
/> />
</div> </div>
<div class="sidebarArea"> <div
v-if="(isFamilyFriendly || !showFamilyFriendlyOnly)"
class="sidebarArea"
>
<watch-video-live-chat <watch-video-live-chat
v-if="!isLoading && isLive" v-if="!isLoading && isLive"
:video-id="videoId" :video-id="videoId"

View File

@ -289,6 +289,10 @@ Settings:
Hide Playlists: Hide Playlists Hide Playlists: Hide Playlists
Hide Live Chat: Hide Live Chat Hide Live Chat: Hide Live Chat
Hide Active Subscriptions: Hide Active Subscriptions Hide Active Subscriptions: Hide Active Subscriptions
Hide Video Description: Hide Video Description
Hide Comments: Hide Comments
Hide Live Streams: Hide Live Streams
Hide Sharing Actions: Hide Sharing Actions
Data Settings: Data Settings:
Data Settings: Data Settings Data Settings: Data Settings
Select Import Type: Select Import Type Select Import Type: Select Import Type
@ -361,6 +365,11 @@ Settings:
Prompt To Skip: Prompt To Skip Prompt To Skip: Prompt To Skip
Do Nothing: Do Nothing Do Nothing: Do Nothing
Category Color: Category Color Category Color: Category Color
Parental Control Settings:
Parental Control Settings: Parental Control Settings
Hide Unsubscribe Button: Hide Unsubscribe Button
Show Family Friendly Only: Show Family Friendly Only
Hide Search Bar: Hide Search Bar
Download Settings: Download Settings:
Download Settings: Download Settings Download Settings: Download Settings
Ask Download Path: Ask for download path Ask Download Path: Ask for download path
@ -756,6 +765,12 @@ Default Invidious instance has been set to $: Default Invidious instance has bee
Default Invidious instance has been cleared: Default Invidious instance has been cleared Default Invidious instance has been cleared: Default Invidious instance has been cleared
'The playlist has ended. Enable loop to continue playing': 'The playlist has ended. Enable 'The playlist has ended. Enable loop to continue playing': 'The playlist has ended. Enable
loop to continue playing' loop to continue playing'
Age Restricted:
# $contentType is replaced with video or channel
This $contentType is age restricted: This $ is age restricted
Type:
Channel: Channel
Video: Video
External link opening has been disabled in the general settings: 'External link opening has been disabled in the general settings' External link opening has been disabled in the general settings: 'External link opening has been disabled in the general settings'
Downloading has completed: '"$" has finished downloading' Downloading has completed: '"$" has finished downloading'
Starting download: 'Starting download of "$"' Starting download: 'Starting download of "$"'