mirror of https://github.com/FreeTubeApp/FreeTube
Merge branch 'development' into piped-support
This commit is contained in:
commit
36105af0f9
|
@ -90,9 +90,9 @@
|
|||
"copy-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.8.1",
|
||||
"css-minimizer-webpack-plugin": "^5.0.1",
|
||||
"electron": "^22.3.14",
|
||||
"electron": "^22.3.15",
|
||||
"electron-builder": "^23.6.0",
|
||||
"eslint": "^8.43.0",
|
||||
"eslint": "^8.44.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
|
@ -124,7 +124,7 @@
|
|||
"vue-devtools": "^5.1.4",
|
||||
"vue-eslint-parser": "^9.3.1",
|
||||
"vue-loader": "^15.10.0",
|
||||
"webpack": "^5.88.0",
|
||||
"webpack": "^5.88.1",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1",
|
||||
"yaml-eslint-parser": "^1.2.2"
|
||||
|
|
|
@ -11,6 +11,8 @@ import baseHandlers from '../datastores/handlers/base'
|
|||
import { extractExpiryTimestamp, ImageCache } from './ImageCache'
|
||||
import { existsSync } from 'fs'
|
||||
|
||||
import packageDetails from '../../package.json'
|
||||
|
||||
if (process.argv.includes('--version')) {
|
||||
app.exit()
|
||||
} else {
|
||||
|
@ -263,6 +265,12 @@ function runApp() {
|
|||
})
|
||||
}
|
||||
|
||||
const fixedUserAgent = session.defaultSession.getUserAgent()
|
||||
.split(' ')
|
||||
.filter(part => !part.includes('Electron') && !part.includes(packageDetails.productName))
|
||||
.join(' ')
|
||||
session.defaultSession.setUserAgent(fixedUserAgent)
|
||||
|
||||
// Set CONSENT cookie on reasonable domains
|
||||
const consentCookieDomains = [
|
||||
'https://www.youtube.com',
|
||||
|
@ -279,10 +287,19 @@ function runApp() {
|
|||
|
||||
// make InnerTube requests work with the fetch function
|
||||
// InnerTube rejects requests if the referer isn't YouTube or empty
|
||||
const innertubeRequestFilter = { urls: ['https://www.youtube.com/youtubei/*'] }
|
||||
const innertubeAndMediaRequestFilter = { urls: ['https://www.youtube.com/youtubei/*', 'https://*.googlevideo.com/videoplayback?*'] }
|
||||
|
||||
session.defaultSession.webRequest.onBeforeSendHeaders(innertubeAndMediaRequestFilter, ({ requestHeaders, url }, callback) => {
|
||||
requestHeaders.Referer = 'https://www.youtube.com/'
|
||||
requestHeaders.Origin = 'https://www.youtube.com'
|
||||
|
||||
if (url.startsWith('https://www.youtube.com/youtubei/')) {
|
||||
requestHeaders['Sec-Fetch-Site'] = 'same-origin'
|
||||
} else {
|
||||
// YouTube doesn't send the Content-Type header for the media requests, so we shouldn't either
|
||||
delete requestHeaders['Content-Type']
|
||||
}
|
||||
|
||||
session.defaultSession.webRequest.onBeforeSendHeaders(innertubeRequestFilter, ({ requestHeaders }, callback) => {
|
||||
requestHeaders.referer = 'https://www.youtube.com'
|
||||
// eslint-disable-next-line n/no-callback-literal
|
||||
callback({ requestHeaders })
|
||||
})
|
||||
|
|
|
@ -48,13 +48,7 @@ export default defineComponent({
|
|||
},
|
||||
hideUpcomingPremieres: function () {
|
||||
return this.$store.getters.getHideUpcomingPremieres
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onVisibilityChanged: function (visible) {
|
||||
this.visible = visible
|
||||
},
|
||||
|
||||
/**
|
||||
* Show or Hide results in the list
|
||||
*
|
||||
|
@ -70,10 +64,9 @@ export default defineComponent({
|
|||
// hide livestreams
|
||||
return false
|
||||
}
|
||||
|
||||
if (this.hideUpcomingPremieres &&
|
||||
// Observed for premieres in Local API Channels.
|
||||
(data.durationText === 'PREMIERE' ||
|
||||
(data.premiereDate != null ||
|
||||
// viewCount is our only method of detecting premieres in RSS
|
||||
// data without sending an additional request.
|
||||
// If we ever get a better flag, use it here instead.
|
||||
|
@ -98,6 +91,11 @@ export default defineComponent({
|
|||
}
|
||||
return true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onVisibilityChanged: function (visible) {
|
||||
this.visible = visible
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
isNullOrEmpty
|
||||
} from '../../helpers/utils'
|
||||
import { getPipedUrlInfo } from '../../helpers/api/piped'
|
||||
import { deArrowData } from '../../helpers/sponsorblock'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FtListVideo',
|
||||
|
@ -347,6 +348,14 @@ export default defineComponent({
|
|||
currentLocale: function () {
|
||||
return this.$i18n.locale.replace('_', '-')
|
||||
},
|
||||
|
||||
useDeArrowTitles: function () {
|
||||
return this.$store.getters.getUseDeArrowTitles
|
||||
},
|
||||
|
||||
deArrowCache: function () {
|
||||
return this.$store.getters.getDeArrowCache(this.id)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
historyIndex() {
|
||||
|
@ -358,6 +367,25 @@ export default defineComponent({
|
|||
this.checkIfWatched()
|
||||
},
|
||||
methods: {
|
||||
getDeArrowDataEntry: async function() {
|
||||
// Read from local cache or remote
|
||||
// Write to cache if read from remote
|
||||
if (!this.useDeArrowTitles) { return null }
|
||||
|
||||
if (this.deArrowCache) { return this.deArrowCache }
|
||||
|
||||
const videoId = this.id
|
||||
const data = await deArrowData(this.id)
|
||||
const cacheData = { videoId, title: null }
|
||||
if (Array.isArray(data?.titles) && data.titles.length > 0 && (data.titles[0].locked || data.titles[0].votes > 0)) {
|
||||
cacheData.title = data.titles[0].title
|
||||
}
|
||||
|
||||
// Save data to cache whether data available or not to prevent duplicate requests
|
||||
this.$store.commit('addVideoToDeArrowCache', cacheData)
|
||||
return cacheData
|
||||
},
|
||||
|
||||
handleExternalPlayer: function () {
|
||||
this.$emit('pause-player')
|
||||
|
||||
|
@ -428,9 +456,9 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
|
||||
parseVideoData: function () {
|
||||
parseVideoData: async function () {
|
||||
this.id = this.data.videoId
|
||||
this.title = this.data.title
|
||||
this.title = (await this.getDeArrowDataEntry())?.title ?? this.data.title
|
||||
// this.thumbnail = this.data.videoThumbnails[4].url
|
||||
|
||||
this.channelName = this.data.author ?? null
|
||||
|
|
|
@ -110,12 +110,7 @@ export default defineComponent({
|
|||
}
|
||||
})
|
||||
|
||||
this.updateAllSubscriptionsList([])
|
||||
this.updateProfileSubscriptions({
|
||||
activeProfile: MAIN_PROFILE_ID,
|
||||
videoList: [],
|
||||
errorChannels: []
|
||||
})
|
||||
this.clearSubscriptionsCache()
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -129,8 +124,7 @@ export default defineComponent({
|
|||
'updateProfile',
|
||||
'removeProfile',
|
||||
'updateActiveProfile',
|
||||
'updateAllSubscriptionsList',
|
||||
'updateProfileSubscriptions'
|
||||
'clearSubscriptionsCache',
|
||||
])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -38,6 +38,10 @@ export default defineComponent({
|
|||
},
|
||||
sponsorBlockShowSkippedToast: function () {
|
||||
return this.$store.getters.getSponsorBlockShowSkippedToast
|
||||
},
|
||||
|
||||
useDeArrowTitles: function () {
|
||||
return this.$store.getters.getUseDeArrowTitles
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -45,6 +49,10 @@ export default defineComponent({
|
|||
this.updateUseSponsorBlock(value)
|
||||
},
|
||||
|
||||
handleUpdateUseDeArrowTitles: function (value) {
|
||||
this.updateUseDeArrowTitles(value)
|
||||
},
|
||||
|
||||
handleUpdateSponsorBlockUrl: function (value) {
|
||||
const sponsorBlockUrlWithoutTrailingSlash = value.replace(/\/$/, '')
|
||||
const sponsorBlockUrlWithoutApiSuffix = sponsorBlockUrlWithoutTrailingSlash.replace(/\/api$/, '')
|
||||
|
@ -58,7 +66,8 @@ export default defineComponent({
|
|||
...mapActions([
|
||||
'updateUseSponsorBlock',
|
||||
'updateSponsorBlockUrl',
|
||||
'updateSponsorBlockShowSkippedToast'
|
||||
'updateSponsorBlockShowSkippedToast',
|
||||
'updateUseDeArrowTitles'
|
||||
])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -8,11 +8,20 @@
|
|||
:default-value="useSponsorBlock"
|
||||
@change="handleUpdateSponsorBlock"
|
||||
/>
|
||||
<ft-toggle-switch
|
||||
:label="$t('Settings.SponsorBlock Settings.UseDeArrowTitles')"
|
||||
:default-value="useDeArrowTitles"
|
||||
:tooltip="$t('Tooltips.SponsorBlock Settings.UseDeArrowTitles')"
|
||||
@change="handleUpdateUseDeArrowTitles"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<div
|
||||
v-if="useSponsorBlock"
|
||||
v-if="useSponsorBlock || useDeArrowTitles"
|
||||
>
|
||||
<ft-flex-box class="settingsFlexStart500px">
|
||||
<ft-flex-box
|
||||
v-if="useSponsorBlock"
|
||||
class="settingsFlexStart500px"
|
||||
>
|
||||
<ft-toggle-switch
|
||||
:label="$t('Settings.SponsorBlock Settings.Notify when sponsor segment is skipped')"
|
||||
:default-value="sponsorBlockShowSkippedToast"
|
||||
|
@ -28,7 +37,9 @@
|
|||
@input="handleUpdateSponsorBlockUrl"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<ft-flex-box>
|
||||
<ft-flex-box
|
||||
v-if="useSponsorBlock"
|
||||
>
|
||||
<ft-sponsor-block-category
|
||||
v-for="category in categories"
|
||||
:key="category"
|
||||
|
|
|
@ -158,10 +158,16 @@ export async function getLocalVideoInfo(id, attemptBypass = false) {
|
|||
const androidInnertube = await createInnertube({ clientType: ClientType.ANDROID, generateSessionLocally: false })
|
||||
const androidInfo = await androidInnertube.getBasicInfo(id, 'ANDROID')
|
||||
|
||||
if (androidInfo.playability_status.status === 'OK') {
|
||||
info.streaming_data = androidInfo.streaming_data
|
||||
// Sometimes when YouTube detects a third party client or has applied an IP-ratelimit,
|
||||
// they replace the response with a different video id
|
||||
// https://github.com/TeamNewPipe/NewPipe/issues/8713
|
||||
// https://github.com/TeamPiped/Piped/issues/2487
|
||||
if (androidInfo.basic_info.id !== id) {
|
||||
console.error(`Failed to fetch android formats. Wrong video ID in response: ${androidInfo.basic_info.id}, expected: ${id}`)
|
||||
} else if (androidInfo.playability_status.status !== 'OK') {
|
||||
console.error('Failed to fetch android formats', JSON.stringify(androidInfo.playability_status))
|
||||
} else {
|
||||
console.error('Failed to fetch android formats', JSON.parse(JSON.stringify(androidInfo.playability_status)))
|
||||
info.streaming_data = androidInfo.streaming_data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch android formats')
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
import store from '../store/index'
|
||||
|
||||
export async function sponsorBlockSkipSegments(videoId, categories) {
|
||||
async function getVideoHash(videoId) {
|
||||
const videoIdBuffer = new TextEncoder().encode(videoId)
|
||||
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', videoIdBuffer)
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
||||
|
||||
const videoIdHashPrefix = hashArray
|
||||
return hashArray
|
||||
.map(byte => byte.toString(16).padStart(2, '0'))
|
||||
.slice(0, 4)
|
||||
.join('')
|
||||
|
||||
}
|
||||
export async function sponsorBlockSkipSegments(videoId, categories) {
|
||||
const videoIdHashPrefix = await getVideoHash(videoId)
|
||||
const requestUrl = `${store.getters.getSponsorBlockUrl}/api/skipSegments/${videoIdHashPrefix}?categories=${JSON.stringify(categories)}`
|
||||
|
||||
try {
|
||||
|
@ -30,3 +31,23 @@ export async function sponsorBlockSkipSegments(videoId, categories) {
|
|||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export async function deArrowData(videoId) {
|
||||
const videoIdHashPrefix = (await getVideoHash(videoId)).substring(0, 4)
|
||||
const requestUrl = `${store.getters.getSponsorBlockUrl}/api/branding/${videoIdHashPrefix}`
|
||||
|
||||
try {
|
||||
const response = await fetch(requestUrl)
|
||||
|
||||
// 404 means that there are no segments registered for the video
|
||||
if (response.status === 404) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const json = await response.json()
|
||||
return json[videoId] ?? undefined
|
||||
} catch (error) {
|
||||
console.error('failed to fetch DeArrow data', requestUrl, error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
|
|
@ -287,6 +287,7 @@ const state = {
|
|||
settingsPassword: '',
|
||||
allowDashAv1Formats: false,
|
||||
commentAutoLoadEnabled: false,
|
||||
useDeArrowTitles: false,
|
||||
}
|
||||
|
||||
const stateWithSideEffects = {
|
||||
|
|
|
@ -1,39 +1,41 @@
|
|||
import { MAIN_PROFILE_ID } from '../../../constants'
|
||||
const defaultCacheEntryValueForForOneChannel = {
|
||||
videos: null,
|
||||
}
|
||||
|
||||
function deepCopy(obj) {
|
||||
return JSON.parse(JSON.stringify(obj))
|
||||
}
|
||||
|
||||
const state = {
|
||||
allSubscriptionsList: [],
|
||||
profileSubscriptions: {
|
||||
activeProfile: MAIN_PROFILE_ID,
|
||||
videoList: [],
|
||||
errorChannels: []
|
||||
}
|
||||
subscriptionsCachePerChannel: {},
|
||||
}
|
||||
|
||||
const getters = {
|
||||
getAllSubscriptionsList: () => {
|
||||
return state.allSubscriptionsList
|
||||
getSubscriptionsCacheEntriesForOneChannel: (state) => (channelId) => {
|
||||
return state.subscriptionsCachePerChannel[channelId]
|
||||
},
|
||||
getProfileSubscriptions: () => {
|
||||
return state.profileSubscriptions
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
updateAllSubscriptionsList ({ commit }, subscriptions) {
|
||||
commit('setAllSubscriptionsList', subscriptions)
|
||||
clearSubscriptionsCache: ({ commit }) => {
|
||||
commit('clearSubscriptionsCachePerChannel')
|
||||
},
|
||||
|
||||
updateSubscriptionsCacheForOneChannel: ({ commit }, payload) => {
|
||||
commit('updateSubscriptionsCacheForOneChannel', payload)
|
||||
},
|
||||
updateProfileSubscriptions ({ commit }, subscriptions) {
|
||||
commit('setProfileSubscriptions', subscriptions)
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
setAllSubscriptionsList (state, allSubscriptionsList) {
|
||||
state.allSubscriptionsList = allSubscriptionsList
|
||||
updateSubscriptionsCacheForOneChannel(state, { channelId, videos }) {
|
||||
const existingObject = state.subscriptionsCachePerChannel[channelId]
|
||||
const newObject = existingObject != null ? existingObject : deepCopy(defaultCacheEntryValueForForOneChannel)
|
||||
if (videos != null) { newObject.videos = videos }
|
||||
state.subscriptionsCachePerChannel[channelId] = newObject
|
||||
},
|
||||
clearSubscriptionsCachePerChannel(state) {
|
||||
state.subscriptionsCachePerChannel = {}
|
||||
},
|
||||
setProfileSubscriptions (state, profileSubscriptions) {
|
||||
state.profileSubscriptions = profileSubscriptions
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
@ -27,6 +27,7 @@ const state = {
|
|||
movies: null
|
||||
},
|
||||
cachedPlaylist: null,
|
||||
deArrowCache: {},
|
||||
showProgressBar: false,
|
||||
progressBarPercentage: 0,
|
||||
regionNames: [],
|
||||
|
@ -57,6 +58,10 @@ const getters = {
|
|||
return state.sessionSearchHistory
|
||||
},
|
||||
|
||||
getDeArrowCache: (state) => (videoId) => {
|
||||
return state.deArrowCache[videoId]
|
||||
},
|
||||
|
||||
getPopularCache () {
|
||||
return state.popularCache
|
||||
},
|
||||
|
@ -565,21 +570,14 @@ const actions = {
|
|||
showExternalPlayerUnsupportedActionToast(externalPlayer, 'looping playlists')
|
||||
}
|
||||
}
|
||||
if (cmdArgs.supportsYtdlProtocol) {
|
||||
args.push(`${cmdArgs.playlistUrl}ytdl://${payload.playlistId}`)
|
||||
} else {
|
||||
args.push(`${cmdArgs.playlistUrl}https://youtube.com/playlist?list=${payload.playlistId}`)
|
||||
}
|
||||
|
||||
args.push(`${cmdArgs.playlistUrl}https://youtube.com/playlist?list=${payload.playlistId}`)
|
||||
} else {
|
||||
if (payload.playlistId != null && payload.playlistId !== '' && !ignoreWarnings) {
|
||||
showExternalPlayerUnsupportedActionToast(externalPlayer, 'opening playlists')
|
||||
}
|
||||
if (payload.videoId != null) {
|
||||
if (cmdArgs.supportsYtdlProtocol) {
|
||||
args.push(`${cmdArgs.videoUrl}ytdl://${payload.videoId}`)
|
||||
} else {
|
||||
args.push(`${cmdArgs.videoUrl}https://www.youtube.com/watch?v=${payload.videoId}`)
|
||||
}
|
||||
args.push(`${cmdArgs.videoUrl}https://www.youtube.com/watch?v=${payload.videoId}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -611,6 +609,18 @@ const mutations = {
|
|||
state.sessionSearchHistory = history
|
||||
},
|
||||
|
||||
setDeArrowCache (state, cache) {
|
||||
state.deArrowCache = cache
|
||||
},
|
||||
|
||||
addVideoToDeArrowCache (state, payload) {
|
||||
const sameVideo = state.deArrowCache[payload.videoId]
|
||||
|
||||
if (!sameVideo) {
|
||||
state.deArrowCache[payload.videoId] = payload
|
||||
}
|
||||
},
|
||||
|
||||
addToSessionSearchHistory (state, payload) {
|
||||
const sameSearch = state.sessionSearchHistory.findIndex((search) => {
|
||||
return search.query === payload.query && searchFiltersMatch(payload.searchSettings, search.searchSettings)
|
||||
|
|
|
@ -262,7 +262,6 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
this.id = this.$route.params.id
|
||||
let currentTab = this.$route.params.currentTab ?? 'videos'
|
||||
this.searchPage = 2
|
||||
this.relatedChannels = []
|
||||
this.latestVideos = []
|
||||
|
@ -290,23 +289,7 @@ export default defineComponent({
|
|||
this.showLiveSortBy = true
|
||||
this.showPlaylistSortBy = true
|
||||
|
||||
if (this.hideChannelShorts && currentTab === 'shorts') {
|
||||
currentTab = 'videos'
|
||||
}
|
||||
|
||||
if (this.hideLiveStreams && currentTab === 'live') {
|
||||
currentTab = 'videos'
|
||||
}
|
||||
|
||||
if (this.hideChannelPlaylists && currentTab === 'playlists') {
|
||||
currentTab = 'videos'
|
||||
}
|
||||
|
||||
if (this.hideChannelCommunity && currentTab === 'community') {
|
||||
currentTab = 'videos'
|
||||
}
|
||||
|
||||
this.currentTab = currentTab
|
||||
this.currentTab = this.currentOrFirstTab(this.$route.params.currentTab)
|
||||
|
||||
if (this.id === '@@@') {
|
||||
this.showShareMenu = false
|
||||
|
@ -395,25 +378,7 @@ export default defineComponent({
|
|||
|
||||
this.id = this.$route.params.id
|
||||
|
||||
let currentTab = this.$route.params.currentTab ?? 'videos'
|
||||
|
||||
if (this.hideChannelShorts && currentTab === 'shorts') {
|
||||
currentTab = 'videos'
|
||||
}
|
||||
|
||||
if (this.hideLiveStreams && currentTab === 'live') {
|
||||
currentTab = 'videos'
|
||||
}
|
||||
|
||||
if (this.hideChannelPlaylists && currentTab === 'playlists') {
|
||||
currentTab = 'videos'
|
||||
}
|
||||
|
||||
if (this.hideChannelCommunity && currentTab === 'community') {
|
||||
currentTab = 'videos'
|
||||
}
|
||||
|
||||
this.currentTab = currentTab
|
||||
this.currentTab = this.currentOrFirstTab(this.$route.params.currentTab)
|
||||
|
||||
if (this.id === '@@@') {
|
||||
this.showShareMenu = false
|
||||
|
@ -453,6 +418,14 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
|
||||
currentOrFirstTab: function (currentTab) {
|
||||
if (this.tabInfoValues.includes(currentTab)) {
|
||||
return currentTab
|
||||
}
|
||||
|
||||
return this.tabInfoValues[0]
|
||||
},
|
||||
|
||||
getChannelLocal: async function () {
|
||||
this.apiUsed = 'local'
|
||||
this.isLoading = true
|
||||
|
|
|
@ -8,7 +8,6 @@ import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
|
|||
import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
|
||||
import FtChannelBubble from '../../components/ft-channel-bubble/ft-channel-bubble.vue'
|
||||
|
||||
import { MAIN_PROFILE_ID } from '../../../constants'
|
||||
import { calculatePublishedDate, copyToClipboard, showToast } from '../../helpers/utils'
|
||||
import { invidiousAPICall } from '../../helpers/api/invidious'
|
||||
import { getLocalChannelVideos } from '../../helpers/api/local'
|
||||
|
@ -30,7 +29,7 @@ export default defineComponent({
|
|||
dataLimit: 100,
|
||||
videoList: [],
|
||||
errorChannels: [],
|
||||
attemptedFetch: false
|
||||
attemptedFetch: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -69,13 +68,27 @@ export default defineComponent({
|
|||
activeProfile: function () {
|
||||
return this.$store.getters.getActiveProfile
|
||||
},
|
||||
|
||||
profileSubscriptions: function () {
|
||||
return this.$store.getters.getProfileSubscriptions
|
||||
activeProfileId: function () {
|
||||
return this.activeProfile._id
|
||||
},
|
||||
|
||||
allSubscriptionsList: function () {
|
||||
return this.$store.getters.getAllSubscriptionsList
|
||||
cacheEntriesForAllActiveProfileChannels() {
|
||||
const entries = []
|
||||
this.activeSubscriptionList.forEach((channel) => {
|
||||
const cacheEntry = this.$store.getters.getSubscriptionsCacheEntriesForOneChannel(channel.id)
|
||||
if (cacheEntry == null) { return }
|
||||
|
||||
entries.push(cacheEntry)
|
||||
})
|
||||
return entries
|
||||
},
|
||||
videoCacheForAllActiveProfileChannelsPresent() {
|
||||
if (this.cacheEntriesForAllActiveProfileChannels.length === 0) { return false }
|
||||
if (this.cacheEntriesForAllActiveProfileChannels.length < this.activeSubscriptionList.length) { return false }
|
||||
|
||||
return this.cacheEntriesForAllActiveProfileChannels.every((cacheEntry) => {
|
||||
return cacheEntry.videos != null
|
||||
})
|
||||
},
|
||||
|
||||
historyCache: function () {
|
||||
|
@ -96,12 +109,13 @@ export default defineComponent({
|
|||
|
||||
fetchSubscriptionsAutomatically: function() {
|
||||
return this.$store.getters.getFetchSubscriptionsAutomatically
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
activeProfile: async function (_) {
|
||||
this.getProfileSubscriptions()
|
||||
}
|
||||
this.isLoading = true
|
||||
this.loadVideosFromCacheSometimes()
|
||||
},
|
||||
},
|
||||
mounted: async function () {
|
||||
document.addEventListener('keydown', this.keyboardShortcutHandler)
|
||||
|
@ -112,66 +126,63 @@ export default defineComponent({
|
|||
this.dataLimit = dataLimit
|
||||
}
|
||||
|
||||
if (this.profileSubscriptions.videoList.length !== 0) {
|
||||
if (this.profileSubscriptions.activeProfile === this.activeProfile._id) {
|
||||
const subscriptionList = JSON.parse(JSON.stringify(this.profileSubscriptions))
|
||||
if (this.hideWatchedSubs) {
|
||||
this.videoList = await Promise.all(subscriptionList.videoList.filter((video) => {
|
||||
const historyIndex = this.historyCache.findIndex((x) => {
|
||||
return x.videoId === video.videoId
|
||||
})
|
||||
|
||||
return historyIndex === -1
|
||||
}))
|
||||
} else {
|
||||
this.videoList = subscriptionList.videoList
|
||||
this.errorChannels = subscriptionList.errorChannels
|
||||
}
|
||||
} else {
|
||||
this.getProfileSubscriptions()
|
||||
}
|
||||
|
||||
this.isLoading = false
|
||||
} else if (this.fetchSubscriptionsAutomatically) {
|
||||
setTimeout(async () => {
|
||||
this.getSubscriptions()
|
||||
}, 300)
|
||||
} else {
|
||||
this.isLoading = false
|
||||
}
|
||||
this.loadVideosFromCacheSometimes()
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
document.removeEventListener('keydown', this.keyboardShortcutHandler)
|
||||
},
|
||||
methods: {
|
||||
loadVideosFromCacheSometimes() {
|
||||
// This method is called on view visible
|
||||
if (this.videoCacheForAllActiveProfileChannelsPresent) {
|
||||
this.loadVideosFromCacheForAllActiveProfileChannels()
|
||||
return
|
||||
}
|
||||
|
||||
this.maybeLoadVideosForSubscriptionsFromRemote()
|
||||
},
|
||||
|
||||
async loadVideosFromCacheForAllActiveProfileChannels() {
|
||||
const videoList = []
|
||||
this.activeSubscriptionList.forEach((channel) => {
|
||||
const channelCacheEntry = this.$store.getters.getSubscriptionsCacheEntriesForOneChannel(channel.id)
|
||||
|
||||
videoList.push(...channelCacheEntry.videos)
|
||||
})
|
||||
this.updateVideoListAfterProcessing(videoList)
|
||||
this.isLoading = false
|
||||
},
|
||||
|
||||
goToChannel: function (id) {
|
||||
this.$router.push({ path: `/channel/${id}` })
|
||||
},
|
||||
|
||||
getSubscriptions: function () {
|
||||
loadVideosForSubscriptionsFromRemote: async function () {
|
||||
if (this.activeSubscriptionList.length === 0) {
|
||||
this.isLoading = false
|
||||
this.videoList = []
|
||||
return
|
||||
}
|
||||
|
||||
const channelsToLoadFromRemote = this.activeSubscriptionList
|
||||
const videoList = []
|
||||
let channelCount = 0
|
||||
this.isLoading = true
|
||||
|
||||
let useRss = this.useRssFeeds
|
||||
if (this.activeSubscriptionList.length >= 125 && !useRss) {
|
||||
if (channelsToLoadFromRemote.length >= 125 && !useRss) {
|
||||
showToast(
|
||||
this.$t('Subscriptions["This profile has a large number of subscriptions. Forcing RSS to avoid rate limiting"]'),
|
||||
10000
|
||||
)
|
||||
useRss = true
|
||||
}
|
||||
this.isLoading = true
|
||||
this.updateShowProgressBar(true)
|
||||
this.setProgressBarPercentage(0)
|
||||
this.attemptedFetch = true
|
||||
|
||||
let videoList = []
|
||||
let channelCount = 0
|
||||
this.errorChannels = []
|
||||
this.activeSubscriptionList.forEach(async (channel) => {
|
||||
const videoListFromRemote = (await Promise.all(channelsToLoadFromRemote.map(async (channel) => {
|
||||
let videos = []
|
||||
if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') {
|
||||
if (useRss) {
|
||||
|
@ -187,86 +198,66 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
|
||||
videoList = videoList.concat(videos)
|
||||
channelCount++
|
||||
const percentageComplete = (channelCount / this.activeSubscriptionList.length) * 100
|
||||
const percentageComplete = (channelCount / channelsToLoadFromRemote.length) * 100
|
||||
this.setProgressBarPercentage(percentageComplete)
|
||||
this.updateSubscriptionsCacheForOneChannel({
|
||||
channelId: channel.id,
|
||||
videos: videos,
|
||||
})
|
||||
return videos
|
||||
}))).flatMap((o) => o)
|
||||
videoList.push(...videoListFromRemote)
|
||||
|
||||
if (channelCount === this.activeSubscriptionList.length) {
|
||||
videoList = await Promise.all(videoList.sort((a, b) => {
|
||||
return b.publishedDate - a.publishedDate
|
||||
}))
|
||||
if (this.hideLiveStreams) {
|
||||
videoList = videoList.filter(item => {
|
||||
return (!item.liveNow && !item.isUpcoming)
|
||||
})
|
||||
}
|
||||
if (this.hideUpcomingPremieres) {
|
||||
videoList = videoList.filter(item => {
|
||||
if (item.isRSS) {
|
||||
// viewCount is our only method of detecting premieres in RSS
|
||||
// data without sending an additional request.
|
||||
// If we ever get a better flag, use it here instead.
|
||||
return item.viewCount !== '0'
|
||||
}
|
||||
// Observed for premieres in Local API Subscriptions.
|
||||
return item.durationText !== 'PREMIERE'
|
||||
})
|
||||
}
|
||||
const profileSubscriptions = {
|
||||
activeProfile: this.activeProfile._id,
|
||||
videoList: videoList,
|
||||
errorChannels: this.errorChannels
|
||||
}
|
||||
this.updateVideoListAfterProcessing(videoList)
|
||||
this.isLoading = false
|
||||
this.updateShowProgressBar(false)
|
||||
},
|
||||
|
||||
this.videoList = await Promise.all(videoList.filter((video) => {
|
||||
if (this.hideWatchedSubs) {
|
||||
const historyIndex = this.historyCache.findIndex((x) => {
|
||||
return x.videoId === video.videoId
|
||||
})
|
||||
|
||||
return historyIndex === -1
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}))
|
||||
this.updateProfileSubscriptions(profileSubscriptions)
|
||||
this.isLoading = false
|
||||
this.updateShowProgressBar(false)
|
||||
|
||||
if (this.activeProfile === MAIN_PROFILE_ID) {
|
||||
this.updateAllSubscriptionsList(profileSubscriptions.videoList)
|
||||
updateVideoListAfterProcessing(videoList) {
|
||||
// Filtering and sorting based in preference
|
||||
videoList.sort((a, b) => {
|
||||
return b.publishedDate - a.publishedDate
|
||||
})
|
||||
if (this.hideLiveStreams) {
|
||||
videoList = videoList.filter(item => {
|
||||
return (!item.liveNow && !item.isUpcoming)
|
||||
})
|
||||
}
|
||||
if (this.hideUpcomingPremieres) {
|
||||
videoList = videoList.filter(item => {
|
||||
if (item.isRSS) {
|
||||
// viewCount is our only method of detecting premieres in RSS
|
||||
// data without sending an additional request.
|
||||
// If we ever get a better flag, use it here instead.
|
||||
return item.viewCount !== '0'
|
||||
}
|
||||
// Observed for premieres in Local API Subscriptions.
|
||||
return item.premiereDate == null
|
||||
})
|
||||
}
|
||||
|
||||
this.videoList = videoList.filter((video) => {
|
||||
if (this.hideWatchedSubs) {
|
||||
const historyIndex = this.historyCache.findIndex((x) => {
|
||||
return x.videoId === video.videoId
|
||||
})
|
||||
|
||||
return historyIndex === -1
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getProfileSubscriptions: async function () {
|
||||
if (this.allSubscriptionsList.length !== 0) {
|
||||
this.isLoading = true
|
||||
this.videoList = await Promise.all(this.allSubscriptionsList.filter((video) => {
|
||||
const channelIndex = this.activeSubscriptionList.findIndex((x) => {
|
||||
return x.id === video.authorId
|
||||
})
|
||||
|
||||
if (this.hideWatchedSubs) {
|
||||
const historyIndex = this.historyCache.findIndex((x) => {
|
||||
return x.videoId === video.videoId
|
||||
})
|
||||
|
||||
return channelIndex !== -1 && historyIndex === -1
|
||||
} else {
|
||||
return channelIndex !== -1
|
||||
}
|
||||
}))
|
||||
this.isLoading = false
|
||||
} else if (this.fetchSubscriptionsAutomatically) {
|
||||
this.getSubscriptions()
|
||||
} else if (this.activeProfile._id === this.profileSubscriptions.activeProfile) {
|
||||
this.videoList = this.profileSubscriptions.videoList
|
||||
maybeLoadVideosForSubscriptionsFromRemote: async function () {
|
||||
if (this.fetchSubscriptionsAutomatically) {
|
||||
// `this.isLoading = false` is called inside `loadVideosForSubscriptionsFromRemote` when needed
|
||||
await this.loadVideosForSubscriptionsFromRemote()
|
||||
} else {
|
||||
this.videoList = []
|
||||
this.attemptedFetch = false
|
||||
this.isLoading = false
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -487,7 +478,7 @@ export default defineComponent({
|
|||
case 'r':
|
||||
case 'R':
|
||||
if (!this.isLoading) {
|
||||
this.getSubscriptions()
|
||||
this.loadVideosForSubscriptionsFromRemote()
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -495,8 +486,7 @@ export default defineComponent({
|
|||
|
||||
...mapActions([
|
||||
'updateShowProgressBar',
|
||||
'updateProfileSubscriptions',
|
||||
'updateAllSubscriptionsList'
|
||||
'updateSubscriptionsCacheForOneChannel',
|
||||
]),
|
||||
|
||||
...mapMutations([
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
:title="$t('Subscriptions.Refresh Subscriptions')"
|
||||
:size="12"
|
||||
theme="primary"
|
||||
@click="getSubscriptions"
|
||||
@click="loadVideosForSubscriptionsFromRemote"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -433,6 +433,7 @@ Settings:
|
|||
Skip Option: تخطي الخِيار
|
||||
Show In Seek Bar: إظهار في الشريط
|
||||
Do Nothing: لا تفعل شيئا
|
||||
UseDeArrowTitles: استخدام عناوين فيديو DeArrow
|
||||
External Player Settings:
|
||||
External Player: المشغل الخارجي
|
||||
Custom External Player Arguments: وسيطات المشغل الخارجي المخصصة
|
||||
|
@ -926,6 +927,8 @@ Tooltips:
|
|||
Hide Channels: أدخل اسم القناة أو رقم تعريف القناة لإخفاء كل الفيديوهات وقوائم
|
||||
التشغيل والقناة نفسها من الظهور في "بحث Google" أو "المحتوى الرائج". يجب أن
|
||||
يكون اسم القناة الذي تم إدخاله مطابقا تماما وحساسا لحالة الأحرف.
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: استبدل عناوين الفيديو بالعناوين التي أرسلها المستخدم من DeArrow.
|
||||
This video is unavailable because of missing formats. This can happen due to country unavailability.: هذا
|
||||
الفيديو غير متاح الآن لعدم وجود ملفات فيديو . هذا قد يكون بسبب أن الفيديو غير متاح
|
||||
في بلدك.
|
||||
|
|
|
@ -414,6 +414,7 @@ Settings:
|
|||
Enable SponsorBlock: Enable SponsorBlock
|
||||
'SponsorBlock API Url (Default is https://sponsor.ajay.app)': SponsorBlock API Url (Default is https://sponsor.ajay.app)
|
||||
Notify when sponsor segment is skipped: Notify when sponsor segment is skipped
|
||||
UseDeArrowTitles: Use DeArrow Video Titles
|
||||
Skip Options:
|
||||
Skip Option: Skip Option
|
||||
Auto Skip: Auto Skip
|
||||
|
@ -848,6 +849,8 @@ Tooltips:
|
|||
when the watch page is closed.
|
||||
Experimental Settings:
|
||||
Replace HTTP Cache: Disables Electron's disk based HTTP cache and enables a custom in-memory image cache. Will lead to increased RAM usage.
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: Replace video titles with user-submitted titles from DeArrow.
|
||||
|
||||
# Toast Messages
|
||||
Local API Error (Click to copy): Local API Error (Click to copy)
|
||||
|
|
|
@ -440,6 +440,7 @@ Settings:
|
|||
Skip Option: Omitir opción
|
||||
Auto Skip: Salto automático
|
||||
Prompt To Skip: Solicitar que se omita
|
||||
UseDeArrowTitles: Utilizar los títulos para el vídeo de DeArrow
|
||||
External Player Settings:
|
||||
Custom External Player Arguments: Argumentos adicionales del reproductor
|
||||
Custom External Player Executable: Ruta alternativa del ejecutable del reproductor
|
||||
|
@ -958,6 +959,9 @@ Tooltips:
|
|||
listas de reproducción y el propio canal y evitar que aparezcan en las búsquedas
|
||||
o en las tendencias. El nombre del canal introducido debe coincidir completamente
|
||||
y distingue entre mayúsculas y minúsculas.
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: Sustituye los títulos de los vídeos por títulos enviados por
|
||||
los usuarios desde DeArrow.
|
||||
More: Más
|
||||
Unknown YouTube url type, cannot be opened in app: Tipo de URL desconocido. No se
|
||||
puede abrir en la aplicación
|
||||
|
|
|
@ -461,6 +461,7 @@ Settings:
|
|||
Prompt To Skip: Inviter à ignorer
|
||||
Do Nothing: Ne rien faire
|
||||
Category Color: Couleur de la catégorie
|
||||
UseDeArrowTitles: Utiliser les titres vidéo de DeArrow
|
||||
External Player Settings:
|
||||
Ignore Unsupported Action Warnings: Ignorer les avertissements concernant les
|
||||
actions non prises en charge
|
||||
|
@ -1000,6 +1001,9 @@ Tooltips:
|
|||
toutes les vidéos, les listes de lecture et la chaîne elle-même d'apparaître
|
||||
dans les recherches ou les tendances. Le nom du canal entré doit être une correspondance
|
||||
complète et est sensible à la casse.
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: Remplacez les titres des vidéos par des titres proposés par
|
||||
les utilisateurs de DeArrow.
|
||||
More: Plus
|
||||
Playing Next Video Interval: Lecture de la prochaine vidéo en un rien de temps. Cliquez
|
||||
pour annuler. | Lecture de la prochaine vidéo dans {nextVideoInterval} seconde.
|
||||
|
|
|
@ -438,6 +438,7 @@ Settings:
|
|||
Do Nothing: לא לעשות כלום
|
||||
Prompt To Skip: לשאול אם לדלג
|
||||
Category Color: צבע קטגוריה
|
||||
UseDeArrowTitles: להשתמש בכותרות סרטונים דרך DeArrow
|
||||
Download Settings:
|
||||
Download Settings: הגדרות הורדה
|
||||
Ask Download Path: לשאול מה נתיב ההורדות
|
||||
|
@ -720,7 +721,7 @@ Video:
|
|||
recap: סיכום
|
||||
External Player:
|
||||
video: סרטון
|
||||
playlist: פלייליסט
|
||||
playlist: רשימת נגינה
|
||||
OpenInTemplate: פתיחה בתוך {externalPlayer}
|
||||
OpeningTemplate: '{videoOrPlaylist} נפתח ב־{externalPlayer}…'
|
||||
UnsupportedActionTemplate: 'ב־{externalPlayer} אין תמיכה בפעולה: {action}'
|
||||
|
@ -778,7 +779,7 @@ Playlist:
|
|||
# On Video Watch Page
|
||||
#* Published
|
||||
#& Views
|
||||
Playlist: פלייליסט
|
||||
Playlist: רשימת נגינה
|
||||
Toggle Theatre Mode: 'הפעלו / בטלו מצב קולנוע'
|
||||
Change Format:
|
||||
Change Media Formats: 'שינוי תצורות הסרטונים'
|
||||
|
@ -865,6 +866,8 @@ Tooltips:
|
|||
External Link Handling: "נא לבחור את התנהגות ברירת המחדל כשנלחץ קישור שלא ניתן
|
||||
לפתוח ב־FreeTube.\nכברירת מחדל FreeTube יפתח את הקישור שנלחץ בדפדפן ברירת המחדל
|
||||
שלך.\n"
|
||||
Region for Trending: מגמות אזוריות מאפשרות לך לבחור סרטונים חמים של איזו מדינה
|
||||
מעניין אותך לראות.
|
||||
Player Settings:
|
||||
Force Local Backend for Legacy Formats: עובד רק כאשר ה־API של Invidious הוא ברירת
|
||||
המחדל שלך. כאשר האפשרות פעילה, ה־API המקומי יופעל וישתמש בתצורות המיושנות שהוחזרו
|
||||
|
@ -912,6 +915,8 @@ Tooltips:
|
|||
Hide Channels: יש למלא את שם או מזהה הערוץ כדי להסתיר את כל הסרטונים, רשימות הנגינה
|
||||
ואת הערוץ עצמו כך שלא יופיע בחיפוש או במובילים. שם הערוץ שמילאת צריך להיות תואם
|
||||
במלואו ותואם מבחינת רישיות (אותיות גדולות/קטנות).
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: החלפת כותרות הסרטונים עם כותרות ששלחו משתמשים ב־DeArrow.
|
||||
More: עוד
|
||||
Open New Window: פתיחת חלון חדש
|
||||
Search Bar:
|
||||
|
|
|
@ -448,6 +448,7 @@ Settings:
|
|||
Do Nothing: Nincs művelet
|
||||
Prompt To Skip: Kihagyás kérése
|
||||
Category Color: Kategória színe
|
||||
UseDeArrowTitles: DeArrow-videocímek használata
|
||||
External Player Settings:
|
||||
Ignore Unsupported Action Warnings: Nem támogatott műveleti figyelmeztetések figyelmen
|
||||
kívül hagyva
|
||||
|
@ -951,6 +952,9 @@ Tooltips:
|
|||
az összes videót, lejátszási listát és magát a csatornát, hogy ne jelenjen meg
|
||||
a keresésben vagy a népszerűségben. A megadott csatornanévnek teljes egyezésnek
|
||||
kell lennie, és megkülönbözteti a kis- és nagybetűket.
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: Cserélje le a videocímeket a DeArrow által beküldött, felhasználó
|
||||
által beküldött címekre.
|
||||
Playing Next Video Interval: A következő videó lejátszása folyamatban van. Kattintson
|
||||
a törléshez. | A következő videó lejátszása {nextVideoInterval} másodperc múlva
|
||||
történik. Kattintson a törléshez. | A következő videó lejátszása {nextVideoInterval}
|
||||
|
|
|
@ -451,6 +451,7 @@ Settings:
|
|||
Prompt To Skip: Chiedi di saltare
|
||||
Do Nothing: Non fare nulla
|
||||
Category Color: Colore della categoria
|
||||
UseDeArrowTitles: Usa i titoli dei video DeArrow
|
||||
External Player Settings:
|
||||
Custom External Player Arguments: Argomenti del lettore esterno personalizzato
|
||||
Custom External Player Executable: File eseguibile del lettore esterno personalizzato
|
||||
|
@ -968,6 +969,9 @@ Tooltips:
|
|||
le playlist e il canale stesso dalla visualizzazione nelle ricerche o nelle
|
||||
tendenze. Il nome del canale inserito deve essere una corrispondenza completa
|
||||
e fa distinzione tra maiuscole e minuscole.
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: Sostituisci i titoli dei video con titoli inviati dagli utenti
|
||||
da DeArrow.
|
||||
Playing Next Video Interval: Riproduzione del video successivo tra un attimo. Clicca
|
||||
per annullare. | Riproduzione del video successivo tra {nextVideoInterval} secondi.
|
||||
Clicca per annullare. | Riproduzione del video successivo tra {nextVideoInterval}
|
||||
|
|
|
@ -91,7 +91,7 @@ Playlists: '再生リスト'
|
|||
User Playlists:
|
||||
Your Playlists: 'あなたの再生リスト'
|
||||
Your saved videos are empty. Click on the save button on the corner of a video to have it listed here: 保存した動画はありません。一覧に表示させるには、ビデオの角にある保存ボタンをクリックします
|
||||
Playlist Message:
|
||||
Playlist Message:
|
||||
このページは、完全に動作する動画リストではありません。保存またはお気に入りと設定した動画のみが表示されます。操作が完了すると、現在ここにあるすべての動画は「お気に入り」の動画リストに移動します。
|
||||
Search bar placeholder: 動画リスト内の検索
|
||||
Empty Search Message: この再生リストに、検索に一致する動画はありません
|
||||
|
@ -404,6 +404,7 @@ Settings:
|
|||
Prompt To Skip: ログイン プロンプトのスキップ
|
||||
Do Nothing: 何もしない
|
||||
Category Color: カテゴリの色
|
||||
UseDeArrowTitles: 動画タイトルに DeArrow を使用する
|
||||
External Player Settings:
|
||||
Custom External Player Arguments: カスタム外部プレーヤー引数
|
||||
Custom External Player Executable: カスタム外部プレーヤーの実行可能ファイル
|
||||
|
@ -848,8 +849,10 @@ Tooltips:
|
|||
Replace HTTP Cache: Electron のディスクに基づく HTTP キャッシュを無効化し、メモリ内で独自の画像キャッシュを使用します。このことにより
|
||||
RAM の使用率は増加します。
|
||||
Distraction Free Settings:
|
||||
Hide Channels: チャンネル名またはチャンネル ID
|
||||
Hide Channels: チャンネル名またはチャンネル ID
|
||||
を入力すると、すべてのビデオ、再生リスト、およびチャンネル自体が検索や人気に表示されなくなります。入力するチャンネル名は完全に一致することが必要で、大文字と小文字を区別します。
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: 動画のタイトルを DeArrow からユーザーが投稿したタイトルに置き換えます。
|
||||
Playing Next Video Interval: すぐに次の動画を再生します。クリックするとキャンセル。|次の動画を {nextVideoInterval}
|
||||
秒で再生します。クリックするとキャンセル。|次の動画を {nextVideoInterval} 秒で再生します。クリックするとキャンセル。
|
||||
More: もっと見る
|
||||
|
|
|
@ -267,6 +267,8 @@ Settings:
|
|||
Enter Fullscreen on Display Rotate: Fullskjermsvisning når skjermen roteres
|
||||
Skip by Scrolling Over Video Player: Hopp over ved å rulle over videoavspiller
|
||||
Allow DASH AV1 formats: Tillat DASH AV1-formater
|
||||
Comment Auto Load:
|
||||
Comment Auto Load: Auto-innlasting av kommentarer
|
||||
Privacy Settings:
|
||||
Privacy Settings: 'Personvernsinnstillinger'
|
||||
Remember History: 'Husk historikk'
|
||||
|
@ -402,6 +404,15 @@ Settings:
|
|||
Hide Video Description: Skjul videobeskrivelse
|
||||
Display Titles Without Excessive Capitalisation: Vis titler uten overdrevet bruk
|
||||
av store bokstaver
|
||||
Hide Featured Channels: Skjult framhevede kanaler
|
||||
Hide Channel Community: Skjul kanalgemenskap
|
||||
Hide Channel Shorts: Skjul kanal-kortvideoer
|
||||
Sections:
|
||||
Side Bar: Sidefelt
|
||||
Channel Page: Kanalside
|
||||
General: Generelt
|
||||
Watch Page: Seerlogg
|
||||
Hide Channel Playlists: Skjul kanalspilleliste
|
||||
The app needs to restart for changes to take effect. Restart and apply change?: Start
|
||||
programmet på ny for å ta i bruk endringene?
|
||||
SponsorBlock Settings:
|
||||
|
@ -418,6 +429,7 @@ Settings:
|
|||
Auto Skip: Automatisk
|
||||
Show In Seek Bar: Vis i blafringsfelt
|
||||
Category Color: Kategorifarge
|
||||
UseDeArrowTitles: Bruk DeArrow-videonavn
|
||||
External Player Settings:
|
||||
Custom External Player Arguments: Egendefinerte argumenter for ekstern avspiller
|
||||
Custom External Player Executable: Egendefinert kjørbar fil for ekstern avspiller
|
||||
|
@ -531,6 +543,13 @@ Channel:
|
|||
Community:
|
||||
Community: Gemenskap
|
||||
This channel currently does not have any posts: Denne kanalen har ingen oppføringer
|
||||
Shorts:
|
||||
This channel does not currently have any shorts: Denne kanalen har ingen kortvideoer
|
||||
Shorts: Kortvideoer
|
||||
Live:
|
||||
Live: Direkte
|
||||
This channel does not currently have any live streams: Denne kanalen har ikke
|
||||
noen direktestrømmer
|
||||
Video:
|
||||
Mark As Watched: 'Marker som sett'
|
||||
Remove From History: 'Fjern fra historikk'
|
||||
|
@ -663,6 +682,8 @@ Video:
|
|||
Premieres on: Har première
|
||||
Premieres: Première
|
||||
Upcoming: Kommende
|
||||
'Live Chat is unavailable for this stream. It may have been disabled by the uploader.': Sanntidssludring
|
||||
er ikke tilgjengelig for denne strømmen. Opplasteren kan ha skrudd det av.
|
||||
Videos:
|
||||
#& Sort By
|
||||
Sort By:
|
||||
|
@ -818,8 +839,8 @@ Tooltips:
|
|||
Preferred API Backend: Velg metoden FreeTube bruker til å hente data. Det lokale
|
||||
API-et er en innebygd utpakker. Invidious-API-et krever en Invidious-tjener
|
||||
å koble til.
|
||||
External Link Handling: "Velg oppførsel når en link som ikke kan åpnes i FreeTube\
|
||||
\ klikkes.\nSom forvalg vil FreeTube åpne lenken i nettleseren din.\n"
|
||||
External Link Handling: "Velg oppførsel når en link som ikke kan åpnes i FreeTube
|
||||
klikkes.\nSom forvalg vil FreeTube åpne lenken i nettleseren din.\n"
|
||||
Subscription Settings:
|
||||
Fetch Feeds from RSS: Bruk RSS istedenfor FreeTube sin forvalgte metode for innhenting
|
||||
av din abonnementsstrøm. RSS er raskere og forhindrer IP-blokkering, men mangler
|
||||
|
@ -869,6 +890,8 @@ Tooltips:
|
|||
spillelister og selve kanalen fra å vises i søk eller på vei opp. Kanalnavnet
|
||||
som skrives inn må være et nøyaktig treff, og gjør forskjell på små og store
|
||||
bokstaver.
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: Erstatt videonavn med brukerinnsendte navn fra DeArrow.
|
||||
A new blog is now available, {blogTitle}. Click to view more: Et nytt blogginnlegg
|
||||
er tilgjengelig, {blogTitle}. Klikk her for å se mer
|
||||
The playlist has been reversed: Spillelisten har blitt snudd
|
||||
|
@ -924,3 +947,7 @@ Clipboard:
|
|||
uten sikker forbindelse
|
||||
Copy failed: Kunne ikke kopiere til utklippstavlen
|
||||
Downloading has completed: «{videoTitle}» er nedlastet
|
||||
Hashtag:
|
||||
Hashtag: Emneknagg
|
||||
This hashtag does not currently have any videos: Denne emneknaggen har ingen videoer
|
||||
enda
|
||||
|
|
|
@ -442,6 +442,7 @@ Settings:
|
|||
Prompt To Skip: Atlamak İçin Sor
|
||||
Do Nothing: Hiçbir Şey Yapma
|
||||
Category Color: Kategori Rengi
|
||||
UseDeArrowTitles: DeArrow Video Başlıklarını Kullan
|
||||
External Player Settings:
|
||||
Custom External Player Arguments: Özel Harici Oynatıcı Argümanları
|
||||
Custom External Player Executable: Özel Harici Oynatıcı Çalıştırılabilir Dosyası
|
||||
|
@ -950,6 +951,9 @@ Tooltips:
|
|||
Hide Channels: Tüm videoların, oynatma listelerinin ve kanalın kendisinin arama
|
||||
veya öne çıkanlarda görünmesini engellemek için bir kanal adı veya kanal kimliği
|
||||
girin. Girilen kanal adı tam olarak eşleşmelidir ve büyük/küçük harfe duyarlıdır.
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: Video başlıklarını DeArrow'dan kullanıcıların gönderdiği başlıklarla
|
||||
değiştir.
|
||||
Playing Next Video Interval: Sonraki video hemen oynatılıyor. İptal etmek için tıklayın.
|
||||
| Sonraki video {nextVideoInterval} saniye içinde oynatılıyor. İptal etmek için
|
||||
tıklayın. | Sonraki video {nextVideoInterval} saniye içinde oynatılıyor. İptal etmek
|
||||
|
|
|
@ -413,6 +413,7 @@ Settings:
|
|||
Prompt To Skip: Запит на пропуск
|
||||
Do Nothing: Нічого не робити
|
||||
Category Color: Колір категорії
|
||||
UseDeArrowTitles: Використовувати назви для відео DeArrow
|
||||
External Player Settings:
|
||||
Custom External Player Arguments: Власні аргументи зовнішнього програвача
|
||||
Custom External Player Executable: Власний виконуваний зовнішній програвач
|
||||
|
@ -866,6 +867,8 @@ Tooltips:
|
|||
Hide Channels: Введіть назву або ID каналу, щоб сховати всі відео, списки відтворення
|
||||
та сам канал від появи в пошуку або в тренді. Введена назва каналу повинна повністю
|
||||
збігатися і чутлива до регістру.
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: Замінити назви відео на надіслані користувачем назви з DeArrow.
|
||||
Local API Error (Click to copy): 'Помилка локального API (натисніть, щоб скопіювати)'
|
||||
Invidious API Error (Click to copy): 'Помилка Invidious API (натисніть, щоб скопіювати)'
|
||||
Falling back to Invidious API: 'Повернення до API Invidious'
|
||||
|
|
|
@ -401,6 +401,7 @@ Settings:
|
|||
Do Nothing: 什么也不做
|
||||
Skip Option: 跳过选项
|
||||
Category Color: 类别颜色
|
||||
UseDeArrowTitles: 使用 DeArrow 视频标题
|
||||
External Player Settings:
|
||||
Custom External Player Arguments: 定制外部播放器的参数
|
||||
Custom External Player Executable: 自定义外部播放器的可执行文件
|
||||
|
@ -837,6 +838,8 @@ Tooltips:
|
|||
Replace HTTP Cache: 禁用 Electron 基于磁盘的 HTTP 缓存,启用自定义内存中图像缓存。会增加内存的使用。
|
||||
Distraction Free Settings:
|
||||
Hide Channels: 输入频道名称或频道 ID 以隐藏所有视频、播放列表和频道本身,使其不出现在搜索结果或热门中。 输入的频道名称必须完全匹配,并且区分大小写。
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: 使用来自 DeArrow 的用户提交的标题替换原始视频标题。
|
||||
More: 更多
|
||||
Open New Window: 打开新窗口
|
||||
Search Bar:
|
||||
|
|
|
@ -403,6 +403,7 @@ Settings:
|
|||
Prompt To Skip: 提示略過
|
||||
Do Nothing: 不要做任何事
|
||||
Category Color: 分類色彩
|
||||
UseDeArrowTitles: 使用 DeArrow 影片標題
|
||||
External Player Settings:
|
||||
Custom External Player Arguments: 自訂外部播放程式參數
|
||||
Custom External Player Executable: 自訂外部播放程式可執行檔
|
||||
|
@ -850,6 +851,8 @@ Tooltips:
|
|||
Replace HTTP Cache: 停用 Electron 以磁碟為基礎的 HTTP 快取並啟用自訂的記憶體圖片快取。會導致記憶體使用量增加。
|
||||
Distraction Free Settings:
|
||||
Hide Channels: 輸入頻道名稱或頻道 ID 以隱藏所有影片、播放清單與頻道本身,使其完全不出現在搜尋或趨勢中。輸入的頻道名稱必須完全符合,且區分大小寫。
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: 將影片標題取代為 DeArrow 使用者遞交的標題。
|
||||
Playing Next Video Interval: 馬上播放下一個影片。點擊取消。| 播放下一個影片的時間為{nextVideoInterval}秒。點擊取消。|
|
||||
播放下一個影片的時間為{nextVideoInterval}秒。點擊取消。
|
||||
More: 更多
|
||||
|
|
97
yarn.lock
97
yarn.lock
|
@ -7,6 +7,11 @@
|
|||
resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.1.1.tgz#9274ec7460652f9c632c59addf24efb1684ef876"
|
||||
integrity sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==
|
||||
|
||||
"@aashutoshrathi/word-wrap@^1.2.3":
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
|
||||
integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
|
||||
|
||||
"@ampproject/remapping@^2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
|
||||
|
@ -1117,14 +1122,14 @@
|
|||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884"
|
||||
integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==
|
||||
|
||||
"@eslint/eslintrc@^2.0.3":
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.3.tgz#4910db5505f4d503f27774bf356e3704818a0331"
|
||||
integrity sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==
|
||||
"@eslint/eslintrc@^2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d"
|
||||
integrity sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==
|
||||
dependencies:
|
||||
ajv "^6.12.4"
|
||||
debug "^4.3.2"
|
||||
espree "^9.5.2"
|
||||
espree "^9.6.0"
|
||||
globals "^13.19.0"
|
||||
ignore "^5.2.0"
|
||||
import-fresh "^3.2.1"
|
||||
|
@ -1132,10 +1137,10 @@
|
|||
minimatch "^3.1.2"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@eslint/js@8.43.0":
|
||||
version "8.43.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.43.0.tgz#559ca3d9ddbd6bf907ad524320a0d14b85586af0"
|
||||
integrity sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==
|
||||
"@eslint/js@8.44.0":
|
||||
version "8.44.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af"
|
||||
integrity sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==
|
||||
|
||||
"@fortawesome/fontawesome-common-types@6.4.0":
|
||||
version "6.4.0"
|
||||
|
@ -1890,6 +1895,11 @@ acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0:
|
|||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
|
||||
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
|
||||
|
||||
acorn@^8.9.0:
|
||||
version "8.9.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59"
|
||||
integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==
|
||||
|
||||
aes-decrypter@3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/aes-decrypter/-/aes-decrypter-3.1.3.tgz#65ff5f2175324d80c41083b0e135d1464b12ac35"
|
||||
|
@ -1936,17 +1946,7 @@ ajv@^6.10.0, ajv@^6.12.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
|
|||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ajv@^8.0.0, ajv@^8.0.1:
|
||||
version "8.11.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f"
|
||||
integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==
|
||||
dependencies:
|
||||
fast-deep-equal "^3.1.1"
|
||||
json-schema-traverse "^1.0.0"
|
||||
require-from-string "^2.0.2"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ajv@^8.9.0:
|
||||
ajv@^8.0.0, ajv@^8.0.1, ajv@^8.9.0:
|
||||
version "8.12.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1"
|
||||
integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==
|
||||
|
@ -3344,10 +3344,10 @@ electron-to-chromium@^1.4.411:
|
|||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.411.tgz#8cb7787f0442fcb4209590e9951bdb482caa93b2"
|
||||
integrity sha512-5VXLW4Qw89vM2WTICHua/y8v7fKGDRVa2VPOtBB9IpLvW316B+xd8yD1wTmLPY2ot/00P/qt87xdolj4aG/Lzg==
|
||||
|
||||
electron@^22.3.14:
|
||||
version "22.3.14"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-22.3.14.tgz#539fc7d7b6df37483aaa351856a28e43092d550e"
|
||||
integrity sha512-WxVcLnC4DrkBLN1/BwpxNkGvVq8iq1hM7lae5nvjnSYg/bwVbuo1Cwc80Keft4MIWKlYCXNiKKqs3qCXV4Aiaw==
|
||||
electron@^22.3.15:
|
||||
version "22.3.15"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-22.3.15.tgz#8da94b6f98529792aae6b9f54c9abbff0a2cd7f6"
|
||||
integrity sha512-KhxJkx2tfB8Q1moUI3sI/x48lehTk3wUEwwaKKkfzSKT3m7nK/g1YSYiYe4c8WuqODAcJKhB1MOvRv3WmhBYBw==
|
||||
dependencies:
|
||||
"@electron/get" "^2.0.0"
|
||||
"@types/node" "^16.11.26"
|
||||
|
@ -3762,15 +3762,15 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4
|
|||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994"
|
||||
integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==
|
||||
|
||||
eslint@^8.43.0:
|
||||
version "8.43.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.43.0.tgz#3e8c6066a57097adfd9d390b8fc93075f257a094"
|
||||
integrity sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==
|
||||
eslint@^8.44.0:
|
||||
version "8.44.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.44.0.tgz#51246e3889b259bbcd1d7d736a0c10add4f0e500"
|
||||
integrity sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.2.0"
|
||||
"@eslint-community/regexpp" "^4.4.0"
|
||||
"@eslint/eslintrc" "^2.0.3"
|
||||
"@eslint/js" "8.43.0"
|
||||
"@eslint/eslintrc" "^2.1.0"
|
||||
"@eslint/js" "8.44.0"
|
||||
"@humanwhocodes/config-array" "^0.11.10"
|
||||
"@humanwhocodes/module-importer" "^1.0.1"
|
||||
"@nodelib/fs.walk" "^1.2.8"
|
||||
|
@ -3782,7 +3782,7 @@ eslint@^8.43.0:
|
|||
escape-string-regexp "^4.0.0"
|
||||
eslint-scope "^7.2.0"
|
||||
eslint-visitor-keys "^3.4.1"
|
||||
espree "^9.5.2"
|
||||
espree "^9.6.0"
|
||||
esquery "^1.4.2"
|
||||
esutils "^2.0.2"
|
||||
fast-deep-equal "^3.1.3"
|
||||
|
@ -3802,17 +3802,17 @@ eslint@^8.43.0:
|
|||
lodash.merge "^4.6.2"
|
||||
minimatch "^3.1.2"
|
||||
natural-compare "^1.4.0"
|
||||
optionator "^0.9.1"
|
||||
optionator "^0.9.3"
|
||||
strip-ansi "^6.0.1"
|
||||
strip-json-comments "^3.1.0"
|
||||
text-table "^0.2.0"
|
||||
|
||||
espree@^9.0.0, espree@^9.3.1, espree@^9.5.2:
|
||||
version "9.5.2"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.2.tgz#e994e7dc33a082a7a82dceaf12883a829353215b"
|
||||
integrity sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==
|
||||
espree@^9.0.0, espree@^9.3.1, espree@^9.6.0:
|
||||
version "9.6.0"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.0.tgz#80869754b1c6560f32e3b6929194a3fe07c5b82f"
|
||||
integrity sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==
|
||||
dependencies:
|
||||
acorn "^8.8.0"
|
||||
acorn "^8.9.0"
|
||||
acorn-jsx "^5.3.2"
|
||||
eslint-visitor-keys "^3.4.1"
|
||||
|
||||
|
@ -6055,17 +6055,17 @@ open@^8.0.9:
|
|||
is-docker "^2.1.1"
|
||||
is-wsl "^2.2.0"
|
||||
|
||||
optionator@^0.9.1:
|
||||
version "0.9.1"
|
||||
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
|
||||
integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
|
||||
optionator@^0.9.3:
|
||||
version "0.9.3"
|
||||
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64"
|
||||
integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==
|
||||
dependencies:
|
||||
"@aashutoshrathi/word-wrap" "^1.2.3"
|
||||
deep-is "^0.1.3"
|
||||
fast-levenshtein "^2.0.6"
|
||||
levn "^0.4.1"
|
||||
prelude-ls "^1.2.1"
|
||||
type-check "^0.4.0"
|
||||
word-wrap "^1.2.3"
|
||||
|
||||
p-cancelable@^2.0.0:
|
||||
version "2.1.1"
|
||||
|
@ -8346,10 +8346,10 @@ webpack-sources@^3.2.3:
|
|||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
||||
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
||||
|
||||
webpack@^5.88.0:
|
||||
version "5.88.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.0.tgz#a07aa2f8e7a64a8f1cec0c6c2e180e3cb34440c8"
|
||||
integrity sha512-O3jDhG5e44qIBSi/P6KpcCcH7HD+nYIHVBhdWFxcLOcIGN8zGo5nqF3BjyNCxIh4p1vFdNnreZv2h2KkoAw3lw==
|
||||
webpack@^5.88.1:
|
||||
version "5.88.1"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.1.tgz#21eba01e81bd5edff1968aea726e2fbfd557d3f8"
|
||||
integrity sha512-FROX3TxQnC/ox4N+3xQoWZzvGXSuscxR32rbzjpXgEzWudJFEJBpdlkkob2ylrv5yzzufD1zph1OoFsLtm6stQ==
|
||||
dependencies:
|
||||
"@types/eslint-scope" "^3.7.3"
|
||||
"@types/estree" "^1.0.0"
|
||||
|
@ -8454,11 +8454,6 @@ wildcard@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
|
||||
integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
|
||||
|
||||
word-wrap@^1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
|
|
Loading…
Reference in New Issue