Merge branch 'development' into piped-support

This commit is contained in:
Chunky programmer 2023-05-30 20:02:17 -04:00
commit 66578d25af
45 changed files with 966 additions and 635 deletions

View File

@ -15,6 +15,8 @@ body:
options:
- label: I have encountered this bug in the [latest release of FreeTube](https://github.com/FreeTubeApp/FreeTube/releases).
required: true
- label: I have encountered this bug in the [official downloads of FreeTube](https://github.com/FreeTubeApp/FreeTube#official-downloads).
required: true
- label: I have searched the issue tracker for [open](https://github.com/FreeTubeApp/FreeTube/issues?q=is%3Aopen+is%3Aissue) and [closed](https://github.com/FreeTubeApp/FreeTube/issues?q=is%3Aissue+is%3Aclosed) issues that are similar to the bug report I want to file, without success.
required: true
- label: I have searched the [documentation](https://docs.freetubeapp.io/) for information that matches the description of the bug I want to file, without success.

View File

@ -81,7 +81,7 @@ Builds are automatically created from changes to our development branch via [Git
The first build with a green check mark is the latest build. You will need to have a GitHub account to download these builds.
### Unofficial Downloads
These builds are maintained by the community. While they should be safe, download at your own risk. There may be issues with using these versus the official builds. Any issues specific with these builds should be sent to their respective maintainer.
These builds are maintained by the community. While they should be safe, download at your own risk. There may be issues with using these versus the official builds. Any issues specific with these builds should be sent to their respective maintainer. <b>Make sure u always try an [official download](https://github.com/freetubeapp/freetube/#official-downloads) before reporting your issue to us!</b>
* Arch User Repository (AUR): [Download](https://aur.archlinux.org/packages/freetube-bin/)

View File

@ -80,14 +80,14 @@
"youtubei.js": "^5.1.0"
},
"devDependencies": {
"@babel/core": "^7.21.8",
"@babel/core": "^7.22.1",
"@babel/eslint-parser": "^7.21.8",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/preset-env": "^7.21.5",
"@babel/preset-env": "^7.22.2",
"@double-great/stylelint-a11y": "^2.0.2",
"babel-loader": "^9.1.2",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.4",
"css-loader": "^6.8.1",
"css-minimizer-webpack-plugin": "^5.0.0",
"electron": "^22.3.2",
"electron-builder": "^23.6.0",
@ -100,21 +100,21 @@
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-unicorn": "^47.0.0",
"eslint-plugin-vue": "^9.13.0",
"eslint-plugin-vue": "^9.14.1",
"eslint-plugin-vuejs-accessibility": "^2.1.0",
"eslint-plugin-yml": "^1.7.0",
"html-webpack-plugin": "^5.5.1",
"js-yaml": "^4.1.0",
"json-minimizer-webpack-plugin": "^4.0.0",
"lefthook": "^1.4.0",
"mini-css-extract-plugin": "^2.7.5",
"lefthook": "^1.4.1",
"mini-css-extract-plugin": "^2.7.6",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.23",
"postcss": "^8.4.24",
"postcss-scss": "^4.0.6",
"prettier": "^2.8.8",
"rimraf": "^5.0.1",
"sass": "^1.62.1",
"sass-loader": "^13.2.2",
"sass-loader": "^13.3.1",
"stylelint": "^14.16.1",
"stylelint-config-sass-guidelines": "^9.0.1",
"stylelint-config-standard": "^29.0.0",
@ -123,7 +123,7 @@
"vue-devtools": "^5.1.4",
"vue-eslint-parser": "^9.3.0",
"vue-loader": "^15.10.0",
"webpack": "^5.83.1",
"webpack": "^5.84.1",
"webpack-cli": "^5.1.1",
"webpack-dev-server": "^4.15.0",
"yaml-eslint-parser": "^1.2.2"

View File

@ -694,7 +694,7 @@ export default defineComponent({
response.filePaths.forEach(filePath => {
if (filePath.endsWith('.db')) {
this.importFreeTubeSubscriptions(textDecode.split('\n'))
this.importFreeTubeHistory(textDecode.split('\n'))
} else if (filePath.endsWith('.json')) {
this.importYouTubeHistory(JSON.parse(textDecode))
}

View File

@ -1,3 +1,7 @@
.groupTitle {
text-align: center;
}
@media only screen and (max-width: 800px) {
br.hide-on-mobile {
display: none;

View File

@ -65,6 +65,9 @@ export default defineComponent({
hideFeaturedChannels: function() {
return this.$store.getters.getHideFeaturedChannels
},
hideChannelShorts: function() {
return this.$store.getters.getHideChannelShorts
},
hideChannelPlaylists: function() {
return this.$store.getters.getHideChannelPlaylists
},
@ -112,6 +115,7 @@ export default defineComponent({
'updateChannelsHidden',
'updateShowDistractionFreeTitles',
'updateHideFeaturedChannels',
'updateHideChannelShorts',
'updateHideChannelPlaylists',
'updateHideChannelCommunity'
])

View File

@ -2,76 +2,13 @@
<ft-settings-section
:title="$t('Settings.Distraction Free Settings.Distraction Free Settings')"
>
<h4
class="groupTitle"
>
{{ $t('Settings.Distraction Free Settings.Sections.Side Bar') }}
</h4>
<div class="switchColumnGrid">
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Video Views')"
:compact="true"
:default-value="hideVideoViews"
@change="updateHideVideoViews"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Video Likes And Dislikes')"
:compact="true"
:default-value="hideVideoLikesAndDislikes"
@change="updateHideVideoLikesAndDislikes"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Channel Subscribers')"
:compact="true"
:default-value="hideChannelSubscriptions"
@change="updateHideChannelSubscriptions"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Comment Likes')"
:compact="true"
:default-value="hideCommentLikes"
@change="updateHideCommentLikes"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Active Subscriptions')"
:compact="true"
:default-value="hideActiveSubscriptions"
@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"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Chapters')"
:compact="true"
:default-value="hideChapters"
@change="updateHideChapters"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Featured Channels')"
:compact="true"
:default-value="hideFeaturedChannels"
@change="updateHideFeaturedChannels"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Channel Playlists')"
:compact="true"
:default-value="hideChannelPlaylists"
@change="updateHideChannelPlaylists"
/>
</div>
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Recommended Videos')"
:compact="true"
:default-value="hideRecommendedVideos"
@change="handleHideRecommendedVideos"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Trending Videos')"
:compact="true"
@ -84,18 +21,137 @@
:default-value="hidePopularVideos"
@change="updateHidePopularVideos"
/>
</div>
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Playlists')"
:compact="true"
:default-value="hidePlaylists"
@change="updateHidePlaylists"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Active Subscriptions')"
:compact="true"
:default-value="hideActiveSubscriptions"
@change="updateHideActiveSubscriptions"
/>
</div>
</div>
<h4
class="groupTitle"
>
{{ $t('Settings.Distraction Free Settings.Sections.Channel Page') }}
</h4>
<div class="switchColumnGrid">
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Channel Shorts')"
:compact="true"
:default-value="hideChannelShorts"
@change="updateHideChannelShorts"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Channel Playlists')"
:compact="true"
:default-value="hideChannelPlaylists"
@change="updateHideChannelPlaylists"
/>
</div>
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Channel Community')"
:compact="true"
:default-value="hideChannelCommunity"
@change="updateHideChannelCommunity"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Featured Channels')"
:compact="true"
:default-value="hideFeaturedChannels"
@change="updateHideFeaturedChannels"
/>
</div>
</div>
<h4
class="groupTitle"
>
{{ $t('Settings.Distraction Free Settings.Sections.Watch Page') }}
</h4>
<div class="switchColumnGrid">
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Video Likes And Dislikes')"
:compact="true"
:default-value="hideVideoLikesAndDislikes"
@change="updateHideVideoLikesAndDislikes"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Chapters')"
:compact="true"
:default-value="hideChapters"
@change="updateHideChapters"
/>
<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 Comment Likes')"
:compact="true"
:default-value="hideCommentLikes"
@change="updateHideCommentLikes"
/>
</div>
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Live Chat')"
:compact="true"
:default-value="hideLiveChat"
@change="updateHideLiveChat"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Recommended Videos')"
:compact="true"
:default-value="hideRecommendedVideos"
@change="handleHideRecommendedVideos"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Comments')"
:compact="true"
:default-value="hideComments"
@change="updateHideComments"
/>
</div>
</div>
<h4
class="groupTitle"
>
{{ $t('Settings.Distraction Free Settings.Sections.General') }}
</h4>
<div class="switchColumnGrid">
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Video Views')"
:compact="true"
:default-value="hideVideoViews"
@change="updateHideVideoViews"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Channel Subscribers')"
:compact="true"
:default-value="hideChannelSubscriptions"
@change="updateHideChannelSubscriptions"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Sharing Actions')"
:compact="true"
:default-value="hideSharingActions"
@change="updateHideSharingActions"
/>
</div>
<div class="switchColumn">
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Live Streams')"
:compact="true"
@ -108,24 +164,12 @@
:default-value="hideUpcomingPremieres"
@change="updateHideUpcomingPremieres"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Comments')"
:compact="true"
:default-value="hideComments"
@change="updateHideComments"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Display Titles Without Excessive Capitalisation')"
:compact="true"
:default-value="showDistractionFreeTitles"
@change="updateShowDistractionFreeTitles"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Channel Community')"
:compact="true"
:default-value="hideChannelCommunity"
@change="updateHideChannelCommunity"
/>
</div>
</div>
<br class="hide-on-mobile">

View File

@ -45,7 +45,7 @@
@click="handleExternalPlayer"
/>
<ft-icon-button
v-if="!isLive"
v-if="!isUpcoming"
:title="$t('Video.Save Video')"
:icon="['fas', 'star']"
class="favoritesIcon"

View File

@ -361,6 +361,8 @@ export default defineComponent({
navigator.mediaSession.setActionHandler('play', () => this.player.play())
navigator.mediaSession.setActionHandler('pause', () => this.player.pause())
}
window.addEventListener('beforeunload', this.stopPowerSaveBlocker)
},
beforeDestroy: function () {
document.removeEventListener('keydown', this.keyboardShortcutHandler)
@ -379,10 +381,8 @@ export default defineComponent({
navigator.mediaSession.playbackState = 'none'
}
if (process.env.IS_ELECTRON && this.powerSaveBlocker !== null) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.STOP_POWER_SAVE_BLOCKER, this.powerSaveBlocker)
}
this.stopPowerSaveBlocker()
window.removeEventListener('beforeunload', this.stopPowerSaveBlocker)
},
methods: {
initializePlayer: async function () {
@ -595,11 +595,7 @@ export default defineComponent({
navigator.mediaSession.playbackState = 'none'
}
if (process.env.IS_ELECTRON && this.powerSaveBlocker !== null) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.STOP_POWER_SAVE_BLOCKER, this.powerSaveBlocker)
this.powerSaveBlocker = null
}
this.stopPowerSaveBlocker()
})
this.player.on('error', (error, message) => {
@ -609,11 +605,7 @@ export default defineComponent({
navigator.mediaSession.playbackState = 'none'
}
if (process.env.IS_ELECTRON && this.powerSaveBlocker !== null) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.STOP_POWER_SAVE_BLOCKER, this.powerSaveBlocker)
this.powerSaveBlocker = null
}
this.stopPowerSaveBlocker()
})
this.player.on('play', async () => {
@ -633,11 +625,7 @@ export default defineComponent({
navigator.mediaSession.playbackState = 'paused'
}
if (process.env.IS_ELECTRON && this.powerSaveBlocker !== null) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.STOP_POWER_SAVE_BLOCKER, this.powerSaveBlocker)
this.powerSaveBlocker = null
}
this.stopPowerSaveBlocker()
})
this.player.on(this.statsModalEventName, () => {
@ -1319,13 +1307,16 @@ export default defineComponent({
},
toggleCaptions: function () {
const tracks = this.player.textTracks().tracks_
// skip videojs-http-streaming's segment-metadata track
// https://github.com/videojs/http-streaming#segment-metadata
const trackIndex = this.useDash ? 1 : 0
if (tracks.length > 1) {
if (tracks[1].mode === 'showing') {
tracks[1].mode = 'disabled'
const tracks = this.player.textTracks()
if (tracks.length > trackIndex) {
if (tracks[trackIndex].mode === 'showing') {
tracks[trackIndex].mode = 'disabled'
} else {
tracks[1].mode = 'showing'
tracks[trackIndex].mode = 'showing'
}
}
},
@ -2116,6 +2107,14 @@ export default defineComponent({
}
},
stopPowerSaveBlocker: function() {
if (process.env.IS_ELECTRON && this.powerSaveBlocker !== null) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.STOP_POWER_SAVE_BLOCKER, this.powerSaveBlocker)
this.powerSaveBlocker = null
}
},
...mapActions([
'updateDefaultCaptionSettings',
'parseScreenshotCustomFileName',

View File

@ -41,20 +41,31 @@ export default defineComponent({
return this.$store.getters.getThumbnailPreference
},
backendPreference: function () {
return this.$store.getters.getBackendPreference
},
hideViews: function () {
return this.$store.getters.getHideVideoViews
},
thumbnail: function () {
let baseUrl
if (this.backendPreference === 'invidious') {
baseUrl = this.currentInvidiousInstance
} else {
baseUrl = 'https://i.ytimg.com'
}
switch (this.thumbnailPreference) {
case 'start':
return `https://i.ytimg.com/vi/${this.firstVideoId}/mq1.jpg`
return `${baseUrl}/vi/${this.firstVideoId}/mq1.jpg`
case 'middle':
return `https://i.ytimg.com/vi/${this.firstVideoId}/mq2.jpg`
return `${baseUrl}/vi/${this.firstVideoId}/mq2.jpg`
case 'end':
return `https://i.ytimg.com/vi/${this.firstVideoId}/mq3.jpg`
return `${baseUrl}/vi/${this.firstVideoId}/mq3.jpg`
default:
return `https://i.ytimg.com/vi/${this.firstVideoId}/mqdefault.jpg`
return `${baseUrl}/vi/${this.firstVideoId}/mqdefault.jpg`
}
}
},

View File

@ -290,3 +290,15 @@ export function filterInvidiousFormats(formats, allowAv1 = false) {
// }
return [...audioFormats, ...h264Formats]
}
export async function getHashtagInvidious(hashtag, page) {
const payload = {
resource: 'hashtag',
id: hashtag,
params: {
page
}
}
const response = await invidiousAPICall(payload)
return response.results
}

View File

@ -9,35 +9,35 @@ import router from '../router/index'
// https://support.google.com/youtube/answer/11585688#change_handle
export const CHANNEL_HANDLE_REGEX = /^@[\w.-]{3,30}$/
const PUBLISHED_TEXT_REGEX = /(\d+)\s?([a-z]+)/i
/**
* @param {string} publishedText
*/
export function calculatePublishedDate(publishedText) {
const date = new Date()
if (publishedText === 'Live') {
return publishedText
}
const textSplit = publishedText.split(' ')
const match = publishedText.match(PUBLISHED_TEXT_REGEX)
if (textSplit[0].toLowerCase() === 'streamed') {
textSplit.shift()
}
const timeFrame = textSplit[1]
const timeAmount = parseInt(textSplit[0])
const timeFrame = match[2]
const timeAmount = parseInt(match[1])
let timeSpan = null
if (timeFrame.indexOf('second') > -1) {
if (timeFrame.startsWith('second') || timeFrame === 's') {
timeSpan = timeAmount * 1000
} else if (timeFrame.indexOf('minute') > -1) {
} else if (timeFrame.startsWith('minute') || timeFrame === 'm') {
timeSpan = timeAmount * 60000
} else if (timeFrame.indexOf('hour') > -1) {
} else if (timeFrame.startsWith('hour') || timeFrame === 'h') {
timeSpan = timeAmount * 3600000
} else if (timeFrame.indexOf('day') > -1) {
} else if (timeFrame.startsWith('day') || timeFrame === 'd') {
timeSpan = timeAmount * 86400000
} else if (timeFrame.indexOf('week') > -1) {
} else if (timeFrame.startsWith('week') || timeFrame === 'w') {
timeSpan = timeAmount * 604800000
} else if (timeFrame.indexOf('month') > -1) {
} else if (timeFrame.startsWith('month') || timeFrame === 'mo') {
timeSpan = timeAmount * 2592000000
} else if (timeFrame.indexOf('year') > -1) {
} else if (timeFrame.startsWith('year') || timeFrame === 'y') {
timeSpan = timeAmount * 31556952000
}
@ -53,33 +53,36 @@ export function toLocalePublicationString ({ publishText, isLive = false, isUpco
} else if (isRSS) {
return publishText
}
const strings = publishText.split(' ')
// filters out the streamed x hours ago and removes the streamed in order to keep the rest of the code working
if (strings[0].toLowerCase() === 'streamed') {
strings.shift()
}
const singular = (strings[0] === '1')
const match = publishText.match(PUBLISHED_TEXT_REGEX)
const singular = (match[1] === '1')
let translationKey = ''
switch (strings[1].substring(0, 2)) {
switch (match[2].substring(0, 2)) {
case 'se':
case 's':
translationKey = 'Video.Published.Second'
break
case 'mi':
case 'm':
translationKey = 'Video.Published.Minute'
break
case 'ho':
case 'h':
translationKey = 'Video.Published.Hour'
break
case 'da':
case 'd':
translationKey = 'Video.Published.Day'
break
case 'we':
case 'w':
translationKey = 'Video.Published.Week'
break
case 'mo':
translationKey = 'Video.Published.Month'
break
case 'ye':
case 'y':
translationKey = 'Video.Published.Year'
break
default:
@ -90,7 +93,7 @@ export function toLocalePublicationString ({ publishText, isLive = false, isUpco
}
const unit = i18n.t(translationKey)
return i18n.t('Video.Publicationtemplate', { number: strings[0], unit })
return i18n.t('Video.Publicationtemplate', { number: match[1], unit })
}
export function buildVTTFileLocally(storyboard, videoLengthSeconds) {

View File

@ -195,6 +195,7 @@ const state = {
hideActiveSubscriptions: false,
hideChannelCommunity: false,
hideChannelPlaylists: false,
hideChannelShorts: false,
hideChannelSubscriptions: false,
hideCommentLikes: false,
hideComments: false,

View File

@ -315,7 +315,7 @@ const actions = {
let urlType = 'unknown'
const channelPattern =
/^\/(?:(?:channel|user|c)\/)?(?<channelId>[^/]+)(?:\/(?<tab>join|featured|videos|live|streams|playlists|about|community|channels))?\/?$/
/^\/(?:(?:channel|user|c)\/)?(?<channelId>[^/]+)(?:\/(?<tab>join|featured|videos|shorts|live|streams|playlists|about|community|channels))?\/?$/
const hashtagPattern = /^\/hashtag\/(?<tag>[^#&/?]+)$/
@ -427,6 +427,9 @@ const actions = {
let subPath = null
switch (match.groups.tab) {
case 'shorts':
subPath = 'shorts'
break
case 'live':
case 'streams':
subPath = 'live'

View File

@ -188,6 +188,10 @@ export default defineComponent({
return this.$store.getters.getHideSharingActions
},
hideChannelShorts: function () {
return this.$store.getters.getHideChannelShorts
},
hideLiveStreams: function () {
return this.$store.getters.getHideLiveStreams
},
@ -211,6 +215,11 @@ export default defineComponent({
]
// remove tabs from the array based on user settings
if (this.hideChannelShorts) {
const index = values.indexOf('shorts')
values.splice(index, 1)
}
if (this.hideLiveStreams) {
const index = values.indexOf('live')
values.splice(index, 1)
@ -268,6 +277,10 @@ export default defineComponent({
this.showLiveSortBy = true
this.showPlaylistSortBy = true
if (this.hideChannelShorts && currentTab === 'shorts') {
currentTab = 'videos'
}
if (this.hideLiveStreams && currentTab === 'live') {
currentTab = 'videos'
}
@ -371,6 +384,10 @@ export default defineComponent({
let currentTab = this.$route.params.currentTab ?? 'videos'
if (this.hideChannelShorts && currentTab === 'shorts') {
currentTab = 'videos'
}
if (this.hideLiveStreams && currentTab === 'live') {
currentTab = 'videos'
}
@ -595,7 +612,7 @@ export default defineComponent({
this.getChannelVideosLocal()
}
if (channel.has_shorts) {
if (!this.hideChannelShorts && channel.has_shorts) {
this.getChannelShortsLocal()
}
@ -887,7 +904,7 @@ export default defineComponent({
this.channelInvidiousVideos()
}
if (response.tabs.includes('shorts')) {
if (!this.hideChannelShorts && response.tabs.includes('shorts')) {
this.channelInvidiousShorts()
}

View File

@ -99,6 +99,7 @@
{{ $t("Channel.Videos.Videos").toUpperCase() }}
</div>
<div
v-if="!hideChannelShorts"
id="shortsTab"
class="tab"
:class="(currentTab==='shorts')?'selectedTab':''"
@ -203,7 +204,7 @@
@change="videoSortBy = $event"
/>
<ft-select
v-if="showShortSortBy"
v-if="!hideChannelShorts && showShortSortBy"
v-show="currentTab === 'shorts' && latestShorts.length > 0"
class="sortSelect"
:value="videoShortLiveSelectValues[0]"
@ -254,14 +255,14 @@
</p>
</ft-flex-box>
<ft-element-list
v-if="currentTab === 'shorts'"
v-if="!hideChannelShorts && currentTab === 'shorts'"
id="shortPanel"
:data="latestShorts"
role="tabpanel"
aria-labelledby="shortsTab"
/>
<ft-flex-box
v-if="currentTab === 'shorts' && latestShorts.length === 0"
v-if="!hideChannelShorts && currentTab === 'shorts' && latestShorts.length === 0"
>
<p class="message">
{{ $t("Channel.Shorts.This channel does not currently have any shorts") }}

View File

@ -5,7 +5,8 @@ import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
import FtLoader from '../../components/ft-loader/ft-loader.vue'
import packageDetails from '../../../../package.json'
import { getHashtagLocal, parseLocalListVideo } from '../../helpers/api/local'
import { isNullOrEmpty } from '../../helpers/utils'
import { copyToClipboard, isNullOrEmpty, showToast } from '../../helpers/utils'
import { getHashtagInvidious } from '../../helpers/api/invidious'
export default defineComponent({
name: 'Hashtag',
@ -21,6 +22,7 @@ export default defineComponent({
hashtagContinuationData: null,
videos: [],
apiUsed: 'local',
pageNumber: 1,
isLoading: true
}
},
@ -34,66 +36,123 @@ export default defineComponent({
},
showFetchMoreButton() {
return !isNullOrEmpty(this.hashtagContinuationData)
return !isNullOrEmpty(this.hashtagContinuationData) || this.apiUsed === 'invidious'
}
},
watch: {
$route() {
this.resetData()
this.getHashtag()
}
},
mounted: function() {
this.resetData()
this.getHashtag()
},
methods: {
resetData: function() {
this.isLoading = true
this.hashtag = ''
this.hashtagContinuationData = null
this.videos = []
this.apiUsed = 'local'
this.getHashtag()
}
},
mounted: function() {
this.isLoading = true
this.getHashtag()
},
methods: {
this.pageNumber = 1
},
getHashtag: async function() {
const hashtag = this.$route.params.hashtag
if (this.backendFallback || this.backendPreference === 'local') {
await this.getLocalHashtag(hashtag)
} else {
this.getInvidiousHashtag(hashtag)
await this.getInvidiousHashtag(hashtag)
}
document.title = `${this.hashtag} - ${packageDetails.productName}`
},
getInvidiousHashtag: function(hashtag) {
this.hashtag = '#' + hashtag
this.apiUsed = 'Invidious'
this.isLoading = false
getInvidiousHashtag: async function(hashtag, page) {
try {
const videos = await getHashtagInvidious(hashtag, page)
this.hashtag = '#' + hashtag
this.isLoading = false
this.apiUsed = 'invidious'
this.videos = this.videos.concat(videos)
this.pageNumber += 1
} catch (error) {
console.error(error)
const errorMessage = this.$t('Invidious API Error (Click to copy)')
showToast(`${errorMessage}: ${error}`, 10000, () => {
copyToClipboard(error)
})
if (process.env.IS_ELECTRON && this.backendPreference === 'invidious' && this.backendFallback) {
showToast(this.$t('Falling back to Local API'))
this.resetData()
this.getLocalHashtag(hashtag)
} else {
this.isLoading = false
}
}
},
getLocalHashtag: async function(hashtag) {
const hashtagData = await getHashtagLocal(hashtag)
this.hashtag = hashtagData.header.hashtag
this.videos = hashtagData.contents.contents.filter(item =>
item.type !== 'ContinuationItem'
).map(item =>
parseLocalListVideo(item.content)
)
this.apiUsed = 'local'
this.hashtagContinuationData = hashtagData.has_continuation ? hashtagData : null
this.isLoading = false
try {
const hashtagData = await getHashtagLocal(hashtag)
this.hashtag = hashtagData.header.hashtag
this.videos = hashtagData.contents.contents.filter(item =>
item.type !== 'ContinuationItem'
).map(item =>
parseLocalListVideo(item.content)
)
this.apiUsed = 'local'
this.hashtagContinuationData = hashtagData.has_continuation ? hashtagData : null
this.isLoading = false
} catch (error) {
console.error(error)
const errorMessage = this.$t('Local API Error (Click to copy)')
showToast(`${errorMessage}: ${error}`, 10000, () => {
copyToClipboard(error)
})
if (this.backendPreference === 'local' && this.backendFallback) {
showToast(this.$t('Falling back to Invidious API'))
this.resetData()
this.getInvidiousHashtag(hashtag)
} else {
this.isLoading = false
}
}
},
getLocalHashtagMore: async function() {
const continuation = await this.hashtagContinuationData.getContinuationData()
const newVideos = continuation.on_response_received_actions[0].contents.filter(item =>
item.type !== 'ContinuationItem'
).map(item =>
parseLocalListVideo(item.content)
)
this.hashtagContinuationData = continuation.has_continuation ? continuation : null
this.videos = this.videos.concat(newVideos)
try {
const continuation = await this.hashtagContinuationData.getContinuationData()
const newVideos = continuation.on_response_received_actions[0].contents.filter(item =>
item.type !== 'ContinuationItem'
).map(item =>
parseLocalListVideo(item.content)
)
this.hashtagContinuationData = continuation.has_continuation ? continuation : null
this.videos = this.videos.concat(newVideos)
} catch (error) {
console.error(error)
const errorMessage = this.$t('Local API Error (Click to copy)')
showToast(`${errorMessage}: ${error}`, 10000, () => {
copyToClipboard(error)
})
if (this.backendPreference === 'local' && this.backendFallback) {
showToast(this.$t('Falling back to Invidious API'))
const hashtag = this.hashtag.substring(1)
this.resetData()
this.getLocalHashtag(hashtag)
} else {
this.isLoading = false
}
}
},
handleFetchMore: function() {
if (this.apiUsed === 'local') {
this.getLocalHashtagMore()
} else if (this.apiUsed === 'invidious') {
this.getInvidiousHashtag(this.hashtag.substring(1), this.pageNumber)
}
}
}

View File

@ -11,11 +11,11 @@
class="elementList"
>
<ft-element-list
v-if="backendFallback || backendPreference === 'local' && videos.length > 0"
v-if="videos.length > 0"
:data="videos"
/>
<ft-flex-box
v-else-if="backendFallback || backendPreference === 'local' && videos.length === 0"
v-else-if="videos.length === 0"
>
<p
class="message"
@ -23,15 +23,6 @@
{{ $t("Hashtag.This hashtag does not currently have any videos") }}
</p>
</ft-flex-box>
<ft-flex-box
v-else
>
<p
class="message"
>
{{ $t("Hashtag.You can only view hashtag pages through the Local API") }}
</p>
</ft-flex-box>
</div>
<div
v-if="showFetchMoreButton"

View File

@ -397,6 +397,11 @@ Settings:
Hide Channel Playlists: إخفاء قوائم تشغيل القناة
Hide Channel Community: إخفاء مجتمع القناة
Hide Channel Shorts: إخفاء Shorts القناة
Sections:
Side Bar: الشريط الجانبي
Channel Page: صفحة القناة
Watch Page: صفحة المشاهدة
General: عام
The app needs to restart for changes to take effect. Restart and apply change?: البرنامج
يحتاج لإعادة التشغيل كي يسري مفعول التغييرات. هل تريد إعادة التشغيل و تطبيق التغييرات؟
Proxy Settings:
@ -977,5 +982,3 @@ Hashtag:
This hashtag does not currently have any videos: هذا الهاشتاج ليس لديه حاليا أي
مقاطع فيديو
Hashtag: هاشتاج
You can only view hashtag pages through the Local API: يمكنك فقط عرض صفحات الهاشتاج
من خلال واجهة برمجة التطبيقات (API) المحلية

View File

@ -406,7 +406,12 @@ Settings:
Hide Featured Channels: Скриване на препоръчаните канали
Hide Channel Playlists: Скриване на плейлистите на каналите
Hide Channel Community: Скриване на общността на каналите
Hide Channel Shorts: Скриване на късите видеа на канала
Hide Channel Shorts: Скриване кратките видеа на канала
Sections:
Channel Page: Страница на канала
Side Bar: Странична лента
Watch Page: Страница за гледане
General: Общи
The app needs to restart for changes to take effect. Restart and apply change?: Приложението
трябва да се рестартира за да се приложат промените. Рестартиране?
Proxy Settings:
@ -632,8 +637,8 @@ Channel:
никакви потоци на живо
Shorts:
This channel does not currently have any shorts: В момента този канал няма никакви
къси видеа
Shorts: Къси видеа
кратки видеа
Shorts: Кратки видеа
Video:
Mark As Watched: 'Отбелязване като гледано'
Remove From History: 'Премахване от историята'
@ -1002,5 +1007,3 @@ Ok: Добре
Hashtag:
Hashtag: Хаштаг
This hashtag does not currently have any videos: В момента този хаштаг няма видеа
You can only view hashtag pages through the Local API: Можете да разглеждате страници
с хаштагове само чрез локалния вътрешен интерфейс

View File

@ -329,6 +329,11 @@ Settings:
Hide Channel Playlists: Skrýt playlisty kanálu
Hide Channel Community: Skrýt komunitu kanálu
Hide Channel Shorts: Skrýt Shorts kanálu
Sections:
Side Bar: Postranní lišta
Channel Page: Stránka kanálu
Watch Page: Stránka sledování
General: Obecné
Data Settings:
Data Settings: 'Nastavení dat'
Select Import Type: 'Vybrat typ importu'
@ -974,7 +979,5 @@ Chapters:
Preferences: Předvolby
Ok: Ok
Hashtag:
You can only view hashtag pages through the Local API: Stránky hashtagů lze momentálně
procházet pouze s lokálním API
Hashtag: Hashtag
This hashtag does not currently have any videos: Tento hashtag nemá žádná videa

View File

@ -273,6 +273,8 @@ Settings:
Enter Fullscreen on Display Rotate: Bei drehen des Bildschirms zu Vollbild wechseln
Skip by Scrolling Over Video Player: Überspringen durch Scrollen über den Videoplayer
Allow DASH AV1 formats: DASH AV1-Formate zulassen
Comment Auto Load:
Comment Auto Load: Kommentare automatisch laden
Subscription Settings:
Subscription Settings: Abonnement-Einstellungen
Hide Videos on Watch: Verstecke Videos bei Wiedergabe
@ -406,6 +408,9 @@ Settings:
Display Titles Without Excessive Capitalisation: Titel ohne übermäßige Großschreibung
anzeigen
Hide Channel Playlists: Kanal-Wiedergabelisten ausblenden
Hide Channel Community: Kanal-Gemeinschaft ausblenden
Hide Channel Shorts: Shorts des jeweiligen Kanals ausblenden
Hide Featured Channels: Hervorgehobene Kanäle ausblenden
The app needs to restart for changes to take effect. Restart and apply change?: Um
die Änderungen anzuwenden muss die Anwendung neustarten. Jetzt neustarten und
Änderungen aktivieren?
@ -581,8 +586,8 @@ Channel:
Location: Standort
Joined: Beigetreten
Added channel to your subscriptions: Der Kanal wurde deinen Abonnements hinzugefügt
Removed subscription from {count} other channel(s): Es wurden {count} andere/n Kanäle/n
deabonniert
Removed subscription from {count} other channel(s): Es wurden {count} anderen Kanälen
gekündigt
Channel has been removed from your subscriptions: Der Kanal wurde von deinen Abonnements
entfernt
Channel Tabs: Kanal-Registerkarten
@ -598,6 +603,10 @@ Channel:
Live: Live
This channel does not currently have any live streams: Dieser Kanal hat derzeit
keine Live-Streams
Shorts:
Shorts: Shorts
This channel does not currently have any shorts: Dieser Kanal hat derzeit keine
Shorts
Video:
Open in YouTube: In YouTube öffnen
Copy YouTube Link: YouTube-Link kopieren
@ -793,10 +802,10 @@ Share:
Invidious Channel URL copied to clipboard: Invidious Kanallink in die Zwischenablage
kopiert
Share Channel: Kanal teilen
Mini Player: Mini-Abspieler
Mini Player: Mini-Wiedergabefenster
Comments:
Comments: Kommentare
Click to View Comments: Klicke, um Kommentare anzuzeigen
Click to View Comments: Hier klicken, um Kommentare anzuzeigen
Getting comment replies, please wait: Kommentare werden geladen, bitte warten
Show Comments: Zeige Kommentare
Hide Comments: Verstecke Kommentare
@ -1017,3 +1026,7 @@ Chapters:
aktuelles Kapitel: {chapterName}'
Preferences: Einstellungen
Ok: OK
Hashtag:
Hashtag: Schlagwort
This hashtag does not currently have any videos: Unter diesem Schlagwort konnten
derzeit keine Videos gefunden werden

View File

@ -280,6 +280,8 @@ Settings:
Enter Fullscreen on Display Rotate: Μετάβαση στην Πλήρη Οθόνη κατά την Περιστροφή
Οθόνης
Allow DASH AV1 formats: Να επιτρέπονται οι μορφές DASH AV1
Comment Auto Load:
Comment Auto Load: Αυτόματη Φόρτωση Σχολίων
Privacy Settings:
Privacy Settings: 'Ρυθμίσεις απορρήτου'
Remember History: 'Διατήρηση ιστορικού'
@ -406,6 +408,15 @@ Settings:
Hide Video Description: Απόκρυψη Περιγραφής Βίντεο
Display Titles Without Excessive Capitalisation: Εμφάνιση Τίτλων Χωρίς Υπερβολική
Χρήση Κεφαλαίων
Sections:
Side Bar: Πλαϊνή Μπάρα
Channel Page: Σελίδα Καναλιού
Watch Page: Σελίδα Παρακολούθησης
General: Γενικά
Hide Featured Channels: Απόκρυψη Προτεινόμενων Καναλιών
Hide Channel Playlists: Απόκρυψη Λιστών Αναπαραγωγής Καναλιών
Hide Channel Community: Απόκρυψη Κοινότητας Καναλιού
Hide Channel Shorts: Απόκρυψη Shorts Καναλιού
The app needs to restart for changes to take effect. Restart and apply change?: Η
εφαρμογή πρέπει να κάνει επανεκκίνηση για να εφαρμοστούν οι αλλαγές. Επανεκκίνηση
και εφαρμογή αλλαγών;
@ -640,6 +651,10 @@ Channel:
Live: Ζωντανά
This channel does not currently have any live streams: Αυτό το κανάλι δεν έχει
προς το παρόν ζωντανές ροές
Shorts:
Shorts: Shorts
This channel does not currently have any shorts: Αυτό το κανάλι δεν έχει προς
το παρόν κανένα shorts
Video:
Mark As Watched: 'Επισήμανση ως παρακολουθημένο'
Remove From History: 'Κατάργηση από το ιστορικό'
@ -782,9 +797,9 @@ Video:
Scroll to Bottom: Κύλιση προς τα Κάτω
Premieres: Πρεμιέρες
Upcoming: Επερχόμενο
'Live Chat is unavailable for this stream. It may have been disabled by the uploader.': "Η\
\ ντανή συνομιλία δεν είναι διαθέσιμη για αυτήν τη ροή.\nΜπορεί να έχει απενεργοποιηθεί\
\ από τον χρήστη."
'Live Chat is unavailable for this stream. It may have been disabled by the uploader.': "Η
ντανή συνομιλία δεν είναι διαθέσιμη για αυτήν τη ροή.\nΜπορεί να έχει απενεργοποιηθεί
από τον χρήστη."
Videos:
#& Sort By
Sort By:
@ -881,8 +896,8 @@ The playlist has been reversed: 'Η λίστα αναπαραγωγής έχει
Playing Next Video: 'Αναπαραγωγή του επόμενου βίντεο'
Playing Previous Video: 'Αναπαραγωγή προηγούμενου βίντεο'
Canceled next video autoplay: 'Η αναπαραγωγή του επόμενου βίντεο έχει ακυρωθεί'
'The playlist has ended. Enable loop to continue playing': 'Η λίστα αναπαραγωγής
έχει τελειώσει. Ενεργοποιήστε τη λειτουργία επανάληψης για συνέχιση της αναπαραγωγής'
'The playlist has ended. Enable loop to continue playing': 'Η λίστα αναπαραγωγής έχει
τελειώσει. Ενεργοποιήστε τη λειτουργία επανάληψης για συνέχιση της αναπαραγωγής'
Yes: 'Ναι'
No: 'Όχι'
@ -941,10 +956,10 @@ Tooltips:
απαιτεί έναν Invidious server για να συνδεθεί.
Invidious Instance: Το στιγμιότυπο Invidious στο οποίο το FreeTube θα συνδεθεί
για κλήσεις διεπαφής.
External Link Handling: "Επιλέξτε την προεπιλεγμένη συμπεριφορά όταν κάνετε κλικ\
\ σε έναν σύνδεσμο, ο οποίος δεν μπορεί να ανοίξει στο FreeTube.\nΑπό προεπιλογή,\
\ το FreeTube θα ανοίξει τον σύνδεσμο που έχει πατηθεί στο προεπιλεγμένο πρόγραμμα\
\ περιήγησης.\n"
External Link Handling: "Επιλέξτε την προεπιλεγμένη συμπεριφορά όταν κάνετε κλικ
σε έναν σύνδεσμο, ο οποίος δεν μπορεί να ανοίξει στο FreeTube.\nΑπό προεπιλογή,
το FreeTube θα ανοίξει τον σύνδεσμο που έχει πατηθεί στο προεπιλεγμένο πρόγραμμα
περιήγησης.\n"
Privacy Settings:
Remove Video Meta Files: Όταν είναι ενεργοποιημένο, το FreeTube διαγράφει αυτόματα
τα μετα-αρχεία που δημιουργήθηκαν κατά την αναπαραγωγή βίντεο, όταν η σελίδα
@ -1027,3 +1042,7 @@ Age Restricted:
Ok: Εντάξει
Preferences: Προτιμήσεις
Screenshot Success: Αποθηκευμένο στιγμιότυπο οθόνης ως "{filePath}"
Hashtag:
Hashtag: Hashtag
This hashtag does not currently have any videos: Αυτό το hashtag δεν έχει προς το
παρόν κανένα βίντεο

View File

@ -321,6 +321,11 @@ Settings:
Fetch Automatically: Fetch Feed Automatically
Distraction Free Settings:
Distraction Free Settings: Distraction Free Settings
Sections:
Side Bar: Side Bar
Channel Page: Channel Page
Watch Page: Watch Page
General: General
Hide Video Views: Hide Video Views
Hide Video Likes And Dislikes: Hide Video Likes And Dislikes
Hide Channel Subscribers: Hide Channel Subscribers
@ -889,7 +894,6 @@ Hashtag:
Hashtag: Hashtag
This hashtag does not currently have any videos: This hashtag does not currently
have any videos
You can only view hashtag pages through the Local API: You can only view hashtag pages through the Local API
Yes: Yes
No: No
Ok: Ok

View File

@ -272,6 +272,8 @@ Settings:
File Name Label: Filename pattern
Skip by Scrolling Over Video Player: Skip by scrolling over video player
Allow DASH AV1 formats: Allow DASH AV1 formats
Comment Auto Load:
Comment Auto Load: Comment auto load
External Player Settings:
External Player Settings: External Player Settings
External Player: External Player
@ -405,6 +407,15 @@ Settings:
Hide Channels Placeholder: Channel name or ID
Display Titles Without Excessive Capitalisation: Display Titles Without Excessive
Capitalisation
Hide Featured Channels: Hide featured channels
Hide Channel Playlists: Hide channel playlists
Hide Channel Community: Hide channel community
Hide Channel Shorts: Hide channel shorts
Sections:
Side Bar: Side Bar
General: General
Channel Page: Channel Page
Watch Page: Watch Page
The app needs to restart for changes to take effect. Restart and apply change?: The
app needs to restart for changes to take effect. Do you want to restart and apply
the changes?
@ -599,6 +610,10 @@ Channel:
This channel does not currently have any live streams: This channel does not currently
have any live streams
Live: Live
Shorts:
Shorts: Shorts
This channel does not currently have any shorts: This channel does not currently
have any shorts
Video:
Mark As Watched: 'Mark As Watched'
Remove From History: 'Remove From History'
@ -632,8 +647,8 @@ Video:
supported in this build.'
'Chat is disabled or the Live Stream has ended.': 'Chat is disabled or the Live
Stream has ended.'
Live chat is enabled. Chat messages will appear here once sent.: 'Live chat is
enabled. Chat messages will appear here once sent.'
Live chat is enabled. Chat messages will appear here once sent.: 'Live chat is enabled. Chat
messages will appear here once sent.'
'Live Chat is currently not supported with the Invidious API. A direct connection to YouTube is required.': 'Live
Chat is currently not supported with the Invidious API. A direct connection to
YouTube is required.'
@ -881,9 +896,9 @@ Tooltips:
Preferred API Backend: Choose the back-end that FreeTube uses to obtain data.
The local API is a built-in extractor. The Invidious API requires an Invidious
server to connect to.
External Link Handling: "Choose the default behaviour when a link, which cannot\
\ be opened in FreeTube, is clicked.\nBy default FreeTube will open the clicked\
\ link in your default browser.\n"
External Link Handling: "Choose the default behaviour when a link, which cannot
be opened in FreeTube, is clicked.\nBy default FreeTube will open the clicked
link in your default browser.\n"
External Player Settings:
External Player: Choosing an external player will display an icon, for opening
the video (playlist if supported) in the external player, on the thumbnail.
@ -955,3 +970,7 @@ Clipboard:
a secure connection
Preferences: Preferences
Ok: OK
Hashtag:
Hashtag: Hashtag
This hashtag does not currently have any videos: This hashtag does not currently
have any videos

View File

@ -1010,5 +1010,3 @@ Hashtag:
Hashtag: Hashtag
This hashtag does not currently have any videos: Este hashtag no tiene actualmente
ningún vídeo
You can only view hashtag pages through the Local API: Sólo puedes ver las páginas
de los hashtags a través de la API local

View File

@ -6,7 +6,7 @@ FreeTube: 'FreeTube'
Zati hau ez dago prest oraindik. Itzuli aurrerago aurrerapenak egin direnean.
# Webkit Menu Bar
File: 'Artxibategia'
File: 'Fitxategia'
Quit: 'Irten'
Edit: 'Editatu'
Undo: 'Desegin'
@ -60,12 +60,14 @@ Search Filters:
Videos: 'Bideoak'
Channels: 'Kanalak'
#& Playlists
Movies: Filmeak
Duration:
Duration: 'Iraupena'
All Durations: 'Edozein iraupen'
Short (< 4 minutes): 'Laburra (<4 minutu)'
Long (> 20 minutes): 'Luzea (> 20 minutu)'
# On Search Page
Medium (4 - 20 minutes): Tartekoa (4 - 20 minutu)
Search Results: 'Emaitzak bilatu'
Fetching results. Please wait: 'Emaitzak ekartzen. Itxaron faborez'
Fetch more results: 'Emaitza gehiago ekarri'
@ -221,8 +223,8 @@ Settings:
Turn on Subtitles by Default: 'Aktibatu Azpitituluak lehenespenez'
Autoplay Videos: 'Bideoen erreprodukzio automatikoa'
Proxy Videos Through Invidious: 'Invidious bitarteko Proxy bideoak'
Autoplay Playlists: 'Erreprodukzio automatikoan diren erreprodukzio zerrendak'
Enable Theatre Mode by Default: 'Gaitu Antzerki modua lehenespenez'
Autoplay Playlists: 'Automatikoki erreproduzitu erreprodukzio-zerrendak'
Enable Theatre Mode by Default: 'Gaitu antzerki modua lehenespenez'
Default Volume: 'Lehenetsitako bolumena'
Default Playback Rate: 'Lehenetsitako erreprodukzio tasa'
Default Video Format:
@ -726,9 +728,9 @@ Tooltips:
Region for Trending: 'Joeren eskualdeak aukera ematen dizu bistaratu nahi dituzun
herrialdeko joerako bideoak hautatzeko. Youtube-k ez ditu onartzen bistaratzen
diren herrialde guztiak.'
External Link Handling: "Lehenetsitako portaera hautatu, Freetube-n ireki ezin\
\ den esteka bat klikatua izan denean.\nLehentasunez, Freetube-k lehenetsitako\
\ nabigatazailean irekiko du klikatutako esteka.\n"
External Link Handling: "Lehenetsitako portaera hautatu, Freetube-n ireki ezin
den esteka bat klikatua izan denean.\nLehentasunez, Freetube-k lehenetsitako
nabigatazailean irekiko du klikatutako esteka.\n"
Player Settings:
Force Local Backend for Legacy Formats: 'Soilik funtzionatzen du Individious-en
APIa zure lehenetsitakoa denean. Aukera dagoenean, tokiko APIa eta azken honek
@ -831,3 +833,4 @@ Age Restricted:
Type:
Channel: Kanala
Video: Bideoa
Preferences: Hobespenak

View File

@ -323,3 +323,5 @@ Search Bar:
Clear Input: پاک کردن ورودی
Are you sure you want to open this link?: آیا مطمئن هستید که میخواهید این لینک را
باز کنید؟
About:
Email: پست الکترونیکی

View File

@ -266,6 +266,8 @@ Settings:
Enter Fullscreen on Display Rotate: Siirry koko näytön kokoiseen näytön kiertoon
Skip by Scrolling Over Video Player: Ohita vierittämällä videosoittimen yli
Allow DASH AV1 formats: Salli DASH AV1 -formaatit
Comment Auto Load:
Comment Auto Load: Kommenttien automaattinen lataus
Subscription Settings:
Subscription Settings: 'Tilausasetukset'
Hide Videos on Watch: 'Piilota katsotut videot'
@ -393,6 +395,10 @@ Settings:
Hide Channels Placeholder: Kanavan nimi tai tunnus
Display Titles Without Excessive Capitalisation: Näytä otsikot ilman liiallista
isoja kirjaimia
Hide Featured Channels: Piilota esillä olevat kanavat
Hide Channel Playlists: Piilota kanavan soittolistat
Hide Channel Community: Piilota kanava yhteisö
Hide Channel Shorts: Piilota kanavan lyhytelokuvat
The app needs to restart for changes to take effect. Restart and apply change?: Sovellus
on käynnistettävä uudelleen, jotta muutokset tulevat voimaan. Käynnistetäänkö
uudelleen?
@ -504,7 +510,7 @@ About:
Please read the: Lue
View License: Katso lisenssi
Please check for duplicates before posting: Tarkista kopioiden varalta ennen julkaisua
Website: Verkkosivu
Website: Verkkosivusto
Mastodon: Mastodon:ista
Translate: Käännös
Credits: Kiitokset
@ -855,9 +861,9 @@ Tooltips:
alkuperäisen pikkukuvan sijaan.
Fallback to Non-Preferred Backend on Failure: Kun valitsemasi API kohtaa ongelman,
Freetube yrittää automaattisestia käyttää toissijaista vaihtoehtoa.
External Link Handling: "Valitse oletuskäyttäytyminen, kun linkkiä, jota ei voi\
\ avata FreeTubessa, napsautetaan.\nOletusarvoisesti FreeTube avaa napsautetun\
\ linkin oletusselaimessasi.\n"
External Link Handling: "Valitse oletuskäyttäytyminen, kun linkkiä, jota ei voi
avata FreeTubessa, napsautetaan.\nOletusarvoisesti FreeTube avaa napsautetun
linkin oletusselaimessasi.\n"
Subscription Settings:
Fetch Feeds from RSS: Kun valittuna, FreeTube käyttää RSS-syötettä oletusmetodin
sijaan tilauslistasi hakemisessa. RSS on nopeampi ja ehkäisee IP-estoa, mutta

View File

@ -419,6 +419,9 @@ Settings:
Display Titles Without Excessive Capitalisation: Afficher les titres sans majuscules
excessives
Hide Channel Playlists: Masquer les listes de lecture des chaînes
Hide Featured Channels: Masquer les chaînes en vedette
Hide Channel Community: Masquer la communauté de la chaîne
Hide Channel Shorts: Masquer les shorts de la chaîne
The app needs to restart for changes to take effect. Restart and apply change?: L'application
doit être redémarrée pour que les changements prennent effet. Redémarrer et appliquer
les changements ?
@ -1048,3 +1051,7 @@ Chapters:
chapitre actuel : {chapterName}'
Preferences: Préférences
Ok: OK
Hashtag:
Hashtag: Mot-clé
This hashtag does not currently have any videos: Ce mot-clé n'a actuellement aucune
vidéo

View File

@ -393,6 +393,11 @@ Settings:
Hide Channel Playlists: הסתרת רשימת נגינה של ערוץ
Hide Channel Community: הסתרת קהילת הערוץ
Hide Channel Shorts: הסתרת ה־Shorts של הערוץ
Sections:
Watch Page: עמוד הצפייה
Channel Page: עמוד ערוץ
Side Bar: סרגל צד
General: כללי
The app needs to restart for changes to take effect. Restart and apply change?: צריך
להפעיל את היישומון מחדש כדי שהשינויים ייכנסו לתוקף. להפעיל מחדש ולהחיל את השינוי?
Proxy Settings:
@ -960,6 +965,4 @@ Preferences: העדפות
Ok: אישור
Hashtag:
This hashtag does not currently have any videos: לתגית ההקבץ הזו אין סרטונים כרגע
You can only view hashtag pages through the Local API: אפשר לצפות בעמודי תגית הקבץ
דרך ה־API המקומי בלבד
Hashtag: תגית הקבץ

View File

@ -996,6 +996,4 @@ Preferences: Korisničke postavke
Ok: U redu
Hashtag:
This hashtag does not currently have any videos: Ovaj hashtag trenutačno nema videa
You can only view hashtag pages through the Local API: Stranice s hashtagovima možeš
gledati samo putem lokalnog API-ja
Hashtag: Hashtag

View File

@ -1002,5 +1002,3 @@ Hashtag:
Hashtag: Kettőskereszt
This hashtag does not currently have any videos: Ez a kettőskereszt jelenleg nem
rendelkezik videókkal
You can only view hashtag pages through the Local API: Kettőskereszt oldalait csak
a helyi API felületen keresztül tudja megtekinteni

View File

@ -409,9 +409,14 @@ Settings:
Display Titles Without Excessive Capitalisation: Visualizza i titoli senza un
uso eccessivo di maiuscole
Hide Featured Channels: Nascondi i canali in evidenza
Hide Channel Playlists: Nascondi le playlist dei canali
Hide Channel Playlists: Nascondi le playlist del canale
Hide Channel Community: Nascondi la comunità del canale
Hide Channel Shorts: Nascondi i video brevi del canale
Sections:
General: Generale
Watch Page: Pagina di visualizzazione
Side Bar: Barra laterale
Channel Page: Pagina del canale
The app needs to restart for changes to take effect. Restart and apply change?: L'app
deve essere riavviata affinché le modifiche abbiano effetto. Riavviare e applicare
la modifica?
@ -1021,5 +1026,3 @@ Hashtag:
This hashtag does not currently have any videos: Questo hashtag attualmente non
ha alcun video
Hashtag: Hashtag
You can only view hashtag pages through the Local API: Puoi visualizzare le pagine
hashtag solo tramite l'API locale

View File

@ -228,3 +228,5 @@ Channel:
Video:
External Player: {}
Tooltips: {}
About:
Email: ელფოსტა

View File

@ -1020,8 +1020,6 @@ Chapters:
Preferences: Preferencje
Ok: Ok
Hashtag:
You can only view hashtag pages through the Local API: Strony hashtagów są dostępne
jedynie poprzez lokalne API
Hashtag: Hashtag
This hashtag does not currently have any videos: Nie ma na razie żadnych filmów
z takim hashtagiem

View File

@ -947,7 +947,5 @@ Clipboard:
Ok: Ok
Hashtag:
Hashtag: Marcador
You can only view hashtag pages through the Local API: Só é possível ver páginas
de hashtag através da API local
This hashtag does not currently have any videos: Esta hashtag não tem atualmente
quaisquer vídeos

View File

@ -1001,5 +1001,3 @@ Hashtag:
Hashtag: Marcador
This hashtag does not currently have any videos: Esta hashtag não tem atualmente
quaisquer vídeos
You can only view hashtag pages through the Local API: Só é possível ver páginas
de hashtag através da API local

View File

@ -1007,5 +1007,3 @@ Hashtag:
Hashtag: Распределительная метка
This hashtag does not currently have any videos: Этой распределительной метки пока
нет ни у одного видео
You can only view hashtag pages through the Local API: Ты можешь видеть страницы
с распределительными метками только через локальный набор функций

View File

@ -33,3 +33,5 @@ Channel:
Video:
External Player: {}
Tooltips: {}
About:
Email: ኢመይል

View File

@ -405,6 +405,11 @@ Settings:
Hide Channel Playlists: Kanal Oynatma Listelerini Gizle
Hide Channel Community: Kanal Topluluğunu Gizle
Hide Channel Shorts: Kanal Kısa Videolarını Gizle
Sections:
Side Bar: Kenar Çubuğu
Channel Page: Kanal Sayfası
Watch Page: İzleme Sayfası
General: Genel
The app needs to restart for changes to take effect. Restart and apply change?: Değişikliklerin
etkili olması için uygulamanın yeniden başlatılması gerekiyor. Yeniden başlatılsın
ve değişiklikler uygulansın mı?
@ -1003,7 +1008,5 @@ Preferences: Tercihler
Ok: Tamam
Hashtag:
Hashtag: Hashtag
You can only view hashtag pages through the Local API: Hashtag sayfalarını yalnızca
Yerel API aracılığıyla görüntüleyebilirsiniz
This hashtag does not currently have any videos: Bu hashtag'in şu anda herhangi
bir videosu yok

View File

@ -332,6 +332,11 @@ Settings:
Hide Channel Playlists: Сховати добірки з каналів
Hide Channel Community: Сховати спільноту каналу
Hide Channel Shorts: Сховати Shorts каналу
Sections:
Side Bar: Бічна панель
Channel Page: Сторінка каналу
Watch Page: Сторінка перегляду
General: Загальні
Data Settings:
Data Settings: 'Налаштування даних'
Select Import Type: 'Оберіть тип імпорту'
@ -940,6 +945,4 @@ Preferences: Налаштування
Ok: Гаразд
Hashtag:
Hashtag: Хештег
You can only view hashtag pages through the Local API: Ви можете переглядати сторінки
з хештегами лише через локальний API
This hashtag does not currently have any videos: За цим хештегом наразі немає відео

View File

@ -368,6 +368,11 @@ Settings:
Hide Channel Playlists: 隐藏频道播放列表
Hide Channel Community: 隐藏频道社区
Hide Channel Shorts: 隐藏频道短视频
Sections:
Side Bar: 侧边栏
Channel Page: 频道页
Watch Page: 观看页
General: 常规
The app needs to restart for changes to take effect. Restart and apply change?: 应用需要重启让修改生效。重启以应用修改?
Proxy Settings:
Proxy Protocol: 代理协议
@ -878,5 +883,4 @@ Preferences: 选项
Ok:
Hashtag:
Hashtag: 标签
You can only view hashtag pages through the Local API: 你只能通过本地 API 浏览标签页面
This hashtag does not currently have any videos: 此标签下当前没有任何短视频

View File

@ -370,6 +370,11 @@ Settings:
Hide Channel Playlists: 隱藏頻道播放清單
Hide Channel Community: 隱藏頻道社群
Hide Channel Shorts: 隱藏頻道短片
Sections:
Side Bar: 側邊欄
Watch Page: 觀看頁面
General: 一般
Channel Page: 頻道頁面
The app needs to restart for changes to take effect. Restart and apply change?: 此變更需要重啟讓修改生效。重啟並且套用變更?
Proxy Settings:
Error getting network information. Is your proxy configured properly?: 取得網路資訊時發生錯誤。您的代理伺服器設定正確嗎?
@ -890,4 +895,3 @@ Ok: 確定
Hashtag:
Hashtag: 主題標籤
This hashtag does not currently have any videos: 此標籤目前沒有任何影片
You can only view hashtag pages through the Local API: 您僅能透過本機 API 檢視主題標籤頁面

848
yarn.lock

File diff suppressed because it is too large Load Diff