Merge branch 'development'

This commit is contained in:
Preston 2021-01-15 15:08:08 -05:00
commit 5fd154fa08
111 changed files with 4671 additions and 951 deletions

View File

@ -147,7 +147,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
asset_name: freetube-${{ env.PACKAGE_VERSION }}-win-x64-portable
asset_name: freetube-${{ env.PACKAGE_VERSION }}-win-x64-portable.zip
asset_path: build/freetube-${{ env.PACKAGE_VERSION }}-win.zip
asset_content_type: application/zip

1
.gitignore vendored
View File

@ -20,3 +20,4 @@ coverage
__coverage__
csak-timelog.json
.idea/
debug/

View File

@ -14,11 +14,11 @@ addressed.
### Browser Extension
Download our browser extension so that you can click on videos in your browser and have them automatically open up in FreeTube.
FreeTube is supported by the [Privacy Redirect](https://github.com/SimonBrazell/privacy-redirect) extension, which will allow you to open YouTube links into FreeTube. You must enable the option within the advanced settings for it to work.
[Download for Firefox](https://addons.mozilla.org/en-US/firefox/addon/freetube-redirect/)
Download Privacy Redirect for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/privacy-redirect/) or [Google Chrome](https://chrome.google.com/webstore/detail/privacy-redirect/pmcmeagblkinmogikoikkdjiligflglb).
[Instructions for Google Chrome](https://github.com/FreeTubeApp/FreeTube/wiki/Browser-Extension)
If you have issues with the extension working with FreeTube, please create an issue in this repository instead of the extension repository.
## How does it work?
FreeTube uses a built in extractor to grab and serve data / videos. The [Invidious API](https://github.com/iv-org/invidious) can also optionally be used. FreeTube does not use any official APIs to obtain data. While YouTube can still see your video requests, it can no
@ -34,6 +34,7 @@ to hide your IP while using FreeTube.
* Two extractor APIs to choose from (Built in or Invidious)
* Subscribe to channels without an account
* Local subscriptions, history, and saved videos
* Organize your subscriptions into "Profiles" to create a more focused feed
* Export & import subscriptions
* Open videos from your browser directly into FreeTube (with extension)
* Mini Player
@ -47,12 +48,12 @@ to hide your IP while using FreeTube.
[FreeTube Website](https://freetubeapp.io/#download)
Flatpak on Flathub: [Download](https://flathub.org/apps/details/io.freetubeapp.FreeTube) [Source](https://github.com/flathub/io.freetubeapp.FreeTube)
### 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.
Flatpak on Flathub: [Download](https://flathub.org/apps/details/io.freetubeapp.FreeTube) [Source](https://github.com/flathub/io.freetubeapp.FreeTube)
Arch User Repository (AUR): [Download](https://aur.archlinux.org/packages/freetube-bin/)
Chocolatey: [Download](https://chocolatey.org/packages/freetube/)
@ -87,7 +88,7 @@ We are actively looking for translations! We use Weblate to make it easy for tr
## Contact
If you ever have any questions, feel free to make an issue here on GitHub. Alternatively, you can email me at FreeTubeApp@protonmail.com or you can join our [Matrix Community](https://riot.im/app/#/group/+freetube:matrix.org). Don't forget to check out the [rules](https://github.com/FreeTubeApp/FreeTube/wiki/Matrix-Server-Info-&-Rules) before joining.
If you ever have any questions, feel free to make an issue here on GitHub. Alternatively, you can email me at FreeTubeApp@protonmail.com or you can join our [Matrix Community](https://matrix.to/#/+freetube:matrix.org). Don't forget to check out the [rules](https://github.com/FreeTubeApp/FreeTube/wiki/Matrix-Server-Info-&-Rules) before joining.
You can also stay up to date by reading the [FreeTube Blog](https://write.as/freetube/). [View the welcome blog](https://write.as/freetube/welcome-to-freetube-blogs).

1448
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,23 +8,24 @@
"url": "https://github.com/FreeTubeApp/FreeTube/issues"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.32",
"@fortawesome/free-solid-svg-icons": "^5.15.1",
"@fortawesome/vue-fontawesome": "^2.0.0",
"@fortawesome/fontawesome-svg-core": "^1.2.34",
"@fortawesome/free-brands-svg-icons": "^5.15.2",
"@fortawesome/free-solid-svg-icons": "^5.15.2",
"@fortawesome/vue-fontawesome": "^2.0.2",
"@silvermine/videojs-quality-selector": "^1.2.4",
"autolinker": "^3.14.2",
"bulma-pro": "^0.2.0",
"dateformat": "^4.3.1",
"electron-context-menu": "^2.3.0",
"dateformat": "^4.4.1",
"electron-context-menu": "^2.4.0",
"jquery": "^3.5.1",
"js-yaml": "^3.14.1",
"js-yaml": "^4.0.0",
"lodash.debounce": "^4.0.8",
"lodash.isequal": "^4.5.0",
"lodash.uniqwith": "^4.5.0",
"markdown": "^0.5.0",
"material-design-icons": "^3.0.1",
"nedb": "^1.8.0",
"opml-to-json": "0.0.3",
"opml-to-json": "1.0.1",
"rss-parser": "^3.10.0",
"video.js": "7.10.2",
"videojs-abloop": "^1.2.0",
@ -35,74 +36,74 @@
"videojs-vtt-thumbnails-freetube": "0.0.15",
"vue": "^2.6.12",
"vue-electron": "^1.0.6",
"vue-i18n": "^8.22.2",
"vue-observe-visibility": "^0.4.6",
"vue-i18n": "^8.22.3",
"vue-observe-visibility": "^1.0.0",
"vue-router": "^3.4.9",
"vuex": "^3.6.0",
"xml2json": "^0.12.0",
"youtube-chat": "^1.1.0",
"youtube-suggest": "^1.1.0",
"yt-channel-info": "^1.1.4",
"yt-comment-scraper": "^1.3.10",
"yt-channel-info": "^1.2.0",
"yt-comment-scraper": "^1.3.11",
"yt-dash-manifest-generator": "^1.1.0",
"yt-trending-scraper": "^1.0.4",
"yt-xml2vtt": "^1.1.3",
"ytdl-core": "^4.2.0",
"ytpl": "^2.0.3",
"ytsr": "^3.0.0"
"yt-trending-scraper": "^1.0.5",
"yt-xml2vtt": "^1.2.0",
"ytdl-core": "^4.4.3",
"ytpl": "^2.0.4",
"ytsr": "^3.2.1"
},
"description": "A private YouTube client",
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-object-rest-spread": "^7.12.1",
"@babel/preset-env": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/preset-typescript": "^7.12.7",
"@typescript-eslint/eslint-plugin": "^4.10.0",
"@typescript-eslint/parser": "^4.10.0",
"@typescript-eslint/eslint-plugin": "^4.13.0",
"@typescript-eslint/parser": "^4.13.0",
"acorn": "^8.0.4",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.2.2",
"copy-webpack-plugin": "6.4.0",
"css-loader": "^5.0.1",
"devtron": "^1.4.0",
"electron": "^11.1.0",
"electron": "^11.1.1",
"electron-builder": "^22.9.1",
"electron-builder-squirrel-windows": "^22.10.3",
"electron-debug": "^3.1.0",
"electron-builder-squirrel-windows": "^22.10.4",
"electron-debug": "^3.2.0",
"electron-rebuild": "^2.3.4",
"eslint": "^7.15.0",
"eslint-config-prettier": "^7.0.0",
"eslint": "^7.17.0",
"eslint-config-prettier": "^7.1.0",
"eslint-config-standard": "^16.0.2",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.3.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^5.0.0",
"eslint-plugin-vue": "^7.2.0",
"eslint-plugin-vue": "^7.4.1",
"fast-glob": "^3.2.4",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^4.5.0",
"html-webpack-plugin": "^4.5.1",
"jest": "^26.6.3",
"mini-css-extract-plugin": "^1.3.3",
"mini-css-extract-plugin": "^1.3.4",
"node-abi": "^2.19.3",
"node-loader": "^1.0.2",
"npm-run-all": "^4.1.5",
"prettier": "^2.2.1",
"sass": "^1.30.0",
"sass-loader": "^10.1.0",
"sass": "^1.32.4",
"sass-loader": "^10.1.1",
"style-loader": "^2.0.0",
"tree-kill": "1.2.2",
"typescript": "^4.1.3",
"url-loader": "^4.1.1",
"vue-devtools": "^5.1.4",
"vue-eslint-parser": "^7.3.0",
"vue-loader": "^15.9.5",
"vue-loader": "^15.9.6",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.12",
"webpack": "4.44.2",
"webpack-cli": "3.3.12",
"webpack-dev-server": "^3.11.0"
"webpack-dev-server": "^3.11.2"
},
"license": "AGPL-3.0-or-later",
"main": "./dist/main.js",
@ -142,5 +143,5 @@
"test": "run-s rebuild:node pack:workers jest",
"test:watch": "run-s rebuild:node pack:workers jest:watch"
},
"version": "0.10.0"
"version": "0.11.0"
}

View File

@ -65,21 +65,57 @@ if (!isDev) {
})
app.on('ready', (event, commandLine, workingDirectory) => {
settingsDb.findOne({
_id: 'disableSmoothScrolling'
settingsDb.find({
$or: [
{ _id: 'disableSmoothScrolling' },
{ _id: 'useProxy' },
{ _id: 'proxyProtocol' },
{ _id: 'proxyHostname' },
{ _id: 'proxyPort' }
]
}, function (err, doc) {
if (err) {
app.exit(0)
return
}
if (doc !== null && doc.value) {
let disableSmoothScrolling = false
let useProxy = false
let proxyProtocol = 'socks5'
let proxyHostname = '127.0.0.1'
let proxyPort = '9050'
if (typeof doc === 'object' && doc.length > 0) {
doc.forEach((dbItem) => {
switch (dbItem._id) {
case 'disableSmoothScrolling':
disableSmoothScrolling = dbItem.value
break
case 'useProxy':
useProxy = dbItem.value
break
case 'proxyProtocol':
proxyProtocol = dbItem.value
break
case 'proxyHostname':
proxyHostname = dbItem.value
break
case 'proxyPort':
proxyPort = dbItem.value
break
}
})
}
if (disableSmoothScrolling) {
app.commandLine.appendSwitch('disable-smooth-scrolling')
} else {
app.commandLine.appendSwitch('enable-smooth-scrolling')
}
createWindow()
const proxyUrl = `${proxyProtocol}://${proxyHostname}:${proxyPort}`
createWindow(useProxy, proxyUrl)
if (isDev) {
installDevTools()
@ -99,21 +135,57 @@ if (!isDev) {
})
app.on('ready', () => {
settingsDb.findOne({
_id: 'disableSmoothScrolling'
settingsDb.find({
$or: [
{ _id: 'disableSmoothScrolling' },
{ _id: 'useProxy' },
{ _id: 'proxyProtocol' },
{ _id: 'proxyHostname' },
{ _id: 'proxyPort' }
]
}, function (err, doc) {
if (err) {
app.exit(0)
return
}
if (doc !== null && doc.value) {
let disableSmoothScrolling = false
let useProxy = false
let proxyProtocol = 'socks5'
let proxyHostname = '127.0.0.1'
let proxyPort = '9050'
if (typeof doc === 'object' && doc.length > 0) {
doc.forEach((dbItem) => {
switch (dbItem._id) {
case 'disableSmoothScrolling':
disableSmoothScrolling = dbItem.value
break
case 'useProxy':
useProxy = dbItem.value
break
case 'proxyProtocol':
proxyProtocol = dbItem.value
break
case 'proxyHostname':
proxyHostname = dbItem.value
break
case 'proxyPort':
proxyPort = dbItem.value
break
}
})
}
if (disableSmoothScrolling) {
app.commandLine.appendSwitch('disable-smooth-scrolling')
} else {
app.commandLine.appendSwitch('enable-smooth-scrolling')
}
createWindow()
const proxyUrl = `${proxyProtocol}://${proxyHostname}:${proxyPort}`
createWindow(useProxy, proxyUrl)
if (isDev) {
installDevTools()
@ -137,7 +209,7 @@ async function installDevTools () {
}
}
function createWindow () {
function createWindow (useProxy = false, proxyUrl = '') {
/**
* Initial window options
*/
@ -164,6 +236,12 @@ function createWindow () {
height: 800
})
if (useProxy) {
mainWindow.webContents.session.setProxy({
proxyRules: proxyUrl
})
}
settingsDb.findOne({
_id: 'bounds'
}, function (err, doc) {
@ -264,6 +342,17 @@ function createWindow () {
mainWindow.close()
createWindow()
})
ipcMain.on('enableProxy', (event, url) => {
console.log(url)
mainWindow.webContents.session.setProxy({
proxyRules: url
})
})
ipcMain.on('disableProxy', () => {
mainWindow.webContents.session.setProxy({})
})
}
app.on('window-all-closed', () => {
@ -306,7 +395,8 @@ app.on('open-url', (event, url) => {
})
/*
* Check if we were passed a freetube:// URL on process startup (linux/win)
* Check if an argument was passed and send it over to the GUI (Linux / Windows).
* Remove freetube:// protocol if present
*/
const url = getLinkUrl(process.argv)
if (url) {
@ -318,12 +408,11 @@ function baseUrl(arg) {
}
function getLinkUrl(argv) {
for (const arg of argv) {
if (arg.indexOf('freetube://') !== -1) {
return baseUrl(arg)
}
if (argv.length > 1) {
return baseUrl(argv[argv.length - 1])
} else {
return null
}
return null
}
/**

View File

@ -88,6 +88,7 @@ export default Vue.extend({
this.$store.dispatch('grabUserSettings')
this.$store.dispatch('grabHistory')
this.$store.dispatch('grabAllProfiles', this.$t('Profile.All Channels'))
this.$store.dispatch('grabAllPlaylists')
this.$store.commit('setUsingElectron', useElectron)
this.checkThemeSettings()
this.checkLocale()
@ -172,7 +173,15 @@ export default Vue.extend({
const message = this.$t('Version $ is now available! Click for more details')
this.updateBannerMessage = message.replace('$', versionNumber)
if (version < versionNumber) {
const appVersion = version.split('.')
const latestVersion = versionNumber.split('.')
if (parseInt(appVersion[0]) < parseInt(latestVersion[0])) {
this.showUpdatesBanner = true
} else if (parseInt(appVersion[1]) < parseInt(latestVersion[1])) {
this.showUpdatesBanner = true
} else if (parseInt(appVersion[2]) < parseInt(latestVersion[2])) {
this.showUpdatesBanner = true
}
}).fail((xhr, textStatus, error) => {

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="20px" height="20px"><path class="loop" d="M12 4V2.21c0-.45-.54-.67-.85-.35l-2.8 2.79c-.2.2-.2.51 0 .71l2.79 2.79c.32.31.86.09.86-.36V6c3.31 0 6 2.69 6 6 0 .79-.15 1.56-.44 2.25-.15.36-.04.77.23 1.04.51.51 1.37.33 1.64-.34.37-.91.57-1.91.57-2.95 0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-.79.15-1.56.44-2.25.15-.36.04-.77-.23-1.04-.51-.51-1.37-.33-1.64.34C4.2 9.96 4 10.96 4 12c0 4.42 3.58 8 8 8v1.79c0 .45.54.67.85.35l2.79-2.79c.2-.2.2-.51 0-.71l-2.79-2.79c-.31-.31-.85-.09-.85.36V18z"/></svg>

After

Width:  |  Height:  |  Size: 577 B

View File

@ -78,6 +78,12 @@ export default Vue.extend({
}
},
methods: {
openProfileSettings: function () {
this.$router.push({
path: '/settings/profile/'
})
},
importSubscriptions: function (option) {
this.showImportSubscriptionsPrompt = false
@ -265,7 +271,11 @@ export default Vue.extend({
return sub.id === subscription.id || sub.name === subscription.name
})
if (subExists === -1) {
const subDuplicateExists = subscriptions.findIndex((sub) => {
return sub.id === subscription.id || sub.name === subscription.name
})
if (subExists === -1 && subDuplicateExists === -1) {
subscriptions.push(subscription)
}

View File

@ -27,6 +27,22 @@
@click="exportHistory"
/>
</ft-flex-box>
<ft-flex-box>
<a
class="center"
href="https://github.com/FreeTubeApp/FreeTube/wiki/Importing-Your-YouTube-Subscriptions"
>
<p>
{{ $t("Settings.Data Settings.How do I import my subscriptions?") }}
</p>
</a>
</ft-flex-box>
<ft-flex-box>
<ft-button
:label="$t('Settings.Data Settings.Manage Subscriptions')"
@click="openProfileSettings"
/>
</ft-flex-box>
<ft-prompt
v-if="showImportSubscriptionsPrompt"
:label="$t('Settings.Data Settings.Select Import Type')"

View File

@ -39,13 +39,15 @@ export default Vue.extend({
},
hideLiveChat: function () {
return this.$store.getters.getHideLiveChat
},
hideActiveSubscriptions: function () {
return this.$store.getters.getHideActiveSubscriptions
}
},
methods: {
handleHideRecommendedVideos: function (value) {
if (value) {
this.updatePlayNextVideo(false)
this.updateDefaultTheatreMode(true)
}
this.updateHideRecommendedVideos(value)
@ -60,6 +62,7 @@ export default Vue.extend({
'updateHideTrendingVideos',
'updateHidePopularVideos',
'updateHideLiveChat',
'updateHideActiveSubscriptions',
'updatePlayNextVideo',
'updateDefaultTheatreMode'
])

View File

@ -33,6 +33,12 @@
:default-value="hideCommentLikes"
@change="updateHideCommentLikes"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Active Subscriptions')"
:compact="true"
:default-value="hideActiveSubscriptions"
@change="updateHideActiveSubscriptions"
/>
</div>
<div class="switchColumn">
<ft-toggle-switch

View File

@ -52,6 +52,9 @@
&:active
background-color: var(--accent-color-active)
&.favorite
color: var(--favorite-icon-color)
.iconDropdown
display: none
position: absolute

View File

@ -14,7 +14,7 @@
<ft-tooltip
v-if="tooltip !== ''"
class="selectTooltip"
position="right"
position="bottom"
:tooltip="tooltip"
/>
</label>

View File

@ -57,6 +57,10 @@ export default Vue.extend({
this.channelLink = this.data.authorUrl
this.playlistLink = this.data.playlistId
this.videoCount = this.data.videoCount
if (this.data.proxyThumbnail === false) {
this.thumbnail = this.data.playlistThumbnail
}
},
parseLocalData: function () {

View File

@ -161,6 +161,26 @@ export default Vue.extend({
},
hideVideoViews: function () {
return this.$store.getters.getHideVideoViews
},
addWatchedStyle: function () {
return this.watched && !this.inHistory
},
favoritesPlaylist: function () {
return this.$store.getters.getFavorites
},
inFavoritesPlaylist: function () {
const index = this.favoritesPlaylist.videos.findIndex((video) => {
return video.videoId === this.id
})
return index !== -1
},
favoriteIconTheme: function () {
return this.inFavoritesPlaylist ? 'base favorite' : 'base'
}
},
mounted: function () {
@ -169,10 +189,11 @@ export default Vue.extend({
},
methods: {
toggleSave: function () {
console.log('TODO: ft-list-video method toggleSave')
this.showToast({
message: this.$t('Saving videos are currently not available. Please wait for a future update')
})
if (this.inFavoritesPlaylist) {
this.removeFromPlaylist()
} else {
this.addToPlaylist()
}
},
handleOptionsClick: function (option) {
@ -392,11 +413,54 @@ export default Vue.extend({
this.watched = false
},
addToPlaylist: function () {
const videoData = {
videoId: this.id,
title: this.title,
author: this.channelName,
authorId: this.channelId,
published: '',
description: this.description,
viewCount: this.viewCount,
lengthSeconds: this.data.lengthSeconds,
timeAdded: new Date().getTime(),
isLive: false,
paid: false,
type: 'video'
}
const payload = {
playlistName: 'Favorites',
videoData: videoData
}
this.addVideo(payload)
this.showToast({
message: this.$t('Video.Video has been saved')
})
},
removeFromPlaylist: function () {
const payload = {
playlistName: 'Favorites',
videoId: this.id
}
this.removeVideo(payload)
this.showToast({
message: this.$t('Video.Video has been removed from your saved list')
})
},
...mapActions([
'showToast',
'toLocalePublicationString',
'updateHistory',
'removeFromHistory'
'removeFromHistory',
'addVideo',
'removeVideo'
])
}
})

View File

@ -5,7 +5,7 @@
list: (listType === 'list' || forceListType === 'list') && forceListType !== 'grid',
grid: (listType === 'grid' || forceListType === 'list') && forceListType !== 'list',
[appearance]: true,
watched: watched
watched: addWatchedStyle
}"
>
<div
@ -32,16 +32,16 @@
</div>
<ft-icon-button
v-if="!isLive"
:title="$t('Video.Save Video')"
icon="star"
class="favoritesIcon"
theme="base"
:theme="favoriteIconTheme"
:padding="appearance === `watchPlaylistItem` ? 5 : 6"
:size="appearance === `watchPlaylistItem` ? 14 : 18"
:class="{ favorited: isFavorited }"
@click="toggleSave(id)"
@click="toggleSave"
/>
<div
v-if="watched"
v-if="addWatchedStyle"
class="videoWatched"
>
{{ $t("Video.Watched") }}

View File

@ -88,7 +88,7 @@ export default Vue.extend({
if (this.searchSettings.type !== 'video') {
const typeRadio = this.$refs.typeRadio
typeRadio.updateSelectedValue('all')
this.updateType('all')
this.$store.commit('setSearchType', 'all')
}
this.$store.commit('setSearchTime', value)
},
@ -99,8 +99,8 @@ export default Vue.extend({
const durationRadio = this.$refs.durationRadio
timeRadio.updateSelectedValue('')
durationRadio.updateSelectedValue('')
this.updateTime('')
this.updateDuration('')
this.$store.commit('setSearchTime', '')
this.$store.commit('setSearchDuration', '')
}
this.$store.commit('setSearchType', value)
},

View File

@ -21,6 +21,7 @@
<ft-tooltip
v-if="tooltip !== ''"
class="selectTooltip"
position="bottom-left"
:tooltip="tooltip"
/>
</label>

View File

@ -15,6 +15,8 @@
.button:focus + .text.bottom,
.button:hover + .text.bottom,
.button:hover + .text.bottom-left,
.button:hover + .text.bottom-left,
.button:focus + .text.top,
.button:hover + .text.top {
-webkit-transform: translate(-50%, 0);
@ -57,6 +59,14 @@
transform: translate(-50%, -1em);
}
.text.bottom-left {
margin-top: 1em;
top: 100%;
left: -100px;
-webkit-transform: translate(-50%, -1em);
transform: translate(-50%, -1em);
}
.text.left {
margin-right:1em;
right: 100%;

View File

@ -7,7 +7,7 @@ export default Vue.extend({
position: {
type: String,
default: 'bottom',
validator: (value) => value === 'bottom' || value === 'left' || value === 'right' || value === 'top'
validator: (value) => value === 'bottom' || value === 'left' || value === 'right' || value === 'top' || value === 'bottom-left'
},
tooltip: {
type: String,

View File

@ -1,4 +1,5 @@
import Vue from 'vue'
import { mapActions } from 'vuex'
import FtCard from '../ft-card/ft-card.vue'
import $ from 'jquery'
@ -17,6 +18,9 @@ export default Vue.extend({
'ft-card': FtCard
},
beforeRouteLeave: function () {
if (this.player !== null) {
this.exitFullWindow()
}
if (this.player !== null && !this.player.isInPictureInPicture()) {
this.player.dispose()
this.player = null
@ -24,6 +28,11 @@ export default Vue.extend({
} else if (this.player.isInPictureInPicture()) {
this.player.play()
}
if (this.usingElectron && this.powerSaveBlocker !== null) {
const { powerSaveBlocker } = require('electron')
powerSaveBlocker.stop(this.powerSaveBlocker)
}
},
props: {
format: {
@ -58,6 +67,7 @@ export default Vue.extend({
data: function () {
return {
id: '',
powerSaveBlocker: null,
volume: 1,
player: null,
useDash: false,
@ -67,7 +77,7 @@ export default Vue.extend({
activeSourceList: [],
mouseTimeout: null,
dataSetup: {
aspectRatio: '16:9',
fluid: true,
nativeTextTracks: false,
plugins: {},
controlBar: {
@ -83,6 +93,7 @@ export default Vue.extend({
'remainingTimeDisplay',
'customControlSpacer',
'playbackRateMenuButton',
'loopButton',
'chaptersButton',
'descriptionsButton',
'subsCapsButton',
@ -112,6 +123,10 @@ export default Vue.extend({
}
},
computed: {
usingElectron: function () {
return this.$store.getters.getUsingElectron
},
defaultPlayback: function () {
return this.$store.getters.getDefaultPlayback
},
@ -148,14 +163,24 @@ export default Vue.extend({
}
this.createFullWindowButton()
this.createLoopButton()
this.determineFormatType()
this.determineMaxFramerate()
},
beforeDestroy: function () {
if (this.player !== null && !this.player.isInPictureInPicture()) {
this.player.dispose()
this.player = null
clearTimeout(this.mouseTimeout)
if (this.player !== null) {
this.exitFullWindow()
if (!this.player.isInPictureInPicture()) {
this.player.dispose()
this.player = null
clearTimeout(this.mouseTimeout)
}
}
if (this.usingElectron && this.powerSaveBlocker !== null) {
const { powerSaveBlocker } = require('electron')
powerSaveBlocker.stop(this.powerSaveBlocker)
}
},
methods: {
@ -170,7 +195,9 @@ export default Vue.extend({
this.player = videojs(videoPlayer, {
html5: {
vhs: {
limitRenditionByPlayerDimensions: false
limitRenditionByPlayerDimensions: false,
smoothQualityChange: false,
allowSeeksWithinUnsafeLiveWindow: true
}
}
})
@ -217,6 +244,7 @@ export default Vue.extend({
this.player.on('ready', function () {
v.$emit('ready')
v.checkAspectRatio()
})
this.player.on('ended', function () {
@ -226,6 +254,39 @@ export default Vue.extend({
this.player.on('error', function (error, message) {
v.$emit('error', error.target.player.error_)
})
this.player.on('play', function () {
if (this.usingElectron) {
const { powerSaveBlocker } = require('electron')
this.powerSaveBlocker = powerSaveBlocker.start('prevent-display-sleep')
}
})
this.player.on('pause', function () {
if (this.usingElectron && this.powerSaveBlocker !== null) {
const { powerSaveBlocker } = require('electron')
powerSaveBlocker.stop(this.powerSaveBlocker)
this.powerSaveBlocker = null
}
})
}
},
checkAspectRatio() {
const videoWidth = this.player.videoWidth()
const videoHeight = this.player.videoHeight()
if (videoWidth === 0 || videoHeight === 0) {
setTimeout(() => {
this.checkAspectRatio()
}, 200)
return
}
if (videoWidth < videoHeight) {
this.player.fluid(false)
this.player.aspectRatio('16:9')
}
},
@ -268,7 +329,6 @@ export default Vue.extend({
}
fs.readFile(this.dashSrc[0].url, (err, data) => {
if (err) {
console.log('caught the error')
this.maxFramerate = 60
return
}
@ -504,7 +564,53 @@ export default Vue.extend({
}
},
createFullWindowButton: function() {
createLoopButton: function () {
const v = this
const VjsButton = videojs.getComponent('Button')
const loopButton = videojs.extend(VjsButton, {
constructor: function(player, options) {
VjsButton.call(this, player, options)
},
handleClick: function() {
v.toggleVideoLoop()
},
createControlTextEl: function (button) {
return $(button).html($('<div id="loopButton" class="vjs-icon-loop loop-white vjs-button loopWhite"></div>')
.attr('title', 'Toggle Loop'))
}
})
videojs.registerComponent('loopButton', loopButton)
},
toggleVideoLoop: async function () {
if (!this.player.loop()) {
const currentTheme = localStorage.getItem('mainColor')
const colorNames = this.$store.state.utils.colorClasses
const colorValues = this.$store.state.utils.colorValues
const nameIndex = colorNames.findIndex((color) => {
return color === currentTheme
})
const themeTextColor = await this.calculateColorLuminance(colorValues[nameIndex])
$('#loopButton').addClass('vjs-icon-loop-active')
if (themeTextColor === '#000000') {
$('#loopButton').addClass('loop-black')
$('#loopButton').removeClass('loop-white')
}
this.player.loop(true)
} else {
$('#loopButton').removeClass('vjs-icon-loop-active')
$('#loopButton').removeClass('loop-black')
$('#loopButton').addClass('loop-white')
this.player.loop(false)
}
},
createFullWindowButton: function () {
const v = this
const VjsButton = videojs.getComponent('Button')
const fullWindowButton = videojs.extend(VjsButton, {
@ -622,6 +728,10 @@ export default Vue.extend({
}
}
if (event.ctrlKey) {
return
}
if (this.player !== null) {
switch (event.which) {
case 32:
@ -794,6 +904,10 @@ export default Vue.extend({
break
}
}
}
},
...mapActions([
'calculateColorLuminance'
])
}
})

View File

@ -34,7 +34,6 @@
<ft-toggle-switch
:label="$t('Settings.Player Settings.Enable Theatre Mode by Default')"
:compact="true"
:disabled="hideRecommendedVideos"
:default-value="defaultTheatreMode"
@change="updateDefaultTheatreMode"
/>

View File

@ -0,0 +1,25 @@
.relative {
position: relative;
}
.card {
width: 85%;
margin: 0 auto;
margin-bottom: 10px;
}
.center {
text-align: center;
}
@media only screen and (max-width: 680px) {
.card {
width: 90%;
}
}
@media only screen and (max-width: 500px) {
.subscriptionSettingsFlexBox {
justify-content: flex-start;
}
}

View File

@ -0,0 +1,155 @@
import Vue from 'vue'
import $ from 'jquery'
import { mapActions } from 'vuex'
import FtCard from '../ft-card/ft-card.vue'
import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue'
import FtButton from '../ft-button/ft-button.vue'
import FtSelect from '../ft-select/ft-select.vue'
import FtInput from '../ft-input/ft-input.vue'
import FtLoader from '../ft-loader/ft-loader.vue'
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
import electron from 'electron'
import debounce from 'lodash.debounce'
export default Vue.extend({
name: 'ProxySettings',
components: {
'ft-card': FtCard,
'ft-toggle-switch': FtToggleSwitch,
'ft-button': FtButton,
'ft-select': FtSelect,
'ft-input': FtInput,
'ft-loader': FtLoader,
'ft-flex-box': FtFlexBox
},
data: function () {
return {
isLoading: false,
dataAvailable: false,
proxyTestUrl: 'https://api.ipify.org?format=json',
proxyTestUrl1: 'https://freegeoip.app/json/',
proxyId: '',
proxyCountry: '',
proxyRegion: '',
proxyCity: '',
proxyHost: '',
protocolNames: [
'HTTP',
'HTTPS',
'SOCKS4',
'SOCKS5'
],
protocolValues: [
'http',
'https',
'socks4',
'socks5'
]
}
},
computed: {
useProxy: function () {
return this.$store.getters.getUseProxy
},
proxyProtocol: function () {
return this.$store.getters.getProxyProtocol
},
proxyHostname: function () {
return this.$store.getters.getProxyHostname
},
proxyPort: function () {
return this.$store.getters.getProxyPort
},
proxyUrl: function () {
return `${this.proxyProtocol}://${this.proxyHostname}:${this.proxyPort}`
}
},
mounted: function () {
this.debounceEnableProxy = debounce(this.enableProxy, 200)
},
beforeDestroy: function () {
if (this.proxyHostname === '') {
this.updateProxyHostname('127.0.0.1')
}
if (this.proxyPort === '') {
this.updateProxyPort('9050')
}
},
methods: {
handleUpdateProxy: function (value) {
if (value) {
this.enableProxy()
} else {
this.disableProxy()
}
this.updateUseProxy(value)
},
handleUpdateProxyProtocol: function (value) {
if (this.useProxy) {
this.enableProxy()
}
this.updateProxyProtocol(value)
},
handleUpdateProxyHostname: function (value) {
if (this.useProxy) {
this.debounceEnableProxy()
}
this.updateProxyHostname(value)
},
handleUpdateProxyPort: function (value) {
if (this.useProxy) {
this.debounceEnableProxy()
}
this.updateProxyPort(value)
},
enableProxy: function () {
electron.ipcRenderer.send('enableProxy', this.proxyUrl)
},
disableProxy: function () {
electron.ipcRenderer.send('disableProxy')
},
testProxy: function () {
this.isLoading = true
if (!this.useProxy) {
this.enableProxy()
}
$.getJSON(this.proxyTestUrl1, (response) => {
console.log(response)
this.proxyIp = response.ip
this.proxyCountry = response.country_name
this.proxyRegion = response.region_name
this.proxyCity = response.city
this.dataAvailable = true
}).fail((xhr, textStatus, error) => {
console.log(xhr)
console.log(textStatus)
console.log(error)
this.showToast({
message: this.$t('Settings.Proxy Settings["Error getting network information. Is your proxy configured properly?"]')
})
this.dataAvailable = false
}).always(() => {
if (!this.useProxy) {
this.disableProxy()
}
this.isLoading = false
})
},
...mapActions([
'showToast',
'updateUseProxy',
'updateProxyProtocol',
'updateProxyHostname',
'updateProxyPort'
])
}
})

View File

@ -0,0 +1,78 @@
<template>
<ft-card
class="relative card"
>
<h3
class="videoTitle"
>
{{ $t("Settings.Proxy Settings.Proxy Settings") }}
</h3>
<ft-flex-box class="subscriptionSettingsFlexBox">
<ft-toggle-switch
:label="$t('Settings.Proxy Settings.Enable Tor / Proxy')"
:default-value="useProxy"
@change="handleUpdateProxy"
/>
</ft-flex-box>
<ft-flex-box>
<ft-select
:placeholder="$t('Settings.Proxy Settings.Proxy Protocol')"
:value="proxyProtocol"
:select-names="protocolNames"
:select-values="protocolValues"
@change="handleUpdateProxyProtocol"
/>
</ft-flex-box>
<ft-flex-box>
<ft-input
:placeholder="$t('Settings.Proxy Settings.Proxy Host')"
:show-arrow="false"
:show-label="true"
:value="proxyHostname"
@input="handleUpdateProxyHostname"
/>
<ft-input
:placeholder="$t('Settings.Proxy Settings.Proxy Port Number')"
:show-arrow="false"
:show-label="true"
:value="proxyPort"
@input="handleUpdateProxyPort"
/>
</ft-flex-box>
<p class="center">
{{ $t('Settings.Proxy Settings.Clicking on Test Proxy will send a request to') }} https://freegeoip.app/json/
</p>
<ft-flex-box>
<ft-button
:label="$t('Settings.Proxy Settings.Test Proxy')"
@click="testProxy"
/>
</ft-flex-box>
<ft-loader
v-if="isLoading"
/>
<div
v-if="!isLoading && dataAvailable"
class="center"
>
<h3>
{{ $t('Settings.Proxy Settings.Your Info') }}
</h3>
<p>
{{ $t('Settings.Proxy Settings.Ip') }}: {{ proxyIp }}
</p>
<p>
{{ $t('Settings.Proxy Settings.Country') }}: {{ proxyCountry }}
</p>
<p>
{{ $t('Settings.Proxy Settings.Region') }}: {{ proxyRegion }}
</p>
<p>
{{ $t('Settings.Proxy Settings.City') }}: {{ proxyCity }}
</p>
</div>
</ft-card>
</template>
<script src="./proxy-settings.js" />
<style scoped src="./proxy-settings.css" />

View File

@ -50,6 +50,9 @@ export default Vue.extend({
},
hideTrendingVideos: function () {
return this.$store.getters.getHideTrendingVideos
},
hideActiveSubscriptions: function () {
return this.$store.getters.getHideActiveSubscriptions
}
},
methods: {

View File

@ -97,26 +97,30 @@
</div>
<hr>
<div
v-for="(channel, index) in activeSubscriptions"
:key="index"
class="navChannel mobileHidden"
:title="channel.name"
@click="goToChannel(channel.id)"
v-if="!hideActiveSubscriptions"
>
<div
class="thumbnailContainer"
v-for="(channel, index) in activeSubscriptions"
:key="index"
class="navChannel mobileHidden"
:title="channel.name"
@click="goToChannel(channel.id)"
>
<img
class="channelThumbnail"
:src="channel.thumbnail"
<div
class="thumbnailContainer"
>
<img
class="channelThumbnail"
:src="channel.thumbnail"
>
</div>
<p
v-if="isOpen"
class="navLabel"
>
{{ channel.name }}
</p>
</div>
<p
v-if="isOpen"
class="navLabel"
>
{{ channel.name }}
</p>
</div>
</div>
</ft-flex-box>

View File

@ -7,7 +7,7 @@ import FtSelect from '../ft-select/ft-select.vue'
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
export default Vue.extend({
name: 'PlayerSettings',
name: 'SubscriptionSettings',
components: {
'ft-card': FtCard,
'ft-toggle-switch': FtToggleSwitch,

View File

@ -77,7 +77,7 @@ export default Vue.extend({
this.debounceSearchResults = debounce(this.getSearchSuggestions, 200)
},
methods: {
goToSearch: function (query) {
goToSearch: async function (query) {
const appWidth = $(window).width()
if (appWidth <= 680) {
@ -89,23 +89,30 @@ export default Vue.extend({
searchInput.blur()
}
this.$store.dispatch('getVideoIdFromUrl', query).then((result) => {
if (result) {
this.$router.push({
path: `/watch/${result}`
})
} else {
router.push({
path: `/search/${encodeURIComponent(query)}`,
query: {
sortBy: this.searchSettings.sortBy,
time: this.searchSettings.time,
type: this.searchSettings.type,
duration: this.searchSettings.duration
}
})
}
})
const videoId = await this.$store.dispatch('getVideoIdFromUrl', query)
const playlistId = await this.$store.dispatch('getPlaylistIdFromUrl', query)
console.log(playlistId)
if (videoId) {
this.$router.push({
path: `/watch/${videoId}`
})
} else if (playlistId) {
this.$router.push({
path: `/playlist/${playlistId}`
})
} else {
router.push({
path: `/search/${encodeURIComponent(query)}`,
query: {
sortBy: this.searchSettings.sortBy,
time: this.searchSettings.time,
type: this.searchSettings.type,
duration: this.searchSettings.duration
}
})
}
this.showFilters = false
},

View File

@ -78,6 +78,18 @@ export default Vue.extend({
downloadLinks: {
type: Array,
required: true
},
watchingPlaylist: {
type: Boolean,
required: true
},
theatrePossible: {
type: Boolean,
required: true
},
lengthSeconds: {
type: Number,
required: true
}
},
data: function () {
@ -111,6 +123,10 @@ export default Vue.extend({
return this.$store.getters.getHideRecommendedVideos
},
hideLiveChat: function () {
return this.$store.getters.getHideLiveChat
},
hideVideoLikesAndDislikes: function () {
return this.$store.getters.getHideVideoLikesAndDislikes
},
@ -119,6 +135,22 @@ export default Vue.extend({
return this.$store.getters.getHideVideoViews
},
favoritesPlaylist: function () {
return this.$store.getters.getFavorites
},
inFavoritesPlaylist: function () {
const index = this.favoritesPlaylist.videos.findIndex((video) => {
return video.videoId === this.id
})
return index !== -1
},
favoriteIconTheme: function () {
return this.inFavoritesPlaylist ? 'base favorite' : 'base'
},
downloadLinkNames: function () {
return this.downloadLinks.map((download) => {
return download.label
@ -196,6 +228,14 @@ export default Vue.extend({
this.$router.push({ path: `/channel/${this.channelId}` })
},
toggleSave: function () {
if (this.inFavoritesPlaylist) {
this.removeFromPlaylist()
} else {
this.addToPlaylist()
}
},
handleSubscription: function () {
if (this.channelId === '') {
return
@ -291,9 +331,52 @@ export default Vue.extend({
shell.openExternal(url)
},
addToPlaylist: function () {
const videoData = {
videoId: this.id,
title: this.title,
author: this.channelName,
authorId: this.channelId,
published: '',
description: this.description,
viewCount: this.viewCount,
lengthSeconds: this.lengthSeconds,
timeAdded: new Date().getTime(),
isLive: false,
paid: false,
type: 'video'
}
const payload = {
playlistName: 'Favorites',
videoData: videoData
}
this.addVideo(payload)
this.showToast({
message: this.$t('Video.Video has been saved')
})
},
removeFromPlaylist: function () {
const payload = {
playlistName: 'Favorites',
videoId: this.id
}
this.removeVideo(payload)
this.showToast({
message: this.$t('Video.Video has been removed from your saved list')
})
},
...mapActions([
'showToast',
'updateProfile'
'updateProfile',
'addVideo',
'removeVideo'
])
}
})

View File

@ -63,7 +63,15 @@
</div>
<div class="videoOptions">
<ft-icon-button
v-if="!hideRecommendedVideos"
v-if="!isUpcoming"
:title="$t('Video.Save Video')"
icon="star"
class="option"
:theme="favoriteIconTheme"
@click="toggleSave"
/>
<ft-icon-button
v-if="theatrePossible"
:title="$t('Toggle Theatre Mode')"
class="theatreModeButton option"
icon="expand-alt"

View File

@ -155,7 +155,7 @@ export default Vue.extend({
comment.message.forEach((text) => {
if (typeof (text.navigationEndpoint) !== 'undefined') {
if (typeof (text.navigationEndpoint.watchEndpoint) !== 'undefined') {
const htmlRef = `<router-link to="/watch/${text.navigationEndpoint.watchEndpoint.videoId}">${text.text}</router-link>`
const htmlRef = `<a href="https://www.youtube.com/watch?v=${text.navigationEndpoint.watchEndpoint.videoId}">${text.text}</a>`
comment.messageHtml = comment.messageHtml + htmlRef
} else {
comment.messageHtml = comment.messageHtml + text.text

View File

@ -6,6 +6,10 @@ import store from './store/index'
// import 'material-design-icons/iconfont/material-icons.css'
import { library } from '@fortawesome/fontawesome-svg-core'
import { fas } from '@fortawesome/free-solid-svg-icons'
import { faGithub } from '@fortawesome/free-brands-svg-icons/faGithub'
import { faBitcoin } from '@fortawesome/free-brands-svg-icons/faBitcoin'
import { faMonero } from '@fortawesome/free-brands-svg-icons/faMonero'
import { faMastodon } from '@fortawesome/free-brands-svg-icons/faMastodon'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import VueI18n from 'vue-i18n'
import yaml from 'js-yaml'
@ -17,13 +21,13 @@ Vue.config.devtools = isDev
Vue.config.performance = isDev
Vue.config.productionTip = isDev
library.add(fas)
library.add(fas, faGithub, faBitcoin, faMonero, faMastodon)
Vue.component('FontAwesomeIcon', FontAwesomeIcon)
Vue.use(VueI18n)
// List of locales approved for use
const activeLocales = ['en-US', 'en_GB', 'ar', 'bg', 'cs', 'da', 'de-DE', 'el', 'es', 'es-MX', 'fi', 'fr-FR', 'he', 'hu', 'hr', 'id', 'it', 'ja', 'nl', 'pl', 'pt', 'pt-BR', 'pt-PT', 'ru', 'sk', 'sl', 'sv', 'tr', 'vi', 'zh-CN', 'zh-TW']
const activeLocales = ['en-US', 'en_GB', 'ar', 'bg', 'cs', 'da', 'de-DE', 'el', 'es', 'es-MX', 'fi', 'fr-FR', 'gl', 'he', 'hu', 'hr', 'id', 'it', 'ja', 'nl', 'pl', 'pt', 'pt-BR', 'pt-PT', 'ru', 'sk', 'sl', 'sv', 'tr', 'vi', 'zh-CN', 'zh-TW']
const messages = {}
/* eslint-disable-next-line */
const fileLocation = isDev ? 'static/locales/' : `${__dirname}/static/locales/`
@ -32,7 +36,7 @@ const fileLocation = isDev ? 'static/locales/' : `${__dirname}/static/locales/`
activeLocales.forEach((locale) => {
try {
// File location when running in dev
const doc = yaml.safeLoad(fs.readFileSync(`${fileLocation}${locale}.yaml`))
const doc = yaml.load(fs.readFileSync(`${fileLocation}${locale}.yaml`))
messages[locale] = doc
} catch (e) {
console.log(e)

View File

@ -169,6 +169,8 @@ $thumbnail-overlay-opacity: 0.85
.description
font-size: 14px
max-height: 50px
overflow-y: hidden
@include low-contrast-when-watched(var(--secondary-text-color))
&.list

View File

@ -4,60 +4,206 @@ let dbLocation
if (window && window.process && window.process.type === 'renderer') {
// Electron is being used
let dbLocation = localStorage.getItem('dbLocation')
// let dbLocation = localStorage.getItem('dbLocation')
//
// if (dbLocation === null) {
// const electron = require('electron')
// dbLocation = electron.remote.app.getPath('userData')
// }
//
// dbLocation += '/playlists.db'
if (dbLocation === null) {
const electron = require('electron')
dbLocation = electron.remote.app.getPath('userData')
}
const electron = require('electron')
dbLocation = electron.remote.app.getPath('userData')
dbLocation += '/playlists.db'
dbLocation = dbLocation + '/playlists.db'
} else {
dbLocation = 'playlists.db'
}
const subDb = new Datastore({
const playlistDb = new Datastore({
filename: dbLocation,
autoload: true
})
const state = {
activePlaylistId: '',
activePlaylistVideoList: [],
watchedVideosWithinPlaylist: []
playlists: [
{
playlistName: 'Favorites',
protected: true,
videos: []
},
{
playlistName: 'WatchLater',
protected: true,
removeOnWatched: true,
videos: []
}
]
}
const mutations = {
addSubscription (state, payload) {
state.subscriptions.push(payload)
},
setSubscriptions (state, payload) {
state.subscriptions = payload
}
const getters = {
getAllPlaylists: () => state.playlists,
getFavorites: () => state.playlists[0],
getPlaylist: (playlistId) => state.playlists.find(playlist => playlist._id === playlistId),
getWatchLater: () => state.playlists[1]
}
const actions = {
addSubscriptions ({ commit }, payload) {
subDb.insert(payload, (err, payload) => {
if (!err) {
commit('addSubscription', payload)
addPlaylist ({ commit }, payload) {
playlistDb.insert(payload, (err, payload) => {
if (err) {
console.error(err)
} else {
commit('addPlaylist', payload)
}
})
},
getSubscriptions ({ commit }, payload) {
subDb.find({}, (err, payload) => {
if (!err) {
commit('setSubscriptions', payload)
addPlaylists ({ commit }, payload) {
playlistDb.insert(payload, (err, payload) => {
if (err) {
console.error(err)
} else {
commit('addPlaylists', payload)
}
})
},
removeSubscription ({ commit }, channelId) {
subDb.remove({ channelId: channelId }, {}, () => {
commit('setSubscriptions', this.state.subscriptions.filter(sub => sub.channelId !== channelId))
addVideo ({ commit }, payload) {
playlistDb.update({ playlistName: payload.playlistName }, { $push: { videos: payload.videoData } }, { upsert: true }, err => {
if (err) {
console.error(err)
} else {
commit('addVideo', payload)
}
})
},
addVideos ({ commit }, payload) {
playlistDb.update({ _id: payload.playlistId }, { $push: { videos: { $each: payload.videosIds } } }, { upsert: true }, err => {
if (err) {
console.error(err)
} else {
commit('addVideos', payload)
}
})
},
grabAllPlaylists({ commit, dispatch }) {
playlistDb.find({}, (err, payload) => {
if (err) {
console.error(err)
} else {
if (payload.length === 0) {
commit('setAllPlaylists', state.playlists)
dispatch('addPlaylists', payload)
} else {
commit('setAllPlaylists', payload)
}
}
})
},
removeAllPlaylists ({ commit }) {
playlistDb.remove({ protected: { $ne: true } }, err => {
if (err) {
console.error(err)
} else {
commit('removeAllPlaylists')
}
})
},
removeAllVideos ({ commit }, playlistName) {
playlistDb.update({ playlistName: playlistName }, { $set: { videos: [] } }, { upsert: true }, err => {
if (err) {
console.error(err)
} else {
commit('removeAllVideos', playlistName)
}
})
},
removePlaylist ({ commit }, playlistId) {
playlistDb.remove({ _id: playlistId, protected: { $ne: true } }, (err, playlistId) => {
if (err) {
console.error(err)
} else {
commit('removePlaylist', playlistId)
}
})
},
removePlaylists ({ commit }, playlistIds) {
playlistDb.remove({ _id: { $in: playlistIds }, protected: { $ne: true } }, (err, playlistIds) => {
if (err) {
console.error(err)
} else {
commit('removePlaylists', playlistIds)
}
})
},
removeVideo ({ commit }, payload) {
playlistDb.update({ playlistName: payload.playlistName }, { $pull: { videos: { videoId: payload.videoId } } }, { upsert: true }, (err, numRemoved) => {
if (err) {
console.error(err)
} else {
commit('removeVideo', payload)
}
})
},
removeVideos ({ commit }, payload) {
playlistDb.update({ _id: payload.playlistName }, { $pull: { videos: { $in: payload.videoId } } }, { upsert: true }, err => {
if (err) {
console.error(err)
} else {
commit('removeVideos', payload)
}
})
}
}
const getters = {}
const mutations = {
addPlaylist (state, payload) {
state.playlists.push(payload)
},
addPlaylists (state, payload) {
state.playlists = state.playlists.concat(payload)
},
addVideo (state, payload) {
const playlist = state.playlists.find(playlist => playlist.playlistName === payload.playlistName)
if (playlist) {
playlist.videos.push(payload.videoData)
}
},
addVideos (state, payload) {
const playlist = state.playlists.find(playlist => playlist._id === payload.playlistId)
if (playlist) {
playlist.videos = playlist.videos.concat(payload.playlistIds)
}
},
removeAllPlaylists (state) {
state.playlists = state.playlists.filter(playlist => playlist.protected !== true)
},
removeAllVideos (state, playlistName) {
const playlist = state.playlists.find(playlist => playlist.playlistName === playlistName)
if (playlist) {
playlist.videos = []
}
},
removeVideo (state, payload) {
const playlist = state.playlists.findIndex(playlist => playlist.playlistName === payload.playlistName)
if (playlist !== -1) {
state.playlists[playlist].videos = state.playlists[playlist].videos.filter(video => video.videoId !== payload.videoId)
}
},
removeVideos (state, payload) {
const playlist = state.playlists.findIndex(playlist => playlist._id === payload.playlistId)
if (playlist !== -1) {
playlist.videos = playlist.videos.filter(video => payload.videoId.indexOf(video) === -1)
}
},
removePlaylist (state, playlistId) {
state.playlists = state.playlists.filter(playlist => playlist._id !== playlistId || playlist.protected)
},
setAllPlaylists (state, payload) {
state.playlists = payload
}
}
export default {
state,
getters,

View File

@ -57,8 +57,10 @@ const state = {
defaultPlayback: 1,
defaultVideoFormat: 'dash',
defaultQuality: '720',
useTor: false,
proxy: 'SOCKS5://127.0.0.1:9050',
useProxy: false,
proxyProtocol: 'socks5',
proxyHostname: '127.0.0.1',
proxyPort: '9050',
debugMode: false,
disableSmoothScrolling: false,
hideWatchedSubs: false,
@ -71,7 +73,8 @@ const state = {
hideRecommendedVideos: false,
hideTrendingVideos: false,
hidePopularVideos: false,
hideLiveChat: false
hideLiveChat: false,
hideActiveSubscriptions: false
}
const getters = {
@ -159,6 +162,22 @@ const getters = {
return state.proxyVideos
},
getUseProxy: () => {
return state.useProxy
},
getProxyProtocol: () => {
return state.proxyProtocol
},
getProxyHostname: () => {
return state.proxyHostname
},
getProxyPort: () => {
return state.proxyPort
},
getDefaultTheatreMode: () => {
return state.defaultTheatreMode
},
@ -224,6 +243,9 @@ const getters = {
},
getHideLiveChat: () => {
return state.hideLiveChat
},
getHideActiveSubscriptions: () => {
return state.hideActiveSubscriptions
}
}
@ -311,6 +333,18 @@ const actions = {
case 'proxyVideos':
commit('setProxyVideos', result.value)
break
case 'useProxy':
commit('setUseProxy', result.value)
break
case 'proxyProtocol':
commit('setProxyProtocol', result.value)
break
case 'proxyHostname':
commit('setProxyHostname', result.value)
break
case 'proxyPort':
commit('setProxyPort', result.value)
break
case 'defaultTheatreMode':
commit('setDefaultTheatreMode', result.value)
break
@ -351,6 +385,9 @@ const actions = {
case 'hideLiveChat':
commit('setHideLiveChat', result.value)
break
case 'hideActiveSubscriptions':
commit('setHideActiveSubscriptions', result.value)
break
}
})
}
@ -583,10 +620,34 @@ const actions = {
})
},
updateUseTor ({ commit }, useTor) {
settingsDb.update({ _id: useTor }, { value: useTor }, { upsert: true }, (err, useTor) => {
updateUseProxy ({ commit }, useProxy) {
settingsDb.update({ _id: 'useProxy' }, { _id: 'useProxy', value: useProxy }, { upsert: true }, (err, numReplaced) => {
if (!err) {
commit('setUseTor', useTor)
commit('setUseProxy', useProxy)
}
})
},
updateProxyProtocol ({ commit }, proxyProtocol) {
settingsDb.update({ _id: 'proxyProtocol' }, { _id: 'proxyProtocol', value: proxyProtocol }, { upsert: true }, (err, numReplaced) => {
if (!err) {
commit('setProxyProtocol', proxyProtocol)
}
})
},
updateProxyHostname ({ commit }, proxyHostname) {
settingsDb.update({ _id: 'proxyHostname' }, { _id: 'proxyHostname', value: proxyHostname }, { upsert: true }, (err, numReplaced) => {
if (!err) {
commit('setProxyHostname', proxyHostname)
}
})
},
updateProxyPort ({ commit }, proxyPort) {
settingsDb.update({ _id: 'proxyPort' }, { _id: 'proxyPort', value: proxyPort }, { upsert: true }, (err, numReplaced) => {
if (!err) {
commit('setProxyPort', proxyPort)
}
})
},
@ -655,6 +716,14 @@ const actions = {
})
},
updateHideActiveSubscriptions ({ commit }, hideActiveSubscriptions) {
settingsDb.update({ _id: 'hideActiveSubscriptions' }, { _id: 'hideActiveSubscriptions', value: hideActiveSubscriptions }, { upsert: true }, (err, numReplaced) => {
if (!err) {
commit('setHideActiveSubscriptions', hideActiveSubscriptions)
}
})
},
updateHideLiveChat ({ commit }, hideLiveChat) {
settingsDb.update({ _id: 'hideLiveChat' }, { _id: 'hideLiveChat', value: hideLiveChat }, { upsert: true }, (err, numReplaced) => {
if (!err) {
@ -749,8 +818,17 @@ const mutations = {
setDefaultTheatreMode (state, defaultTheatreMode) {
state.defaultTheatreMode = defaultTheatreMode
},
setUseTor (state, useTor) {
state.useTor = useTor
setUseProxy (state, useProxy) {
state.useProxy = useProxy
},
setProxyProtocol (state, proxyProtocol) {
state.proxyProtocol = proxyProtocol
},
setProxyHostname (state, proxyHostname) {
state.proxyHostname = proxyHostname
},
setProxyPort (state, proxyPort) {
state.proxyPort = proxyPort
},
setDebugMode (state, debugMode) {
state.debugMode = debugMode
@ -796,6 +874,9 @@ const mutations = {
},
setHideLiveChat (state, hideLiveChat) {
state.hideLiveChat = hideLiveChat
},
setHideActiveSubscriptions (state, hideActiveSubscriptions) {
state.hideActiveSubscriptions = hideActiveSubscriptions
}
}

View File

@ -212,6 +212,12 @@ const actions = {
return urlObject.pathname.slice(1)
}
},
// youtube.com/embed
function() {
if (urlObject.pathname.match(/^\/embed\/[A-Za-z0-9_-]+$/)) {
return urlObject.pathname.replace('/embed/', '')
}
},
// cloudtube
function() {
if (urlObject.host.match(/^cadence\.(gq|moe)$/) && urlObject.pathname.match(/^\/cloudtube\/video\/[A-Za-z0-9_-]+$/)) {
@ -223,6 +229,27 @@ const actions = {
return extractors.reduce((a, c) => a || c(), null) || false
},
getPlaylistIdFromUrl (_, url) {
/** @type {URL} */
let urlObject
try {
urlObject = new URL(url)
} catch (e) {
return false
}
const extractors = [
// anything with /playlist?list=
function() {
if (urlObject.pathname === '/playlist' && urlObject.searchParams.has('list')) {
return urlObject.searchParams.get('list')
}
}
]
return extractors.reduce((a, c) => a || c(), null) || false
},
padNumberWithLeadingZeros(_, payload) {
let numberString = payload.number.toString()
while (numberString.length < payload.length) {

View File

@ -8,6 +8,7 @@
--bg-color: #f1f1f1;
--link-color: var(--accent-color);
--link-visited-color: var(--accent-color-visited);
--favorite-icon-color: #FFD600;
--card-bg-color: #FFFFFF;
--secondary-card-bg-color: #eeeeee;
--scrollbar-color: #CCCCCC;
@ -32,6 +33,7 @@
--bg-color: #212121;
--link-color: var(--accent-color);
--link-visited-color: var(--accent-color-visited);
--favorite-icon-color: #FFEA00;
--card-bg-color: #303030;
--secondary-card-bg-color: rgba(0, 0, 0, 0.75);
--scrollbar-color: #414141;
@ -55,6 +57,7 @@
--bg-color: #000000;
--link-color: var(--accent-color);
--link-visited-color: var(--accent-color-visited);
--favorite-icon-color: #FFEA00;
--card-bg-color: #000000;
--secondary-card-bg-color: rgba(0, 0, 0, 0.75);
--scrollbar-color: #515151;

View File

@ -449,7 +449,6 @@ body.vjs-full-window {
content: url(assets/img/open_fullwindow.svg);
}
.vjs-icon-fullwindow-exit, .video-js.vjs-fullwindow .vjs-fullwindow-control .vjs-icon-placeholder {
font-family: VideoJS;
font-weight: normal;
@ -459,6 +458,30 @@ body.vjs-full-window {
content: url(assets/img/close_fullwindow.svg);
}
.vjs-icon-loop, .video-js .vjs-fullwindow-control .vjs-icon-placeholder {
color: white !important;
margin-top: 5px !important;
padding-top: 5px !important;
cursor: pointer;
}
.vjs-icon-loop-active {
background-color: var(--primary-color);
}
.vjs-icon-loop:before, .video-js.vjs-fullwindow .vjs-fullwindow-control .vjs-icon-placeholder:before {
content: url(assets/img/loop.svg);
/* filter: invert(1) drop-shadow(1px 0px 0px var(--primary-color)); */
}
.loop-black:before {
filter: brightness(0%);
}
.loop-white:before {
filter: none;
}
.vjs-full-window .video-js.vjs-fullscreen {
position: fixed;
overflow: hidden;

View File

@ -1,7 +1,8 @@
.card {
width: 85%;
margin: 0 auto;
margin-bottom: 60px;
margin-bottom: 10px;
word-wrap: break-word;
}
.logo {

View File

@ -1,6 +1,7 @@
import Vue from 'vue'
import FtCard from '../../components/ft-card/ft-card.vue'
import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
import FtButton from '../../components/ft-button/ft-button.vue'
const { version } = require('../../../../package.json')
@ -8,13 +9,97 @@ export default Vue.extend({
name: 'About',
components: {
'ft-card': FtCard,
'ft-element-list': FtElementList
'ft-element-list': FtElementList,
'ft-button': FtButton
},
data: function () {
return {
versionNumber: `v${version}`
versionNumber: `v${version}`,
chunks: [
{
icon: ['fab', 'github'],
title: this.$t('About.Source code'),
content: `<a href="https://github.com/FreeTubeApp/FreeTube">GitHub: FreeTubeApp/FreeTube</a><br>${this.$t('About.Licensed under the AGPLv3')} <a href="https://www.gnu.org/licenses/agpl-3.0.en.html">${this.$t('About.View License')}</a>`
},
{
icon: 'file-download',
title: this.$t('About.Downloads / Changelog'),
content: `<a href="https://github.com/FreeTubeApp/FreeTube/releases">${this.$t('About.GitHub releases')}</a>`
},
{
icon: 'question-circle',
title: this.$t('About.Help'),
content: `<a href="https://github.com/FreeTubeApp/FreeTube/wiki">${this.$t('About.FreeTube Wiki')}</a> / <a href="https://github.com/FreeTubeApp/FreeTube/wiki/F.A.Q.">${this.$t('About.FAQ')}</a>`
},
{
icon: 'exclamation-circle',
title: this.$t('About.Report a problem'),
content: `<a href="https://github.com/FreeTubeApp/FreeTube/issues">${this.$t('About.GitHub issues')}</a><br>${this.$t('About.Please check for duplicates before posting')}`
},
{
icon: 'globe',
title: this.$t('About.Website'),
content: '<a href="https://freetubeapp.io/">https://freetubeapp.io/</a>'
},
{
icon: 'newspaper',
title: this.$t('About.Blog'),
content: '<a href="https://freetube.writeas.com/">https://freetube.writeas.com/</a>'
},
{
icon: 'envelope',
title: this.$t('About.Email'),
content: '<a href="mailto:FreeTubeApp@protonmail.com">FreeTubeApp@protonmail.com</a>'
},
{
icon: ['fab', 'mastodon'],
title: this.$t('About.Mastodon'),
content: '<a href="https://mastodon.technology/@FreeTube">@FreeTube@mastodon.technology</a>'
},
{
icon: 'comment-dots',
title: this.$t('About.Chat on Matrix'),
content: `<a href="https://matrix.to/#/#freetube:matrix.org?via=matrix.org&via=privacytools.io&via=tchncs.de">#freetube:matrix.org</a><br>${this.$t('About.Please read the')} <a href="https://github.com/FreeTubeApp/FreeTube/wiki/Matrix-Channel-Info-&-Rules">${this.$t('About.room rules')}</a>`
},
{
icon: 'language',
title: this.$t('About.Translate'),
content: '<a href="https://hosted.weblate.org/engage/free-tube/">https://hosted.weblate.org/engage/free-tube/</a>'
},
{
icon: 'users',
title: this.$t('About.Credits'),
content: `${this.$t('About.FreeTube is made possible by')} <a href="https://github.com/FreeTubeApp/FreeTube/wiki/Credits">${this.$t('About.these people and projects')}</a>`
},
{
icon: 'heart',
title: `${this.$t('About.Donate')} - Liberapay`,
content: '<a href="https://liberapay.com/FreeTube">https://liberapay.com/FreeTube</a>'
},
{
icon: ['fab', 'bitcoin'],
title: `${this.$t('About.Donate')} - BTC`,
content: '<a href="bitcoin:1Lih7Ho5gnxb1CwPD4o59ss78pwo2T91eS">1Lih7Ho5gnxb1CwPD4o59ss78pwo2T91eS</a>'
},
{
icon: ['fab', 'monero'],
title: `${this.$t('About.Donate')} - XMR`,
content: '<a href="monero:48WyAPdjwc6VokeXACxSZCFeKEXBiYPV6GjfvBsfg4CrUJ95LLCQSfpM9pvNKy5GE5H4hNaw99P8RZyzmaU9kb1pD7kzhCB">48WyAPdjwc6VokeXACxSZCFeKEXBiYPV6GjfvBsfg4CrUJ95LLCQSfpM9pvNKy5GE5H4hNaw99P8RZyzmaU9kb1pD7kzhCB</a>'
}
]
}
},
mounted: function () {
computed: {
usingElectron: function () {
return this.$store.getters.getUsingElectron
}
},
methods: {
openUrl: function (url) {
if (this.usingElectron) {
const shell = require('electron').shell
shell.openExternal(url)
}
}
}
})

View File

@ -0,0 +1,50 @@
.card
width: 85%
margin: 0 auto
margin-bottom: 60px
@media only screen and (max-width: 680px)
width: 90%
.brand
text-align: center
.logo
width: 500px
max-width: 100%
.version
font-size: 2em
.about-chunks
max-width: 860px
margin: 80px auto
display: grid
grid-template-columns: 1fr 1fr
grid-gap: 16px
@media only screen and (max-width: 650px)
grid-template-columns: 1fr
.chunk
background-color: var(--bg-color)
margin: 0
padding: 18px
border-radius: 8px
display: grid
grid-template: "icon title" auto "icon content" 1fr / auto 1fr
justify-content: start
align-items: start
grid-gap: 6px 14px
word-break: break-word
box-shadow: 0px 1px 4px -1px rgba(0, 0, 0, 0.5)
@each $area in icon title content
.#{$area}
grid-area: $area
.icon
font-size: 24px
.title
margin: 0

View File

@ -1,64 +1,40 @@
<template>
<div>
<ft-card class="card center">
<h3 class="leftAlign">
About
</h3>
<img
src="_icons/logoColor.png"
class="logo"
>
<ft-card class="card">
<h1>
{{ versionNumber }} Beta
{{ $t("About.About") }}
</h1>
<h3>
<font-awesome-icon
icon="envelope"
/>
&nbsp;&nbsp;FreeTubeApp@protonmail.com
</h3>
<h2>
This software is FOSS and released under the
<a href="https://www.gnu.org/licenses/agpl-3.0.en.html">GNU Affero General Public License v3.0</a>.
</h2>
<p>
Found a bug? Want to suggest a feature? Want to help out? Check out our
<a href="https://github.com/FreeTubeApp/FreeTube">GitHub</a>
page. Pull requests are welcome.
</p>
<p>
Thank you very much to the
<a href="https://github.com/FreeTubeApp/FreeTube/wiki/Credits">People and Projects</a>
that make FreeTube possible!
</p>
<p>
Want to chat? Join our
<a href="https://app.element.io/#/group/+freetube:matrix.org">Element / Matrix Server</a>
. Please check the
<a href="https://github.com/FreeTubeApp/FreeTube/wiki/Matrix-Server-Info-&-Rules">rules</a>
before joining.
</p>
<p>
Looking for help? Check out our
<a href="https://github.com/FreeTubeApp/FreeTube/wiki">Wiki</a>
page.
</p>
<p>
Check out our
<a href="https://addons.mozilla.org/en-US/firefox/addon/freetube-redirect/">Firefox</a>
extension!
</p>
<p>
If you enjoy using FreeTube, consider donating via
<a href="https://liberapay.com/FreeTube">Liberapay</a>
or through our Bitcoin address.
</p>
<p>
BTC: 1Lih7Ho5gnxb1CwPD4o59ss78pwo2T91eS
</p>
<section class="brand">
<img
src="_icons/logoColor.png"
class="logo"
>
<div class="version">
{{ versionNumber }} {{ $t("About.Beta") }}
</div>
</section>
<section class="about-chunks">
<figure
v-for="chunk in chunks"
:key="chunk.title"
class="chunk"
>
<font-awesome-icon
class="icon"
:icon="chunk.icon"
/>
<h3 class="title">
{{ chunk.title }}
</h3>
<div
class="content"
v-html="chunk.content"
/>
</figure>
</section>
</ft-card>
</div>
</template>
<script src="./About.js" />
<style scoped src="./About.css" />
<style scoped src="./About.sass" lang="sass" />

View File

@ -410,7 +410,10 @@ export default Vue.extend({
getPlaylistsLocal: function () {
ytch.getChannelPlaylistInfo(this.id, this.playlistSortBy).then((response) => {
console.log(response)
this.latestPlaylists = response.items
this.latestPlaylists = response.items.map((item) => {
item.proxyThumbnail = false
return item
})
this.playlistContinuationString = response.continuation
this.isElementListLoading = false
}).catch((err) => {

View File

@ -154,7 +154,7 @@ export default Vue.extend({
published: publishDate,
publishedText: publishDate,
lengthSeconds: videoDuration,
liveNow: video.live,
liveNow: video.isLive,
paid: false,
premium: false,
isUpcoming: false,
@ -179,7 +179,7 @@ export default Vue.extend({
query: payload.query,
data: this.shownResults,
searchSettings: this.searchSettings,
nextPageRef: result.nextpageRef
nextPageRef: result.continuation
}
this.$store.commit('addToSessionSearchHistory', historyPayload)
@ -200,7 +200,6 @@ export default Vue.extend({
this.performSearchInvidious(payload)
} else {
this.isLoading = false
// TODO: Show toast with error message
}
})
},

View File

@ -8,6 +8,7 @@ import SubscriptionSettings from '../../components/subscription-settings/subscri
import PrivacySettings from '../../components/privacy-settings/privacy-settings.vue'
import DataSettings from '../../components/data-settings/data-settings.vue'
import DistractionSettings from '../../components/distraction-settings/distraction-settings.vue'
import ProxySettings from '../../components/proxy-settings/proxy-settings.vue'
export default Vue.extend({
name: 'Settings',
@ -20,6 +21,7 @@ export default Vue.extend({
'subscription-settings': SubscriptionSettings,
'privacy-settings': PrivacySettings,
'data-settings': DataSettings,
'distraction-settings': DistractionSettings
'distraction-settings': DistractionSettings,
'proxy-settings': ProxySettings
}
})

View File

@ -7,6 +7,7 @@
<distraction-settings />
<privacy-settings />
<data-settings />
<proxy-settings />
</div>
</template>

View File

@ -1,6 +1,8 @@
import Vue from 'vue'
import FtCard from '../../components/ft-card/ft-card.vue'
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
import FtTooltip from '../../components/ft-tooltip/ft-tooltip.vue'
import FtLoader from '../../components/ft-loader/ft-loader.vue'
import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
export default Vue.extend({
@ -8,8 +10,48 @@ export default Vue.extend({
components: {
'ft-card': FtCard,
'ft-flex-box': FtFlexBox,
'ft-tooltip': FtTooltip,
'ft-loader': FtLoader,
'ft-element-list': FtElementList
},
data: function () {
return {
isLoading: false,
dataLimit: 100
}
},
computed: {
favoritesPlaylist: function () {
return this.$store.getters.getFavorites
},
activeData: function () {
if (this.favoritesPlaylist.videos.length < this.dataLimit) {
return this.favoritesPlaylist.videos
} else {
return this.favoritesPlaylist.videos.slice(0, this.dataLimit)
}
}
},
watch: {
activeData() {
this.isLoading = true
setTimeout(() => {
this.isLoading = false
}, 100)
}
},
mounted: function () {
const limit = sessionStorage.getItem('favoritesLimit')
if (limit !== null) {
this.dataLimit = limit
}
},
methods: {
increaseLimit: function () {
this.dataLimit += 100
sessionStorage.setItem('favoritesLimit', this.dataLimit)
}
}
})

View File

@ -1,12 +1,42 @@
<template>
<div>
<ft-card class="card">
<h3>{{ $t("User Playlists.Your Playlists") }}</h3>
<ft-flex-box>
<ft-loader
v-if="isLoading"
:fullscreen="true"
/>
<ft-card
v-else
class="card"
>
<h3>
{{ $t("User Playlists.Your Playlists") }}
<ft-tooltip
class="selectTooltip"
position="bottom"
:tooltip="$t('User Playlists.Playlist Message')"
/>
</h3>
<ft-flex-box
v-if="activeData.length === 0"
>
<p class="message">
{{ $t("This part of the app is not ready yet. Come back later when progress has been made.") }}
{{ $t("User Playlists['Your saved videos are empty. Click on the save button on the corner of a video to have it listed here']") }}
</p>
</ft-flex-box>
<ft-element-list
v-else
:data="activeData"
/>
<ft-flex-box
v-if="activeData.length < favoritesPlaylist.videos.length"
>
<ft-button
label="Load More"
background-color="var(--primary-color)"
text-color="var(--text-with-main-color)"
@click="increaseLimit"
/>
</ft-flex-box>
</ft-card>
</div>
</template>

View File

@ -119,6 +119,12 @@ export default Vue.extend({
playNextVideo: function () {
return this.$store.getters.getPlayNextVideo
},
hideRecommendedVideos: function () {
return this.$store.getters.getHideRecommendedVideos
},
hideLiveChat: function () {
return this.$store.getters.getHideLiveChat
},
youtubeNoCookieEmbeddedFrame: function () {
return `<iframe width='560' height='315' src='https://www.youtube-nocookie.com/embed/${this.videoId}?rel=0' frameborder='0' allow='autoplay; encrypted-media' allowfullscreen></iframe>`
@ -128,6 +134,9 @@ export default Vue.extend({
},
hideVideoLikesAndDislikes: function () {
return this.$store.getters.getHideVideoLikesAndDislikes
},
theatrePossible: function() {
return !this.hideRecommendedVideos || (!this.hideLiveChat && this.isLive) || this.watchingPlaylist
}
},
watch: {
@ -197,7 +206,7 @@ export default Vue.extend({
console.log(result)
const playabilityStatus = result.player_response.playabilityStatus
if (playabilityStatus.status !== 'OK') {
if (playabilityStatus.status === 'UNPLAYABLE') {
const errorScreen = playabilityStatus.errorScreen.playerErrorMessageRenderer
const reason = errorScreen.reason.simpleText
let subReason
@ -228,7 +237,9 @@ export default Vue.extend({
this.channelId = result.player_response.videoDetails.channelId
this.channelName = result.videoDetails.author.name
console.log(result)
this.channelThumbnail = result.videoDetails.author.thumbnails[0].url
if (result.videoDetails.author.thumbnails.length > 0) {
this.channelThumbnail = result.videoDetails.author.thumbnails[0].url
}
} else {
this.channelId = result.player_response.videoDetails.channelId
this.channelName = result.player_response.videoDetails.author
@ -248,7 +259,7 @@ export default Vue.extend({
this.thumbnail = `https://i.ytimg.com/vi/${this.videoId}/maxres3.jpg`
break
default:
this.thumbnail = result.videoDetails.thumbnail.thumbnails[result.videoDetails.thumbnail.thumbnails.length - 1].url
this.thumbnail = result.videoDetails.thumbnails[result.videoDetails.thumbnails.length - 1].url
break
}
@ -264,8 +275,8 @@ export default Vue.extend({
this.videoLikeCount = null
this.videoDislikeCount = null
} else {
this.videoLikeCount = result.videoDetails.likes
this.videoDislikeCount = result.videoDetails.dislikes
this.videoLikeCount = isNaN(result.videoDetails.likes) ? 0 : result.videoDetails.likes
this.videoDislikeCount = isNaN(result.videoDetails.dislikes) ? 0 : result.videoDetails.dislikes
}
this.isLive = result.player_response.videoDetails.isLive
this.isLiveContent = result.player_response.videoDetails.isLiveContent

View File

@ -22,7 +22,7 @@
@media only screen and (max-width: 900px)
+single-column-template
&.isLoading
&.isLoading, &.noSidebar
+single-column-template
.videoArea
@ -73,6 +73,9 @@
@media only screen and (min-width: 901px)
min-width: 380px
@at-root .noSidebar#{&}
grid-area: auto
.watchVideoPlaylist, .watchVideoSidebar, .theatrePlaylist
margin: 0 8px 16px

View File

@ -3,7 +3,8 @@
class="videoLayout"
:class="{
isLoading,
useTheatreMode
useTheatreMode,
noSidebar: !theatrePossible
}"
>
<ft-loader
@ -78,6 +79,9 @@
:is-live="isLive"
:is-upcoming="isUpcoming"
:download-links="downloadLinks"
:watching-playlist="watchingPlaylist"
:theatre-possible="theatrePossible"
:length-seconds="videoLengthSeconds"
class="watchVideo"
:class="{ theatreWatchVideo: useTheatreMode }"
@theatre-mode="toggleTheatreMode"

View File

@ -71,6 +71,7 @@
{"id":328,"name":"غيانا","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"هايتي","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"هندوراس","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"هونغ كونغ","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"المجر","alpha2":"hu","alpha3":"hun"},
{"id":352,"name":"آيسلندا","alpha2":"is","alpha3":"isl"},
{"id":356,"name":"الهند","alpha2":"in","alpha3":"ind"},

View File

@ -72,6 +72,7 @@
{"id":328,"name":"Гвиана","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Хаити","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Хондурас","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Хонг Конг","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Унгария","alpha2":"hu","alpha3":"hun"},
{"id":352,"name":"Исландия","alpha2":"is","alpha3":"isl"},
{"id":356,"name":"Индия","alpha2":"in","alpha3":"ind"},

View File

@ -54,6 +54,7 @@
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hongkong","alpha2":"hk","alpha3":"hkg"},
{"id":152,"name":"Chile","alpha2":"cl","alpha3":"chl"},
{"id":191,"name":"Chorvatsko","alpha2":"hr","alpha3":"hrv"},
{"id":356,"name":"Indie","alpha2":"in","alpha3":"ind"},

View File

@ -65,6 +65,7 @@
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":112,"name":"Hviderusland","alpha2":"by","alpha3":"blr"},
{"id":356,"name":"Indien","alpha2":"in","alpha3":"ind"},
{"id":360,"name":"Indonesien","alpha2":"id","alpha3":"idn"},

View File

@ -57,6 +57,7 @@
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hongkong","alpha2":"hk","alpha3":"hkg"},
{"id":356,"name":"Indien","alpha2":"in","alpha3":"ind"},
{"id":360,"name":"Indonesien","alpha2":"id","alpha3":"idn"},
{"id":368,"name":"Irak","alpha2":"iq","alpha3":"irq"},

View File

@ -58,6 +58,7 @@
{"id":724,"name":"Hispaania","alpha2":"es","alpha3":"esp"},
{"id":528,"name":"Holland","alpha2":"nl","alpha3":"nld"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":191,"name":"Horvaatia","alpha2":"hr","alpha3":"hrv"},
{"id":372,"name":"Iirimaa","alpha2":"ie","alpha3":"irl"},
{"id":376,"name":"Iisrael","alpha2":"il","alpha3":"isr"},

View File

@ -137,6 +137,7 @@
{"id":512,"name":"Ομάν","alpha2":"om","alpha3":"omn"},
{"id":583,"name":"Ομόσπονδες Πολιτείες της Μικρονησίας","alpha2":"fm","alpha3":"fsm"},
{"id":340,"name":"Ονδούρα","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Χονγκ Κονγκ","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Ουγγαρία","alpha2":"hu","alpha3":"hun"},
{"id":800,"name":"Ουγκάντα","alpha2":"ug","alpha3":"uga"},
{"id":860,"name":"Ουζμπεκιστάν","alpha2":"uz","alpha3":"uzb"},

View File

@ -72,6 +72,7 @@
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Hungary","alpha2":"hu","alpha3":"hun"},
{"id":352,"name":"Iceland","alpha2":"is","alpha3":"isl"},
{"id":356,"name":"India","alpha2":"in","alpha3":"ind"},

View File

@ -72,6 +72,7 @@
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Hungary","alpha2":"hu","alpha3":"hun"},
{"id":352,"name":"Iceland","alpha2":"is","alpha3":"isl"},
{"id":356,"name":"India","alpha2":"in","alpha3":"ind"},

View File

@ -75,6 +75,7 @@
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haití","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Hungría","alpha2":"hu","alpha3":"hun"},
{"id":356,"name":"India","alpha2":"in","alpha3":"ind"},
{"id":360,"name":"Indonesia","alpha2":"id","alpha3":"idn"},

View File

@ -75,6 +75,7 @@
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haití","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Hungría","alpha2":"hu","alpha3":"hun"},
{"id":356,"name":"India","alpha2":"in","alpha3":"ind"},
{"id":360,"name":"Indonesia","alpha2":"id","alpha3":"idn"},

View File

@ -75,6 +75,7 @@
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haití","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Hungría","alpha2":"hu","alpha3":"hun"},
{"id":356,"name":"India","alpha2":"in","alpha3":"ind"},
{"id":360,"name":"Indonesia","alpha2":"id","alpha3":"idn"},

View File

@ -68,6 +68,7 @@
{"id":728,"name":"Hego Sudan","alpha2":"ss","alpha3":"ssd"},
{"id":528,"name":"Herbehereak","alpha2":"nl","alpha3":"nld"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Hungaria","alpha2":"hu","alpha3":"hun"},
{"id":356,"name":"India","alpha2":"in","alpha3":"ind"},
{"id":360,"name":"Indonesia","alpha2":"id","alpha3":"idn"},

View File

@ -52,6 +52,7 @@
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":360,"name":"Indonesia","alpha2":"id","alpha3":"idn"},
{"id":356,"name":"Intia","alpha2":"in","alpha3":"ind"},
{"id":368,"name":"Irak","alpha2":"iq","alpha3":"irq"},

View File

@ -76,6 +76,7 @@
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haïti","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Hongrie","alpha2":"hu","alpha3":"hun"},
{"id":356,"name":"Inde","alpha2":"in","alpha3":"ind"},
{"id":360,"name":"Indonésie","alpha2":"id","alpha3":"idn"},

View File

@ -67,6 +67,7 @@
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":528,"name":"Hollandia","alpha2":"nl","alpha3":"nld"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":191,"name":"Horvátország","alpha2":"hr","alpha3":"hrv"},
{"id":356,"name":"India","alpha2":"in","alpha3":"ind"},
{"id":360,"name":"Indonézia","alpha2":"id","alpha3":"idn"},

View File

@ -75,6 +75,7 @@
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":356,"name":"India","alpha2":"in","alpha3":"ind"},
{"id":360,"name":"Indonesia","alpha2":"id","alpha3":"idn"},
{"id":364,"name":"Iran","alpha2":"ir","alpha3":"irn"},

View File

@ -158,6 +158,7 @@
{"id":68,"name":"ボリビア多民族国","alpha2":"bo","alpha3":"bol"},
{"id":620,"name":"ポルトガル","alpha2":"pt","alpha3":"prt"},
{"id":340,"name":"ホンジュラス","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"香港","alpha2":"hk","alpha3":"hkg"},
{"id":584,"name":"マーシャル諸島","alpha2":"mh","alpha3":"mhl"},
{"id":450,"name":"マダガスカル","alpha2":"mg","alpha3":"mdg"},
{"id":454,"name":"マラウイ","alpha2":"mw","alpha3":"mwi"},

View File

@ -124,6 +124,7 @@
{"id":36,"name":"오스트레일리아","alpha2":"au","alpha3":"aus"},
{"id":40,"name":"오스트리아","alpha2":"at","alpha3":"aut"},
{"id":340,"name":"온두라스","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"홍콩","alpha2":"hk","alpha3":"hkg"},
{"id":400,"name":"요르단","alpha2":"jo","alpha3":"jor"},
{"id":800,"name":"우간다","alpha2":"ug","alpha3":"uga"},
{"id":858,"name":"우루과이","alpha2":"uy","alpha3":"ury"},

View File

@ -53,6 +53,7 @@
{"id":328,"name":"Gajana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haitis","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Hondūras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Honkongas","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Vengrija","alpha2":"hu","alpha3":"hun"},
{"id":352,"name":"Islandija","alpha2":"is","alpha3":"isl"},
{"id":356,"name":"Indija","alpha2":"in","alpha3":"ind"},

View File

@ -59,6 +59,7 @@
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":300,"name":"Hellas","alpha2":"gr","alpha3":"grc"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":112,"name":"Hviterussland","alpha2":"by","alpha3":"blr"},
{"id":356,"name":"India","alpha2":"in","alpha3":"ind"},
{"id":360,"name":"Indonesia","alpha2":"id","alpha3":"idn"},

View File

@ -64,6 +64,7 @@
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haïti","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Hongarije","alpha2":"hu","alpha3":"hun"},
{"id":372,"name":"Ierland","alpha2":"ie","alpha3":"irl"},
{"id":352,"name":"IJsland","alpha2":"is","alpha3":"isl"},

View File

@ -64,6 +64,7 @@
{"id":724,"name":"Hiszpania","alpha2":"es","alpha3":"esp"},
{"id":528,"name":"Holandia","alpha2":"nl","alpha3":"nld"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hongkong","alpha2":"hk","alpha3":"hkg"},
{"id":356,"name":"Indie","alpha2":"in","alpha3":"ind"},
{"id":360,"name":"Indonezja","alpha2":"id","alpha3":"idn"},
{"id":368,"name":"Irak","alpha2":"iq","alpha3":"irq"},

View File

@ -82,6 +82,7 @@
{"id":226,"name":"Guiné Equatorial","alpha2":"gq","alpha3":"gnq"},
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Hungria","alpha2":"hu","alpha3":"hun"},
{"id":887,"name":"Iêmen","alpha2":"ye","alpha3":"yem"},
{"id":356,"name":"Índia","alpha2":"in","alpha3":"ind"},

View File

@ -82,6 +82,7 @@
{"id":226,"name":"Guiné Equatorial","alpha2":"gq","alpha3":"gnq"},
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Hungria","alpha2":"hu","alpha3":"hun"},
{"id":887,"name":"Iêmen","alpha2":"ye","alpha3":"yem"},
{"id":356,"name":"Índia","alpha2":"in","alpha3":"ind"},

View File

@ -82,6 +82,7 @@
{"id":226,"name":"Guiné Equatorial","alpha2":"gq","alpha3":"gnq"},
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Hungria","alpha2":"hu","alpha3":"hun"},
{"id":887,"name":"Iêmen","alpha2":"ye","alpha3":"yem"},
{"id":356,"name":"Índia","alpha2":"in","alpha3":"ind"},

View File

@ -71,6 +71,7 @@
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"Ungaria","alpha2":"hu","alpha3":"hun"},
{"id":352,"name":"Islanda","alpha2":"is","alpha3":"isl"},
{"id":356,"name":"India","alpha2":"in","alpha3":"ind"},

View File

@ -42,6 +42,7 @@
{"id":624,"name":"Гвинея-Бисау","alpha2":"gw","alpha3":"gnb"},
{"id":276,"name":"Германия","alpha2":"de","alpha3":"deu"},
{"id":340,"name":"Гондурас","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Гонконг","alpha2":"hk","alpha3":"hkg"},
{"id":308,"name":"Гренада","alpha2":"gd","alpha3":"grd"},
{"id":300,"name":"Греция","alpha2":"gr","alpha3":"grc"},
{"id":268,"name":"Грузия","alpha2":"ge","alpha3":"geo"},

View File

@ -57,6 +57,7 @@
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":528,"name":"Holandsko","alpha2":"nl","alpha3":"nld"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hongkong","alpha2":"hk","alpha3":"hkg"},
{"id":191,"name":"Chorvátsko","alpha2":"hr","alpha3":"hrv"},
{"id":356,"name":"India","alpha2":"in","alpha3":"ind"},
{"id":360,"name":"Indonézia","alpha2":"id","alpha3":"idn"},

View File

@ -70,6 +70,7 @@
{"id":624,"name":"Guinea-Bissau","alpha2":"gw","alpha3":"gnb"},
{"id":328,"name":"Guyana","alpha2":"gy","alpha3":"guy"},
{"id":340,"name":"Honduras","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Hong Kong","alpha2":"hk","alpha3":"hkg"},
{"id":191,"name":"Kroatien","alpha2":"hr","alpha3":"hrv"},
{"id":332,"name":"Haiti","alpha2":"ht","alpha3":"hti"},
{"id":348,"name":"Ungern","alpha2":"hu","alpha3":"hun"},

View File

@ -72,6 +72,7 @@
{"id":328,"name":"กายอานา","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"เฮติ","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"ฮอนดูรัส","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"ฮ่องกง","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"ฮังการี","alpha2":"hu","alpha3":"hun"},
{"id":352,"name":"ไอซ์แลนด์","alpha2":"is","alpha3":"isl"},
{"id":356,"name":"อินเดีย","alpha2":"in","alpha3":"ind"},

View File

@ -39,6 +39,7 @@
{"id":324,"name":"Гвінея","alpha2":"gn","alpha3":"gin"},
{"id":624,"name":"Гвінея-Бісау","alpha2":"gw","alpha3":"gnb"},
{"id":340,"name":"Гондурас","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"Гонконг","alpha2":"hk","alpha3":"hkg"},
{"id":308,"name":"Гренада","alpha2":"gd","alpha3":"grd"},
{"id":300,"name":"Греція","alpha2":"gr","alpha3":"grc"},
{"id":268,"name":"Грузія","alpha2":"ge","alpha3":"geo"},

View File

@ -72,6 +72,7 @@
{"id":328,"name":"蓋亞那","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"海地","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"宏都拉斯","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"香港","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"匈牙利","alpha2":"hu","alpha3":"hun"},
{"id":352,"name":"冰島","alpha2":"is","alpha3":"isl"},
{"id":356,"name":"印度","alpha2":"in","alpha3":"ind"},

View File

@ -72,6 +72,7 @@
{"id":328,"name":"圭亚那","alpha2":"gy","alpha3":"guy"},
{"id":332,"name":"海地","alpha2":"ht","alpha3":"hti"},
{"id":340,"name":"洪都拉斯","alpha2":"hn","alpha3":"hnd"},
{"id":344,"name":"香港","alpha2":"hk","alpha3":"hkg"},
{"id":348,"name":"匈牙利","alpha2":"hu","alpha3":"hun"},
{"id":352,"name":"冰岛","alpha2":"is","alpha3":"isl"},
{"id":356,"name":"印度","alpha2":"in","alpha3":"ind"},

View File

@ -561,12 +561,22 @@ Version $ is now available! Click for more details: الإصدار $ متاح
على المزيد من التفاصيل
Tooltips:
General Settings:
Thumbnail Preference: كلّ الصّور المصغّرة في FreeTube سيتمّ استبدالها بإيطار من
Thumbnail Preference: كلّ الصّور المصغّرة في FreeTube سيتمّ استبدالها بإطار من
الفيديو بدل من الصّورة المصغّرة الافتراضيّة
Preferred API Backend: اختر الواجهة الخلفية التي يستخدمها FreeTube لجلب البيانات.
الAPI المحلي هو مستخرج محلي. الAPI التابع لInvidious يتطلب التواصل مع خادم شبكة
Invidious
Invidious Instance: واجهة انفيديوس البرمجية المستخدمة من قبل فريتيوب. امسح الواجهة
البرمجية الحالية للاختيار من قائمة الواجهات العلنية
Preferred API Backend: اختر الواجهة الخلفية التي يستخدمها FreeTube لجلب البيانات
. الAPI المحلي هو مستخرج محلي . الAPI التابع لInvidious يتطلب التواصل مع خادم
شبكة Invidious .
Invidious Instance: واجهة Invidious البرمجية المستخدمة من قبل FreeTube. امسح الواجهة
البرمجية الحالية للاختيار من قائمة الواجهات
Fallback to Non-Preferred Backend on Failure: عند تواجد مشكلة مع الواجهة البرمجية
المفضلة لك، سوف يقوم فريتيوب باستخدام الواجهات الأخرى عند تفعيل هذا الخيار
Region for Trending: يمكّنك اختيار الدولة من عرض الفيديوهات الشائعة التابعة للدولة
التي تفضل . ليست كل البلدان المعروضة في هذه القائمة مدعومة من قبل يوتيوب
Player Settings:
Proxy Videos Through Invidious: سيتم الاتصال ب Invidious لتقديم مقاطع الفيديو
بدلاً من إجراء اتصال مباشر مع يوتيوب. يلغي تفضيل الواجهة البرمجية
Force Local Backend for Legacy Formats: تعمل فقط عندما تكون واجهة Invidious البرمجية
هي الاختيار الافتراضي . عند تفعيل هذا الخيار ، ستعمل الواجهة البرمجية المحلية
باستخدام التنسيقات القديمة التي تم إرجاعها من قبلها بدلاً من أن تستخدم الصيغ
التي تم إرجاعها من قبل Invidious . يساعد هذا الخيار عندما لا تعمل الفيديوهات
التي تم إرجاعها من قبل Invidious بسبب قيود البلد

522
static/locales/bn.yaml Normal file
View File

@ -0,0 +1,522 @@
# Put the name of your locale in the same language
Locale Name: ''
FreeTube: 'ফ্রিটিউব'
# Currently on Subscriptions, Playlists, and History
'This part of the app is not ready yet. Come back later when progress has been made.': >
# Webkit Menu Bar
File: 'ফাইল'
Quit: 'প্রস্থান'
Edit: 'সম্পাদনা'
Undo: 'পূর্বাবস্থায় ফিরো'
Redo: 'আবার করো'
Cut: 'কাটো'
Copy: 'অনুলিপি'
Paste: 'লেপন'
Delete: 'মুছো'
Select all: ''
Reload: 'পুনরায় লোড করো'
Force Reload: ''
Toggle Developer Tools: ''
Actual size: ''
Zoom in: ''
Zoom out: ''
Toggle fullscreen: ''
Window: ''
Minimize: ''
Close: ''
Back: ''
Forward: ''
Version $ is now available! Click for more details: ''
Download From Site: ''
A new blog is now available, $. Click to view more: ''
# Search Bar
Search / Go to URL: ''
# In Filter Button
Search Filters:
Search Filters: ''
Sort By:
Sort By: ''
Most Relevant: ''
Rating: ''
Upload Date: ''
View Count: ''
Time:
Time: ''
Any Time: ''
Last Hour: ''
Today: ''
This Week: ''
This Month: ''
This Year: ''
Type:
Type: ''
All Types: ''
Videos: ''
Channels: ''
#& Playlists
Duration:
Duration: 'সময়কাল'
All Durations: ''
Short (< 4 minutes): ''
Long (> 20 minutes): ''
# On Search Page
Search Results: ''
Fetching results. Please wait: ''
Fetch more results: ''
# Sidebar
Subscriptions:
# On Subscriptions Page
Subscriptions: ''
Latest Subscriptions: ''
This profile has a large number of subscriptions. Forcing RSS to avoid rate limiting: ''
'Your Subscription list is currently empty. Start adding subscriptions to see them here.': ''
'Getting Subscriptions. Please wait.': ''
Refresh Subscriptions: ''
Load More Videos: ''
Trending: ''
Most Popular: ''
Playlists: ''
User Playlists:
Your Playlists: ''
Playlist Message: ''
Your saved videos are empty. Click on the save button on the corner of a video to have it listed here: ''
History:
# On History Page
History: ''
Watch History: ''
Your history list is currently empty.: ''
Settings:
# On Settings Page
Settings: ''
The app needs to restart for changes to take effect. Restart and apply change?: ''
General Settings:
General Settings: ''
Check for Updates: ''
Check for Latest Blog Posts: ''
Fallback to Non-Preferred Backend on Failure: ''
Enable Search Suggestions: ''
Default Landing Page: ''
Locale Preference: ''
Preferred API Backend:
Preferred API Backend: ''
Local API: ''
Invidious API: ''
Video View Type:
Video View Type: ''
Grid: ''
List: ''
Thumbnail Preference:
Thumbnail Preference: ''
Default: ''
Beginning: ''
Middle: ''
End: ''
'Invidious Instance (Default is https://invidious.snopyta.org)': ''
Region for Trending: ''
#! List countries
Theme Settings:
Theme Settings: ''
Match Top Bar with Main Color: ''
Expand Side Bar by Default: ''
Disable Smooth Scrolling: ''
UI Scale: ''
Base Theme:
Base Theme: ''
Black: ''
Dark: ''
Light: ''
Main Color Theme:
Main Color Theme: ''
Red: ''
Pink: ''
Purple: ''
Deep Purple: ''
Indigo: ''
Blue: ''
Light Blue: ''
Cyan: ''
Teal: ''
Green: ''
Light Green: ''
Lime: ''
Yellow: ''
Amber: ''
Orange: ''
Deep Orange: ''
Secondary Color Theme: ''
#* Main Color Theme
Player Settings:
Player Settings: ''
Force Local Backend for Legacy Formats: ''
Play Next Video: ''
Turn on Subtitles by Default: ''
Autoplay Videos: ''
Proxy Videos Through Invidious: ''
Autoplay Playlists: ''
Enable Theatre Mode by Default: ''
Default Volume: ''
Default Playback Rate: ''
Default Video Format:
Default Video Format: ''
Dash Formats: ''
Legacy Formats: ''
Audio Formats: ''
Default Quality:
Default Quality: ''
Auto: ''
144p: ''
240p: ''
360p: ''
480p: ''
720p: ''
1080p: ''
1440p: ''
4k: ''
8k: ''
Privacy Settings:
Privacy Settings: ''
Remember History: ''
Save Watched Progress: ''
Clear Search Cache: ''
Are you sure you want to clear out your search cache?: ''
Search cache has been cleared: ''
Remove Watch History: ''
Are you sure you want to remove your entire watch history?: ''
Watch history has been cleared: ''
Remove All Subscriptions / Profiles: ''
Are you sure you want to remove all subscriptions and profiles? This cannot be undone.: ''
Subscription Settings:
Subscription Settings: ''
Hide Videos on Watch: ''
Fetch Feeds from RSS: ''
Manage Subscriptions: ''
Distraction Free Settings:
Distraction Free Settings: ''
Hide Video Views: ''
Hide Video Likes And Dislikes: ''
Hide Channel Subscribers: ''
Hide Comment Likes: ''
Hide Recommended Videos: ''
Hide Trending Videos: ''
Hide Popular Videos: ''
Hide Live Chat: ''
Hide Active Subscriptions: ''
Data Settings:
Data Settings: ''
Select Import Type: ''
Select Export Type: ''
Import Subscriptions: ''
Import FreeTube: ''
Import YouTube: ''
Import NewPipe: ''
Check for Legacy Subscriptions: ''
Export Subscriptions: ''
Export FreeTube: ''
Export YouTube: ''
Export NewPipe: ''
Import History: ''
Export History: ''
Profile object has insufficient data, skipping item: ''
All subscriptions and profiles have been successfully imported: ''
All subscriptions have been successfully imported: ''
One or more subscriptions were unable to be imported: ''
Invalid subscriptions file: ''
This might take a while, please wait: ''
Invalid history file: ''
Subscriptions have been successfully exported: ''
History object has insufficient data, skipping item: ''
All watched history has been successfully imported: ''
All watched history has been successfully exported: ''
Unable to read file: ''
Unable to write file: ''
Unknown data key: ''
How do I import my subscriptions?: ''
Manage Subscriptions: ''
Proxy Settings:
Proxy Settings: ''
Enable Tor / Proxy: ''
Proxy Protocol: ''
Proxy Host: ''
Proxy Port Number: ''
Clicking on Test Proxy will send a request to: ''
Test Proxy: ''
Your Info: ''
Ip: ''
Country: ''
Region: ''
City: ''
Error getting network information. Is your proxy configured properly?: ''
About:
#On About page
About: ''
Contact: ''
Help: ''
Useful Links: ''
Donate: ''
Beta: ''
License: ''
Email: ''
Channel Rules: ''
Report an Issue: ''
Wiki: ''
FAQ: ''
Credits: ''
Blog: ''
Release Notes: ''
Source Code: ''
Website: ''
Translate via Weblate: ''
Profile:
Profile Select: ''
All Channels: ''
Profile Manager: ''
Create New Profile: ''
Edit Profile: ''
Color Picker: ''
Custom Color: ''
Profile Preview: ''
Create Profile: ''
Update Profile: ''
Make Default Profile: ''
Delete Profile: ''
Are you sure you want to delete this profile?: ''
All subscriptions will also be deleted.: ''
Profile could not be found: ''
Your profile name cannot be empty: ''
Profile has been created: ''
Profile has been updated: ''
Your default profile has been set to $: ''
Removed $ from your profiles: ''
Your default profile has been changed to your primary profile: ''
$ is now the active profile: ''
Subscription List: ''
Other Channels: ''
$ selected: ''
Select All: ''
Select None: ''
Delete Selected: ''
Add Selected To Profile: ''
No channel(s) have been selected: ''
? This is your primary profile. Are you sure you want to delete the selected channels? The
same channels will be deleted in any profile they are found in.
: ''
Are you sure you want to delete the selected channels? This will not delete the channel from any other profile.: ''
#On Channel Page
Channel:
Subscriber: ''
Subscribers: ''
Subscribe: ''
Unsubscribe: ''
Channel has been removed from your subscriptions: ''
Removed subscription from $ other channel(s): ''
Added channel to your subscriptions: ''
Search Channel: ''
Your search results have returned 0 results: ''
Sort By: ''
Videos:
Videos: ''
This channel does not currently have any videos: ''
Sort Types:
Newest: ''
Oldest: ''
Most Popular: ''
Playlists:
Playlists: ''
This channel does not currently have any playlists: ''
Sort Types:
Last Video Added: ''
Newest: ''
Oldest: ''
About:
About: ''
Channel Description: ''
Featured Channels: ''
Video:
Mark As Watched: ''
Remove From History: ''
Video has been marked as watched: ''
Video has been removed from your history: ''
Save Video: ''
Video has been saved: ''
Video has been removed from your saved list: ''
Open in YouTube: ''
Copy YouTube Link: ''
Open YouTube Embedded Player: ''
Copy YouTube Embedded Player Link: ''
Open in Invidious: ''
Copy Invidious Link: ''
Open Channel in YouTube: ''
Copy YouTube Channel Link: ''
Open Channel in Invidious: ''
Copy Invidious Channel Link: ''
View: ''
Views: ''
Loop Playlist: ''
Shuffle Playlist: ''
Reverse Playlist: ''
Play Next Video: ''
Play Previous Video: ''
# Context is "X People Watching"
Watching: ''
Watched: ''
Autoplay: ''
Starting soon, please refresh the page to check again: ''
# As in a Live Video
Live: ''
Live Now: ''
Live Chat: ''
Enable Live Chat: ''
Live Chat is currently not supported in this build.: ''
'Chat is disabled or the Live Stream has ended.': ''
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.': ''
Download Video: ''
video only: ''
audio only: ''
Audio:
Low: ''
Medium: ''
High: ''
Best: ''
Published:
Jan: ''
Feb: ''
Mar: ''
Apr: ''
May: ''
Jun: ''
Jul: ''
Aug: ''
Sep: ''
Oct: ''
Nov: ''
Dec: ''
Second: ''
Seconds: ''
Minute: ''
Minutes: ''
Hour: ''
Hours: ''
Day: ''
Days: ''
Week: ''
Weeks: ''
Month: ''
Months: ''
Year: ''
Years: ''
Ago: ''
Upcoming: ''
Published on: ''
Streamed on: ''
Started streaming on: ''
# $ is replaced with the number and % with the unit (days, hours, minutes...)
Publicationtemplate: ''
#& Videos
Videos:
#& Sort By
Sort By:
Newest: ''
Oldest: ''
#& Most Popular
#& Playlists
Playlist:
#& About
View Full Playlist: ''
Videos: ''
View: ''
Views: ''
Last Updated On: ''
Share Playlist:
Share Playlist: ''
Copy YouTube Link: ''
Open in YouTube: ''
Copy Invidious Link: ''
Open in Invidious: ''
# On Video Watch Page
#* Published
#& Views
Toggle Theatre Mode: ''
Change Format:
Change Video Formats: ''
Use Dash Formats: ''
Use Legacy Formats: ''
Use Audio Formats: ''
Dash formats are not available for this video: ''
Audio formats are not available for this video: ''
Share:
Share Video: ''
Include Timestamp: ''
Copy Link: ''
Open Link: ''
Copy Embed: ''
Open Embed: ''
# On Click
Invidious URL copied to clipboard: ''
Invidious Embed URL copied to clipboard: ''
Invidious Channel URL copied to clipboard: ''
YouTube URL copied to clipboard: ''
YouTube Embed URL copied to clipboard: ''
YouTube Channel URL copied to clipboard: ''
Mini Player: ''
Comments:
Comments: ''
Click to View Comments: ''
Getting comment replies, please wait: ''
There are no more comments for this video: ''
Show Comments: ''
Hide Comments: ''
Sort by: ''
Top comments: ''
Newest first: ''
# Context: View 10 Replies, View 1 Reply
View: ''
Hide: ''
Replies: ''
Reply: ''
There are no comments available for this video: ''
Load More Comments: ''
No more comments available: ''
Up Next: ''
#Tooltips
Tooltips:
General Settings:
Preferred API Backend: ''
Fallback to Non-Preferred Backend on Failure: ''
Thumbnail Preference: ''
Invidious Instance: ''
Region for Trending: ''
Player Settings:
Force Local Backend for Legacy Formats: ''
Proxy Videos Through Invidious: ''
Default Video Format: ''
Subscription Settings:
Fetch Feeds from RSS: ''
# Toast Messages
Local API Error (Click to copy): ''
Invidious API Error (Click to copy): ''
Falling back to Invidious API: ''
Falling back to the local API: ''
This video is unavailable because of missing formats. This can happen due to country unavailability.: ''
Subscriptions have not yet been implemented: ''
Loop is now disabled: ''
Loop is now enabled: ''
Shuffle is now disabled: ''
Shuffle is now enabled: ''
The playlist has been reversed: ''
Playing Next Video: ''
Playing Previous Video: ''
Playing next video in 5 seconds. Click to cancel: ''
Canceled next video autoplay: ''
'The playlist has ended. Enable loop to continue playing': ''
Yes: ''
No: ''

535
static/locales/bs.yaml Normal file
View File

@ -0,0 +1,535 @@
# Put the name of your locale in the same language
Locale Name: 'Bosanski'
FreeTube: 'FreeTube'
# Currently on Subscriptions, Playlists, and History
'This part of the app is not ready yet. Come back later when progress has been made.': >-
Ovaj dio aplikacije još nije spreman. Vratite se kasnije kad se postigne napredak.
# Webkit Menu Bar
File: 'Fajl'
Quit: 'Napusti'
Edit: 'Uredi'
Undo: 'Ukini'
Redo: 'Ponovi'
Cut: 'Izreži'
Copy: 'Kopiraj'
Paste: 'Umetni'
Delete: 'Izbriši'
Select all: 'Odabri sve'
Reload: 'Ponovo učitaj'
Force Reload: 'Silom učitaj ponovo'
Toggle Developer Tools: 'Uključi alate za programere'
Actual size: 'Prirodna veličina'
Zoom in: 'Približi'
Zoom out: 'Umanji'
Toggle fullscreen: 'Prebaci preko cijelog ekrana'
Window: 'Prozor'
Minimize: 'Smanji'
Close: 'Zatvori'
Back: 'Nazad'
Forward: 'Unapred'
Version $ is now available! Click for more details: 'Verzija $ je sada dostupna!
Kliknite za više detalja'
Download From Site: 'Instaliraj preko stranice'
A new blog is now available, $. Click to view more: 'Sada je dostupan novi blog, $.
Kliknite da vidite više'
# Search Bar
Search / Go to URL: 'Pretražite / idite na URL'
# In Filter Button
Search Filters:
Search Filters: 'Pretraži filtere'
Sort By:
Sort By: 'Poredaj po'
Most Relevant: 'Naj relevantnije'
Rating: 'Ocjena'
Upload Date: 'Datum uploada'
View Count: 'Broj pregleda'
Time:
Time: 'Vrjeme'
Any Time: 'Bilo kada'
Last Hour: 'Zadnji sat'
Today: 'Danas'
This Week: 'Ove sedmice'
This Month: 'Ovog Mjeseca'
This Year: 'Ove godine'
Type:
Type: 'Vrsta'
All Types: 'Sve vrste'
Videos: 'Video'
Channels: 'Kanali'
#& Playlists
Duration:
Duration: 'Trajanje'
All Durations: 'Sva trajanja'
Short (< 4 minutes): 'Kratko (<4 minute)'
Long (> 20 minutes): 'Dugo (>20 minuta)'
# On Search Page
Search Results: 'Rezultati pretrage'
Fetching results. Please wait: 'Dohvaćanje rezultata. Molimo sačekajte'
Fetch more results: 'Dohvati više rezultata'
# Sidebar
Subscriptions:
# On Subscriptions Page
Subscriptions: 'Pretplate'
Latest Subscriptions: 'Najnovije pretplate'
This profile has a large number of subscriptions. Forcing RSS to avoid rate limiting: 'Ovaj
profil ima veliki broj pretplata. Prisiljavanje RSS-a da izbjegne ograničenje
brzine'
'Your Subscription list is currently empty. Start adding subscriptions to see them here.': 'Vaša
lista pretplata je trenutno prazna. Počnite dodavati pretplate da biste ih vidjeli
ovdje.'
'Getting Subscriptions. Please wait.': 'Preuzimanje pretplata. Molimo sačekajte.'
Refresh Subscriptions: 'Osvježi pretplate'
Load More Videos: 'Očitaj više videozapisa'
Trending: 'U trendu'
Most Popular: 'Naj popularnije'
Playlists: 'playliste'
User Playlists:
Your Playlists: 'Tvoje playliste'
History:
# On History Page
History: 'Istorija'
Watch History: 'Istorija gledanja'
Your history list is currently empty.: 'Vaša lista istorije je trenutno prazna.'
Settings:
# On Settings Page
Settings: 'Postavke'
The app needs to restart for changes to take effect. Restart and apply change?: 'Aplikacija
se treba restartovati da bi promjene stupile na snagu. Restartuj i primijeniti
promjenu?'
General Settings:
General Settings: 'Generalne postavke'
Check for Updates: 'Provjeri ima li update'
Check for Latest Blog Posts: 'Potraži najnovije objave na blogu'
Fallback to Non-Preferred Backend on Failure: 'Vraćanje na neželjeni backend u
slučaju neuspjeha'
Enable Search Suggestions: 'Omogući prijedloge pretraživanja'
Default Landing Page: 'Zadana odredišna stranica'
Locale Preference: 'Jezik'
Preferred API Backend:
Preferred API Backend: 'Poželjni API Backend'
Local API: 'Lokalni API'
Invidious API: 'Invidious API'
Video View Type:
Video View Type: 'Način prikazanja videa'
Grid: 'Popločan'
List: 'Popis'
Thumbnail Preference:
Thumbnail Preference: 'Preferencija minijature'
Default: 'Standard'
Beginning: 'Početak'
Middle: 'Sredina'
End: 'Kraj'
'Invidious Instance (Default is https://invidious.snopyta.org)': 'Invidious primjer
(standard je https://invidious.snopyta.org)'
Region for Trending: 'Regiona za trend'
#! List countries
Theme Settings:
Theme Settings: 'Postavke teme'
Match Top Bar with Main Color: 'Koristi glavnu boju u gornjoj traci'
Expand Side Bar by Default: ''
Disable Smooth Scrolling: ''
UI Scale: ''
Base Theme:
Base Theme: ''
Black: ''
Dark: ''
Light: ''
Main Color Theme:
Main Color Theme: ''
Red: ''
Pink: ''
Purple: ''
Deep Purple: ''
Indigo: ''
Blue: ''
Light Blue: ''
Cyan: ''
Teal: ''
Green: ''
Light Green: ''
Lime: ''
Yellow: ''
Amber: ''
Orange: ''
Deep Orange: ''
Secondary Color Theme: ''
#* Main Color Theme
Player Settings:
Player Settings: ''
Force Local Backend for Legacy Formats: ''
Play Next Video: ''
Turn on Subtitles by Default: ''
Autoplay Videos: ''
Proxy Videos Through Invidious: ''
Autoplay Playlists: ''
Enable Theatre Mode by Default: ''
Default Volume: ''
Default Playback Rate: ''
Default Video Format:
Default Video Format: ''
Dash Formats: ''
Legacy Formats: ''
Audio Formats: ''
Default Quality:
Default Quality: ''
Auto: ''
144p: ''
240p: ''
360p: ''
480p: ''
720p: ''
1080p: ''
1440p: ''
4k: ''
8k: ''
Privacy Settings:
Privacy Settings: ''
Remember History: ''
Save Watched Progress: ''
Clear Search Cache: ''
Are you sure you want to clear out your search cache?: ''
Search cache has been cleared: ''
Remove Watch History: ''
Are you sure you want to remove your entire watch history?: ''
Watch history has been cleared: ''
Remove All Subscriptions / Profiles: ''
Are you sure you want to remove all subscriptions and profiles? This cannot be undone.: ''
Subscription Settings:
Subscription Settings: ''
Hide Videos on Watch: ''
Fetch Feeds from RSS: ''
Manage Subscriptions: ''
Distraction Free Settings:
Distraction Free Settings: ''
Hide Video Views: ''
Hide Video Likes And Dislikes: ''
Hide Channel Subscribers: ''
Hide Comment Likes: ''
Hide Recommended Videos: ''
Hide Trending Videos: ''
Hide Popular Videos: ''
Hide Live Chat: ''
Data Settings:
Data Settings: ''
Select Import Type: ''
Select Export Type: ''
Import Subscriptions: ''
Import FreeTube: ''
Import YouTube: ''
Import NewPipe: ''
Check for Legacy Subscriptions: ''
Export Subscriptions: ''
Export FreeTube: ''
Export YouTube: ''
Export NewPipe: ''
Import History: ''
Export History: ''
Profile object has insufficient data, skipping item: ''
All subscriptions and profiles have been successfully imported: ''
All subscriptions have been successfully imported: ''
One or more subscriptions were unable to be imported: ''
Invalid subscriptions file: ''
This might take a while, please wait: ''
Invalid history file: ''
Subscriptions have been successfully exported: ''
History object has insufficient data, skipping item: ''
All watched history has been successfully imported: ''
All watched history has been successfully exported: ''
Unable to read file: ''
Unable to write file: ''
Unknown data key: ''
How do I import my subscriptions?: ''
Advanced Settings:
Advanced Settings: ''
Enable Debug Mode (Prints data to the console): ''
'Proxy Address (Example: SOCKS5://127.0.0.1:9050 )': ''
'Clicking "TEST PROXY" button will send a request to https://ipinfo.io/json': ''
Use Tor / Proxy for API calls: ''
TEST PROXY: ''
#& Invidious Instance (Default is https://invidious.snopyta.org)
See Public Instances: ''
Clear History:
Clear History: ''
# On Click
Are you sure you want to delete your history?: ''
#& Yes
#& No
Clear Subscriptions:
Clear Subscriptions: ''
# On Click
Are you sure you want to remove all subscriptions?: ''
#& Yes
#& No
About:
#On About page
About: ''
#& About
'This software is FOSS and released under the GNU Affero General Public License v3.0.': ''
'Found a bug? Want to suggest a feature? Want to help out? Check out our GitHub page. Pull requests are welcome.': >
Thank you very much to the People and Projects that make FreeTube possible!: ''
'Want to chat? Join our Element / Matrix Server . Please check the rules before joining.': ''
'Looking for help? Check out our Wiki page.': ''
Check out our Firefox extension!: ''
'If you enjoy using FreeTube, consider donating via Liberapay or through our Bitcoin address.': ''
#~ 'BTC: 1Lih7Ho5gnxb1CwPD4o59ss78pwo2T91eS'
Latest FreeTube News: ''
Profile:
Profile Select: ''
All Channels: ''
Profile Manager: ''
Create New Profile: ''
Edit Profile: ''
Color Picker: ''
Custom Color: ''
Profile Preview: ''
Create Profile: ''
Update Profile: ''
Make Default Profile: ''
Delete Profile: ''
Are you sure you want to delete this profile?: ''
All subscriptions will also be deleted.: ''
Profile could not be found: ''
Your profile name cannot be empty: ''
Profile has been created: ''
Profile has been updated: ''
Your default profile has been set to $: ''
Removed $ from your profiles: ''
Your default profile has been changed to your primary profile: ''
$ is now the active profile: ''
Subscription List: ''
Other Channels: ''
$ selected: ''
Select All: ''
Select None: ''
Delete Selected: ''
Add Selected To Profile: ''
No channel(s) have been selected: ''
? This is your primary profile. Are you sure you want to delete the selected channels? The
same channels will be deleted in any profile they are found in.
: ''
Are you sure you want to delete the selected channels? This will not delete the channel from any other profile.: ''
#On Channel Page
Channel:
Subscriber: ''
Subscribers: ''
Subscribe: ''
Unsubscribe: ''
Channel has been removed from your subscriptions: ''
Removed subscription from $ other channel(s): ''
Added channel to your subscriptions: ''
Search Channel: ''
Your search results have returned 0 results: ''
Sort By: ''
Videos:
Videos: ''
This channel does not currently have any videos: ''
Sort Types:
Newest: ''
Oldest: ''
Most Popular: ''
Playlists:
Playlists: ''
This channel does not currently have any playlists: ''
Sort Types:
Last Video Added: ''
Newest: ''
Oldest: ''
About:
About: ''
Channel Description: ''
Featured Channels: ''
Video:
Mark As Watched: ''
Remove From History: ''
Video has been marked as watched: ''
Video has been removed from your history: ''
Open in YouTube: ''
Copy YouTube Link: ''
Open YouTube Embedded Player: ''
Copy YouTube Embedded Player Link: ''
Open in Invidious: ''
Copy Invidious Link: ''
Open Channel in YouTube: ''
Copy YouTube Channel Link: ''
Open Channel in Invidious: ''
Copy Invidious Channel Link: ''
View: ''
Views: ''
Loop Playlist: ''
Shuffle Playlist: ''
Reverse Playlist: ''
Play Next Video: ''
Play Previous Video: ''
# Context is "X People Watching"
Watching: ''
Watched: ''
Autoplay: ''
Starting soon, please refresh the page to check again: ''
# As in a Live Video
Live: ''
Live Now: ''
Live Chat: ''
Enable Live Chat: ''
Live Chat is currently not supported in this build.: ''
'Chat is disabled or the Live Stream has ended.': ''
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.': ''
Download Video: ''
video only: ''
audio only: ''
Audio:
Low: ''
Medium: ''
High: ''
Best: ''
Published:
Jan: ''
Feb: ''
Mar: ''
Apr: ''
May: ''
Jun: ''
Jul: ''
Aug: ''
Sep: ''
Oct: ''
Nov: ''
Dec: ''
Second: ''
Seconds: ''
Minute: ''
Minutes: ''
Hour: ''
Hours: ''
Day: ''
Days: ''
Week: ''
Weeks: ''
Month: ''
Months: ''
Year: ''
Years: ''
Ago: ''
Upcoming: ''
Published on: ''
Streamed on: ''
Started streaming on: ''
# $ is replaced with the number and % with the unit (days, hours, minutes...)
Publicationtemplate: ''
#& Videos
Videos:
#& Sort By
Sort By:
Newest: ''
Oldest: ''
#& Most Popular
#& Playlists
Playlist:
#& About
View Full Playlist: ''
Videos: ''
View: ''
Views: ''
Last Updated On: ''
Share Playlist:
Share Playlist: ''
Copy YouTube Link: ''
Open in YouTube: ''
Copy Invidious Link: ''
Open in Invidious: ''
# On Video Watch Page
#* Published
#& Views
Toggle Theatre Mode: ''
Change Format:
Change Video Formats: ''
Use Dash Formats: ''
Use Legacy Formats: ''
Use Audio Formats: ''
Dash formats are not available for this video: ''
Audio formats are not available for this video: ''
Share:
Share Video: ''
Include Timestamp: ''
Copy Link: ''
Open Link: ''
Copy Embed: ''
Open Embed: ''
# On Click
Invidious URL copied to clipboard: ''
Invidious Embed URL copied to clipboard: ''
Invidious Channel URL copied to clipboard: ''
YouTube URL copied to clipboard: ''
YouTube Embed URL copied to clipboard: ''
YouTube Channel URL copied to clipboard: ''
Mini Player: ''
Comments:
Comments: ''
Click to View Comments: ''
Getting comment replies, please wait: ''
There are no more comments for this video: ''
Show Comments: ''
Hide Comments: ''
Sort by: ''
Top comments: ''
Newest first: ''
# Context: View 10 Replies, View 1 Reply
View: ''
Hide: ''
Replies: ''
Reply: ''
There are no comments available for this video: ''
Load More Comments: ''
No more comments available: ''
Up Next: ''
#Tooltips
Tooltips:
General Settings:
Preferred API Backend: ''
Fallback to Non-Preferred Backend on Failure: ''
Thumbnail Preference: ''
Invidious Instance: ''
Region for Trending: ''
Player Settings:
Force Local Backend for Legacy Formats: ''
Proxy Videos Through Invidious: ''
Default Video Format: ''
Subscription Settings:
Fetch Feeds from RSS: ''
# Toast Messages
Local API Error (Click to copy): ''
Invidious API Error (Click to copy): ''
Falling back to Invidious API: ''
Falling back to the local API: ''
This video is unavailable because of missing formats. This can happen due to country unavailability.: ''
Subscriptions have not yet been implemented: ''
Loop is now disabled: ''
Loop is now enabled: ''
Shuffle is now disabled: ''
Shuffle is now enabled: ''
The playlist has been reversed: ''
Playing Next Video: ''
Playing Previous Video: ''
Playing next video in 5 seconds. Click to cancel: ''
Canceled next video autoplay: ''
'The playlist has ended. Enable loop to continue playing': ''
Yes: ''
No: 'Ne'

View File

@ -132,8 +132,8 @@ Settings:
Base Theme:
Base Theme: 'Základní motiv'
Black: 'Černý'
Dark: 'Temný'
Light: 'Lehký'
Dark: 'Tmavý'
Light: 'Světlý'
Main Color Theme:
Main Color Theme: 'Hlavní barevný motiv'
Red: 'Červený'
@ -212,6 +212,7 @@ Settings:
Hide Trending Videos: 'Skrýt trendy'
Hide Popular Videos: 'Skrýt populární videa'
Hide Live Chat: 'Skrýt chat'
Hide Active Subscriptions: Skrýt aktivní odběry
Data Settings:
Data Settings: 'Nastavení dat'
Select Import Type: 'Vybrat typ importu'
@ -458,7 +459,7 @@ Video:
Upcoming: 'Premiéra'
Published on: 'Publikováno'
# $ is replaced with the number and % with the unit (days, hours, minutes...)
Publicationtemplate: '$ % před'
Publicationtemplate: 'před $ %'
#& Videos
Started streaming on: Začátek vysílání
Streamed on: Vysíláno

View File

@ -6,7 +6,7 @@ FreeTube: FreeTube
# Webkit Menu Bar
File: Datei
Quit: Verlassen
Quit: Beenden
Edit: Bearbeiten
Undo: Rückgängig
Redo: Wiederherstellen
@ -16,7 +16,7 @@ Paste: Einfügen
Delete: Löschen
Select all: Alles auswählen
Reload: Aktualisieren
Force Reload: Erzwinge Aktualisierung
Force Reload: Aktualisierung erzwingen
Toggle Developer Tools: Aktiviere Entwicklerwerkzeuge
Actual size: Originalgröße
Zoom in: Vergrößern
@ -116,7 +116,7 @@ Settings:
(Standard ist https://invidious.snopyta.org)
Region for Trending: Region für Trends
#! List countries
Check for Latest Blog Posts: Nach aktuellstem Blogeintrag suchen
Check for Latest Blog Posts: Nach aktuellsten Blogeinträgen suchen
Check for Updates: Nach Aktualisierungen suchen
Theme Settings:
Theme Settings: Thema Einstellungen
@ -151,7 +151,7 @@ Settings:
Expand Side Bar by Default: Seitenleiste standardmäßig erweitern
Player Settings:
Player Settings: Videoabspieler-Einstellungen
Force Local Backend for Legacy Formats: Lokales System für Altformat erzwingen
Force Local Backend for Legacy Formats: Lokales System für Legacy Formate erzwingen
Remember History: Verlauf speichern
Play Next Video: Spiele nächstes Video
Turn on Subtitles by Default: Untertitel standardmäßig aktivieren
@ -282,6 +282,7 @@ Settings:
Hide Video Likes And Dislikes: Videobewertungen verbergen
Hide Video Views: Video-Aufrufe verbergen
Distraction Free Settings: Einstellungen für ablenkungsfreien Modus
Hide Active Subscriptions: Aktive Abonnements 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?
@ -320,6 +321,23 @@ About:
#On Channel Page
This software is FOSS and released under the GNU Affero General Public License v3.0.: Diese
freie Software ist unter der AGPL-3.0 lizenziert.
Translate via Weblate: Auf Weblate übersetzen
Website: Webseite
Source Code: Quellcode
Release Notes: Versionshinweise
Blog: Blog
Credits: Beiträge
FAQ: Häufig gestellte Fragen
Wiki: Wiki
Report an Issue: Fehler melden
Channel Rules: Kanalregeln
Email: Email
License: Lizenz
Beta: Beta
Donate: Spenden
Useful Links: Nützliche Links
Help: Hilfe
Contact: Kontakt
Channel:
Subscriber: Abonnement
Subscribers: Abonnements
@ -468,7 +486,7 @@ Share Playlist:
# On Video Watch Page
#* Published
#& Views
Toggle Theatre Mode: Aktiviere Kinomodus
Toggle Theatre Mode: Kinomodus umschalten
Change Format:
Change Video Formats: Ändere Videoformat
Use Dash Formats: Nutze DASH-Format

View File

@ -31,7 +31,7 @@ Back: 'Μετάβαση πίσω'
Forward: 'Μετάβαση μπροστά'
# Search Bar
Search / Go to URL: 'Αναζήτηση/Τοποθετήστε Διεύθυνση URL'
Search / Go to URL: 'Αναζήτηση/Μετάβαση στη URL'
# In Filter Button
Search Filters:
Search Filters: 'Φίλτρα αναζήτησης'
@ -39,16 +39,16 @@ Search Filters:
Sort By: 'Ταξινόμηση κατά'
Most Relevant: 'Πιο σχετικό'
Rating: 'Αξιολόγηση'
Upload Date: 'Ημερομηνία αποστολής'
Upload Date: 'Ημερομηνία μεταφόρτωσης'
View Count: 'Πλήθος Προβολών'
Time:
Time: 'Χρόνος'
Any Time: 'Οποιαδήποτε περίοδος'
Last Hour: 'Τελευταία ώρα'
Time: 'Ώρα'
Any Time: 'Οποτεδήποτε'
Last Hour: 'Τελευταίας ώρας'
Today: 'Σήμερα'
This Week: 'Τρέχουσα Εβδομάδα'
This Month: 'Αυτό το μήνα'
This Year: 'Αυτού του χρόνου'
This Year: 'Τρέχων έτος'
Type:
Type: 'Είδος'
All Types: 'Όλα τα είδη'
@ -70,8 +70,8 @@ Subscriptions:
Subscriptions: 'Εγγραφές'
Latest Subscriptions: 'Τελευταίες Εγγραφές'
'Your Subscription list is currently empty. Start adding subscriptions to see them here.': 'Η
λίστα εγγραφών σας είναι προς το παρόν κενή. Ξεκινήστε να προσθέτετε εγγραφές/συνδρομές
για να τις παρακολουθείτε εδώ.'
λίστα συνδρομών σας είναι προς το παρόν κενή. Προσθέστε συνδρομές για να τις παρακολουθείτε
από εδώ.'
'Getting Subscriptions. Please wait.': 'Ανάκτηση Εγγραφών/Συνδρομών. Παρακαλώ περιμένετε.'
Refresh Subscriptions: 'Ανανέωση Εγγραφών/Συνδρομών'
This profile has a large number of subscriptions. Forcing RSS to avoid rate limiting: Αυτό
@ -98,7 +98,7 @@ Settings:
υποστήριξης σε περίπτωση αποτυχίας'
Enable Search Suggestions: 'Ενεργοποίηση προτάσεων αναζήτησης'
Default Landing Page: 'Προεπιλεγμένη σελίδα προορισμού'
Locale Preference: 'Προτιμώμενη Γλώσσα'
Locale Preference: 'Προτιμήσεις γλώσσας'
Preferred API Backend:
Preferred API Backend: 'Προτιμώμενο συστήμα (API)'
Local API: 'Τοπική διεπαφή προγραμματισμού εφαρμογών (API)'
@ -114,7 +114,7 @@ Settings:
Middle: 'Μέση'
End: 'Τέλος'
'Invidious Instance (Default is https://invidious.snopyta.org)': 'Διακομιστής
Invidious (Ο προκαθορισμένος είναι https://invidious.snopyta.org)'
Invidious (προεπιλογή https://invidious.snopyta.org)'
Region for Trending: 'Περιοχή που καθορίζει την καρτέλα των τάσεων'
#! List countries
Check for Latest Blog Posts: Ελέγξτε για τις τελευταίες δημοσιεύσεις ιστολογίου

View File

@ -86,6 +86,8 @@ Most Popular: Most Popular
Playlists: Playlists
User Playlists:
Your Playlists: Your Playlists
Playlist Message: This page is not reflective of fully working playlists. It only lists videos that you have saved or favorited. When the work has finished, all videos currently here will be migrated to a 'Favorites' playlist.
Your saved videos are empty. Click on the save button on the corner of a video to have it listed here: Your saved videos are empty. Click on the save button on the corner of a video to have it listed here
History:
# On History Page
History: History
@ -213,6 +215,7 @@ Settings:
Hide Trending Videos: Hide Trending Videos
Hide Popular Videos: Hide Popular Videos
Hide Live Chat: Hide Live Chat
Hide Active Subscriptions: Hide Active Subscriptions
Data Settings:
Data Settings: Data Settings
Select Import Type: Select Import Type
@ -251,62 +254,48 @@ Settings:
Unable to write file: Unable to write file
Unknown data key: Unknown data key
How do I import my subscriptions?: How do I import my subscriptions?
Advanced Settings:
Advanced Settings: Advanced Settings
Enable Debug Mode (Prints data to the console): Enable Debug Mode (Prints data
to the console)
'Proxy Address (Example: SOCKS5://127.0.0.1:9050 )': 'Proxy Address (Example:
SOCKS5://127.0.0.1:9050 )'
'Clicking "TEST PROXY" button will send a request to https://ipinfo.io/json': Clicking
"TEST PROXY" button will send a request to https://ipinfo.io/json
Use Tor / Proxy for API calls: Use Tor / Proxy for API calls
TEST PROXY: TEST PROXY
#& Invidious Instance (Default is https://invidious.snopyta.org)
See Public Instances: See Public Instances
Clear History:
Clear History: Clear History
# On Click
Are you sure you want to delete your history?: Are you sure you want to delete
your history?
#& Yes
#& No
Clear Subscriptions:
Clear Subscriptions: Clear Subscriptions
# On Click
Are you sure you want to remove all subscriptions?: Are you sure you want to
remove all subscriptions?
#& Yes
#& No
Manage Subscriptions: Manage Subscriptions
Proxy Settings:
Proxy Settings: Proxy Settings
Enable Tor / Proxy: Enable Tor / Proxy
Proxy Protocol: Proxy Protocol
Proxy Host: Proxy Host
Proxy Port Number: Proxy Port Number
Clicking on Test Proxy will send a request to: Clicking on Test Proxy will send a request to
Test Proxy: Test Proxy
Your Info: Your Info
Ip: Ip
Country: Country
Region: Region
City: City
Error getting network information. Is your proxy configured properly?: Error getting network information. Is your proxy configured properly?
About:
#On About page
About: About
#& About
'This software is FOSS and released under the GNU Affero General Public License v3.0.': This
copylefted software is freely licensed AGPL-3.0.
'Found a bug? Want to suggest a feature? Want to help out? Check out our GitHub page. Pull requests are welcome.': >-
Found a bug? Want to suggest a feature? Want to help out? Check out our GitHub
page. Pull requests welcome.
Thank you very much to the People and Projects that make FreeTube possible!: Thank
you very much to the People and Projects that make FreeTube possible!
'Want to chat? Join our Element / Matrix Server . Please check the rules before joining.': Want
to chat? Join our Element / Matrix Server . Please check the rules before joining.
'Looking for help? Check out our Wiki page.': Looking for help? Check out our Wiki
page.
Check out our Firefox extension!: Check out our Firefox extension!
'If you enjoy using FreeTube, consider donating via Liberapay or through our Bitcoin address.': If
you enjoy using FreeTube, consider donating via Liberapay or through our Bitcoin
address.
#~ 'BTC: 1Lih7Ho5gnxb1CwPD4o59ss78pwo2T91eS'
Latest FreeTube News: Latest FreeTube News
Beta: Beta
Source code: Source code
Licensed under the AGPLv3: Licensed under the AGPLv3
View License: View License
Downloads / Changelog: Downloads / Changelog
GitHub releases: GitHub releases
Help: Help
FreeTube Wiki: FreeTube Wiki
FAQ: FAQ
Report a problem: Report a problem
GitHub issues: GitHub issues
Please check for duplicates before posting: Please check for duplicates before posting
Website: Website
Blog: Blog
Email: Email
Mastodon: Mastodon
Chat on Matrix: Chat on Matrix
Please read the: Please read the
room rules: room rules
Translate: Translate
Credits: Credits
FreeTube is made possible by: FreeTube is made possible by
these people and projects: these people and projects
Donate: Donate
Profile:
Profile Select: Profile Select
@ -388,6 +377,9 @@ Video:
Remove From History: Remove From History
Video has been marked as watched: Video has been marked as watched
Video has been removed from your history: Video has been removed from your history
Save Video: Save Video
Video has been saved: Video has been saved
Video has been removed from your saved list: Video has been removed from your saved list
Open in YouTube: Open in YouTube
Copy YouTube Link: Copy YouTube Link
Open YouTube Embedded Player: Open YouTube Embedded Player

View File

@ -123,7 +123,7 @@ Settings:
#! List countries
Theme Settings:
Theme Settings: 'Theme Settings'
Match Top Bar with Main Color: 'Match Top Bar with Main Colour'
Match Top Bar with Main Color: 'Match top bar with main colour'
Base Theme:
Base Theme: 'Base Theme'
Black: 'Black'
@ -246,7 +246,7 @@ Settings:
'Proxy Address (Example: SOCKS5://127.0.0.1:9050 )': 'Proxy Address (Example:
SOCKS5://127.0.0.1:9050 )'
'Clicking "TEST PROXY" button will send a request to https://ipinfo.io/json': 'Clicking
"TEST PROXY" button will send a request to https://ipinfo.io/json'
TEST PROXY button will send a request to https://ipinfo.io/json'
Use Tor / Proxy for API calls: 'Use Tor / Proxy for API calls'
TEST PROXY: 'TEST PROXY'
#& Invidious Instance (Default is https://invidious.snopyta.org)
@ -275,39 +275,31 @@ Settings:
Hide Channel Subscribers: Hide Channel Subscribers
Hide Video Likes And Dislikes: Hide Video Likes And Dislikes
Hide Video Views: Hide Video Views
Hide Active Subscriptions: Hide Active Subscriptions
Distraction Free Settings: Distraction Free Settings
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?
About:
#On About page
About: 'About'
#& About
'This software is FOSS and released under the GNU Affero General Public License v3.0.': 'This
copylefted software is freely licenced AGPL-3.0.'
'Found a bug? Want to suggest a feature? Want to help out? Check out our GitHub page. Pull requests are welcome.': >-
Found a bug? Want to suggest a feature? Want to help out? Check out our GitHub
page. Pull requests welcome.
Thank you very much to the People and Projects that make FreeTube possible!: 'Thank
you very much to the People and Projects that make FreeTube possible!'
'Want to chat? Join our Element / Matrix Server . Please check the rules before joining.': 'Want
to chat? Join our Element / Matrix Server . Please check the rules before joining.'
'Looking for help? Check out our Wiki page.': 'Looking for help? Check out our Wiki
page.'
Check out our Firefox extension!: 'Check out our Firefox extension!'
'If you enjoy using FreeTube, consider donating via Liberapay or through our Bitcoin address.': 'If
you enjoy using FreeTube, consider donating via Liberapay or through our Bitcoin
address.'
#~ 'BTC: 1Lih7Ho5gnxb1CwPD4o59ss78pwo2T91eS'
Latest FreeTube News: 'Latest FreeTube News'
About: About
Contact: Contact
Help: Help
Useful Links: Useful Links
Donate: Donate
Beta: Beta
License: License
Email: Email
Channel Rules: Channel Rules
Report an Issue: Report an Issue
Wiki: Wiki
FAQ: FAQ
Credits: Credits
Blog: Blog
Release Notes: Release Notes
Source Code: Source Code
Website: Website
Translate via Weblate: Translate via Weblate
Profile:
Profile Select: 'Profile Select'

View File

@ -270,6 +270,7 @@ Settings:
Hide Comment Likes: No mostrar likes de comentarios
Hide Channel Subscribers: No mostrar suscriptores en canales
Distraction Free Settings: No distraer
Hide Active Subscriptions: Ocultar suscripciones activas
The app needs to restart for changes to take effect. Restart and apply change?: ¿Quieres
reiniciar FreeTube ahora para aplicar los cambios?
About:

View File

@ -200,7 +200,7 @@ Settings:
'Proxy Address (Example: SOCKS5://127.0.0.1:9050 )': 'Adresse Proxy (Exemple :
SOCKS5://127.0.0.1:9050 )'
'Clicking "TEST PROXY" button will send a request to https://ipinfo.io/json': 'Cliquer
sur le bouton "TEST PROXY" enverra une demande à https://ipinfo.io/json'
sur le bouton « TEST PROXY » enverra une demande à https://ipinfo.io/json'
Use Tor / Proxy for API calls: 'Utiliser Tor / Proxy pour les appels de l''API'
TEST PROXY: 'TESTER LE PROXY'
#& Invidious Instance (Default is https://invidious.snopyta.org)
@ -275,6 +275,7 @@ Settings:
One or more subscriptions were unable to be imported: Un ou plusieurs abonnements
nont pas pu être importés
Check for Legacy Subscriptions: Vérifier les abonnements Legacy
Manage Subscriptions: Gérer les abonnements
Distraction Free Settings:
Hide Video Likes And Dislikes: Masquer les J'aime et Je n'aime pas des vidéos
Hide Comment Likes: Masquer les J'aime dans les commentaires
@ -284,10 +285,28 @@ Settings:
Hide Popular Videos: Masquer les vidéos populaires
Hide Trending Videos: Masquer les tendances
Hide Recommended Videos: Masquer les vidéos recommandées
Distraction Free Settings: Mode Sans Distraction
Distraction Free Settings: Mode sans distraction
Hide Active Subscriptions: Masquer les abonnements actifs
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 ?
Proxy Settings:
Error getting network information. Is your proxy configured properly?: Erreur
lors de l'obtention des informations sur le réseau. Votre proxy est-il correctement
configuré ?
City: Ville
Region: Région
Country: Pays
Ip: Ip
Your Info: Vos informations
Test Proxy: Test du Proxy
Clicking on Test Proxy will send a request to: En cliquant sur Test Proxy, une
demande sera envoyée à
Proxy Port Number: Numéro de port du proxy
Proxy Host: Hôte du proxy
Proxy Protocol: Protocole du proxy
Enable Tor / Proxy: Activer Tor / Proxy
Proxy Settings: Paramètres du proxy
About:
#On About page
About: 'À propos'
@ -296,9 +315,9 @@ About:
logiciel est sous licence AGPL-3.0.'
'Found a bug? Want to suggest a feature? Want to help out? Check out our GitHub page. Pull requests are welcome.': >-
Vous avez trouvé un bug ? Vous souhaitez suggérer une nouvelle fonctionnalité
? Vous voulez aider de façon générale ? Rendez-vous sur la page GitHub. Les "Pull
requests" sont les bienvenues.
Vous avez trouvé une erreur ? Vous souhaitez suggérer une nouvelle fonctionnalité
? Vous voulez aider de façon générale ? Rendez-vous sur la page GitHub. Les pull
requests sont les bienvenues.
Thank you very much to the People and Projects that make FreeTube possible!: 'Un
grand merci à toutes les personnes et à tous les projets qui ont rendu FreeTube
@ -322,6 +341,30 @@ About:
Latest FreeTube News: 'Les dernières actualités FreeTube'
#On Channel Page
Channel Rules: Règles du salon
Website: Site internet
Source Code: Code source
Release Notes: Notes de version
Email: Courriel
License: Licence
Help: Aide
Contact: Contacts
Translate via Weblate: Traduire via Weblate
Blog: Blog
Credits: Crédits
FAQ: FAQ
Wiki: Wiki
Report an Issue: Signaler un problème
Beta: Bêta
Donate: Faire un don
Useful Links: Liens utiles
Translate: Traduire
Chat on Matrix: Chatter sur Matrix
Mastodon: Mastodon
Report a problem: Signaler un problème
View License: Voir la license
Licensed under the AGPLv3: Sous licence AGPLv3
Source code: Code source
Channel:
Subscriber: 'Abonnement'
Subscribers: 'Abonnements'

602
static/locales/gl.yaml Normal file
View File

@ -0,0 +1,602 @@
# Put the name of your locale in the same language
Locale Name: 'Galego'
FreeTube: 'FreeTube'
# Currently on Subscriptions, Playlists, and History
'This part of the app is not ready yet. Come back later when progress has been made.': >-
Esta parte da aplicación non está lista aínda. Volve máis adiante, cando se avance
no desenvolvemento.
# Webkit Menu Bar
File: 'Arquivo'
Quit: 'Saír'
Edit: 'Editar'
Undo: 'Desfacer'
Redo: 'Refacer'
Cut: 'Cortar'
Copy: 'Copiar'
Paste: 'Pegar'
Delete: 'Eliminar'
Select all: 'Seleccionar todo'
Reload: 'Recargar'
Force Reload: 'Forzar recarga'
Toggle Developer Tools: 'Activar Ferramentas de Desenvolvedor'
Actual size: 'Tamaño real'
Zoom in: 'Agrandar'
Zoom out: 'Reducir'
Toggle fullscreen: 'Activar pantalla completa'
Window: 'Ventá'
Minimize: 'Minimizar'
Close: 'Pechar'
Back: 'Atrás'
Forward: 'Adiante'
Version $ is now available! Click for more details: 'A versión $ está dispoñible! Fai
clic para veres máis detalles'
Download From Site: 'Descargar do sitio web'
A new blog is now available, $. Click to view more: 'Hai unha nova entrada no blog
dispoñible, $. Fai clic para veres máis'
# Search Bar
Search / Go to URL: 'Buscar / Ir á URL'
# In Filter Button
Search Filters:
Search Filters: 'Buscar filtros'
Sort By:
Sort By: 'Clasificar por'
Most Relevant: 'Máis relevante'
Rating: 'Valoración'
Upload Date: 'Data de subida'
View Count: 'Visualizacións'
Time:
Time: 'Tempo'
Any Time: 'Calquera momento'
Last Hour: 'Última hora'
Today: 'Hoxe'
This Week: 'Esta semana'
This Month: 'Este mes'
This Year: 'Este ano'
Type:
Type: 'Tipo'
All Types: 'Tódolos tipos'
Videos: 'Vídeos'
Channels: 'Canles'
#& Playlists
Duration:
Duration: 'Duración'
All Durations: 'Tódalas duracións'
Short (< 4 minutes): 'Curta (< 4 minutos)'
Long (> 20 minutes): 'Longa (> 20 minutos)'
# On Search Page
Search Results: 'Resultados da busca'
Fetching results. Please wait: 'Buscando resultados. Por favor, agarda'
Fetch more results: 'Buscar máis resultados'
# Sidebar
Subscriptions:
# On Subscriptions Page
Subscriptions: 'Subscricións'
Latest Subscriptions: 'Últimas subscricións'
This profile has a large number of subscriptions. Forcing RSS to avoid rate limiting: 'Este
perfil ten unha gran cantidade de subscricións. Forzarase RSS para evitar o límite'
'Your Subscription list is currently empty. Start adding subscriptions to see them here.': 'A
túa listaxe de subscricións está baleira. Engade algunhas para as ver aquí.'
'Getting Subscriptions. Please wait.': 'Obtendo subscricións. Por favor, agarda.'
Refresh Subscriptions: 'Actualizar subscricións'
Load More Videos: 'Cargar máis vídeos'
Trending: 'Tendencias'
Most Popular: 'Máis populares'
Playlists: 'Listaxes de reprodución'
User Playlists:
Your Playlists: 'As túas listaxes de reprodución'
History:
# On History Page
History: 'Histórico'
Watch History: 'Histórico de visualizacións'
Your history list is currently empty.: 'A túa listaxe histórica está baleira.'
Settings:
# On Settings Page
Settings: 'Axustes'
The app needs to restart for changes to take effect. Restart and apply change?: 'Queres
reiniciar a aplicación para aplicar os cambios?'
General Settings:
General Settings: 'Axustes xerais'
Check for Updates: 'Comprobar actualizacións'
Check for Latest Blog Posts: 'Comprobar se hai novas entradas no blog'
Fallback to Non-Preferred Backend on Failure: 'Usar motor API non preferido en
caso de falla'
Enable Search Suggestions: 'Activar suxestións de busca'
Default Landing Page: 'Páxina inicial'
Locale Preference: 'Preferencia de localización'
Preferred API Backend:
Preferred API Backend: 'Motor API principal'
Local API: 'API local'
Invidious API: 'API de Invidious'
Video View Type:
Video View Type: 'Disposición dos vídeos'
Grid: 'Grella'
List: 'Listaxe'
Thumbnail Preference:
Thumbnail Preference: 'Preferencia de miniaturas'
Default: 'Por defecto'
Beginning: 'Principio'
Middle: 'Metade'
End: 'Final'
'Invidious Instance (Default is https://invidious.snopyta.org)': 'Instancia de
Invidious (por defecto é https://invidious.snopyta.org)'
Region for Trending: 'Rexión para as tendencias'
#! List countries
Theme Settings:
Theme Settings: 'Axustes de tema'
Match Top Bar with Main Color: 'Combinar cor principal coa barra superior'
Expand Side Bar by Default: 'Expandir barra lateral por defecto'
Disable Smooth Scrolling: 'Desactivar desprazamento suavizado'
UI Scale: 'Escala da interface'
Base Theme:
Base Theme: 'Tema base'
Black: 'Negro'
Dark: 'Escuro'
Light: 'Claro'
Main Color Theme:
Main Color Theme: 'Cor principal'
Red: 'Vermello'
Pink: 'Rosa'
Purple: 'Morado'
Deep Purple: 'Morado escuro'
Indigo: 'Índigo'
Blue: 'Azul'
Light Blue: 'Azul claro'
Cyan: 'Ciano'
Teal: 'Azul petróleo'
Green: 'Verde'
Light Green: 'Verde claro'
Lime: 'Verde lima'
Yellow: 'Amarelo'
Amber: 'Ámbar'
Orange: 'Laranxa'
Deep Orange: 'Laranxa escuro'
Secondary Color Theme: 'Cor secundaria'
#* Main Color Theme
Player Settings:
Player Settings: 'Axustes do reprodutor'
Force Local Backend for Legacy Formats: 'Forzar API local para formatos antigos
(Legacy)'
Play Next Video: 'Reproducir próximo vídeo'
Turn on Subtitles by Default: 'Activar subtítulos por defecto'
Autoplay Videos: 'Reproducir vídeos automaticamente'
Proxy Videos Through Invidious: 'Utilizar Invidious como intermediario'
Autoplay Playlists: 'Reproducir listaxes de reprodución automaticamente'
Enable Theatre Mode by Default: 'Activar modo cinema por defecto'
Default Volume: 'Volume predeterminado'
Default Playback Rate: 'Velocidade de reprodución predeterminada'
Default Video Format:
Default Video Format: 'Formato de vídeo predeterminado'
Dash Formats: 'Formato dash'
Legacy Formats: 'Formato antigo (Legacy)'
Audio Formats: 'Formato de audio'
Default Quality:
Default Quality: 'Calidade predeterminada'
Auto: 'Automática'
144p: '144p'
240p: '240p'
360p: '360p'
480p: '480p'
720p: '720p'
1080p: '1080p'
1440p: '1440p'
4k: '4k'
8k: '8k'
Privacy Settings:
Privacy Settings: 'Axustes de privacidade'
Remember History: 'Lembrar histórico'
Save Watched Progress: 'Gardar progreso da reprodución'
Clear Search Cache: 'Limpar caché de busca'
Are you sure you want to clear out your search cache?: 'Estás seguro de querer
limpar o caché de busca?'
Search cache has been cleared: 'Caché de busca limpado'
Remove Watch History: 'Limpar histórico de reprodución'
Are you sure you want to remove your entire watch history?: 'Estás seguro de querer
limpar por completo o histórico de busca?'
Watch history has been cleared: 'Histórico de busca limpado'
Remove All Subscriptions / Profiles: 'Limpar tódalas subscricións / perfís'
Are you sure you want to remove all subscriptions and profiles? This cannot be undone.: 'Estás
seguro de querer limpar tódalas subscricións e perfís? Esta acción é irreversible.'
Subscription Settings:
Subscription Settings: 'Axustes de subscricións'
Hide Videos on Watch: 'Agochar vídeos visualizados'
Fetch Feeds from RSS: 'Obter subscricións através de RSS'
Manage Subscriptions: 'Xestionar subscricións'
Distraction Free Settings:
Distraction Free Settings: 'Sen distraccións'
Hide Video Views: 'Agochar as visualizacións nos vídeos'
Hide Video Likes And Dislikes: 'Agochar likes/dislikes nos vídeos'
Hide Channel Subscribers: 'Agochar subscritores nas canles'
Hide Comment Likes: 'Agochar likes dos comentarios'
Hide Recommended Videos: 'Agochar vídeos recomendados'
Hide Trending Videos: 'Agochar vídeos en tendencias'
Hide Popular Videos: 'Agochar vídeos populares'
Hide Live Chat: 'Agochar chat en vivo'
Data Settings:
Data Settings: 'Axustes dos datos'
Select Import Type: 'Escoller tipo de importación'
Select Export Type: 'Escoller tipo de exportación'
Import Subscriptions: 'Importar subscricións'
Import FreeTube: 'Importar FreeTube'
Import YouTube: 'Importar YouTube'
Import NewPipe: 'Importar NewPipe'
Check for Legacy Subscriptions: 'Comprobar subscricións antigas (Legacy)'
Export Subscriptions: 'Exportar subscricións'
Export FreeTube: 'Exportar Freetube'
Export YouTube: 'Exportar YouTube'
Export NewPipe: 'Exportar NewPipe'
Import History: 'Importar histórico'
Export History: 'Exportar histórico'
Profile object has insufficient data, skipping item: 'Este perfil ten datos insuficientes,
omitindo'
All subscriptions and profiles have been successfully imported: 'Tódalas subscricións
e perfís foron importados correctamente'
All subscriptions have been successfully imported: 'Tódalas subscricións foron
importadas correctamente'
One or more subscriptions were unable to be imported: 'Unha ou máis subscricións
non puideron ser importadas'
Invalid subscriptions file: 'Ficheiro de subcricións inválido'
This might take a while, please wait: 'Isto pode levar un cacho. Por favor, agarda'
Invalid history file: 'Ficheiro de histórico inválido'
Subscriptions have been successfully exported: 'As subscricións foron exportadas
correctamente'
History object has insufficient data, skipping item: 'O histórico ten datos insuficientes,
omitindo'
All watched history has been successfully imported: 'O histórico de visualizacións
foi importado correctamente'
All watched history has been successfully exported: 'O histórico de visualizacións
foi exportado correctamente'
Unable to read file: 'Imposible ler o ficheiro'
Unable to write file: 'Imposible escribir o ficheiro'
Unknown data key: 'Chave de datos descoñecida'
How do I import my subscriptions?: 'Como podo importar as subscricións?'
Advanced Settings:
Advanced Settings: ''
Enable Debug Mode (Prints data to the console): 'Activar modo de depuración (escribe
datos na consola)'
'Proxy Address (Example: SOCKS5://127.0.0.1:9050 )': 'Enderezo proxy (Exemplo:
SOCKS5://127.0.0.1:9050 )'
'Clicking "TEST PROXY" button will send a request to https://ipinfo.io/json': 'Premendo
en "TESTAR PROXY" enviarase unha petición a https://ipinfo.io/json'
Use Tor / Proxy for API calls: 'Usar Tor / Proxy para chamadas API'
TEST PROXY: 'TESTAR PROXY'
#& Invidious Instance (Default is https://invidious.snopyta.org)
See Public Instances: 'Ver instancias públicas'
Clear History:
Clear History: 'Limpar histórico'
# On Click
Are you sure you want to delete your history?: 'Estás seguro de querer limpar
o histórico?'
#& Yes
#& No
Clear Subscriptions:
Clear Subscriptions: 'Limpar subscricións'
# On Click
Are you sure you want to remove all subscriptions?: 'Estás seguro de querer
limpar tódalas subscricións?'
#& Yes
#& No
About:
#On About page
About: 'Sobre'
#& About
'This software is FOSS and released under the GNU Affero General Public License v3.0.': 'Este
software ten licencia libre AGPL-3.0.'
'Found a bug? Want to suggest a feature? Want to help out? Check out our GitHub page. Pull requests are welcome.': >-
Atopaches un erro? Tes algunha suxestión? Queres axudar? Visita a nosa páxina
de GitHub. Calquera petición é benvida.
Thank you very much to the People and Projects that make FreeTube possible!: 'Moitas
grazas a tódalas persoas e proxectos que fan FreeTube posible!'
'Want to chat? Join our Element / Matrix Server . Please check the rules before joining.': 'Queres
chatear? Únete ao noso servidor Element / Matrix. Lembra comprobar as regras antes
de te unir.'
'Looking for help? Check out our Wiki page.': 'Precisas axuda? Visita a nosa páxina
Wiki.'
Check out our Firefox extension!: 'Comproba a nosa extensión para Firefox!'
'If you enjoy using FreeTube, consider donating via Liberapay or through our Bitcoin address.': 'Se
estás a desfrutar FreeTube, considera doar através de Liberapay ou ao noso enderezo
Bitcoin.'
#~ 'BTC: 1Lih7Ho5gnxb1CwPD4o59ss78pwo2T91eS'
Latest FreeTube News: 'Noticias recentes sobre FreeTube'
Profile:
Profile Select: 'Selección de perfil'
All Channels: 'Tódalas canles'
Profile Manager: 'Xestor de perfís'
Create New Profile: 'Crear novo perfil'
Edit Profile: 'Editar perfil'
Color Picker: 'Selector de cor'
Custom Color: 'Cor personalizada'
Profile Preview: 'Vista previa do perfil'
Create Profile: 'Crear perfil'
Update Profile: 'Actualizar perfil'
Make Default Profile: 'Converter en perfil predeterminado'
Delete Profile: 'Eliminar perfil'
Are you sure you want to delete this profile?: 'Estás seguro de querer eliminar
este perfil?'
All subscriptions will also be deleted.: 'Tamén se eliminarán tódalas subscricións.'
Profile could not be found: 'Non se atopou o perfil'
Your profile name cannot be empty: 'O nome do perfil non pode estar en branco'
Profile has been created: 'Creouse o perfil'
Profile has been updated: 'Actualizouse o perfil'
Your default profile has been set to $: '$ é agora o teu perfil predeterminado'
Removed $ from your profiles: 'Eliminouse $ dos teus perfís'
Your default profile has been changed to your primary profile: 'O teu perfil predeterminado
cambiouse ao teu perfil primario'
$ is now the active profile: '$ é agora o perfil activo'
Subscription List: 'Listaxe de subcricións'
Other Channels: 'Outras canles'
$ selected: '$ seleccionado'
Select All: 'Seleccionar todos'
Select None: 'Non seleccionar ningún'
Delete Selected: 'Eliminar seleccionados'
Add Selected To Profile: 'Engadir seleccionados ao perfil'
No channel(s) have been selected: 'Ningunha canle foi seleccionada'
? This is your primary profile. Are you sure you want to delete the selected channels? The
same channels will be deleted in any profile they are found in.
: 'Este é o teu perfil principal. Estás seguro de querer eliminar as canles seleccionadas? Eliminaranse
as mesmas en calquera perfil no que se atopen.'
Are you sure you want to delete the selected channels? This will not delete the channel from any other profile.: 'Estás
seguro de querer eliminar as canles seleccionadas? Esta acción non as eliminará
de ningún outro perfil.'
#On Channel Page
Channel:
Subscriber: 'Subscritor'
Subscribers: 'Subscritores'
Subscribe: 'Subscribirse'
Unsubscribe: 'Desubscribirse'
Channel has been removed from your subscriptions: 'Esta canle foi eliminada das
túas subscricións'
Removed subscription from $ other channel(s): ''
Added channel to your subscriptions: 'Canle engadida ás túas subscricións'
Search Channel: 'Buscar na canle'
Your search results have returned 0 results: 'A busca non atopou resultados'
Sort By: 'Ordenar por'
Videos:
Videos: 'Vídeos'
This channel does not currently have any videos: 'Esta canle aínda non ten ningún
vídeo'
Sort Types:
Newest: 'Máis recentes'
Oldest: 'Máis antigos'
Most Popular: 'Máis populares'
Playlists:
Playlists: 'Listaxes de reprodución'
This channel does not currently have any playlists: 'Esta canle non ten ningunha
listaxe de reprodución actualmente'
Sort Types:
Last Video Added: 'Último vídeo engadido'
Newest: 'O máis recente'
Oldest: 'O máis antigo'
About:
About: 'Sobre'
Channel Description: 'Descrición da canle'
Featured Channels: 'Canles destacadas'
Video:
Mark As Watched: 'Marcar como visto'
Remove From History: 'Eliminar do histórico'
Video has been marked as watched: 'O vídeo marcouse como visto'
Video has been removed from your history: 'O vídeo eliminouse do teu histórico'
Open in YouTube: 'Abrir no YouTube'
Copy YouTube Link: 'Copiar ligazón de YouTube'
Open YouTube Embedded Player: 'Abrir reprodutor integrado de YouTube'
Copy YouTube Embedded Player Link: 'Copiar ligazón do preprodutor integrado de YouTube'
Open in Invidious: 'Abrir no Invidious'
Copy Invidious Link: 'Copiar ligazón de Invidious'
Open Channel in YouTube: 'Abrir canle no YouTube'
Copy YouTube Channel Link: 'Copiar ligazón de YouTube da canle'
Open Channel in Invidious: 'Abrir canle no Invidious'
Copy Invidious Channel Link: 'Copiar ligazón de Invidious da canle'
View: ''
Views: 'Visualizacións'
Loop Playlist: 'Reprodución en bucle'
Shuffle Playlist: 'Reprodución aleatoria'
Reverse Playlist: 'Inverter listaxe de reprodución'
Play Next Video: 'Reproducir próximo vídeo'
Play Previous Video: 'Reproducir vídeo anterior'
# Context is "X People Watching"
Watching: 'A ver'
Watched: 'Visto'
Autoplay: 'Reprodución automática'
Starting soon, please refresh the page to check again: 'Comezará en breve. Por favor,
actualiza a páxina para verificares'
# As in a Live Video
Live: 'En vivo'
Live Now: 'En vivo agora'
Live Chat: 'Chat en vivo'
Enable Live Chat: 'Activar chat en vivo'
Live Chat is currently not supported in this build.: 'O chat en vivo non está soportado
nesta versión.'
'Chat is disabled or the Live Stream has ended.': 'O chat foi desactivado ou a transmisión
en vivo rematou.'
Live chat is enabled. Chat messages will appear here once sent.: 'Chat en vivo
activado. As mensaxes aparecerán aquí ao seren enviadas.'
'Live Chat is currently not supported with the Invidious API. A direct connection to YouTube is required.': 'Chat
en vivo actualmente non soportado coa API de Invidious. Precísase dunha conexión
directa con YouTube.'
Download Video: 'Descargar vídeo'
video only: 'Só vídeo'
audio only: 'Só audio'
Audio:
Low: 'Baixa'
Medium: 'Media'
High: 'Alta'
Best: 'Máxima'
Published:
Jan: 'Xan'
Feb: 'Feb'
Mar: 'Mar'
Apr: 'Abr'
May: 'Mai'
Jun: 'Xuñ'
Jul: 'Xul'
Aug: 'Ago'
Sep: 'Set'
Oct: 'Out'
Nov: 'Nov'
Dec: 'Dec'
Second: ''
Seconds: 'Segundos'
Minute: 'Minuto'
Minutes: 'Minutos'
Hour: 'Hora'
Hours: 'Horas'
Day: 'Día'
Days: 'Días'
Week: 'Semana'
Weeks: 'Semanas'
Month: 'Mes'
Months: 'Meses'
Year: 'Ano'
Years: 'Anos'
Ago: 'Hai'
Upcoming: 'Estrea'
Published on: 'Publicado'
Streamed on: 'Transmitido'
Started streaming on: 'A transmisión comezou'
# $ is replaced with the number and % with the unit (days, hours, minutes...)
Publicationtemplate: 'Hai $ %'
#& Videos
Videos:
#& Sort By
Sort By:
Newest: 'Máis recente'
Oldest: 'Máis antigo'
#& Most Popular
#& Playlists
Playlist:
#& About
View Full Playlist: 'Ver listaxe de reprodución completa'
Videos: 'Vídeos'
View: 'Visualización'
Views: 'Visualizacións'
Last Updated On: 'Última actualización'
Share Playlist:
Share Playlist: 'Compartir listaxe de reprodución'
Copy YouTube Link: 'Copiar ligazón de YouTube'
Open in YouTube: 'Abrir no YouTube'
Copy Invidious Link: 'Copiar ligazón de Invidious'
Open in Invidious: 'Abrir en Invidious'
# On Video Watch Page
#* Published
#& Views
Toggle Theatre Mode: 'Activar modo cinema'
Change Format:
Change Video Formats: 'Mudar formato do vídeo'
Use Dash Formats: 'Utilizar formato Dash'
Use Legacy Formats: 'Utilizar formato antigo (Legacy)'
Use Audio Formats: 'Utilizar formato só de audio'
Dash formats are not available for this video: 'Formato Dash non dispoñible para
este vídeo'
Audio formats are not available for this video: 'Formato só de audio non dispoñible
para este vídeo'
Share:
Share Video: 'Compartir vídeo'
Include Timestamp: 'Incluír marcación de tempo'
Copy Link: 'Copiar ligazón'
Open Link: 'Abrir ligazón'
Copy Embed: 'Copiar código integrado'
Open Embed: 'Abrir código integrado'
# On Click
Invidious URL copied to clipboard: 'Ligazón de Invidious copiada á área de transferencia'
Invidious Embed URL copied to clipboard: 'Ligazón integrada de Invidious copiada
á área de transferencia'
Invidious Channel URL copied to clipboard: 'Ligazón de Invidious da canle copiada
á área de transferencia'
YouTube URL copied to clipboard: 'Ligazón de YouTube copiada á área de transferencia'
YouTube Embed URL copied to clipboard: 'Ligazón integrada de YouTube copiada á área
de transferencia'
YouTube Channel URL copied to clipboard: 'Ligazón de YouTube da canle copiada á
área de transferencia'
Mini Player: 'Mini-reprodutor'
Comments:
Comments: 'Comentarios'
Click to View Comments: 'Premer para ver comentarios'
Getting comment replies, please wait: 'Obtendo respostas ao comentario. Por favor,
agarda'
There are no more comments for this video: 'Non hai máis comentarios neste vídeo'
Show Comments: 'Amosar comentarios'
Hide Comments: 'Agochar comentarios'
Sort by: 'Ordenar por'
Top comments: 'Comentarios destacados'
Newest first: 'Máis recentes'
# Context: View 10 Replies, View 1 Reply
View: 'Amosar'
Hide: 'Agochar'
Replies: 'Respostas'
Reply: 'Resposta'
There are no comments available for this video: 'Este vídeo non contén ningún comentario'
Load More Comments: 'Cargar máis comentarios'
No more comments available: 'Non hai máis comentarios'
Up Next: 'Seguintes'
#Tooltips
Tooltips:
General Settings:
Preferred API Backend: 'Escolle o back-end que FreeTube utiliza para obter os
datos. A API local é un extractor incluído. A API de Invidious require un servidor
Invidious ao que se conectar.'
Fallback to Non-Preferred Backend on Failure: 'Cando a túa API primaria falla,
FreeTube tentará utilizar a API secundaria automaticamente se se habilita'
Thumbnail Preference: 'Tódalas miniaturas en FreeTube serán remplazadas cunha
imaxe do vídeo no canto da miniatura predeterminada'
Invidious Instance: 'A instancia de Invidious á que FreeTube se conectará para
as chamadas API. Limpa a instancia actual para veres unha listaxe de instancias
públicas'
Region for Trending: 'A rexión das tendencias permíteche escoller os vídeos máis
populares nun Estado. Non tódolos Estados que se amosan están soportados por
YouTube'
Player Settings:
Force Local Backend for Legacy Formats: 'Só funcionará se a API de Invidious está
escollida por defecto. Cando estea activa, a API local usará formatos antigos
(Legacy) no canto dos de Invidious. Axuda cando Invidious non pode reproducir
un vídeo por causa das restricións rexionais'
Proxy Videos Through Invidious: 'Conectarase a Invidious para obter vídeos no
canto de recorrer directamente a YouTube. Sobrescribirá a preferencia de API'
Default Video Format: 'Selecciona os formatos usados cando se reproduce un vídeo.
Os formatos Dash teñen resolucións máis altas. Os formatos antigos (Legacy)
están limitados a 720p pero utilizan menos largura de banda. Os formatos de
audio reproducen unicamente audio'
Subscription Settings:
Fetch Feeds from RSS: 'Cando está activado, FreeTube usará RSS no canto do método
predeterminao para obter as subscricións. RSS é máis rápido e impide o bloqueo
da IP, pero non proporciona certa información como a duración dun vídeo ou se
é en vivo'
# Toast Messages
Local API Error (Click to copy): 'Erro de API local (Preme para copiar)'
Invidious API Error (Click to copy): 'Erro de API Invidious (Preme para copiar)'
Falling back to Invidious API: 'Recorrendo á API Invidious'
Falling back to the local API: 'Recorrendo á API local'
This video is unavailable because of missing formats. This can happen due to country unavailability.: 'Este
vídeo non está dispoñible por ter un formato inválido. Isto pode pasar pola non
dispoñibilidade rexional'
Subscriptions have not yet been implemented: 'As subscricións aínda non foron implementadas'
Loop is now disabled: 'Reprodución en bucle desactivada'
Loop is now enabled: 'Reprodución en bucle activada'
Shuffle is now disabled: 'Reprodución aleatoria desactivada'
Shuffle is now enabled: 'Reprodución aleatoria activada'
The playlist has been reversed: 'A listaxe de reprodución foi invertida'
Playing Next Video: 'A reproducir próximo vídeo'
Playing Previous Video: 'A reproducir vídeo anterior'
Playing next video in 5 seconds. Click to cancel: 'O próximo vídeo reproducirase
en 5 segundos. Preme para cancelar.'
Canceled next video autoplay: 'Reprodución cancelada'
'The playlist has ended. Enable loop to continue playing': 'A listaxe de reprodución
rematou. Activa a reprodución en bucle para continuar reproducindo'
Yes: 'Si'
No: 'Non'

View File

@ -86,6 +86,11 @@ Most Popular: 'הכי פופולרי'
Playlists: 'פלייליסטים'
User Playlists:
Your Playlists: 'הפלייליסטים שלך'
Playlist Message: העמוד הזה אינו משקף רשימות נגינה תקינות לחלוטין. הוא מציג רק סרטונים
ששמרת או הוספת כמועדפים. עם סיום העבודה, כל הסרטונים שכאן ייובאו אל רשימת הנגינה
‚מועדפים’.
Your saved videos are empty. Click on the save button on the corner of a video to have it listed here: לא
שמרת סרטונים. יש ללחוץ על כפתור השמירה בפינת הסרטון כדי שיופיע כאן
History:
# On History Page
History: 'היסטוריה'
@ -236,6 +241,7 @@ Settings:
Unknown data key: 'מפתח נתון לא ידוע'
How do I import my subscriptions?: 'איך לייבא את המנויים שלי?'
Check for Legacy Subscriptions: איתור מינויים מיושנים
Manage Subscriptions: ניהול מינויים
Advanced Settings:
Advanced Settings: 'הגדרות מתקדמות'
Enable Debug Mode (Prints data to the console): 'הפעל מצב Debug ( מציג נתונים
@ -273,8 +279,25 @@ Settings:
Hide Video Likes And Dislikes: הסתרת לייקים ודיסלייקים לסרטון
Distraction Free Settings: הגדרות להשבתת הסחות
Hide Video Views: הסתרת תצוגות סרטונים
Hide Active Subscriptions: הסתרת מינויים פעילים
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?: קבלת פרטי
הרשת נכשלה. המתווך שלך מוגדר כראוי?
City: עיר
Region: אזור
Country: מדינה
Ip: IP
Your Info: הפרטים שלך
Test Proxy: בדיקת המתווך
Clicking on Test Proxy will send a request to: לחיצה על בדיקת המתווך תשלח בקשה
אל
Proxy Port Number: מספר פתחת מתווך
Proxy Host: מארח מתווך
Proxy Protocol: פרוטוקול מתווך
Enable Tor / Proxy: הפעלת Tor / מתווך
Proxy Settings: הגדרות מתווך
About:
#On About page
About: 'על אודות'
@ -304,6 +327,23 @@ About:
Latest FreeTube News: 'מה חדש ב-FreeTube'
Translate via Weblate: תרגום עם Weblate
Website: אתר
Source Code: קוד מקור
Release Notes: הערות מהדורה
Blog: בלוג
Credits: תודות
FAQ: שו״ת
Wiki: ויקי
Report an Issue: דיווח על תקלה
Channel Rules: כללי הערוץ
Email: דוא״ל
License: רישיון
Beta: בטא
Donate: תרומה
Useful Links: קישורים שימושיים
Help: עזרה
Contact: יצירת קשר
Profile:
Profile Select: 'בחירת פרופיל'
All Channels: 'כל הערוצים'
@ -456,6 +496,9 @@ Video:
Open Channel in YouTube: פתיחת הערוץ ב־YouTube
Started streaming on: ההזרמה החלה ב־
Streamed on: הוזרם ב־
Video has been removed from your saved list: הסרטון הוסר מרשימת השמורים שלך
Video has been saved: סרטון נשמר
Save Video: שמירת סרטון
Videos:
#& Sort By
Sort By:

View File

@ -277,6 +277,7 @@ Settings:
Hide Video Likes And Dislikes: Sakrij ocjenjivanje videa
Hide Video Views: Sakrij broj gledanja videa
Distraction Free Settings: Postavke za nesmetan rad
Hide Active Subscriptions: Sakrij aktivne pretplate
The app needs to restart for changes to take effect. Restart and apply change?: Za
primjenu promjena, program se mora ponovo pokrenuti. Ponovo pokrenuti program?
About:
@ -310,6 +311,23 @@ About:
Latest FreeTube News: 'Najnovije vijesti o programu FreeTube'
Translate via Weblate: Prevedi putem Weblatea
Website: Web-stranica
Source Code: Izvorni kod
Release Notes: Napomene uz izdanje
Blog: Blog
Credits: Zasluge
FAQ: Često postavljena pitanja
Wiki: Wiki
Report an Issue: Prijavi problem
Channel Rules: Pravila kanala
Email: E-adresa
License: Licenca
Beta: Beta
Donate: Doniraj
Useful Links: Korisne poveznice
Help: Pomoć
Contact: Kontakt
Profile:
All Channels: 'Svi kanali'
Profile Manager: 'Upravljač profila'

Some files were not shown because too many files have changed in this diff Show More