mirror of https://github.com/FreeTubeApp/FreeTube
Merge branch 'development' into piped-support
This commit is contained in:
commit
d4b2ac9329
|
@ -64,7 +64,7 @@ jobs:
|
|||
|
||||
- name: Set Version Number Variable
|
||||
id: versionNumber
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
IS_DEV: ${{ contains(github.ref, 'development') }}
|
||||
IS_RC: ${{ contains(github.ref, 'RC') }}
|
||||
|
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: github/issue-labeler@v3.2
|
||||
- uses: github/issue-labeler@v3.3
|
||||
with:
|
||||
configuration-path: .github/issue-labeler.yml
|
||||
enable-versioned-regex: 0
|
||||
|
|
16
package.json
16
package.json
|
@ -58,11 +58,11 @@
|
|||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||
"@fortawesome/vue-fontawesome": "^2.0.10",
|
||||
"@seald-io/nedb": "^4.0.2",
|
||||
"@silvermine/videojs-quality-selector": "^1.3.0",
|
||||
"@silvermine/videojs-quality-selector": "^1.3.1",
|
||||
"autolinker": "^4.0.0",
|
||||
"electron-context-menu": "^3.6.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"marked": "^9.1.5",
|
||||
"marked": "^10.0.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"process": "^0.11.10",
|
||||
"video.js": "7.21.5",
|
||||
|
@ -80,23 +80,23 @@
|
|||
"youtubei.js": "^7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.2",
|
||||
"@babel/eslint-parser": "^7.22.15",
|
||||
"@babel/core": "^7.23.3",
|
||||
"@babel/eslint-parser": "^7.23.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/preset-env": "^7.23.2",
|
||||
"@babel/preset-env": "^7.23.3",
|
||||
"@double-great/stylelint-a11y": "^2.0.2",
|
||||
"babel-loader": "^9.1.3",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.8.1",
|
||||
"css-minimizer-webpack-plugin": "^5.0.1",
|
||||
"electron": "^27.0.3",
|
||||
"electron": "^27.1.0",
|
||||
"electron-builder": "^24.6.4",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-import": "^2.29.0",
|
||||
"eslint-plugin-jsonc": "^2.10.0",
|
||||
"eslint-plugin-n": "^16.2.0",
|
||||
"eslint-plugin-n": "^16.3.1",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-unicorn": "^49.0.0",
|
||||
|
|
|
@ -70,11 +70,6 @@ const SyncEvents = {
|
|||
}
|
||||
}
|
||||
|
||||
// https://v2.vuejs.org/v2/api/#provide-inject
|
||||
const Injectables = {
|
||||
SHOW_OUTLINES: 'showOutlines'
|
||||
}
|
||||
|
||||
// Utils
|
||||
const MAIN_PROFILE_ID = 'allChannels'
|
||||
|
||||
|
@ -82,6 +77,5 @@ export {
|
|||
IpcChannels,
|
||||
DBActions,
|
||||
SyncEvents,
|
||||
Injectables,
|
||||
MAIN_PROFILE_ID
|
||||
}
|
||||
|
|
|
@ -1330,6 +1330,13 @@ function runApp() {
|
|||
},
|
||||
type: 'normal'
|
||||
},
|
||||
{
|
||||
label: 'Profile Manager',
|
||||
click: (_menuItem, browserWindow, _event) => {
|
||||
navigateTo('/settings/profile/', browserWindow)
|
||||
},
|
||||
type: 'normal'
|
||||
},
|
||||
].filter((v) => v !== false),
|
||||
},
|
||||
{
|
||||
|
|
|
@ -10,7 +10,7 @@ import FtButton from './components/ft-button/ft-button.vue'
|
|||
import FtToast from './components/ft-toast/ft-toast.vue'
|
||||
import FtProgressBar from './components/ft-progress-bar/ft-progress-bar.vue'
|
||||
import { marked } from 'marked'
|
||||
import { Injectables, IpcChannels } from '../constants'
|
||||
import { IpcChannels } from '../constants'
|
||||
import packageDetails from '../../package.json'
|
||||
import { openExternalLink, openInternalPath, showToast } from './helpers/utils'
|
||||
|
||||
|
@ -30,15 +30,9 @@ export default defineComponent({
|
|||
FtToast,
|
||||
FtProgressBar
|
||||
},
|
||||
provide: function () {
|
||||
return {
|
||||
[Injectables.SHOW_OUTLINES]: this.showOutlines
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
dataReady: false,
|
||||
hideOutlines: true,
|
||||
showUpdatesBanner: false,
|
||||
showBlogBanner: false,
|
||||
showReleaseNotes: false,
|
||||
|
@ -59,6 +53,9 @@ export default defineComponent({
|
|||
showProgressBar: function () {
|
||||
return this.$store.getters.getShowProgressBar
|
||||
},
|
||||
outlinesHidden: function () {
|
||||
return this.$store.getters.getOutlinesHidden
|
||||
},
|
||||
isLocaleRightToLeft: function () {
|
||||
return this.locale === 'ar' || this.locale === 'fa' || this.locale === 'he' ||
|
||||
this.locale === 'ur' || this.locale === 'yi' || this.locale === 'ku'
|
||||
|
@ -114,6 +111,10 @@ export default defineComponent({
|
|||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
},
|
||||
|
||||
landingPage: function() {
|
||||
return '/' + this.$store.getters.getLandingPage
|
||||
},
|
||||
|
||||
externalLinkOpeningPromptNames: function () {
|
||||
return [
|
||||
this.$t('Yes'),
|
||||
|
@ -139,7 +140,7 @@ export default defineComponent({
|
|||
$route () {
|
||||
// react to route changes...
|
||||
// Hide top nav filter panel on page change
|
||||
this.$refs.topNav.hideFilters()
|
||||
this.$refs.topNav?.hideFilters()
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
@ -189,7 +190,13 @@ export default defineComponent({
|
|||
})
|
||||
|
||||
this.$router.afterEach((to, from) => {
|
||||
this.$refs.topNav.navigateHistory()
|
||||
this.$refs.topNav?.navigateHistory()
|
||||
})
|
||||
|
||||
this.$router.onReady(() => {
|
||||
if (this.$router.currentRoute.path === '/' && this.landingPage !== '/subscriptions') {
|
||||
this.$router.replace({ path: this.landingPage })
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
@ -302,7 +309,7 @@ export default defineComponent({
|
|||
activateKeyboardShortcuts: function () {
|
||||
document.addEventListener('keydown', this.handleKeyboardShortcuts)
|
||||
document.addEventListener('mousedown', () => {
|
||||
this.hideOutlines = true
|
||||
this.hideOutlines()
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -327,7 +334,7 @@ export default defineComponent({
|
|||
}
|
||||
switch (event.key) {
|
||||
case 'Tab':
|
||||
this.hideOutlines = false
|
||||
this.showOutlines()
|
||||
break
|
||||
case 'L':
|
||||
case 'l':
|
||||
|
@ -528,15 +535,6 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* provided to all child components, see `provide` near the top of this file
|
||||
* after injecting it, they can show outlines during keyboard navigation
|
||||
* e.g. cycling through tabs with the arrow keys
|
||||
*/
|
||||
showOutlines: function () {
|
||||
this.hideOutlines = false
|
||||
},
|
||||
|
||||
...mapMutations([
|
||||
'setInvidiousInstancesList'
|
||||
]),
|
||||
|
@ -555,7 +553,9 @@ export default defineComponent({
|
|||
'setupListenersToSyncWindows',
|
||||
'updateBaseTheme',
|
||||
'updateMainColor',
|
||||
'updateSecColor'
|
||||
'updateSecColor',
|
||||
'showOutlines',
|
||||
'hideOutlines'
|
||||
])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
v-if="dataReady"
|
||||
id="app"
|
||||
:class="{
|
||||
hideOutlines: hideOutlines,
|
||||
hideOutlines: outlinesHidden,
|
||||
isLocaleRightToLeft: isLocaleRightToLeft
|
||||
}"
|
||||
>
|
||||
|
|
|
@ -163,7 +163,7 @@ export default defineComponent({
|
|||
const message = this.$t('Settings.Data Settings.Profile object has insufficient data, skipping item')
|
||||
showToast(message)
|
||||
} else {
|
||||
if (profileObject.name === 'All Channels' || profileObject._id === MAIN_PROFILE_ID) {
|
||||
if (profileObject._id === MAIN_PROFILE_ID) {
|
||||
this.primaryProfile.subscriptions = this.primaryProfile.subscriptions.concat(profileObject.subscriptions)
|
||||
this.primaryProfile.subscriptions = this.primaryProfile.subscriptions.filter((sub, index) => {
|
||||
const profileIndex = this.primaryProfile.subscriptions.findIndex((x) => {
|
||||
|
|
|
@ -4,6 +4,8 @@ import FtSettingsSection from '../ft-settings-section/ft-settings-section.vue'
|
|||
import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue'
|
||||
import FtInputTags from '../../components/ft-input-tags/ft-input-tags.vue'
|
||||
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
|
||||
import { showToast } from '../../helpers/utils'
|
||||
import { checkYoutubeChannelId, findChannelTagInfo } from '../../helpers/channels'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PlayerSettings',
|
||||
|
@ -13,7 +15,18 @@ export default defineComponent({
|
|||
'ft-input-tags': FtInputTags,
|
||||
'ft-flex-box': FtFlexBox,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
channelHiderDisabled: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
backendOptions: function () {
|
||||
return {
|
||||
preference: this.$store.getters.getBackendPreference,
|
||||
fallback: this.$store.getters.getBackendFallback
|
||||
}
|
||||
},
|
||||
hideVideoViews: function () {
|
||||
return this.$store.getters.getHideVideoViews
|
||||
},
|
||||
|
@ -92,14 +105,20 @@ export default defineComponent({
|
|||
hideSubscriptionsLive: function () {
|
||||
return this.$store.getters.getHideSubscriptionsLive
|
||||
},
|
||||
hideSubscriptionsCommunity: function() {
|
||||
hideSubscriptionsCommunity: function () {
|
||||
return this.$store.getters.getHideSubscriptionsCommunity
|
||||
},
|
||||
showDistractionFreeTitles: function () {
|
||||
return this.$store.getters.getShowDistractionFreeTitles
|
||||
},
|
||||
channelsHidden: function () {
|
||||
return JSON.parse(this.$store.getters.getChannelsHidden)
|
||||
return JSON.parse(this.$store.getters.getChannelsHidden).map((ch) => {
|
||||
// Legacy support
|
||||
if (typeof ch === 'string') {
|
||||
return { name: ch, preferredName: '', icon: '' }
|
||||
}
|
||||
return ch
|
||||
})
|
||||
},
|
||||
hideSubscriptionsLiveTooltip: function () {
|
||||
return this.$t('Tooltips.Distraction Free Settings.Hide Subscriptions Live', {
|
||||
|
@ -109,6 +128,9 @@ export default defineComponent({
|
|||
})
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
this.verifyChannelsHidden()
|
||||
},
|
||||
methods: {
|
||||
handleHideRecommendedVideos: function (value) {
|
||||
if (value) {
|
||||
|
@ -117,9 +139,51 @@ export default defineComponent({
|
|||
|
||||
this.updateHideRecommendedVideos(value)
|
||||
},
|
||||
handleInvalidChannel: function () {
|
||||
showToast(this.$t('Settings.Distraction Free Settings.Hide Channels Invalid'))
|
||||
},
|
||||
handleChannelAPIError: function () {
|
||||
showToast(this.$t('Settings.Distraction Free Settings.Hide Channels API Error'))
|
||||
},
|
||||
handleChannelsHidden: function (value) {
|
||||
this.updateChannelsHidden(JSON.stringify(value))
|
||||
},
|
||||
handleChannelsExists: function () {
|
||||
showToast(this.$t('Settings.Distraction Free Settings.Hide Channels Already Exists'))
|
||||
},
|
||||
validateChannelId: function (text) {
|
||||
return checkYoutubeChannelId(text)
|
||||
},
|
||||
findChannelTagInfo: async function (text) {
|
||||
return await findChannelTagInfo(text, this.backendOptions)
|
||||
},
|
||||
verifyChannelsHidden: async function () {
|
||||
const channelsHiddenCpy = [...this.channelsHidden]
|
||||
|
||||
for (let i = 0; i < channelsHiddenCpy.length; i++) {
|
||||
const tag = this.channelsHidden[i]
|
||||
|
||||
// if channel has been processed and confirmed as non existent, skip
|
||||
if (tag.invalid) continue
|
||||
|
||||
// process if no preferred name and is possibly a YouTube ID
|
||||
if (tag.preferredName === '' && checkYoutubeChannelId(tag.name)) {
|
||||
this.channelHiderDisabled = true
|
||||
|
||||
const { preferredName, icon, iconHref, invalidId } = await this.findChannelTagInfo(tag.name)
|
||||
if (invalidId) {
|
||||
channelsHiddenCpy[i] = { name: tag.name, invalid: invalidId }
|
||||
} else {
|
||||
channelsHiddenCpy[i] = { name: tag.name, preferredName, icon, iconHref }
|
||||
}
|
||||
|
||||
// update on every tag in case it closes
|
||||
this.handleChannelsHidden(channelsHiddenCpy)
|
||||
}
|
||||
}
|
||||
|
||||
this.channelHiderDisabled = false
|
||||
},
|
||||
|
||||
...mapActions([
|
||||
'updateHideVideoViews',
|
||||
|
|
|
@ -230,12 +230,19 @@
|
|||
<br class="hide-on-mobile">
|
||||
<ft-flex-box>
|
||||
<ft-input-tags
|
||||
:disabled="channelHiderDisabled"
|
||||
:disabled-msg="$t('Settings.Distraction Free Settings.Hide Channels Disabled Message')"
|
||||
:label="$t('Settings.Distraction Free Settings.Hide Channels')"
|
||||
:placeholder="$t('Settings.Distraction Free Settings.Hide Channels Placeholder')"
|
||||
:tag-name-placeholder="$t('Settings.Distraction Free Settings.Hide Channels Placeholder')"
|
||||
:show-action-button="true"
|
||||
:tag-list="channelsHidden"
|
||||
:tooltip="$t('Tooltips.Distraction Free Settings.Hide Channels')"
|
||||
:validate-tag-name="validateChannelId"
|
||||
:find-tag-info="findChannelTagInfo"
|
||||
@invalid-name="handleInvalidChannel"
|
||||
@error-find-tag-info="handleChannelAPIError"
|
||||
@change="handleChannelsHidden"
|
||||
@already-exists="handleChannelsExists"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
</ft-settings-section>
|
||||
|
|
|
@ -11,17 +11,12 @@
|
|||
overflow: hidden;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
-webkit-transition: background 0.2s ease-out;
|
||||
-moz-transition: background 0.2s ease-out;
|
||||
-o-transition: background 0.2s ease-out;
|
||||
transition: background 0.2s ease-out;
|
||||
}
|
||||
|
||||
.bubblePadding:hover {
|
||||
background-color: var(--side-nav-hover-color);
|
||||
color: var(--side-nav-hover-text-color);
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,9 +11,7 @@
|
|||
display: block;
|
||||
float: var(--float-left-ltr-rtl-value);
|
||||
block-size: 10px;
|
||||
inset-inline-start: 5px;
|
||||
position: relative;
|
||||
inset-block-start: 8px;
|
||||
inline-size: 10px;
|
||||
}
|
||||
|
||||
|
@ -29,30 +27,45 @@
|
|||
}
|
||||
|
||||
.option-text {
|
||||
border-radius: 5px;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
padding-block: 5px;
|
||||
padding-inline: 25px;
|
||||
margin-inline-start: 10px;
|
||||
}
|
||||
|
||||
.option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-block-end: 10px;
|
||||
border-radius: 5px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
padding-block: 5px;
|
||||
padding-inline-start: 10px;
|
||||
margin-block-end: 10px;
|
||||
}
|
||||
|
||||
.option > img {
|
||||
margin-inline-start: 10px;
|
||||
block-size: 125px;
|
||||
}
|
||||
|
||||
.correct-option {
|
||||
border-color: #78da71;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.correct-option .filled-circle {
|
||||
background-color: #78da71;
|
||||
}
|
||||
|
||||
.incorrect-option {
|
||||
background-color: #dd4e4e;
|
||||
border-color: #dd4e4e;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.reveal-answer {
|
||||
text-align: center;
|
||||
.reveal-answer {
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.reveal-answer:hover > .option-text, .reveal-answer:focus > .option-text {
|
||||
.reveal-answer:hover, .reveal-answer:focus {
|
||||
background-color: var(--side-nav-hover-color)
|
||||
}
|
||||
|
|
|
@ -18,5 +18,11 @@ export default defineComponent({
|
|||
formattedVotes: function () {
|
||||
return formatNumber(this.data.totalVotes)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// Use smallest as it's resized to 125px anyways and they're usually all larger than that
|
||||
findSmallestPollImage: function (images) {
|
||||
return images.reduce((prev, img) => (img.height < prev.height) ? img : prev, images[0]).url
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
<div
|
||||
v-if="data.type === 'quiz'"
|
||||
class="option quiz-option"
|
||||
:class="revealAnswer && choice.isCorrect ? 'correct-option' : ''"
|
||||
>
|
||||
<span class="empty-circle">
|
||||
<span :class="revealAnswer && choice.isCorrect ? 'filled-circle' : ''" />
|
||||
</span>
|
||||
<div
|
||||
class="option-text"
|
||||
:class="revealAnswer && choice.isCorrect ? 'correct-option' : ''"
|
||||
>
|
||||
{{ choice.text }}
|
||||
</div>
|
||||
|
@ -29,6 +29,11 @@
|
|||
class="option poll-option"
|
||||
>
|
||||
<span class="empty-circle" />
|
||||
<img
|
||||
v-if="choice.image"
|
||||
:src="findSmallestPollImage(choice.image)"
|
||||
alt=""
|
||||
>
|
||||
<div class="option-text">
|
||||
{{ choice.text }}
|
||||
</div>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
}"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
:aria-expanded="dropdownShown"
|
||||
@click="handleIconClick"
|
||||
@mousedown="handleIconMouseDown"
|
||||
@keydown.enter.prevent="handleIconClick"
|
||||
|
@ -33,7 +34,6 @@
|
|||
v-if="dropdownOptions.length > 0"
|
||||
class="list"
|
||||
role="listbox"
|
||||
:aria-expanded="dropdownShown"
|
||||
>
|
||||
<li
|
||||
v-for="(option, index) in dropdownOptions"
|
||||
|
@ -71,7 +71,6 @@
|
|||
v-if="dropdownOptions.length > 0"
|
||||
class="list"
|
||||
role="listbox"
|
||||
:aria-expanded="dropdownShown"
|
||||
>
|
||||
<li
|
||||
v-for="(option, index) in dropdownOptions"
|
||||
|
|
|
@ -7,8 +7,13 @@
|
|||
inline-size: 60%;
|
||||
}
|
||||
|
||||
.disabledMsg {
|
||||
color: rgb(233, 255, 108);
|
||||
padding-block-end: 10px;
|
||||
}
|
||||
|
||||
.ft-tag-box ul {
|
||||
overflow: auto;
|
||||
overflow: visible;
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
@ -19,24 +24,36 @@
|
|||
background-color: var(--card-bg-color);
|
||||
margin: 5px;
|
||||
border-radius: 5px;
|
||||
display:flex;
|
||||
display: flex;
|
||||
float: var(--float-left-ltr-rtl-value);
|
||||
}
|
||||
|
||||
.ft-tag-box li>span {
|
||||
padding-block: 10px;
|
||||
padding-inline-start: 10px;
|
||||
padding-inline: 10px;
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
hyphens: auto;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.tag-icon {
|
||||
border-radius: 50%;
|
||||
block-size: 24px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.tag-icon-link {
|
||||
margin: auto;
|
||||
margin-inline-start: 10px;
|
||||
}
|
||||
|
||||
.removeTagButton {
|
||||
color: var(--primary-text-color);
|
||||
opacity: 0.5;
|
||||
padding: 10px;
|
||||
padding-inline-start: 0px;
|
||||
}
|
||||
|
||||
.removeTagButton:hover {
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
import { defineComponent } from 'vue'
|
||||
import FtInput from '../ft-input/ft-input.vue'
|
||||
import FtTooltip from '../ft-tooltip/ft-tooltip.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FtInputTags',
|
||||
components: {
|
||||
'ft-input': FtInput,
|
||||
'ft-tooltip': FtTooltip
|
||||
},
|
||||
props: {
|
||||
placeholder: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabledMsg: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
tagNamePlaceholder: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
|
@ -28,27 +34,48 @@ export default defineComponent({
|
|||
tooltip: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
validateTagName: {
|
||||
type: Function,
|
||||
default: (_) => true
|
||||
},
|
||||
findTagInfo: {
|
||||
type: Function,
|
||||
default: (_) => ({ preferredName: '', icon: '' }),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateTags: function (text, e) {
|
||||
// text entered add tag and update tag list
|
||||
const trimmedText = text.trim()
|
||||
if (!this.tagList.includes(trimmedText)) {
|
||||
const newList = this.tagList.slice(0)
|
||||
newList.push(trimmedText)
|
||||
this.$emit('change', newList)
|
||||
updateTags: async function (text, _e) {
|
||||
// get text without spaces after last '/' in url, if any
|
||||
const name = text.split('/').pop().trim()
|
||||
|
||||
if (!this.validateTagName(name)) {
|
||||
this.$emit('invalid-name')
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.tagList.some((tag) => tag.name === name)) {
|
||||
// tag info searching allow api calls to be used
|
||||
const { preferredName, icon, iconHref, err } = await this.findTagInfo(name)
|
||||
|
||||
if (err) {
|
||||
this.$emit('error-find-tag-info')
|
||||
return
|
||||
}
|
||||
|
||||
const newTag = { name, preferredName, icon, iconHref }
|
||||
this.$emit('change', [...this.tagList, newTag])
|
||||
} else {
|
||||
this.$emit('already-exists')
|
||||
}
|
||||
|
||||
// clear input box
|
||||
this.$refs.childinput.handleClearTextClick()
|
||||
this.$refs.tagNameInput.handleClearTextClick()
|
||||
},
|
||||
removeTag: function (tag) {
|
||||
// Remove tag from list
|
||||
const tagName = tag.trim()
|
||||
if (this.tagList.includes(tagName)) {
|
||||
const newList = this.tagList.slice(0)
|
||||
const index = newList.indexOf(tagName)
|
||||
newList.splice(index, 1)
|
||||
if (this.tagList.some((tmpTag) => tmpTag.name === tag.name)) {
|
||||
const newList = this.tagList.filter((tmpTag) => tmpTag.name !== tag.name)
|
||||
this.$emit('change', newList)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,16 @@
|
|||
<div
|
||||
class="ft-input-tags-component"
|
||||
>
|
||||
<div
|
||||
v-if="disabled"
|
||||
class="disabledMsg"
|
||||
>
|
||||
{{ disabledMsg }}
|
||||
</div>
|
||||
<ft-input
|
||||
ref="childinput"
|
||||
:placeholder="placeholder"
|
||||
ref="tagNameInput"
|
||||
:disabled="disabled"
|
||||
:placeholder="tagNamePlaceholder"
|
||||
:label="label"
|
||||
:show-label="true"
|
||||
:tooltip="tooltip"
|
||||
|
@ -13,15 +20,26 @@
|
|||
:force-action-button-icon-name="['fas', 'arrow-right']"
|
||||
@click="updateTags"
|
||||
/>
|
||||
|
||||
<div class="ft-tag-box">
|
||||
<ul>
|
||||
<li
|
||||
v-for="tag in tagList"
|
||||
:key="tag.id"
|
||||
>
|
||||
<span>{{ tag }}</span>
|
||||
<router-link
|
||||
v-if="tag.icon"
|
||||
:to="tag.iconHref ?? ''"
|
||||
class="tag-icon-link"
|
||||
>
|
||||
<img
|
||||
:src="tag.icon"
|
||||
alt=""
|
||||
class="tag-icon"
|
||||
>
|
||||
</router-link>
|
||||
<span>{{ (tag.preferredName) ? tag.preferredName : tag.name }}</span>
|
||||
<font-awesome-icon
|
||||
v-if="!disabled"
|
||||
:icon="['fas', 'fa-times']"
|
||||
class="removeTagButton"
|
||||
tabindex="0"
|
||||
|
|
|
@ -57,8 +57,6 @@
|
|||
border-radius: 100%;
|
||||
color: var(--primary-text-color);
|
||||
opacity: 0;
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
|
@ -74,8 +72,6 @@
|
|||
.clearInputTextButton.visible:active {
|
||||
background-color: var(--tertiary-text-color);
|
||||
color: var(--side-nav-active-text-color);
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
|
@ -172,8 +168,6 @@
|
|||
.inputAction.enabled:hover {
|
||||
background-color: var(--side-nav-hover-color);
|
||||
color: var(--side-nav-hover-text-color);
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
|
@ -184,8 +178,6 @@
|
|||
.inputAction.enabled:active {
|
||||
background-color: var(--tertiary-text-color);
|
||||
color: var(--side-nav-active-text-color);
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,13 @@ export default defineComponent({
|
|||
// Some component users like channel view will have this disabled
|
||||
if (!this.useChannelsHiddenPreference) { return [] }
|
||||
|
||||
return JSON.parse(this.$store.getters.getChannelsHidden)
|
||||
return JSON.parse(this.$store.getters.getChannelsHidden).map((ch) => {
|
||||
// Legacy support
|
||||
if (typeof ch === 'string') {
|
||||
return { name: ch, preferredName: '', icon: '' }
|
||||
}
|
||||
return ch
|
||||
})
|
||||
},
|
||||
hideUpcomingPremieres: function () {
|
||||
return this.$store.getters.getHideUpcomingPremieres
|
||||
|
@ -87,7 +93,7 @@ export default defineComponent({
|
|||
// hide upcoming
|
||||
return false
|
||||
}
|
||||
if (this.channelsHidden.includes(data.authorId) || this.channelsHidden.includes(data.author)) {
|
||||
if (this.channelsHidden.some(ch => ch.name === data.authorId) || this.channelsHidden.some(ch => ch.name === data.author)) {
|
||||
// hide videos by author
|
||||
return false
|
||||
}
|
||||
|
@ -101,7 +107,7 @@ export default defineComponent({
|
|||
data.author,
|
||||
data.authorId,
|
||||
]
|
||||
if (attrsToCheck.some(a => a != null && this.channelsHidden.includes(a))) {
|
||||
if (attrsToCheck.some(a => a != null && this.channelsHidden.some(ch => ch.name === a))) {
|
||||
// hide channels by author
|
||||
return false
|
||||
}
|
||||
|
@ -115,7 +121,7 @@ export default defineComponent({
|
|||
data.author,
|
||||
data.authorId,
|
||||
]
|
||||
if (attrsToCheck.some(a => a != null && this.channelsHidden.includes(a))) {
|
||||
if (attrsToCheck.some(a => a != null && this.channelsHidden.some(ch => ch.name === a))) {
|
||||
// hide playlists by author
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -58,12 +58,18 @@ export default defineComponent({
|
|||
// Some component users like channel view will have this disabled
|
||||
if (!this.useChannelsHiddenPreference) { return [] }
|
||||
|
||||
return JSON.parse(this.$store.getters.getChannelsHidden)
|
||||
return JSON.parse(this.$store.getters.getChannelsHidden).map((ch) => {
|
||||
// Legacy support
|
||||
if (typeof ch === 'string') {
|
||||
return { name: ch, preferredName: '', icon: '' }
|
||||
}
|
||||
return ch
|
||||
})
|
||||
},
|
||||
|
||||
shouldBeVisible() {
|
||||
return !(this.channelsHidden.includes(this.data.authorId) ||
|
||||
this.channelsHidden.includes(this.data.author))
|
||||
return !(this.channelsHidden.some(ch => ch.name === this.data.authorId) ||
|
||||
this.channelsHidden.some(ch => ch.name === this.data.author))
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
|
|
@ -234,8 +234,25 @@ export default defineComponent({
|
|||
{
|
||||
label: this.$t('Video.Open Channel in Invidious'),
|
||||
value: 'openInvidiousChannel'
|
||||
},
|
||||
{
|
||||
type: 'divider'
|
||||
}
|
||||
)
|
||||
|
||||
const hiddenChannels = JSON.parse(this.$store.getters.getChannelsHidden)
|
||||
const channelShouldBeHidden = hiddenChannels.some(c => c === this.channelId)
|
||||
if (channelShouldBeHidden) {
|
||||
options.push({
|
||||
label: this.$t('Video.Unhide Channel'),
|
||||
value: 'unhideChannel'
|
||||
})
|
||||
} else {
|
||||
options.push({
|
||||
label: this.$t('Video.Hide Channel'),
|
||||
value: 'hideChannel'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,7 +403,7 @@ export default defineComponent({
|
|||
const videoId = this.id
|
||||
const data = await deArrowData(this.id)
|
||||
const cacheData = { videoId, title: null }
|
||||
if (Array.isArray(data?.titles) && data.titles.length > 0 && (data.titles[0].locked || data.titles[0].votes > 0)) {
|
||||
if (Array.isArray(data?.titles) && data.titles.length > 0 && (data.titles[0].locked || data.titles[0].votes >= 0)) {
|
||||
cacheData.title = data.titles[0].title
|
||||
}
|
||||
|
||||
|
@ -461,6 +478,12 @@ export default defineComponent({
|
|||
case 'openInvidiousChannel':
|
||||
openExternalLink(this.invidiousChannelUrl)
|
||||
break
|
||||
case 'hideChannel':
|
||||
this.hideChannel(this.channelName, this.channelId)
|
||||
break
|
||||
case 'unhideChannel':
|
||||
this.unhideChannel(this.channelName, this.channelId)
|
||||
break
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -648,12 +671,28 @@ export default defineComponent({
|
|||
showToast(this.$t('Video.Video has been removed from your saved list'))
|
||||
},
|
||||
|
||||
hideChannel: function(channelName, channelId) {
|
||||
const hiddenChannels = JSON.parse(this.$store.getters.getChannelsHidden)
|
||||
hiddenChannels.push(channelId)
|
||||
this.updateChannelsHidden(JSON.stringify(hiddenChannels))
|
||||
|
||||
showToast(this.$t('Channel Hidden', { channel: channelName }))
|
||||
},
|
||||
|
||||
unhideChannel: function(channelName, channelId) {
|
||||
const hiddenChannels = JSON.parse(this.$store.getters.getChannelsHidden)
|
||||
this.updateChannelsHidden(JSON.stringify(hiddenChannels.filter(c => c !== channelId)))
|
||||
|
||||
showToast(this.$t('Channel Unhidden', { channel: channelName }))
|
||||
},
|
||||
|
||||
...mapActions([
|
||||
'openInExternalPlayer',
|
||||
'updateHistory',
|
||||
'removeFromHistory',
|
||||
'addVideo',
|
||||
'removeVideo'
|
||||
'removeVideo',
|
||||
'updateChannelsHidden'
|
||||
])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -4,17 +4,12 @@
|
|||
padding-block: 10px 30px;
|
||||
padding-inline: 10px;
|
||||
cursor: pointer;
|
||||
-webkit-transition: background 0.2s ease-out;
|
||||
-moz-transition: background 0.2s ease-out;
|
||||
-o-transition: background 0.2s ease-out;
|
||||
transition: background 0.2s ease-out;
|
||||
}
|
||||
|
||||
.bubblePadding:hover {
|
||||
background-color: var(--side-nav-hover-color);
|
||||
color: var(--side-nav-hover-text-color);
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { defineComponent } from 'vue'
|
||||
import { sanitizeForHtmlId } from '../../helpers/accessibility'
|
||||
import { MAIN_PROFILE_ID } from '../../../constants'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FtProfileBubble',
|
||||
|
@ -22,23 +23,17 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
isMainProfile: function () {
|
||||
return this.profileId === MAIN_PROFILE_ID
|
||||
},
|
||||
sanitizedId: function() {
|
||||
return 'profileBubble' + sanitizeForHtmlId(this.profileId)
|
||||
},
|
||||
profileInitial: function () {
|
||||
return this?.profileName?.length > 0 ? Array.from(this.profileName)[0].toUpperCase() : ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goToProfile: function (event) {
|
||||
if (event instanceof KeyboardEvent) {
|
||||
if (event.target.getAttribute('role') === 'link' && event.key !== 'Enter') {
|
||||
return
|
||||
}
|
||||
}
|
||||
this.$router.push({
|
||||
path: `/settings/profile/edit/${this.profileId}`
|
||||
})
|
||||
return this?.profileName?.length > 0 ? Array.from(this.translatedProfileName)[0].toUpperCase() : ''
|
||||
},
|
||||
translatedProfileName: function () {
|
||||
return this.isMainProfile ? this.$t('Profile.All Channels') : this.profileName
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
<div
|
||||
class="bubblePadding"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
:aria-labelledby="sanitizedId"
|
||||
@click="goToProfile"
|
||||
@keydown.space.prevent="goToProfile($event)"
|
||||
@keydown.enter.prevent="goToProfile($event)"
|
||||
@click="$emit('click')"
|
||||
@keydown.space.enter.prevent="$emit('click')"
|
||||
>
|
||||
<div
|
||||
class="bubble"
|
||||
|
@ -19,7 +19,7 @@
|
|||
:id="sanitizedId"
|
||||
class="profileName"
|
||||
>
|
||||
{{ profileName }}
|
||||
{{ translatedProfileName }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
h2, .selectedCount {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card {
|
||||
inline-size: 85%;
|
||||
margin-block: 0 15px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 680px) {
|
||||
.card {
|
||||
inline-size: 90%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@ export default defineComponent({
|
|||
})
|
||||
|
||||
this.subscriptions = subscriptions
|
||||
this.selectNone()
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
|
@ -114,20 +115,13 @@ export default defineComponent({
|
|||
handleDeletePromptClick: function (value) {
|
||||
if (value !== 'no' && value !== null) {
|
||||
if (this.isMainProfile) {
|
||||
const channelsToRemove = this.subscriptions.filter((channel) => {
|
||||
return channel.selected
|
||||
})
|
||||
|
||||
this.subscriptions = this.subscriptions.filter((channel) => {
|
||||
return !channel.selected
|
||||
})
|
||||
const channelsToRemove = this.subscriptions.filter((channel) => channel.selected)
|
||||
this.subscriptions = this.subscriptions.filter((channel) => !channel.selected)
|
||||
|
||||
this.profileList.forEach((x) => {
|
||||
const profile = deepCopy(x)
|
||||
profile.subscriptions = profile.subscriptions.filter((channel) => {
|
||||
const index = channelsToRemove.findIndex((y) => {
|
||||
return y.id === channel.id
|
||||
})
|
||||
const index = channelsToRemove.findIndex((y) => y.id === channel.id)
|
||||
|
||||
return index === -1
|
||||
})
|
||||
|
@ -157,9 +151,7 @@ export default defineComponent({
|
|||
|
||||
handleChannelClick: function (index) {
|
||||
this.subscriptions[index].selected = !this.subscriptions[index].selected
|
||||
this.selectedLength = this.subscriptions.filter((channel) => {
|
||||
return channel.selected
|
||||
}).length
|
||||
this.selectedLength = this.subscriptions.filter((channel) => channel.selected).length
|
||||
},
|
||||
|
||||
selectAll: function () {
|
||||
|
@ -174,9 +166,7 @@ export default defineComponent({
|
|||
return channel
|
||||
})
|
||||
|
||||
this.selectedLength = this.subscriptions.filter((channel) => {
|
||||
return channel.selected
|
||||
}).length
|
||||
this.selectedLength = this.subscriptions.filter((channel) => channel.selected).length
|
||||
},
|
||||
|
||||
selectNone: function () {
|
||||
|
@ -191,9 +181,7 @@ export default defineComponent({
|
|||
return channel
|
||||
})
|
||||
|
||||
this.selectedLength = this.subscriptions.filter((channel) => {
|
||||
return channel.selected
|
||||
}).length
|
||||
this.selectedLength = this.subscriptions.filter((channel) => channel.selected).length
|
||||
},
|
||||
|
||||
...mapActions([
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<h2>
|
||||
{{ $t("Profile.Subscription List") }}
|
||||
</h2>
|
||||
<p>
|
||||
<p class="selectedCount">
|
||||
{{ selectedText }}
|
||||
</p>
|
||||
<ft-flex-box>
|
||||
|
|
|
@ -1,46 +1,89 @@
|
|||
.card {
|
||||
inline-size: 85%;
|
||||
margin-block: 0 15px;
|
||||
margin-inline: auto;
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-block: 0 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.message {
|
||||
color: var(--tertiary-text-color);
|
||||
}
|
||||
|
||||
.profileName {
|
||||
inline-size: 400px;
|
||||
.profileName, .colorSelection {
|
||||
margin: auto;
|
||||
inline-size: 350px;
|
||||
}
|
||||
|
||||
.bottomMargin {
|
||||
margin-block-end: 30px;
|
||||
.profileEdit {
|
||||
flex-direction: column;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.profileEdit > * {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.colorOptions {
|
||||
max-inline-size: 1000px;
|
||||
margin-block: 0 30px;
|
||||
margin-inline: auto;
|
||||
justify-content: center;
|
||||
min-inline-size: 250px;
|
||||
}
|
||||
|
||||
.customColorSection {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
margin-block-start: 5px;
|
||||
}
|
||||
|
||||
.colorOption {
|
||||
inline-size: 100px;
|
||||
block-size: 100px;
|
||||
margin: 10px;
|
||||
inline-size: 40px;
|
||||
block-size: 40px;
|
||||
margin: 2px;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
-webkit-border-radius: 50%;
|
||||
}
|
||||
|
||||
.initial {
|
||||
font-size: 50px;
|
||||
line-height: 1em;
|
||||
font-size: 37.5px;
|
||||
text-align: center;
|
||||
padding-block: 25px;
|
||||
padding-inline: 0px;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.colorOption:has(.initial) {
|
||||
inline-size: 75px;
|
||||
block-size: 75px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.profilePreviewSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.secondEditRow {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.card {
|
||||
inline-size: 85%;
|
||||
margin-block: 0 60px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 680px) {
|
||||
.card {
|
||||
inline-size: 90%;
|
||||
|
|
|
@ -19,13 +19,17 @@ export default defineComponent({
|
|||
'ft-button': FtButton
|
||||
},
|
||||
props: {
|
||||
profile: {
|
||||
type: Object,
|
||||
isMainProfile: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
isNew: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
profile: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
|
@ -42,14 +46,11 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
isMainProfile: function () {
|
||||
return this.profileId === MAIN_PROFILE_ID
|
||||
},
|
||||
colorValues: function () {
|
||||
return colors.map(color => color.value)
|
||||
},
|
||||
profileInitial: function () {
|
||||
return this?.profileName?.length > 0 ? Array.from(this.profileName)[0].toUpperCase() : ''
|
||||
return this?.profileName?.length > 0 ? Array.from(this.translatedProfileName)[0].toUpperCase() : ''
|
||||
},
|
||||
activeProfile: function () {
|
||||
return this.$store.getters.getActiveProfile
|
||||
|
@ -65,6 +66,15 @@ export default defineComponent({
|
|||
this.$t('Yes'),
|
||||
this.$t('No')
|
||||
]
|
||||
},
|
||||
editOrCreateProfileLabel: function () {
|
||||
return this.isNew ? this.$t('Profile.Create Profile') : this.$t('Profile.Edit Profile')
|
||||
},
|
||||
editOrCreateProfileNameLabel: function () {
|
||||
return this.isNew ? this.$t('Profile.Create Profile Name') : this.$t('Profile.Edit Profile Name')
|
||||
},
|
||||
translatedProfileName: function () {
|
||||
return this.isMainProfile ? this.$t('Profile.All Channels') : this.profileName
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -73,7 +83,7 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
created: function () {
|
||||
this.profileId = this.$route.params.id
|
||||
this.profileId = this.profile._id
|
||||
this.profileName = this.profile.name
|
||||
this.profileBgColor = this.profile.bgColor
|
||||
this.profileTextColor = this.profile.textColor
|
||||
|
@ -110,9 +120,7 @@ export default defineComponent({
|
|||
if (this.isNew) {
|
||||
this.createProfile(profile)
|
||||
showToast(this.$t('Profile.Profile has been created'))
|
||||
this.$router.push({
|
||||
path: '/settings/profile/'
|
||||
})
|
||||
this.$emit('new-profile-created')
|
||||
} else {
|
||||
this.updateProfile(profile)
|
||||
showToast(this.$t('Profile.Profile has been updated'))
|
||||
|
@ -121,7 +129,7 @@ export default defineComponent({
|
|||
|
||||
setDefaultProfile: function () {
|
||||
this.updateDefaultProfile(this.profileId)
|
||||
const message = this.$t('Profile.Your default profile has been set to {profile}', { profile: this.profileName })
|
||||
const message = this.$t('Profile.Your default profile has been set to {profile}', { profile: this.translatedProfileName })
|
||||
showToast(message)
|
||||
},
|
||||
|
||||
|
@ -132,7 +140,7 @@ export default defineComponent({
|
|||
|
||||
this.removeProfile(this.profileId)
|
||||
|
||||
const message = this.$t('Profile.Removed {profile} from your profiles', { profile: this.profileName })
|
||||
const message = this.$t('Profile.Removed {profile} from your profiles', { profile: this.translatedProfileName })
|
||||
showToast(message)
|
||||
|
||||
if (this.defaultProfile === this.profileId) {
|
||||
|
@ -140,9 +148,7 @@ export default defineComponent({
|
|||
showToast(this.$t('Profile.Your default profile has been changed to your primary profile'))
|
||||
}
|
||||
|
||||
this.$router.push({
|
||||
path: '/settings/profile/'
|
||||
})
|
||||
this.$emit('profile-deleted')
|
||||
},
|
||||
|
||||
...mapActions([
|
||||
|
|
|
@ -1,95 +1,97 @@
|
|||
<template>
|
||||
<div>
|
||||
<ft-card class="card">
|
||||
<h2>{{ $t("Profile.Edit Profile") }}</h2>
|
||||
<ft-flex-box>
|
||||
<ft-input
|
||||
class="profileName"
|
||||
placeholder="Profile Name"
|
||||
:value="profileName"
|
||||
:show-action-button="false"
|
||||
@input="e => profileName = e"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<h3>{{ $t("Profile.Color Picker") }}</h3>
|
||||
<ft-flex-box
|
||||
class="bottomMargin colorOptions"
|
||||
>
|
||||
<div
|
||||
v-for="(color, index) in colorValues"
|
||||
:key="index"
|
||||
class="colorOption"
|
||||
:title="color + ' ' + $t('Profile.Custom Color')"
|
||||
:style="{ background: color }"
|
||||
tabindex="0"
|
||||
@click="profileBgColor = color"
|
||||
@keydown.space.prevent="profileBgColor = color"
|
||||
@keydown.enter.prevent="profileBgColor = color"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<ft-flex-box
|
||||
class="bottomMargin"
|
||||
>
|
||||
<h2>{{ editOrCreateProfileLabel }}</h2>
|
||||
<ft-flex-box class="profileEdit">
|
||||
<div>
|
||||
<label for="colorPicker">{{ $t("Profile.Custom Color") }}</label>
|
||||
<input
|
||||
id="colorPicker"
|
||||
v-model="profileBgColor"
|
||||
type="color"
|
||||
<h3>{{ $t("Profile.Color Picker") }}</h3>
|
||||
<ft-flex-box
|
||||
class="colorOptions"
|
||||
>
|
||||
<div
|
||||
v-for="(color, index) in colorValues"
|
||||
:key="index"
|
||||
class="colorOption"
|
||||
:title="color + ' ' + $t('Profile.Custom Color')"
|
||||
:style="{ background: color }"
|
||||
tabindex="0"
|
||||
@click="profileBgColor = color"
|
||||
@keydown.space.prevent="profileBgColor = color"
|
||||
@keydown.enter.prevent="profileBgColor = color"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<div class="customColorSection">
|
||||
<label for="colorPicker">{{ $t("Profile.Custom Color") }}</label>
|
||||
<input
|
||||
id="colorPicker"
|
||||
v-model="profileBgColor"
|
||||
type="color"
|
||||
>
|
||||
</div>
|
||||
<ft-input
|
||||
class="colorSelection"
|
||||
placeholder=""
|
||||
:value="profileBgColor"
|
||||
:show-action-button="false"
|
||||
:disabled="true"
|
||||
/>
|
||||
</div>
|
||||
</ft-flex-box>
|
||||
<ft-flex-box>
|
||||
<ft-input
|
||||
class="profileName"
|
||||
placeholder=""
|
||||
:value="profileBgColor"
|
||||
:show-action-button="false"
|
||||
:disabled="true"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<h3>{{ $t("Profile.Profile Preview") }}</h3>
|
||||
<ft-flex-box
|
||||
class="bottomMargin"
|
||||
>
|
||||
<div
|
||||
class="colorOption"
|
||||
:style="{ background: profileBgColor, color: profileTextColor }"
|
||||
style="cursor: default"
|
||||
>
|
||||
<div
|
||||
class="initial"
|
||||
>
|
||||
{{ profileInitial }}
|
||||
<div class="secondEditRow">
|
||||
<div>
|
||||
<h3>{{ editOrCreateProfileNameLabel }}</h3>
|
||||
<ft-input
|
||||
class="profileName"
|
||||
:placeholder="$t('Profile.Profile Name')"
|
||||
:disabled="isMainProfile"
|
||||
:value="translatedProfileName"
|
||||
:show-action-button="false"
|
||||
@input="e => profileName = e"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h3>{{ $t("Profile.Profile Preview") }}</h3>
|
||||
<div class="profilePreviewSection">
|
||||
<div
|
||||
class="colorOption"
|
||||
:style="{ background: profileBgColor, color: profileTextColor }"
|
||||
style="cursor: default"
|
||||
>
|
||||
<div
|
||||
class="initial"
|
||||
>
|
||||
{{ profileInitial }}
|
||||
</div>
|
||||
</div>
|
||||
<ft-flex-box>
|
||||
<ft-button
|
||||
v-if="isNew"
|
||||
:label="$t('Profile.Create Profile')"
|
||||
@click="saveProfile"
|
||||
/>
|
||||
<template
|
||||
v-else
|
||||
>
|
||||
<ft-button
|
||||
:label="$t('Profile.Update Profile')"
|
||||
@click="saveProfile"
|
||||
/>
|
||||
<ft-button
|
||||
:label="$t('Profile.Make Default Profile')"
|
||||
@click="setDefaultProfile"
|
||||
/>
|
||||
<ft-button
|
||||
v-if="!isMainProfile"
|
||||
:label="$t('Profile.Delete Profile')"
|
||||
text-color="var(--text-with-main-color)"
|
||||
background-color="var(--primary-color)"
|
||||
@click="openDeletePrompt"
|
||||
/>
|
||||
</template>
|
||||
</ft-flex-box>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ft-flex-box>
|
||||
<ft-flex-box>
|
||||
<ft-button
|
||||
v-if="isNew"
|
||||
:label="$t('Profile.Create Profile')"
|
||||
@click="saveProfile"
|
||||
/>
|
||||
<template
|
||||
v-else
|
||||
>
|
||||
<ft-button
|
||||
:label="$t('Profile.Update Profile')"
|
||||
@click="saveProfile"
|
||||
/>
|
||||
<ft-button
|
||||
:label="$t('Profile.Make Default Profile')"
|
||||
@click="setDefaultProfile"
|
||||
/>
|
||||
<ft-button
|
||||
v-if="!isMainProfile"
|
||||
:label="$t('Profile.Delete Profile')"
|
||||
text-color="var(--text-with-main-color)"
|
||||
background-color="var(--primary-color)"
|
||||
@click="openDeletePrompt"
|
||||
/>
|
||||
</template>
|
||||
</ft-flex-box>
|
||||
</ft-card>
|
||||
<ft-prompt
|
||||
v-if="showDeletePrompt"
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
.card {
|
||||
inline-size: 85%;
|
||||
margin-block: 0 30px;
|
||||
margin-inline: auto;
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.selected {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
:deep(.select-label) {
|
||||
inline-size: 95%;
|
||||
.card {
|
||||
inline-size: 85%;
|
||||
margin-block: 0 15px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
:deep(.select) {
|
||||
text-align-last: center;
|
||||
@media only screen and (max-width: 680px) {
|
||||
.card {
|
||||
inline-size: 90%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import FtButton from '../../components/ft-button/ft-button.vue'
|
|||
import FtSelect from '../ft-select/ft-select.vue'
|
||||
import { deepCopy, showToast } from '../../helpers/utils'
|
||||
import { youtubeImageUrlToInvidious } from '../../helpers/api/invidious'
|
||||
import { MAIN_PROFILE_ID } from '../../../constants'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FtProfileFilterChannelsList',
|
||||
|
@ -46,17 +47,20 @@ export default defineComponent({
|
|||
return this.$store.getters.getProfileList
|
||||
},
|
||||
profileNameList: function () {
|
||||
return this.profileList.flatMap((profile) => profile.name !== this.profile.name ? [profile.name] : [])
|
||||
return this.profileList.flatMap((profile) => profile.name !== this.profile.name ? [this.translatedProfileName(profile)] : [])
|
||||
},
|
||||
selectedText: function () {
|
||||
return this.$t('Profile.{number} selected', { number: this.selectedLength })
|
||||
},
|
||||
locale: function () {
|
||||
return this.$i18n.locale.replace('_', '-')
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
profile: 'updateChannelList',
|
||||
profile: function () {
|
||||
this.updateChannelList()
|
||||
this.selectNone()
|
||||
},
|
||||
filteredProfileIndex: 'updateChannelList'
|
||||
},
|
||||
mounted: function () {
|
||||
|
@ -80,7 +84,9 @@ export default defineComponent({
|
|||
},
|
||||
methods: {
|
||||
updateChannelList () {
|
||||
this.channels = deepCopy(this.profileList[this.filteredProfileIndex].subscriptions).sort((a, b) => {
|
||||
const filterProfileName = this.profileNameList[this.filteredProfileIndex]
|
||||
const filterProfile = this.profileList.find((profile) => this.translatedProfileName(profile) === filterProfileName)
|
||||
this.channels = deepCopy(filterProfile.subscriptions).sort((a, b) => {
|
||||
return a.name?.toLowerCase().localeCompare(b.name?.toLowerCase(), this.locale)
|
||||
}).filter((channel) => {
|
||||
const index = this.profile.subscriptions.findIndex((sub) => {
|
||||
|
@ -105,7 +111,8 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
handleProfileFilterChange: function (change) {
|
||||
this.filteredProfileIndex = this.profileList.findIndex(profile => profile.name === change)
|
||||
this.selectNone()
|
||||
this.filteredProfileIndex = this.profileNameList.indexOf(change)
|
||||
},
|
||||
|
||||
addChannelToProfile: function () {
|
||||
|
@ -158,6 +165,10 @@ export default defineComponent({
|
|||
}).length
|
||||
},
|
||||
|
||||
translatedProfileName: function (profile) {
|
||||
return profile._id === MAIN_PROFILE_ID ? this.$t('Profile.All Channels') : profile.name
|
||||
},
|
||||
|
||||
...mapActions([
|
||||
'updateProfile'
|
||||
])
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<ft-flex-box>
|
||||
<ft-select
|
||||
:placeholder="$t('Profile.Profile Filter')"
|
||||
:value="profileNameList[0]"
|
||||
:value="profileNameList[filteredProfileIndex]"
|
||||
:select-names="profileNameList"
|
||||
:select-values="profileNameList"
|
||||
@change="handleProfileFilterChange"
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
inset-block-start: 60px;
|
||||
inset-inline-end: 10px;
|
||||
min-inline-size: 250px;
|
||||
block-size: 400px;
|
||||
block-size: auto;
|
||||
padding: 5px;
|
||||
background-color: var(--card-bg-color);
|
||||
box-shadow: 0 0 4px var(--scrollbar-color-hover);
|
||||
|
@ -35,24 +35,33 @@
|
|||
|
||||
.profileWrapper {
|
||||
margin-block-start: 60px;
|
||||
block-size: 340px;
|
||||
block-size: auto;
|
||||
overflow-y: auto;
|
||||
/*
|
||||
profile list max height: 90% of window size - 100 px. It's scaled to be 340px on 800x600 resolution.
|
||||
Offset of 100px is to compensate for the fixed size of elements above the list, which takes more screen space on lower resolutions
|
||||
*/
|
||||
max-block-size: calc(90vh - 100px);
|
||||
min-block-size: 340px;
|
||||
}
|
||||
|
||||
/* Navbar changes position to horizontal with this media rule.
|
||||
Height adjust for profile list so it won't cover navbar. */
|
||||
@media only screen and (max-width: 680px){
|
||||
.profileWrapper {
|
||||
max-block-size: calc(95vh - 180px);
|
||||
}
|
||||
}
|
||||
|
||||
.profile {
|
||||
cursor: pointer;
|
||||
block-size: 50px;
|
||||
-webkit-transition: background 0.2s ease-out;
|
||||
-moz-transition: background 0.2s ease-out;
|
||||
-o-transition: background 0.2s ease-out;
|
||||
transition: background 0.2s ease-out;
|
||||
}
|
||||
|
||||
.profile:hover {
|
||||
background-color: var(--side-nav-hover-color);
|
||||
color: var(--side-nav-hover-text-color);
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import { mapActions } from 'vuex'
|
|||
import FtCard from '../../components/ft-card/ft-card.vue'
|
||||
import FtIconButton from '../../components/ft-icon-button/ft-icon-button.vue'
|
||||
import { showToast } from '../../helpers/utils'
|
||||
import { MAIN_PROFILE_ID } from '../../../constants'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FtProfileSelector',
|
||||
|
@ -26,15 +27,19 @@ export default defineComponent({
|
|||
},
|
||||
activeProfileInitial: function () {
|
||||
// use Array.from, so that emojis don't get split up into individual character codes
|
||||
return this.activeProfile?.name?.length > 0 ? Array.from(this.activeProfile.name)[0].toUpperCase() : ''
|
||||
return this.activeProfile?.name?.length > 0 ? Array.from(this.translatedProfileName(this.activeProfile))[0].toUpperCase() : ''
|
||||
},
|
||||
profileInitials: function () {
|
||||
return this.profileList.map((profile) => {
|
||||
return profile?.name?.length > 0 ? Array.from(profile.name)[0].toUpperCase() : ''
|
||||
return profile?.name?.length > 0 ? Array.from(this.translatedProfileName(profile))[0].toUpperCase() : ''
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isActiveProfile: function (profile) {
|
||||
return profile._id === this.activeProfile._id
|
||||
},
|
||||
|
||||
toggleProfileList: function () {
|
||||
this.profileListShown = !this.profileListShown
|
||||
|
||||
|
@ -77,13 +82,17 @@ export default defineComponent({
|
|||
if (targetProfile) {
|
||||
this.updateActiveProfile(targetProfile._id)
|
||||
|
||||
showToast(this.$t('Profile.{profile} is now the active profile', { profile: profile.name }))
|
||||
showToast(this.$t('Profile.{profile} is now the active profile', { profile: this.translatedProfileName(profile) }))
|
||||
}
|
||||
}
|
||||
|
||||
this.profileListShown = false
|
||||
},
|
||||
|
||||
translatedProfileName: function (profile) {
|
||||
return profile._id === MAIN_PROFILE_ID ? this.$t('Profile.All Channels') : profile.name
|
||||
},
|
||||
|
||||
...mapActions([
|
||||
'updateActiveProfile'
|
||||
])
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
:style="{ background: activeProfile.bgColor, color: activeProfile.textColor }"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
:aria-expanded="profileListShown"
|
||||
aria-controls="profileSelectorList"
|
||||
@click="toggleProfileList"
|
||||
@mousedown="handleIconMouseDown"
|
||||
@keydown.space.prevent="toggleProfileList"
|
||||
|
@ -19,6 +21,7 @@
|
|||
</div>
|
||||
<ft-card
|
||||
v-show="profileListShown"
|
||||
id="profileSelectorList"
|
||||
ref="profileList"
|
||||
class="profileList"
|
||||
tabindex="-1"
|
||||
|
@ -46,7 +49,7 @@
|
|||
:key="index"
|
||||
class="profile"
|
||||
:aria-labelledby="'profile-' + index + '-name'"
|
||||
aria-selected="false"
|
||||
:aria-selected="isActiveProfile(profile)"
|
||||
tabindex="0"
|
||||
role="option"
|
||||
@click="setActiveProfile(profile)"
|
||||
|
@ -66,7 +69,7 @@
|
|||
:id="'profile-' + index + '-name'"
|
||||
class="profileName"
|
||||
>
|
||||
{{ profile.name }}
|
||||
{{ translatedProfileName(profile) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { defineComponent } from 'vue'
|
||||
import { mapActions } from 'vuex'
|
||||
import FtCard from '../../components/ft-card/ft-card.vue'
|
||||
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
|
||||
import FtButton from '../../components/ft-button/ft-button.vue'
|
||||
import { Injectables } from '../../../constants'
|
||||
import { sanitizeForHtmlId } from '../../helpers/accessibility'
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -12,9 +12,6 @@ export default defineComponent({
|
|||
'ft-flex-box': FtFlexBox,
|
||||
'ft-button': FtButton
|
||||
},
|
||||
inject: {
|
||||
showOutlines: Injectables.SHOW_OUTLINES
|
||||
},
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
|
@ -101,6 +98,10 @@ export default defineComponent({
|
|||
const direction = (e.key === 'ArrowLeft') ? -1 : 1
|
||||
this.focusItem(parseInt(currentIndex) + direction)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
...mapActions([
|
||||
'showOutlines'
|
||||
])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -7,5 +7,10 @@ export default defineComponent({
|
|||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
allSettingsSectionsExpandedByDefault: function () {
|
||||
return this.$store.getters.getAllSettingsSectionsExpandedByDefault
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -27,9 +27,8 @@
|
|||
}
|
||||
|
||||
.sectionLine {
|
||||
background-color: var(--primary-color);
|
||||
border: 0;
|
||||
block-size: 2px;
|
||||
border-block-start: 2px solid var(--primary-color);
|
||||
margin-block-start: -1px;
|
||||
inline-size: 100%;
|
||||
}
|
||||
|
@ -44,6 +43,7 @@
|
|||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
margin-inline-start: 2%;
|
||||
margin-block: 0.5em;
|
||||
}
|
||||
|
||||
:deep(.switchGrid) {
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<template>
|
||||
<details class="settingsSection">
|
||||
<details
|
||||
:open="allSettingsSectionsExpandedByDefault ? 'true' : null"
|
||||
class="settingsSection"
|
||||
>
|
||||
<summary class="sectionHeader">
|
||||
<h3 class="sectionTitle">
|
||||
{{ title }}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
/* Ensures style here overrides style of .btn */
|
||||
.subscribeButton.btn {
|
||||
align-self: center;
|
||||
block-size: 50%;
|
||||
margin-block-end: 10px;
|
||||
min-inline-size: 150px;
|
||||
white-space: initial;
|
||||
}
|
|
@ -25,29 +25,58 @@ export default defineComponent({
|
|||
type: String,
|
||||
required: true
|
||||
},
|
||||
hideProfileDropdownToggle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
openDropdownOnSubscribe: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
subscriptionCountText: {
|
||||
default: '',
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
isProfileDropdownOpen: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
profileInitials: function () {
|
||||
return this.profileDisplayList.map((profile) => {
|
||||
return profile?.name?.length > 0 ? Array.from(profile.name)[0].toUpperCase() : ''
|
||||
})
|
||||
},
|
||||
|
||||
profileList: function () {
|
||||
return this.$store.getters.getProfileList
|
||||
},
|
||||
|
||||
/* sort by 'All Channels' -> active profile -> unsubscribed channels -> subscribed channels */
|
||||
profileDisplayList: function () {
|
||||
const mainProfileAndActiveProfile = [this.profileList[0]]
|
||||
if (this.activeProfile._id !== MAIN_PROFILE_ID) {
|
||||
mainProfileAndActiveProfile.push(this.activeProfile)
|
||||
}
|
||||
|
||||
return [
|
||||
...mainProfileAndActiveProfile,
|
||||
...this.profileList.filter((profile, i) =>
|
||||
i !== 0 && !this.isActiveProfile(profile) && !this.isProfileSubscribed(profile)),
|
||||
...this.profileList.filter((profile, i) =>
|
||||
i !== 0 && !this.isActiveProfile(profile) && this.isProfileSubscribed(profile))
|
||||
]
|
||||
},
|
||||
|
||||
activeProfile: function () {
|
||||
return this.$store.getters.getActiveProfile
|
||||
},
|
||||
|
||||
subscriptionInfo: function () {
|
||||
return this.activeProfile.subscriptions.find((channel) => {
|
||||
return channel.id === this.channelId
|
||||
}) ?? null
|
||||
},
|
||||
|
||||
isSubscribed: function () {
|
||||
return this.subscriptionInfo !== null
|
||||
return this.subscriptionInfoForProfile(this.activeProfile)
|
||||
},
|
||||
|
||||
hideChannelSubscriptions: function () {
|
||||
|
@ -55,23 +84,27 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
subscribedText: function () {
|
||||
let subscribedValue = (this.isSubscribed ? this.$t('Channel.Unsubscribe') : this.$t('Channel.Subscribe')).toUpperCase()
|
||||
let subscribedValue = (this.isProfileSubscribed(this.activeProfile) ? this.$t('Channel.Unsubscribe') : this.$t('Channel.Subscribe')).toUpperCase()
|
||||
if (this.subscriptionCountText !== '' && !this.hideChannelSubscriptions) {
|
||||
subscribedValue += ' ' + this.subscriptionCountText
|
||||
}
|
||||
return subscribedValue
|
||||
},
|
||||
|
||||
isProfileDropdownEnabled: function () {
|
||||
return !this.hideProfileDropdownToggle && this.profileList.length > 1
|
||||
}
|
||||
|
||||
},
|
||||
methods: {
|
||||
handleSubscription: function () {
|
||||
handleSubscription: function (profile = this.activeProfile) {
|
||||
if (this.channelId === '') {
|
||||
return
|
||||
}
|
||||
|
||||
const currentProfile = deepCopy(this.activeProfile)
|
||||
const currentProfile = deepCopy(profile)
|
||||
|
||||
if (this.isSubscribed) {
|
||||
if (this.isProfileSubscribed(profile)) {
|
||||
currentProfile.subscriptions = currentProfile.subscriptions.filter((channel) => {
|
||||
return channel.id !== this.channelId
|
||||
})
|
||||
|
@ -79,16 +112,16 @@ export default defineComponent({
|
|||
this.updateProfile(currentProfile)
|
||||
showToast(this.$t('Channel.Channel has been removed from your subscriptions'))
|
||||
|
||||
if (this.activeProfile._id === MAIN_PROFILE_ID) {
|
||||
if (profile._id === MAIN_PROFILE_ID) {
|
||||
// Check if a subscription exists in a different profile.
|
||||
// Remove from there as well.
|
||||
let duplicateSubscriptions = 0
|
||||
|
||||
this.profileList.forEach((profile) => {
|
||||
if (profile._id === MAIN_PROFILE_ID) {
|
||||
this.profileList.forEach((profileInList) => {
|
||||
if (profileInList._id === MAIN_PROFILE_ID) {
|
||||
return
|
||||
}
|
||||
duplicateSubscriptions += this.unsubscribe(profile, this.channelId)
|
||||
duplicateSubscriptions += this.unsubscribe(profileInList, this.channelId)
|
||||
})
|
||||
|
||||
if (duplicateSubscriptions > 0) {
|
||||
|
@ -107,7 +140,7 @@ export default defineComponent({
|
|||
this.updateProfile(currentProfile)
|
||||
showToast(this.$t('Channel.Added channel to your subscriptions'))
|
||||
|
||||
if (this.activeProfile._id !== MAIN_PROFILE_ID) {
|
||||
if (profile._id !== MAIN_PROFILE_ID) {
|
||||
const primaryProfile = deepCopy(this.profileList.find(prof => {
|
||||
return prof._id === MAIN_PROFILE_ID
|
||||
}))
|
||||
|
@ -122,6 +155,34 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isProfileDropdownEnabled && this.openDropdownOnSubscribe && !this.isProfileDropdownOpen) {
|
||||
this.toggleProfileDropdown()
|
||||
}
|
||||
},
|
||||
|
||||
handleProfileDropdownFocusOut: function () {
|
||||
if (!this.$refs.subscribeButton.matches(':focus-within')) {
|
||||
this.isProfileDropdownOpen = false
|
||||
}
|
||||
},
|
||||
|
||||
toggleProfileDropdown: function() {
|
||||
this.isProfileDropdownOpen = !this.isProfileDropdownOpen
|
||||
},
|
||||
|
||||
isActiveProfile: function (profile) {
|
||||
return profile._id === this.activeProfile._id
|
||||
},
|
||||
|
||||
subscriptionInfoForProfile: function (profile) {
|
||||
return profile.subscriptions.find((channel) => {
|
||||
return channel.id === this.channelId
|
||||
}) ?? null
|
||||
},
|
||||
|
||||
isProfileSubscribed: function (profile) {
|
||||
return this.subscriptionInfoForProfile(profile) !== null
|
||||
},
|
||||
|
||||
unsubscribe: function(profile, channelId) {
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
.buttonList {
|
||||
margin: 5px;
|
||||
margin-block-end: 10px;
|
||||
border-radius: 4px;
|
||||
block-size: fit-content;
|
||||
box-shadow: 0px 1px 2px rgb(0 0 0 / 50%);
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
/* addresses odd clipping behavior when adjusting window size */
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.ftSubscribeButton {
|
||||
position: relative;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
/* Ensures style here overrides style of .btn */
|
||||
.subscribeButton.btn {
|
||||
min-inline-size: 150px;
|
||||
white-space: initial;
|
||||
}
|
||||
|
||||
.subscribeButton.btn, .profileDropdownToggle.btn {
|
||||
align-self: center;
|
||||
margin-block: 0;
|
||||
margin-inline: 0;
|
||||
}
|
||||
|
||||
.dropdownOpened {
|
||||
.subscribeButton, .profileDropdownToggle {
|
||||
border-end-start-radius: 0;
|
||||
border-end-end-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.profileDropdownToggle.btn {
|
||||
border-inline-start: none !important;
|
||||
border-start-start-radius: 0;
|
||||
border-end-start-radius: 0;
|
||||
display: inline-block;
|
||||
min-inline-size: 1em;
|
||||
padding-inline: 10px;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.hasProfileDropdownToggle {
|
||||
.subscribeButton.btn {
|
||||
min-inline-size: 100px;
|
||||
padding-inline: 5px;
|
||||
border-inline-end: 2px solid var(--primary-color-active) !important;
|
||||
border-start-end-radius: 0;
|
||||
border-end-end-radius: 0;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
}
|
||||
|
||||
.hasProfileDropdownToggle > .subscribeButton.btn, .profileDropdownToggle.btn {
|
||||
padding-block: 5px;
|
||||
padding-inline: 6px;
|
||||
box-shadow: none;
|
||||
flex: auto;
|
||||
block-size: 2em;
|
||||
}
|
||||
|
||||
.profileDropdown {
|
||||
background-color: var(--side-nav-color);
|
||||
box-shadow: 0 1px 2px rgb(0 0 0 / 50%);
|
||||
color: var(--secondary-text-color);
|
||||
display: inline;
|
||||
font-size: 12px;
|
||||
max-block-size: 200px;
|
||||
margin-block: -10px 0;
|
||||
margin-inline: 5px 0;
|
||||
overflow-y: scroll;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
z-index: 3;
|
||||
// accounts for parent's left and right margins
|
||||
inline-size: calc(100% - 10px);
|
||||
|
||||
.profileList {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding-inline: 0;
|
||||
}
|
||||
|
||||
.profile {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
padding-inline-start: 0.5em;
|
||||
block-size: 50px;
|
||||
align-items: center;
|
||||
transition: background 0.2s ease-out;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--side-nav-hover-color);
|
||||
color: var(--side-nav-hover-text-color);
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
.colorOption {
|
||||
inline-size: 40px;
|
||||
block-size: 40px;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
-webkit-border-radius: 50%;
|
||||
}
|
||||
|
||||
.initial {
|
||||
font-size: 20px;
|
||||
line-height: 1em;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.profileName {
|
||||
padding-inline-end: 1em;
|
||||
text-align: start;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&.subscribed {
|
||||
background-color: var(--primary-color);
|
||||
|
||||
.profileName {
|
||||
color: var(--text-with-main-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,83 @@
|
|||
<template>
|
||||
<ft-button
|
||||
:label="subscribedText"
|
||||
class="subscribeButton"
|
||||
background-color="var(--primary-color)"
|
||||
text-color="var(--text-with-main-color)"
|
||||
@click="handleSubscription"
|
||||
/>
|
||||
<div
|
||||
ref="subscribeButton"
|
||||
class="ftSubscribeButton"
|
||||
:class="{ dropdownOpened: isProfileDropdownOpen}"
|
||||
@focusout="handleProfileDropdownFocusOut"
|
||||
>
|
||||
<div
|
||||
class="buttonList"
|
||||
:class="{ hasProfileDropdownToggle: isProfileDropdownEnabled}"
|
||||
>
|
||||
<ft-button
|
||||
:label="subscribedText"
|
||||
:no-border="true"
|
||||
class="subscribeButton"
|
||||
background-color="var(--primary-color)"
|
||||
text-color="var(--text-with-main-color)"
|
||||
@click="handleSubscription"
|
||||
/>
|
||||
<ft-button
|
||||
v-if="isProfileDropdownEnabled"
|
||||
:no-border="true"
|
||||
:title="isProfileDropdownOpen ? $t('Profile.Close Profile Dropdown') : $t('Profile.Open Profile Dropdown')"
|
||||
class="profileDropdownToggle"
|
||||
background-color="var(--primary-color)"
|
||||
text-color="var(--text-with-main-color)"
|
||||
:aria-expanded="isProfileDropdownOpen"
|
||||
@click="toggleProfileDropdown"
|
||||
>
|
||||
<font-awesome-icon
|
||||
:icon="isProfileDropdownOpen ? ['fas', 'angle-up'] : ['fas', 'angle-down']"
|
||||
/>
|
||||
</ft-button>
|
||||
</div>
|
||||
<template v-if="isProfileDropdownOpen">
|
||||
<div
|
||||
tabindex="-1"
|
||||
class="profileDropdown"
|
||||
>
|
||||
<ul
|
||||
class="profileList"
|
||||
>
|
||||
<li
|
||||
v-for="(profile, index) in profileDisplayList"
|
||||
:id="'subscription-profile-' + index"
|
||||
:key="index"
|
||||
class="profile"
|
||||
:class="{
|
||||
subscribed: isProfileSubscribed(profile)
|
||||
}"
|
||||
:aria-labelledby="'subscription-profile-' + index + '-name'"
|
||||
:aria-selected="isActiveProfile(profile)"
|
||||
:aria-checked="isProfileSubscribed(profile)"
|
||||
tabindex="0"
|
||||
role="checkbox"
|
||||
@click.stop.prevent="handleSubscription(profile)"
|
||||
@keydown.space.stop.prevent="handleSubscription(profile)"
|
||||
>
|
||||
<div
|
||||
class="colorOption"
|
||||
:style="{ background: profile.bgColor, color: profile.textColor }"
|
||||
>
|
||||
<div
|
||||
class="initial"
|
||||
>
|
||||
{{ isProfileSubscribed(profile) ? '✓' : profileInitials[index] }}
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
:id="'subscription-profile-' + index + '-name'"
|
||||
class="profileName"
|
||||
>
|
||||
{{ profile.name }}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./ft-subscribe-button.js" />
|
||||
<style src="./ft-subscribe-button.css" />
|
||||
<style lang="scss" src="./ft-subscribe-button.scss" />
|
||||
|
|
|
@ -32,9 +32,9 @@
|
|||
}
|
||||
|
||||
.text {
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
background-color: color-mix(in srgb, var(--primary-text-color) 80%, transparent);
|
||||
border-radius: 20px;
|
||||
color: #fff;
|
||||
color: var(--card-bg-color);
|
||||
font-size: 1rem;
|
||||
line-height: 120%;
|
||||
margin: 0;
|
||||
|
|
|
@ -24,20 +24,6 @@ export default defineComponent({
|
|||
'local',
|
||||
'piped'
|
||||
],
|
||||
defaultPageNames: [
|
||||
'Subscriptions',
|
||||
'Trending',
|
||||
'Most Popular',
|
||||
'Playlists',
|
||||
'History'
|
||||
],
|
||||
defaultPageValues: [
|
||||
'subscriptions',
|
||||
'trending',
|
||||
'mostPopular',
|
||||
'playlists',
|
||||
'history'
|
||||
],
|
||||
viewTypeValues: [
|
||||
'grid',
|
||||
'list'
|
||||
|
@ -54,6 +40,15 @@ export default defineComponent({
|
|||
'',
|
||||
'openLinkAfterPrompt',
|
||||
'doNothing'
|
||||
],
|
||||
includedDefaultPageNames: [
|
||||
'subscriptions',
|
||||
'subscribedChannels',
|
||||
'trending',
|
||||
'popular',
|
||||
'userPlaylists',
|
||||
'history',
|
||||
'settings'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -82,10 +77,38 @@ export default defineComponent({
|
|||
checkForBlogPosts: function () {
|
||||
return this.$store.getters.getCheckForBlogPosts
|
||||
},
|
||||
hidePlaylists: function () {
|
||||
return this.$store.getters.getHidePlaylists
|
||||
},
|
||||
hidePopularVideos: function () {
|
||||
return this.$store.getters.getHidePopularVideos
|
||||
},
|
||||
hideTrendingVideos: function () {
|
||||
return this.$store.getters.getHideTrendingVideos
|
||||
},
|
||||
defaultPages: function () {
|
||||
let includedPageNames = this.includedDefaultPageNames
|
||||
if (this.hideTrendingVideos) includedPageNames = includedPageNames.filter((pageName) => pageName !== 'trending')
|
||||
if (this.hidePlaylists) includedPageNames = includedPageNames.filter((pageName) => pageName !== 'userPlaylists')
|
||||
if (!(!this.hidePopularVideos && (this.backendFallback || this.backendPreference === 'invidious'))) includedPageNames = includedPageNames.filter((pageName) => pageName !== 'popular')
|
||||
return this.$router.getRoutes().filter((route) => includedPageNames.includes(route.name))
|
||||
},
|
||||
defaultPageNames: function () {
|
||||
return this.defaultPages.map((route) => this.$t(route.meta.title))
|
||||
},
|
||||
defaultPageValues: function () {
|
||||
// avoid Vue parsing issues by excluding '/' from path values
|
||||
return this.defaultPages.map((route) => route.path.substring(1))
|
||||
},
|
||||
backendPreference: function () {
|
||||
return this.$store.getters.getBackendPreference
|
||||
},
|
||||
landingPage: function () {
|
||||
const landingPage = this.$store.getters.getLandingPage
|
||||
// invalidate landing page selection & restore to default value if no longer valid
|
||||
if (!this.defaultPageValues.includes(landingPage)) {
|
||||
this.updateLandingPage('subscriptions')
|
||||
}
|
||||
return this.$store.getters.getLandingPage
|
||||
},
|
||||
region: function () {
|
||||
|
|
|
@ -53,7 +53,6 @@
|
|||
@change="handleFallbackApiBackend"
|
||||
/>
|
||||
<ft-select
|
||||
v-if="false"
|
||||
:placeholder="$t('Settings.General Settings.Default Landing Page')"
|
||||
:value="landingPage"
|
||||
:select-names="defaultPageNames"
|
||||
|
|
|
@ -14,16 +14,12 @@
|
|||
.navOption:hover {
|
||||
background-color: var(--side-nav-hover-color);
|
||||
color: var(--side-nav-hover-text-color);
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
.navOption:active {
|
||||
background-color: var(--side-nav-active-color);
|
||||
color: var(--side-nav-active-text-color);
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,16 +53,12 @@
|
|||
.navOption:hover, .navChannel:hover {
|
||||
background-color: var(--side-nav-hover-color);
|
||||
color: var(--side-nav-hover-text-color);
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
.navOption:active, .navChannel:active {
|
||||
background-color: var(--side-nav-active-color);
|
||||
color: var(--side-nav-active-text-color);
|
||||
-moz-transition: background 0.2s ease-in;
|
||||
-o-transition: background 0.2s ease-in;
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ export default defineComponent({
|
|||
hideWatchedSubs: function () {
|
||||
return this.$store.getters.getHideWatchedSubs
|
||||
},
|
||||
onlyShowLatestFromChannel: function () {
|
||||
return this.$store.getters.getOnlyShowLatestFromChannel
|
||||
},
|
||||
useRssFeeds: function () {
|
||||
return this.$store.getters.getUseRssFeeds
|
||||
},
|
||||
|
@ -24,7 +27,8 @@ export default defineComponent({
|
|||
...mapActions([
|
||||
'updateHideWatchedSubs',
|
||||
'updateUseRssFeeds',
|
||||
'updateFetchSubscriptionsAutomatically'
|
||||
'updateFetchSubscriptionsAutomatically',
|
||||
'updateOnlyShowLatestFromChannel'
|
||||
])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -26,6 +26,12 @@
|
|||
:compact="true"
|
||||
@change="updateHideWatchedSubs"
|
||||
/>
|
||||
<ft-toggle-switch
|
||||
:label="$t('Settings.Subscription Settings.Only Show Latest Video for Each Channel')"
|
||||
:default-value="onlyShowLatestFromChannel"
|
||||
:compact="true"
|
||||
@change="updateOnlyShowLatestFromChannel"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ft-settings-section>
|
||||
|
|
|
@ -40,6 +40,20 @@ export default defineComponent({
|
|||
return this.$store.getters.getHideHeaderLogo
|
||||
},
|
||||
|
||||
landingPage: function () {
|
||||
return this.$store.getters.getLandingPage
|
||||
},
|
||||
|
||||
headerLogoTitle: function () {
|
||||
return this.$t('Go to page',
|
||||
{
|
||||
page: this.$t(this.$router.getRoutes()
|
||||
.find((route) => route.path === '/' + this.landingPage)
|
||||
.meta.title
|
||||
)
|
||||
})
|
||||
},
|
||||
|
||||
enableSearchSuggestions: function () {
|
||||
return this.$store.getters.getEnableSearchSuggestions
|
||||
},
|
||||
|
|
|
@ -59,10 +59,10 @@
|
|||
dir="ltr"
|
||||
role="link"
|
||||
tabindex="0"
|
||||
:title="$t('Subscriptions.Subscriptions')"
|
||||
@click="navigate('subscriptions')"
|
||||
@keydown.space.prevent="navigate('subscriptions')"
|
||||
@keydown.enter.prevent="navigate('subscriptions')"
|
||||
:title="headerLogoTitle"
|
||||
@click="navigate(landingPage)"
|
||||
@keydown.space.prevent="navigate(landingPage)"
|
||||
@keydown.enter.prevent="navigate(landingPage)"
|
||||
>
|
||||
<div
|
||||
class="logoIcon"
|
||||
|
|
|
@ -24,12 +24,21 @@
|
|||
|
||||
.commentsTitle {
|
||||
padding-block: 1em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.commentSort {
|
||||
float: var(--float-right-ltr-rtl-value);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
.commentSort {
|
||||
float: none;
|
||||
inline-size: fit-content;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.comment {
|
||||
padding: 15px;
|
||||
position: relative;
|
||||
|
|
|
@ -2,6 +2,22 @@
|
|||
<ft-card
|
||||
class="card"
|
||||
>
|
||||
<h3
|
||||
v-if="commentData.length > 0 && !isLoading && showComments"
|
||||
class="commentsTitle"
|
||||
>
|
||||
{{ $t("Comments.Comments") }}
|
||||
<span
|
||||
class="hideComments"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="showComments = false"
|
||||
@keydown.space.prevent="showComments = false"
|
||||
@keydown.enter.prevent="showComments = false"
|
||||
>
|
||||
{{ $t("Comments.Hide Comments") }}
|
||||
</span>
|
||||
</h3>
|
||||
<h4
|
||||
v-if="canPerformInitialCommentLoading"
|
||||
class="getCommentsTitle"
|
||||
|
@ -33,22 +49,6 @@
|
|||
:select-values="sortValues"
|
||||
@change="handleSortChange"
|
||||
/>
|
||||
<h3
|
||||
v-if="commentData.length > 0 && !isLoading && showComments"
|
||||
class="commentsTitle"
|
||||
>
|
||||
{{ $t("Comments.Comments") }}
|
||||
<span
|
||||
class="hideComments"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="showComments = false"
|
||||
@keydown.space.prevent="showComments = false"
|
||||
@keydown.enter.prevent="showComments = false"
|
||||
>
|
||||
{{ $t("Comments.Hide Comments") }}
|
||||
</span>
|
||||
</h3>
|
||||
<div
|
||||
v-if="commentData.length > 0 && showComments"
|
||||
>
|
||||
|
|
|
@ -22,6 +22,13 @@
|
|||
row-gap: 16px;
|
||||
}
|
||||
|
||||
.watchingCount {
|
||||
font-weight: normal;
|
||||
margin-inline-start: 5px;
|
||||
font-size: 15px;
|
||||
color: var(--tertiary-text-color);
|
||||
}
|
||||
|
||||
.message {
|
||||
font-size: 18px;
|
||||
color: var(--tertiary-text-color);
|
||||
|
|
|
@ -6,6 +6,7 @@ import FtButton from '../ft-button/ft-button.vue'
|
|||
import autolinker from 'autolinker'
|
||||
import { getRandomColorClass } from '../../helpers/colors'
|
||||
import { getLocalVideoInfo, parseLocalTextRuns } from '../../helpers/api/local'
|
||||
import { formatNumber } from '../../helpers/utils'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'WatchVideoLiveChat',
|
||||
|
@ -30,6 +31,7 @@ export default defineComponent({
|
|||
},
|
||||
data: function () {
|
||||
return {
|
||||
/** @type {import('youtubei.js').YT.LiveChat|null} */
|
||||
liveChatInstance: null,
|
||||
isLoading: true,
|
||||
hasError: false,
|
||||
|
@ -52,7 +54,9 @@ export default defineComponent({
|
|||
amount: '',
|
||||
colorClass: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
/** @type {number|null} */
|
||||
watchingCount: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -78,6 +82,14 @@ export default defineComponent({
|
|||
|
||||
scrollingBehaviour: function () {
|
||||
return this.$store.getters.getDisableSmoothScrolling ? 'auto' : 'smooth'
|
||||
},
|
||||
|
||||
hideVideoViews: function () {
|
||||
return this.$store.getters.getHideVideoViews
|
||||
},
|
||||
|
||||
formattedWatchingCount: function () {
|
||||
return this.watchingCount !== null ? formatNumber(this.watchingCount) : '0'
|
||||
}
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
|
@ -185,6 +197,12 @@ export default defineComponent({
|
|||
}
|
||||
})
|
||||
|
||||
this.liveChatInstance.on('metadata-update', metadata => {
|
||||
if (!this.hideVideoViews && metadata.views && !isNaN(metadata.views.original_view_count)) {
|
||||
this.watchingCount = metadata.views.original_view_count
|
||||
}
|
||||
})
|
||||
|
||||
this.liveChatInstance.once('end', () => {
|
||||
this.hasEnded = true
|
||||
this.liveChatInstance = null
|
||||
|
|
|
@ -42,7 +42,15 @@
|
|||
v-else
|
||||
class="relative"
|
||||
>
|
||||
<h4>{{ $t("Video.Live Chat") }}</h4>
|
||||
<h4>
|
||||
{{ $t("Video.Live Chat") }}
|
||||
<span
|
||||
v-if="!hideVideoViews && watchingCount !== null"
|
||||
class="watchingCount"
|
||||
>
|
||||
{{ $tc('Global.Counts.Watching Count', watchingCount, { count: formattedWatchingCount }) }}
|
||||
</span>
|
||||
</h4>
|
||||
<div
|
||||
v-if="superChatComments.length > 0"
|
||||
class="superChatComments"
|
||||
|
|
|
@ -482,9 +482,13 @@ export function parseLocalListPlaylist(playlist, author = undefined) {
|
|||
channelName = playlist.author.name
|
||||
channelId = playlist.author.id
|
||||
}
|
||||
} else {
|
||||
} else if (author) {
|
||||
channelName = author.name
|
||||
channelId = author.id
|
||||
} else if (playlist.author?.name) {
|
||||
// auto-generated album playlists don't have an author
|
||||
// so in search results, the author text is "Playlist" and doesn't have a link or channel ID
|
||||
channelName = playlist.author.name
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
import { invidiousGetChannelInfo } from './api/invidious'
|
||||
import { getLocalChannel } from './api/local'
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {{
|
||||
* preference: string,
|
||||
* fallback: boolean,
|
||||
* invalid: boolean,
|
||||
* }} backendOptions
|
||||
*/
|
||||
async function findChannelById(id, backendOptions) {
|
||||
try {
|
||||
if (!process.env.IS_ELECTRON || backendOptions.preference === 'invidious') {
|
||||
return await invidiousGetChannelInfo(id)
|
||||
} else {
|
||||
return await getLocalChannel(id)
|
||||
}
|
||||
} catch (err) {
|
||||
// don't bother with fallback if channel doesn't exist
|
||||
if (err.message && err.message === 'This channel does not exist.') {
|
||||
return { invalid: true }
|
||||
}
|
||||
if (process.env.IS_ELECTRON && backendOptions.fallback) {
|
||||
if (backendOptions.preference === 'invidious') {
|
||||
return await getLocalChannel(id)
|
||||
}
|
||||
if (backendOptions.preference === 'local') {
|
||||
return await invidiousGetChannelInfo(id)
|
||||
}
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {{
|
||||
* preference: string,
|
||||
* fallback: boolean,
|
||||
* }} backendOptions
|
||||
* @returns {Promise<{icon: string, iconHref: string, preferredName: string} | { invalidId: boolean }>}
|
||||
*/
|
||||
export async function findChannelTagInfo(id, backendOptions) {
|
||||
if (!checkYoutubeChannelId(id)) return { invalidId: true }
|
||||
try {
|
||||
const channel = await findChannelById(id, backendOptions)
|
||||
if (!process.env.IS_ELECTRON || backendOptions.preference === 'invidious') {
|
||||
if (channel.invalid) return { invalidId: true }
|
||||
return {
|
||||
preferredName: channel.author,
|
||||
icon: channel.authorThumbnails[0].url
|
||||
}
|
||||
} else {
|
||||
if (channel.alert) return { invalidId: true }
|
||||
return {
|
||||
preferredName: channel.header.author.name,
|
||||
icon: channel.header.author.thumbnails.pop().url,
|
||||
iconHref: `/channel/${id}`
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return { preferredName: '', icon: '', iconHref: '', err }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether Id provided might be a YouTube Id
|
||||
* @param {string} id
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function checkYoutubeChannelId(id) {
|
||||
return /^UC[\w-]{22}$/.test(id)
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
import store from '../store/index'
|
||||
|
||||
async function getVideoHash(videoId) {
|
||||
const videoIdBuffer = new TextEncoder().encode(videoId)
|
||||
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', videoIdBuffer)
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
||||
const hashArray = new Uint8Array(hashBuffer)
|
||||
|
||||
return hashArray
|
||||
.map(byte => byte.toString(16).padStart(2, '0'))
|
||||
.slice(0, 4)
|
||||
.join('')
|
||||
return hashArray[0].toString(16).padStart(2, '0') +
|
||||
hashArray[1].toString(16).padStart(2, '0')
|
||||
}
|
||||
|
||||
export async function sponsorBlockSkipSegments(videoId, categories) {
|
||||
const videoIdHashPrefix = await getVideoHash(videoId)
|
||||
const requestUrl = `${store.getters.getSponsorBlockUrl}/api/skipSegments/${videoIdHashPrefix}?categories=${JSON.stringify(categories)}`
|
||||
|
@ -22,6 +22,11 @@ export async function sponsorBlockSkipSegments(videoId, categories) {
|
|||
return []
|
||||
}
|
||||
|
||||
// Sometimes the sponsor block server goes down or returns other errors
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.text())
|
||||
}
|
||||
|
||||
const json = await response.json()
|
||||
return json
|
||||
.filter((result) => result.videoID === videoId)
|
||||
|
@ -33,7 +38,7 @@ export async function sponsorBlockSkipSegments(videoId, categories) {
|
|||
}
|
||||
|
||||
export async function deArrowData(videoId) {
|
||||
const videoIdHashPrefix = (await getVideoHash(videoId)).substring(0, 4)
|
||||
const videoIdHashPrefix = await getVideoHash(videoId)
|
||||
const requestUrl = `${store.getters.getSponsorBlockUrl}/api/branding/${videoIdHashPrefix}`
|
||||
|
||||
try {
|
||||
|
|
|
@ -8,11 +8,6 @@ import { calculatePublishedDate } from './utils'
|
|||
export function updateVideoListAfterProcessing(videos) {
|
||||
let videoList = videos
|
||||
|
||||
// Filtering and sorting based in preference
|
||||
videoList.sort((a, b) => {
|
||||
return b.publishedDate - a.publishedDate
|
||||
})
|
||||
|
||||
if (store.getters.getHideLiveStreams) {
|
||||
videoList = videoList.filter(item => {
|
||||
return (!item.liveNow && !item.isUpcoming)
|
||||
|
@ -45,6 +40,26 @@ export function updateVideoListAfterProcessing(videos) {
|
|||
})
|
||||
}
|
||||
|
||||
// ordered last to show first eligible video from channel
|
||||
// if the first one incidentally failed one of the above checks
|
||||
if (store.getters.getOnlyShowLatestFromChannel) {
|
||||
const authors = new Set()
|
||||
videoList = videoList.filter((video) => {
|
||||
if (!video.authorId) {
|
||||
return true
|
||||
} else if (!authors.has(video.authorId)) {
|
||||
authors.add(video.authorId)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
videoList.sort((a, b) => {
|
||||
return b.publishedDate - a.publishedDate
|
||||
})
|
||||
|
||||
return videoList
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'
|
|||
// to avoid code conflict and duplicate entries
|
||||
import {
|
||||
faAngleDown,
|
||||
faAngleUp,
|
||||
faArrowDown,
|
||||
faArrowLeft,
|
||||
faArrowRight,
|
||||
|
@ -81,6 +82,7 @@ Vue.config.productionTip = process.env.NODE_ENV === 'development'
|
|||
library.add(
|
||||
// solid icons
|
||||
faAngleDown,
|
||||
faAngleUp,
|
||||
faArrowDown,
|
||||
faArrowLeft,
|
||||
faArrowRight,
|
||||
|
|
|
@ -3,7 +3,6 @@ import Router from 'vue-router'
|
|||
import Subscriptions from '../views/Subscriptions/Subscriptions.vue'
|
||||
import SubscribedChannels from '../views/SubscribedChannels/SubscribedChannels.vue'
|
||||
import ProfileSettings from '../views/ProfileSettings/ProfileSettings.vue'
|
||||
import ProfileEdit from '../views/ProfileEdit/ProfileEdit.vue'
|
||||
import Trending from '../views/Trending/Trending.vue'
|
||||
import Popular from '../views/Popular/Popular.vue'
|
||||
import UserPlaylists from '../views/UserPlaylists/UserPlaylists.vue'
|
||||
|
@ -22,6 +21,7 @@ const router = new Router({
|
|||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'default',
|
||||
meta: {
|
||||
title: 'Subscriptions.Subscriptions'
|
||||
},
|
||||
|
@ -29,6 +29,7 @@ const router = new Router({
|
|||
},
|
||||
{
|
||||
path: '/subscriptions',
|
||||
name: 'subscriptions',
|
||||
meta: {
|
||||
title: 'Subscriptions.Subscriptions'
|
||||
},
|
||||
|
@ -36,36 +37,15 @@ const router = new Router({
|
|||
},
|
||||
{
|
||||
path: '/subscribedchannels',
|
||||
name: 'subscribedChannels',
|
||||
meta: {
|
||||
title: 'Channels.Title'
|
||||
},
|
||||
component: SubscribedChannels
|
||||
},
|
||||
{
|
||||
path: '/settings/profile',
|
||||
meta: {
|
||||
title: 'Profile.Profile Settings'
|
||||
},
|
||||
component: ProfileSettings
|
||||
},
|
||||
{
|
||||
path: '/settings/profile/new',
|
||||
name: 'newProfile',
|
||||
meta: {
|
||||
title: 'Profile.Create New Profile'
|
||||
},
|
||||
component: ProfileEdit
|
||||
},
|
||||
{
|
||||
path: '/settings/profile/edit/:id',
|
||||
name: 'editProfile',
|
||||
meta: {
|
||||
title: 'Profile.Edit Profile'
|
||||
},
|
||||
component: ProfileEdit
|
||||
},
|
||||
{
|
||||
path: '/trending',
|
||||
name: 'trending',
|
||||
meta: {
|
||||
title: 'Trending.Trending'
|
||||
},
|
||||
|
@ -73,6 +53,7 @@ const router = new Router({
|
|||
},
|
||||
{
|
||||
path: '/popular',
|
||||
name: 'popular',
|
||||
meta: {
|
||||
title: 'Most Popular'
|
||||
},
|
||||
|
@ -80,6 +61,7 @@ const router = new Router({
|
|||
},
|
||||
{
|
||||
path: '/userplaylists',
|
||||
name: 'userPlaylists',
|
||||
meta: {
|
||||
title: 'User Playlists.Your Playlists'
|
||||
},
|
||||
|
@ -95,6 +77,7 @@ const router = new Router({
|
|||
},
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'settings',
|
||||
meta: {
|
||||
title: 'Settings.Settings'
|
||||
},
|
||||
|
@ -102,11 +85,20 @@ const router = new Router({
|
|||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
meta: {
|
||||
title: 'About.About'
|
||||
},
|
||||
component: About
|
||||
},
|
||||
{
|
||||
path: '/settings/profile',
|
||||
name: 'profileSettings',
|
||||
meta: {
|
||||
title: 'Profile.Profile Settings'
|
||||
},
|
||||
component: ProfileSettings
|
||||
},
|
||||
{
|
||||
path: '/search/:query',
|
||||
meta: {
|
||||
|
|
|
@ -162,6 +162,7 @@ const defaultSideEffectsTriggerId = settingId =>
|
|||
/*****/
|
||||
|
||||
const state = {
|
||||
allSettingsSectionsExpandedByDefault: false,
|
||||
autoplayPlaylists: true,
|
||||
autoplayVideos: true,
|
||||
backendFallback: process.env.IS_ELECTRON,
|
||||
|
@ -230,6 +231,7 @@ const state = {
|
|||
landingPage: 'subscriptions',
|
||||
listType: 'grid',
|
||||
maxVideoPlaybackRate: 3,
|
||||
onlyShowLatestFromChannel: false,
|
||||
playNextVideo: false,
|
||||
proxyHostname: '127.0.0.1',
|
||||
proxyPort: '9050',
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
|
||||
const state = {
|
||||
isSideNavOpen: false,
|
||||
outlinesHidden: true,
|
||||
sessionSearchHistory: [],
|
||||
popularCache: null,
|
||||
trendingCache: {
|
||||
|
@ -51,6 +52,10 @@ const getters = {
|
|||
return state.isSideNavOpen
|
||||
},
|
||||
|
||||
getOutlinesHidden() {
|
||||
return state.outlinesHidden
|
||||
},
|
||||
|
||||
getCurrentVolume () {
|
||||
return state.currentVolume
|
||||
},
|
||||
|
@ -117,6 +122,14 @@ const getters = {
|
|||
}
|
||||
|
||||
const actions = {
|
||||
showOutlines({ commit }) {
|
||||
commit('setOutlinesHidden', false)
|
||||
},
|
||||
|
||||
hideOutlines({ commit }) {
|
||||
commit('setOutlinesHidden', true)
|
||||
},
|
||||
|
||||
async downloadMedia({ rootState }, { url, title, extension, fallingBackPath }) {
|
||||
if (!process.env.IS_ELECTRON) {
|
||||
openExternalLink(url)
|
||||
|
@ -633,6 +646,10 @@ const mutations = {
|
|||
state.isSideNavOpen = !state.isSideNavOpen
|
||||
},
|
||||
|
||||
setOutlinesHidden(state, value) {
|
||||
state.outlinesHidden = value
|
||||
},
|
||||
|
||||
setShowProgressBar (state, value) {
|
||||
state.showProgressBar = value
|
||||
},
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
display: flex;
|
||||
gap: 30px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.subscribeButton {
|
||||
|
|
|
@ -32,7 +32,6 @@ import {
|
|||
parseLocalListVideo,
|
||||
parseLocalSubscriberCount
|
||||
} from '../../helpers/api/local'
|
||||
import { Injectables } from '../../../constants'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Channel',
|
||||
|
@ -48,9 +47,6 @@ export default defineComponent({
|
|||
'ft-subscribe-button': FtSubscribeButton,
|
||||
'channel-about': ChannelAbout
|
||||
},
|
||||
inject: {
|
||||
showOutlines: Injectables.SHOW_OUTLINES
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
isLoading: false,
|
||||
|
@ -1874,6 +1870,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
...mapActions([
|
||||
'showOutlines',
|
||||
'updateSubscriptionDetails'
|
||||
])
|
||||
}
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
import { defineComponent } from 'vue'
|
||||
import { mapGetters } from 'vuex'
|
||||
import FtLoader from '../../components/ft-loader/ft-loader.vue'
|
||||
import FtProfileEdit from '../../components/ft-profile-edit/ft-profile-edit.vue'
|
||||
import FtProfileChannelList from '../../components/ft-profile-channel-list/ft-profile-channel-list.vue'
|
||||
import FtProfileFilterChannelsList from '../../components/ft-profile-filter-channels-list/ft-profile-filter-channels-list.vue'
|
||||
import { MAIN_PROFILE_ID } from '../../../constants'
|
||||
import { calculateColorLuminance, getRandomColor } from '../../helpers/colors'
|
||||
import { showToast } from '../../helpers/utils'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ProfileEdit',
|
||||
components: {
|
||||
'ft-loader': FtLoader,
|
||||
'ft-profile-edit': FtProfileEdit,
|
||||
'ft-profile-channel-list': FtProfileChannelList,
|
||||
'ft-profile-filter-channels-list': FtProfileFilterChannelsList
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
isLoading: true,
|
||||
isNew: false,
|
||||
profileId: '',
|
||||
profile: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'profileById'
|
||||
]),
|
||||
|
||||
profileList: function () {
|
||||
return this.$store.getters.getProfileList
|
||||
},
|
||||
|
||||
isMainProfile: function () {
|
||||
return this.profileId === MAIN_PROFILE_ID
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
profileList: {
|
||||
handler: function () {
|
||||
const profile = this.profileById(this.profileId)
|
||||
if (!profile) {
|
||||
showToast(this.$t('Profile.Profile could not be found'))
|
||||
this.$router.push({
|
||||
path: '/settings/profile/'
|
||||
})
|
||||
}
|
||||
this.profile = profile
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
const profileType = this.$route.name
|
||||
|
||||
this.deletePromptLabel = `${this.$t('Profile.Are you sure you want to delete this profile?')} ${this.$t('Profile["All subscriptions will also be deleted."]')}`
|
||||
|
||||
if (profileType === 'newProfile') {
|
||||
this.isNew = true
|
||||
const bgColor = getRandomColor()
|
||||
const textColor = calculateColorLuminance(bgColor)
|
||||
this.profile = {
|
||||
name: '',
|
||||
bgColor: bgColor,
|
||||
textColor: textColor,
|
||||
subscriptions: []
|
||||
}
|
||||
} else {
|
||||
this.isNew = false
|
||||
this.profileId = this.$route.params.id
|
||||
|
||||
const profile = this.profileById(this.profileId)
|
||||
if (!profile) {
|
||||
showToast(this.$t('Profile.Profile could not be found'))
|
||||
this.$router.push({
|
||||
path: '/settings/profile/'
|
||||
})
|
||||
}
|
||||
this.profile = profile
|
||||
}
|
||||
|
||||
this.isLoading = false
|
||||
}
|
||||
})
|
|
@ -1,27 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<ft-loader
|
||||
v-if="isLoading"
|
||||
:fullscreen="true"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
>
|
||||
<ft-profile-edit
|
||||
:profile="profile"
|
||||
:is-new="isNew"
|
||||
/>
|
||||
<ft-profile-channel-list
|
||||
v-if="!isNew"
|
||||
:profile="profile"
|
||||
:is-main-profile="isMainProfile"
|
||||
/>
|
||||
<ft-profile-filter-channels-list
|
||||
v-if="!isNew && !isMainProfile"
|
||||
:profile="profile"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./ProfileEdit.js" />
|
|
@ -1,7 +1,5 @@
|
|||
.card {
|
||||
inline-size: 85%;
|
||||
margin-block: 0 60px;
|
||||
margin-inline: auto;
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.message {
|
||||
|
@ -14,6 +12,23 @@
|
|||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.profileList > .openedProfile {
|
||||
background-color: var(--primary-text-color);
|
||||
transition: background 0.2s ease-out;
|
||||
color: var(--card-bg-color);
|
||||
}
|
||||
|
||||
.profileList > .openedProfile:hover, .profileList > .openedProfile:focus-visible {
|
||||
background-color: var(--tertiary-text-color);
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
.card {
|
||||
inline-size: 85%;
|
||||
margin-block: 0 15px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 680px) {
|
||||
.card {
|
||||
inline-size: 90%;
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
import { defineComponent } from 'vue'
|
||||
import { mapGetters } from 'vuex'
|
||||
import FtCard from '../../components/ft-card/ft-card.vue'
|
||||
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
|
||||
import FtProfileBubble from '../../components/ft-profile-bubble/ft-profile-bubble.vue'
|
||||
import FtButton from '../../components/ft-button/ft-button.vue'
|
||||
import FtLoader from '../../components/ft-loader/ft-loader.vue'
|
||||
import FtProfileEdit from '../../components/ft-profile-edit/ft-profile-edit.vue'
|
||||
import FtProfileChannelList from '../../components/ft-profile-channel-list/ft-profile-channel-list.vue'
|
||||
import FtProfileFilterChannelsList from '../../components/ft-profile-filter-channels-list/ft-profile-filter-channels-list.vue'
|
||||
import { calculateColorLuminance, getRandomColor } from '../../helpers/colors'
|
||||
import { MAIN_PROFILE_ID } from '../../../constants'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ProfileSettings',
|
||||
|
@ -10,18 +17,74 @@ export default defineComponent({
|
|||
'ft-card': FtCard,
|
||||
'ft-flex-box': FtFlexBox,
|
||||
'ft-profile-bubble': FtProfileBubble,
|
||||
'ft-button': FtButton
|
||||
'ft-button': FtButton,
|
||||
'ft-loader': FtLoader,
|
||||
'ft-profile-edit': FtProfileEdit,
|
||||
'ft-profile-channel-list': FtProfileChannelList,
|
||||
'ft-profile-filter-channels-list': FtProfileFilterChannelsList
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
isNewProfileOpen: false,
|
||||
openSettingsProfileId: null,
|
||||
openSettingsProfile: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'profileById'
|
||||
]),
|
||||
profileList: function () {
|
||||
return this.$store.getters.getProfileList
|
||||
},
|
||||
isMainProfile: function () {
|
||||
return this.openSettingsProfileId === MAIN_PROFILE_ID
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
profileList: {
|
||||
handler: function () {
|
||||
this.openSettingsProfile = this.getProfileById(this.openSettingsProfileId)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
newProfile: function () {
|
||||
this.$router.push({
|
||||
path: '/settings/profile/new/'
|
||||
})
|
||||
openSettingsForNewProfile: function () {
|
||||
this.isNewProfileOpen = true
|
||||
const bgColor = getRandomColor()
|
||||
const textColor = calculateColorLuminance(bgColor)
|
||||
this.openSettingsProfile = {
|
||||
name: '',
|
||||
bgColor: bgColor,
|
||||
textColor: textColor,
|
||||
subscriptions: []
|
||||
}
|
||||
this.openSettingsProfileId = -1
|
||||
},
|
||||
openSettingsForProfileWithId: function (profileId) {
|
||||
if (this.profileId === this.openSettingsProfileId) {
|
||||
return
|
||||
}
|
||||
this.isNewProfileOpen = false
|
||||
this.openSettingsProfileId = profileId
|
||||
this.openSettingsProfile = this.getProfileById(profileId)
|
||||
},
|
||||
getProfileById: function (profileId) {
|
||||
if (!profileId) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.profileById(profileId)
|
||||
},
|
||||
handleNewProfileCreated: function () {
|
||||
this.isNewProfileOpen = false
|
||||
this.openSettingsProfile = null
|
||||
this.openSettingsProfileId = -1
|
||||
},
|
||||
handleProfileDeleted: function () {
|
||||
this.openSettingsProfile = null
|
||||
this.openSettingsProfileId = -1
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -12,15 +12,39 @@
|
|||
:profile-name="profile.name"
|
||||
:background-color="profile.bgColor"
|
||||
:text-color="profile.textColor"
|
||||
:class="{ openedProfile: openSettingsProfile?._id === profile._id }"
|
||||
@click="openSettingsForProfileWithId(profile._id)"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<ft-flex-box>
|
||||
<ft-button
|
||||
v-if="!isNewProfileOpen"
|
||||
:label="$t('Profile.Create New Profile')"
|
||||
@click="newProfile"
|
||||
@click="openSettingsForNewProfile"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
</ft-card>
|
||||
<div
|
||||
v-if="openSettingsProfile"
|
||||
:key="openSettingsProfileId"
|
||||
>
|
||||
<ft-profile-channel-list
|
||||
v-if="!isNewProfileOpen"
|
||||
:profile="openSettingsProfile"
|
||||
:is-main-profile="isMainProfile"
|
||||
/>
|
||||
<ft-profile-filter-channels-list
|
||||
v-if="!isNewProfileOpen && !isMainProfile"
|
||||
:profile="openSettingsProfile"
|
||||
/>
|
||||
<ft-profile-edit
|
||||
:profile="openSettingsProfile"
|
||||
:is-new="isNewProfileOpen"
|
||||
:is-main-profile="isMainProfile"
|
||||
@new-profile-created="handleNewProfileCreated"
|
||||
@profile-deleted="handleProfileDeleted"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,8 +1,23 @@
|
|||
hr {
|
||||
block-size: 2px;
|
||||
inline-size: 85%;
|
||||
margin-block: 0;
|
||||
margin-inline: auto;
|
||||
border: 0;
|
||||
background-color: var(--scrollbar-color-hover);
|
||||
border-block-start: 2px solid var(--scrollbar-color-hover);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
hr {
|
||||
inline-size: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.switchColumnGrid {
|
||||
inline-size: 85%;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.settingsToggle {
|
||||
padding-block: 0;
|
||||
margin-block: 10px 5px;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { defineComponent } from 'vue'
|
||||
import { mapActions } from 'vuex'
|
||||
import GeneralSettings from '../../components/general-settings/general-settings.vue'
|
||||
import ThemeSettings from '../../components/theme-settings/theme-settings.vue'
|
||||
import PlayerSettings from '../../components/player-settings/player-settings.vue'
|
||||
|
@ -14,6 +15,7 @@ import ParentControlSettings from '../../components/parental-control-settings/pa
|
|||
import ExperimentalSettings from '../../components/experimental-settings/experimental-settings.vue'
|
||||
import PasswordSettings from '../../components/password-settings/password-settings.vue'
|
||||
import PasswordDialog from '../../components/password-dialog/password-dialog.vue'
|
||||
import FtToggleSwitch from '../../components/ft-toggle-switch/ft-toggle-switch.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Settings',
|
||||
|
@ -33,6 +35,7 @@ export default defineComponent({
|
|||
'experimental-settings': ExperimentalSettings,
|
||||
'password-settings': PasswordSettings,
|
||||
'password-dialog': PasswordDialog,
|
||||
'ft-toggle-switch': FtToggleSwitch
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
|
@ -46,11 +49,20 @@ export default defineComponent({
|
|||
|
||||
settingsPassword: function () {
|
||||
return this.$store.getters.getSettingsPassword
|
||||
}
|
||||
},
|
||||
|
||||
allSettingsSectionsExpandedByDefault: function () {
|
||||
return this.$store.getters.getAllSettingsSectionsExpandedByDefault
|
||||
},
|
||||
},
|
||||
created: function () {
|
||||
if (this.settingsPassword === '') {
|
||||
this.unlocked = true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions([
|
||||
'updateAllSettingsSectionsExpandedByDefault',
|
||||
])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
<template>
|
||||
<div>
|
||||
<template v-if="unlocked">
|
||||
<div class="switchColumnGrid">
|
||||
<div class="switchColumn">
|
||||
<ft-toggle-switch
|
||||
class="settingsToggle"
|
||||
:label="$t('Settings.Expand All Settings Sections')"
|
||||
:default-value="allSettingsSectionsExpandedByDefault"
|
||||
:compact="false"
|
||||
@change="updateAllSettingsSectionsExpandedByDefault"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<general-settings />
|
||||
<hr>
|
||||
<theme-settings />
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<ft-flex-box class="channels">
|
||||
<div
|
||||
v-for="channel in channelList"
|
||||
:key="channel.key"
|
||||
:key="channel.id"
|
||||
class="channel"
|
||||
>
|
||||
<router-link
|
||||
|
@ -53,10 +53,10 @@
|
|||
class="unsubscribeContainer"
|
||||
>
|
||||
<ft-subscribe-button
|
||||
class="btn"
|
||||
:channel-id="channel.id"
|
||||
:channel-name="channel.name"
|
||||
:channel-thumbnail="channel.thumbnail"
|
||||
:open-dropdown-on-subscribe="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { defineComponent } from 'vue'
|
||||
import { mapActions } from 'vuex'
|
||||
|
||||
import SubscriptionsVideos from '../../components/subscriptions-videos/subscriptions-videos.vue'
|
||||
import SubscriptionsLive from '../../components/subscriptions-live/subscriptions-live.vue'
|
||||
|
@ -7,7 +8,6 @@ import SubscriptionsCommunity from '../../components/subscriptions-community/sub
|
|||
|
||||
import FtCard from '../../components/ft-card/ft-card.vue'
|
||||
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
|
||||
import { Injectables } from '../../../constants'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Subscriptions',
|
||||
|
@ -19,9 +19,6 @@ export default defineComponent({
|
|||
'ft-card': FtCard,
|
||||
'ft-flex-box': FtFlexBox
|
||||
},
|
||||
inject: {
|
||||
showOutlines: Injectables.SHOW_OUTLINES
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
currentTab: 'videos'
|
||||
|
@ -148,6 +145,10 @@ export default defineComponent({
|
|||
this.$refs[visibleTabs[index]].focus()
|
||||
this.showOutlines()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
...mapActions([
|
||||
'showOutlines'
|
||||
])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { defineComponent } from 'vue'
|
||||
import { mapActions } from 'vuex'
|
||||
import FtCard from '../../components/ft-card/ft-card.vue'
|
||||
import FtLoader from '../../components/ft-loader/ft-loader.vue'
|
||||
import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
|
||||
|
@ -8,7 +9,6 @@ import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
|
|||
import { copyToClipboard, showToast } from '../../helpers/utils'
|
||||
import { getLocalTrending } from '../../helpers/api/local'
|
||||
import { invidiousAPICall } from '../../helpers/api/invidious'
|
||||
import { Injectables } from '../../../constants'
|
||||
import { getPipedTrending } from '../../helpers/api/piped'
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -20,9 +20,6 @@ export default defineComponent({
|
|||
'ft-icon-button': FtIconButton,
|
||||
'ft-flex-box': FtFlexBox
|
||||
},
|
||||
inject: {
|
||||
showOutlines: Injectables.SHOW_OUTLINES
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
isLoading: false,
|
||||
|
@ -248,6 +245,10 @@ export default defineComponent({
|
|||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
...mapActions([
|
||||
'showOutlines'
|
||||
])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -413,7 +413,7 @@ Settings:
|
|||
Hide Chapters: إخفاء الفصول
|
||||
Hide Upcoming Premieres: إخفاء العروض الأولى القادمة
|
||||
Hide Channels: إخفاء مقاطع الفيديو من القنوات
|
||||
Hide Channels Placeholder: اسم القناة أو معرفها
|
||||
Hide Channels Placeholder: معرف القناة
|
||||
Display Titles Without Excessive Capitalisation: عرض العناوين بدون احرف كبيرة
|
||||
بشكل مفرط
|
||||
Hide Featured Channels: إخفاء القنوات المميزة
|
||||
|
@ -434,6 +434,12 @@ Settings:
|
|||
Hide Profile Pictures in Comments: إخفاء صور الملف الشخصي في التعليقات
|
||||
Blur Thumbnails: اخفاء الصور المصغرة
|
||||
Hide Subscriptions Community: إخفاء مجتمع الاشتراكات
|
||||
Hide Channels Invalid: معرف القناة المقدم غير صالح
|
||||
Hide Channels Disabled Message: تم حظر بعض القنوات باستخدام المعرّف ولم تتم معالجتها.
|
||||
يتم حظر الميزة أثناء تحديث هذه المعرفات
|
||||
Hide Channels Already Exists: معرف القناة موجود بالفعل
|
||||
Hide Channels API Error: حدث خطأ أثناء استرداد المستخدم بالمعرف المدخل. يرجى
|
||||
التحقق مرة أخرى إذا كان المعرف صحيحا.
|
||||
The app needs to restart for changes to take effect. Restart and apply change?: البرنامج
|
||||
يحتاج لإعادة التشغيل كي يسري مفعول التغييرات. هل تريد إعادة التشغيل و تطبيق التغييرات؟
|
||||
Proxy Settings:
|
||||
|
@ -603,6 +609,8 @@ Profile:
|
|||
Profile Filter: مرشح الملف الشخصي
|
||||
Profile Settings: إعدادات الملف الشخصي
|
||||
Toggle Profile List: تبديل قائمة الملف الشخصي
|
||||
Open Profile Dropdown: فتح القائمة المنسدلة للملف الشخصي
|
||||
Close Profile Dropdown: إغلاق القائمة المنسدلة للملف الشخصي
|
||||
Channel:
|
||||
Subscribe: 'اشتراك'
|
||||
Unsubscribe: 'إلغاء الاشتراك'
|
||||
|
@ -805,6 +813,8 @@ Video:
|
|||
'Live Chat is unavailable for this stream. It may have been disabled by the uploader.': الدردشة
|
||||
المباشرة غير متاحة لهذا البث. ربما تم تعطيلها من قبل القائم بالتحميل.
|
||||
Pause on Current Video: توقف مؤقتًا على الفيديو الحالي
|
||||
Unhide Channel: عرض القناة
|
||||
Hide Channel: إخفاء القناة
|
||||
Videos:
|
||||
#& Sort By
|
||||
Sort By:
|
||||
|
@ -964,9 +974,9 @@ Tooltips:
|
|||
وتمكين ذاكرة تخزين مؤقت للصور في الذاكرة. سيؤدي إلى زيادة استخدام ذاكرة الوصول
|
||||
العشوائي.
|
||||
Distraction Free Settings:
|
||||
Hide Channels: أدخل اسم قناة أو معرّف القناة لإخفاء جميع مقاطع الفيديو وقوائم
|
||||
التشغيل والقناة نفسها من الظهور في البحث والشهرة والأكثر شعبية والموصى بها.
|
||||
يجب أن يكون اسم القناة الذي تم إدخاله مطابقًا تمامًا وحساسًا لحالة الأحرف.
|
||||
Hide Channels: أدخل معرف القناة لإخفاء جميع مقاطع الفيديو وقوائم التشغيل والقناة
|
||||
نفسها من الظهور في نتائج البحث والشائعة والأكثر شهرة والموصى بها. يجب أن يكون
|
||||
معرف القناة الذي تم إدخاله متطابقًا تمامًا وأن يكون حساسًا لحالة الأحرف.
|
||||
Hide Subscriptions Live: يتم تجاوز هذا الإعداد من خلال إعداد "{appWideSetting}"
|
||||
على مستوى التطبيق، في قسم "{subsection}" من "{settingsSection}"
|
||||
SponsorBlock Settings:
|
||||
|
@ -1031,3 +1041,6 @@ Playlist will pause when current video is finished: ستتوقف قائمة ال
|
|||
انتهاء الفيديو الحالي
|
||||
Playlist will not pause when current video is finished: لن تتوقف قائمة التشغيل مؤقتًا
|
||||
عند انتهاء الفيديو الحالي
|
||||
Channel Hidden: تم إضافة {channel} إلى مرشح القناة
|
||||
Go to page: إذهب إلى {page}
|
||||
Channel Unhidden: تمت إزالة {channel} من مرشح القناة
|
||||
|
|
|
@ -0,0 +1,870 @@
|
|||
# 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: 'پەڕگە'
|
||||
New Window: 'پەنجەرەی نوێ'
|
||||
Preferences: 'هەڵبژاردەکان'
|
||||
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: 'پێشەوە'
|
||||
Open New Window: 'کردنەوەی پەنجەرەیەکی نوێ'
|
||||
|
||||
Version {versionNumber} is now available! Click for more details: 'ئێستا وەشانی {versionNumber}
|
||||
بەردەستە..بۆ زانیاری زۆرتر کرتە بکە'
|
||||
Download From Site: 'لە وێبگەوە دایگرە'
|
||||
A new blog is now available, {blogTitle}. Click to view more: 'بلۆگێکی نوێ بەردەستە،
|
||||
{blogTitle}. کرتە بکە بۆ بینینی'
|
||||
Are you sure you want to open this link?: 'دڵنیایت دەتەوێت ئەم بەستەرە بکەیتەوە؟'
|
||||
|
||||
# Global
|
||||
# Anything shared among components / views should be put here
|
||||
Global:
|
||||
Videos: 'ڤیدیۆکان'
|
||||
Shorts: ''
|
||||
Live: 'ڕاستەوخۆ'
|
||||
Community: 'کۆمەڵگە'
|
||||
Counts:
|
||||
Video Count: '١ ڤیدیۆ | {count} ڤیدیۆ'
|
||||
Channel Count: '١ کەناڵ | {count} کەناڵ'
|
||||
Subscriber Count: '١ بەشداربوو | {count} بەشداربوو'
|
||||
View Count: 'بینینەک | {count} بینین'
|
||||
Watching Count: '١ تەمەشاکردن | {count} تەمەشاکردن'
|
||||
|
||||
# Search Bar
|
||||
Search / Go to URL: 'گەڕان/ بڕۆ بۆ ئرڵ'
|
||||
Search Bar:
|
||||
Clear Input: ''
|
||||
# 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: 'کەناڵ'
|
||||
Movies: 'فیلم'
|
||||
#& Playlists
|
||||
Duration:
|
||||
Duration: 'ماوە'
|
||||
All Durations: 'گشت ماوەکان'
|
||||
Short (< 4 minutes): 'کورت (< ٤ خولەک)'
|
||||
Medium (4 - 20 minutes): 'ناوەند (٤ - ٢٠ خولەک)'
|
||||
Long (> 20 minutes): 'درێژ (> ٢٠ خولەک)'
|
||||
# On Search Page
|
||||
Search Results: 'ئەنجامەکانی گەڕان'
|
||||
Fetching results. Please wait: ''
|
||||
Fetch more results: ''
|
||||
There are no more results for this search: 'ئەنجامەکی تر نییە بۆ ئەم گەڕانە'
|
||||
# Sidebar
|
||||
Subscriptions:
|
||||
# On Subscriptions Page
|
||||
Subscriptions: ''
|
||||
# channels that were likely deleted
|
||||
Error Channels: ''
|
||||
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.': ''
|
||||
Disabled Automatic Fetching: ''
|
||||
Empty Channels: 'کەناڵە بەشداربووەکانت هێشتا هیچ ڤیدیۆیەکیان نییە.'
|
||||
'Getting Subscriptions. Please wait.': ''
|
||||
Empty Posts: ''
|
||||
Refresh Subscriptions: ''
|
||||
Load More Videos: 'بارکردنی ڤیدیۆی زۆرتر'
|
||||
Load More Posts: ''
|
||||
Subscriptions Tabs: ''
|
||||
All Subscription Tabs Hidden: ''
|
||||
More: 'زۆرتر'
|
||||
Channels:
|
||||
Channels: 'کەناڵەکان'
|
||||
Title: 'پێڕستی کەناڵەکان'
|
||||
Search bar placeholder: ''
|
||||
Count: '{number} کەناڵ دۆزرانەوە.'
|
||||
Empty: 'ئێستا پێڕستی کەناڵەکانت بەتاڵە.'
|
||||
Unsubscribe: ''
|
||||
Unsubscribed: ''
|
||||
Unsubscribe Prompt: ''
|
||||
Trending:
|
||||
Trending: ''
|
||||
Default: 'بنەڕەت'
|
||||
Music: 'مۆسیقا'
|
||||
Gaming: 'یاری'
|
||||
Movies: 'فیلم'
|
||||
Trending Tabs: ''
|
||||
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: ''
|
||||
Empty Search Message: ''
|
||||
Search bar placeholder: 'لەناو پێڕستی لێدان بگەڕێ'
|
||||
History:
|
||||
# On History Page
|
||||
History: 'مێژوو'
|
||||
Watch History: 'مێژووی تەمەشاکردن'
|
||||
Your history list is currently empty.: 'ئێستا لیستەی مێژووت بەتاڵە.'
|
||||
Empty Search Message: 'هیچ ڤیدیۆیەک لە مێژووت نەدۆزرایەوە کە بەرانبەری گەڕانەکەت
|
||||
بێت'
|
||||
Search bar placeholder: "لەناو مێژوو بگەڕێ"
|
||||
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: ''
|
||||
System Default: 'بنەڕەتی سیستەم'
|
||||
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: 'کۆتایی'
|
||||
Hidden: 'شاراوە'
|
||||
Blur: ''
|
||||
Current Invidious Instance: ''
|
||||
The currently set default instance is {instance}: ''
|
||||
No default instance has been set: ''
|
||||
Current instance will be randomized on startup: ''
|
||||
Set Current Instance as Default: ''
|
||||
Clear Default Instance: ''
|
||||
View all Invidious instance information: ''
|
||||
Region for Trending: ''
|
||||
#! List countries
|
||||
External Link Handling:
|
||||
External Link Handling: ''
|
||||
Open Link: 'کردنەوەی بەستەر'
|
||||
Ask Before Opening Link: 'پێش کردنەوەی بەستەر بپرسە'
|
||||
No Action: 'هیچ مەکە'
|
||||
Theme Settings:
|
||||
Theme Settings: 'ڕێکخستنەکانی ڕووکار'
|
||||
Match Top Bar with Main Color: ''
|
||||
Expand Side Bar by Default: ''
|
||||
Disable Smooth Scrolling: ''
|
||||
UI Scale: ''
|
||||
Hide Side Bar Labels: ''
|
||||
Hide FreeTube Header Logo: ''
|
||||
Base Theme:
|
||||
Base Theme: 'ڕووکاری بنچینە'
|
||||
Black: 'ڕەش'
|
||||
Dark: 'تاریک'
|
||||
System Default: 'بنەڕەتی سیستەم'
|
||||
Light: 'ڕووناک'
|
||||
Dracula: ''
|
||||
Catppuccin Mocha: ''
|
||||
Pastel Pink: ''
|
||||
Hot Pink: ''
|
||||
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: 'نارنجی تۆخ'
|
||||
Dracula Cyan: ''
|
||||
Dracula Green: ''
|
||||
Dracula Orange: ''
|
||||
Dracula Pink: ''
|
||||
Dracula Purple: ''
|
||||
Dracula Red: ''
|
||||
Dracula Yellow: ''
|
||||
Catppuccin Mocha Rosewater: ''
|
||||
Catppuccin Mocha Flamingo: ''
|
||||
Catppuccin Mocha Pink: ''
|
||||
Catppuccin Mocha Mauve: ''
|
||||
Catppuccin Mocha Red: ''
|
||||
Catppuccin Mocha Maroon: ''
|
||||
Catppuccin Mocha Peach: ''
|
||||
Catppuccin Mocha Yellow: ''
|
||||
Catppuccin Mocha Green: ''
|
||||
Catppuccin Mocha Teal: ''
|
||||
Catppuccin Mocha Sky: ''
|
||||
Catppuccin Mocha Sapphire: ''
|
||||
Catppuccin Mocha Blue: ''
|
||||
Catppuccin Mocha Lavender: ''
|
||||
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: 'کاراکردنی بنەڕەتیی شێوازی شانۆیی'
|
||||
Scroll Volume Over Video Player: ''
|
||||
Scroll Playback Rate Over Video Player: ''
|
||||
Skip by Scrolling Over Video Player: ''
|
||||
Display Play Button In Video Player: ''
|
||||
Enter Fullscreen on Display Rotate: ''
|
||||
Next Video Interval: ''
|
||||
Fast-Forward / Rewind Interval: ''
|
||||
Default Volume: ''
|
||||
Default Playback Rate: ''
|
||||
Max Video Playback Rate: ''
|
||||
Video Playback Rate Interval: ''
|
||||
Default Video Format:
|
||||
Default Video Format: ''
|
||||
Dash Formats: ''
|
||||
Legacy Formats: ''
|
||||
Audio Formats: ''
|
||||
Default Quality:
|
||||
Default Quality: 'جۆرایەتی بنەڕەت'
|
||||
Auto: 'خۆکار'
|
||||
144p: '١٤٤p'
|
||||
240p: '٢٤٠p'
|
||||
360p: '٣٦٠p'
|
||||
480p: '٤٨٠p'
|
||||
720p: '٧٢٠p'
|
||||
1080p: '١٠٨٠p'
|
||||
1440p: '١٤٤٠p'
|
||||
4k: '٤k'
|
||||
8k: '٨k'
|
||||
Allow DASH AV1 formats: ''
|
||||
Screenshot:
|
||||
Enable: ''
|
||||
Format Label: ''
|
||||
Quality Label: ''
|
||||
Ask Path: 'بۆ بوخچەی پاشەکەوت بپرسە'
|
||||
Folder Label: ''
|
||||
Folder Button: 'دیاریکردنی بوخچە'
|
||||
File Name Label: ''
|
||||
File Name Tooltip: ''
|
||||
Error:
|
||||
Forbidden Characters: ''
|
||||
Empty File Name: ''
|
||||
Comment Auto Load:
|
||||
Comment Auto Load: ''
|
||||
External Player Settings:
|
||||
External Player Settings: 'ڕێکخستنەکانی لێدەری دەرەکی'
|
||||
External Player: 'لێدەری دەرەکی'
|
||||
Ignore Unsupported Action Warnings: ''
|
||||
Custom External Player Executable: ''
|
||||
Custom External Player Arguments: ''
|
||||
Players:
|
||||
None:
|
||||
Name: 'هیچیان'
|
||||
Privacy Settings:
|
||||
Privacy Settings: 'ڕێکخستنەکانی نهێنێتی'
|
||||
Remember History: ''
|
||||
Save Watched Progress: ''
|
||||
Save Watched Videos With Last Viewed Playlist: ''
|
||||
Automatically Remove Video Meta Files: ''
|
||||
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: ''
|
||||
Fetch Automatically: ''
|
||||
Distraction Free Settings:
|
||||
Distraction Free Settings: ''
|
||||
Sections:
|
||||
Side Bar: ''
|
||||
Subscriptions Page: ''
|
||||
Channel Page: 'پەڕەی کەناڵ'
|
||||
Watch Page: 'پەڕەی تەمەشاکردن'
|
||||
General: 'گشتی'
|
||||
Hide Video Views: 'شاردنەوەی ژمارەی بینراوەکانی ڤیدیۆ'
|
||||
Hide Video Likes And Dislikes: ''
|
||||
Hide Channel Subscribers: ''
|
||||
Hide Comment Likes: ''
|
||||
Hide Recommended Videos: 'شاردنەوەی ڤیدیۆ پێشنیازکراوەکان'
|
||||
Hide Trending Videos: ''
|
||||
Hide Popular Videos: ''
|
||||
Hide Playlists: 'شاردنەوەی پێڕستی لێدان'
|
||||
Hide Live Chat: ''
|
||||
Hide Active Subscriptions: ''
|
||||
Hide Video Description: ''
|
||||
Hide Comments: ''
|
||||
Hide Profile Pictures in Comments: ''
|
||||
Display Titles Without Excessive Capitalisation: ''
|
||||
Hide Live Streams: ''
|
||||
Hide Upcoming Premieres: ''
|
||||
Hide Sharing Actions: ''
|
||||
Hide Chapters: ''
|
||||
Hide Channels: ''
|
||||
Hide Channels Disabled Message: ''
|
||||
Hide Channels Placeholder: ''
|
||||
Hide Channels Invalid: ''
|
||||
Hide Channels API Error: ''
|
||||
Hide Channels Already Exists: ''
|
||||
Hide Featured Channels: ''
|
||||
Hide Channel Playlists: ''
|
||||
Hide Channel Community: ''
|
||||
Hide Channel Shorts: ''
|
||||
Hide Channel Podcasts: ''
|
||||
Hide Channel Releases: ''
|
||||
Hide Subscriptions Videos: ''
|
||||
Hide Subscriptions Shorts: ''
|
||||
Hide Subscriptions Live: ''
|
||||
Hide Subscriptions Community: ''
|
||||
Data Settings:
|
||||
Data Settings: 'ڕێکخستنەکانی دراوە'
|
||||
Select Import Type: 'دیاریکردنی جۆری هاوردە'
|
||||
Select Export Type: 'دیاریکردنی جۆری هەناردە'
|
||||
Import Subscriptions: ''
|
||||
Subscription File: ''
|
||||
History File: 'پەڕگەی مێژوو'
|
||||
Playlist File: 'پەڕگەی پێڕستی لێدان'
|
||||
Check for Legacy Subscriptions: ''
|
||||
Export Subscriptions: ''
|
||||
Export FreeTube: 'هەناردەکردنی فریتیوب'
|
||||
Export YouTube: 'هەناردەکردنی یوتیوب'
|
||||
Export NewPipe: 'هەناردەکردنی نیوپایپ'
|
||||
Import History: 'هاوردەکردنی مێژوو'
|
||||
Export History: 'هەناردەکردنی مێژوو'
|
||||
Import Playlists: 'هاوردەکردنی پێڕستی لێدان'
|
||||
Export Playlists: 'هەناردەکردنی پێڕستی لێدان'
|
||||
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: ''
|
||||
Playlist insufficient data: ''
|
||||
All playlists has been successfully imported: ''
|
||||
All playlists 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?: ''
|
||||
SponsorBlock Settings:
|
||||
SponsorBlock Settings: ''
|
||||
Enable SponsorBlock: ''
|
||||
'SponsorBlock API Url (Default is https://sponsor.ajay.app)': ''
|
||||
Notify when sponsor segment is skipped: ''
|
||||
UseDeArrowTitles: ''
|
||||
Skip Options:
|
||||
Skip Option: ''
|
||||
Auto Skip: ''
|
||||
Show In Seek Bar: ''
|
||||
Prompt To Skip: ''
|
||||
Do Nothing: 'هیچ مەکە'
|
||||
Category Color: ''
|
||||
Parental Control Settings:
|
||||
Parental Control Settings: ''
|
||||
Hide Unsubscribe Button: ''
|
||||
Show Family Friendly Only: ''
|
||||
Hide Search Bar: 'شاردنەوەی میلی گەڕان'
|
||||
Download Settings:
|
||||
Download Settings: 'ڕێکخستنەکانی داگرتن'
|
||||
Ask Download Path: ''
|
||||
Choose Path: ''
|
||||
Download Behavior: ''
|
||||
Download in app: ''
|
||||
Open in web browser: ''
|
||||
Experimental Settings:
|
||||
Experimental Settings: ''
|
||||
Warning: ''
|
||||
Replace HTTP Cache: ''
|
||||
Password Dialog:
|
||||
Password: 'تێپەڕەوشە'
|
||||
Enter Password To Unlock: 'تێپەڕەوشە بنووسە بۆ کردنەوەی کڵۆمی ڕێکخستنەکان'
|
||||
Password Incorrect: 'تێپەڕەوشەی نادرووست'
|
||||
Unlock: 'کردنەوەی کڵۆم'
|
||||
Password Settings:
|
||||
Password Settings: 'ڕێکخستنەکانی تێپەڕەوشە'
|
||||
Set Password To Prevent Access: ''
|
||||
Set Password: 'دانانی تێپەڕەوشە'
|
||||
Remove Password: 'لادانی تێپەڕەوشە'
|
||||
About:
|
||||
#On About page
|
||||
About: 'دەربارە'
|
||||
Beta: ''
|
||||
Source code: 'کۆدی سەرچاوە'
|
||||
Licensed under the AGPLv3: 'مۆڵەتی وەشانی سێیەمی AGPL هەیە'
|
||||
View License: 'بینینی مۆڵەت'
|
||||
Downloads / Changelog: ''
|
||||
GitHub releases: ''
|
||||
Help: 'یارمەتی'
|
||||
FreeTube Wiki: 'ویکی فریتیوب'
|
||||
FAQ: 'پرسیارە دووبارەکان'
|
||||
Discussions: ''
|
||||
Report a problem: 'سکاڵا لە کێشەیەک بکە'
|
||||
GitHub issues: ''
|
||||
Please check for duplicates before posting: ''
|
||||
Website: 'وێبگە'
|
||||
Blog: 'بلۆگ'
|
||||
Email: 'ئیمێڵ'
|
||||
Mastodon: 'ماستادۆن'
|
||||
Chat on Matrix: ''
|
||||
Please read the: ''
|
||||
room rules: ''
|
||||
Translate: 'وەرگێڕان'
|
||||
Credits: ''
|
||||
FreeTube is made possible by: ''
|
||||
these people and projects: ''
|
||||
Donate: 'بەخشین'
|
||||
|
||||
Profile:
|
||||
Profile Settings: ''
|
||||
Toggle Profile List: ''
|
||||
Profile Select: ''
|
||||
Profile Filter: ''
|
||||
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 {profile}: ''
|
||||
Removed {profile} from your profiles: ''
|
||||
Your default profile has been changed to your primary profile: ''
|
||||
'{profile} is now the active profile': ''
|
||||
Subscription List: ''
|
||||
Other Channels: ''
|
||||
'{number} selected': '{number} دیاریکراوە'
|
||||
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:
|
||||
Subscribe: ''
|
||||
Unsubscribe: ''
|
||||
Channel has been removed from your subscriptions: ''
|
||||
Removed subscription from {count} other channel(s): ''
|
||||
Added channel to your subscriptions: ''
|
||||
Search Channel: ''
|
||||
Your search results have returned 0 results: ''
|
||||
Sort By: 'ڕیزکردن بە'
|
||||
This channel does not exist: 'ئەم کەناڵە بوونی نییە'
|
||||
This channel does not allow searching: 'ئەم کەناڵە ڕێ بە گەڕان نادات'
|
||||
This channel is age-restricted and currently cannot be viewed in FreeTube.: ''
|
||||
Channel Tabs: ''
|
||||
Videos:
|
||||
Videos: 'ڤیدیۆکان'
|
||||
This channel does not currently have any videos: 'ئەم کەناڵە ئێستا هیچ ڤیدیۆیەکی
|
||||
نییە'
|
||||
Sort Types:
|
||||
Newest: 'نوێترین'
|
||||
Oldest: 'کۆنترین'
|
||||
Most Popular: ''
|
||||
Shorts:
|
||||
This channel does not currently have any shorts: ''
|
||||
Live:
|
||||
Live: 'ڕاستەوخۆ'
|
||||
This channel does not currently have any live streams: ''
|
||||
Playlists:
|
||||
Playlists: 'پێڕستی لێدان'
|
||||
This channel does not currently have any playlists: 'ئەم کەناڵە ئێستا هیچ پێڕستێکی
|
||||
لێدانی نییە'
|
||||
Sort Types:
|
||||
Last Video Added: 'دوایین ڤیدیۆ زیادکراوەکان'
|
||||
Newest: 'نوێترین'
|
||||
Oldest: 'کۆنترین'
|
||||
Podcasts:
|
||||
Podcasts: 'پۆدکاستەکان'
|
||||
This channel does not currently have any podcasts: 'ئەم کەناڵە ئێستا هیچ پۆدکاستێکی
|
||||
نییە'
|
||||
Releases:
|
||||
Releases: ''
|
||||
This channel does not currently have any releases: ''
|
||||
About:
|
||||
About: 'دەربارە'
|
||||
Channel Description: 'پێناسی کەناڵ'
|
||||
Tags:
|
||||
Tags: ''
|
||||
Search for: ''
|
||||
Details: 'وردەکاری'
|
||||
Joined: ''
|
||||
Location: ''
|
||||
Featured Channels: ''
|
||||
Community:
|
||||
This channel currently does not have any posts: ''
|
||||
votes: '{votes} دەنگ'
|
||||
Reveal Answers: ''
|
||||
Hide Answers: 'شاردنەوەی وەڵامەکان'
|
||||
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: ''
|
||||
Views: 'بینراو'
|
||||
Loop Playlist: ''
|
||||
Shuffle Playlist: ''
|
||||
Reverse Playlist: ''
|
||||
Play Next Video: 'لێدانی ڤیدیۆی دواتر'
|
||||
Play Previous Video: 'لێدانی ڤیدیۆی پێشوو'
|
||||
Pause on Current Video: ''
|
||||
Watched: 'تەمەشاکراو'
|
||||
Autoplay: 'خۆلێدان'
|
||||
Starting soon, please refresh the page to check again: ''
|
||||
# As in a Live Video
|
||||
Premieres on: ''
|
||||
Premieres: ''
|
||||
Upcoming: ''
|
||||
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.': ''
|
||||
'Live Chat is unavailable for this stream. It may have been disabled by the uploader.': ''
|
||||
Show Super Chat Comment: ''
|
||||
Scroll to Bottom: ''
|
||||
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: ''
|
||||
In less than a minute: ''
|
||||
Published on: 'بڵاوکرایەوە لە'
|
||||
Streamed on: ''
|
||||
Started streaming on: ''
|
||||
translated from English: ''
|
||||
Publicationtemplate: ''
|
||||
Skipped segment: ''
|
||||
Sponsor Block category:
|
||||
sponsor: ''
|
||||
intro: ''
|
||||
outro: ''
|
||||
self-promotion: ''
|
||||
interaction: ''
|
||||
music offtopic: ''
|
||||
recap: ''
|
||||
filler: ''
|
||||
External Player:
|
||||
OpenInTemplate: ''
|
||||
video: 'ڤیدیۆ'
|
||||
playlist: 'پێڕستی لێدان'
|
||||
OpeningTemplate: ''
|
||||
UnsupportedActionTemplate: ''
|
||||
Unsupported Actions:
|
||||
starting video at offset: ''
|
||||
setting a playback rate: ''
|
||||
opening playlists: ''
|
||||
opening specific video in a playlist (falling back to opening the video): ''
|
||||
reversing playlists: ''
|
||||
shuffling playlists: ''
|
||||
looping playlists: ''
|
||||
Stats:
|
||||
Video statistics are not available for legacy videos: ''
|
||||
Video ID: ''
|
||||
Resolution: ''
|
||||
Player Dimensions: ''
|
||||
Bitrate: ''
|
||||
Volume: ''
|
||||
Bandwidth: ''
|
||||
Buffered: ''
|
||||
Dropped / Total Frames: ''
|
||||
Mimetype: ''
|
||||
#& Videos
|
||||
Unhide Channel: پیشاندانی کەناڵ
|
||||
Hide Channel: شاردنەوەی کەناڵ
|
||||
Videos:
|
||||
#& Sort By
|
||||
Sort By:
|
||||
Newest: 'نوێترین'
|
||||
Oldest: 'کۆنترین'
|
||||
#& Most Popular
|
||||
#& Playlists
|
||||
Playlist:
|
||||
#& About
|
||||
Playlist: 'پێڕستی لێدان'
|
||||
View Full Playlist: ''
|
||||
Videos: 'ڤیدیۆکان'
|
||||
View: 'بینراو'
|
||||
Views: 'بینراو'
|
||||
Last Updated On: ''
|
||||
|
||||
# On Video Watch Page
|
||||
#* Published
|
||||
#& Views
|
||||
Toggle Theatre Mode: ''
|
||||
Change Format:
|
||||
Change Media 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: 'هاوبەشکردنی ڤیدیۆ'
|
||||
Share Channel: 'هاوبەشکردنی کەناڵ'
|
||||
Share Playlist: 'هاوبەشکردنی پێڕستی لێدان'
|
||||
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: ''
|
||||
Clipboard:
|
||||
Copy failed: ''
|
||||
Cannot access clipboard without a secure connection: ''
|
||||
|
||||
Chapters:
|
||||
Chapters: ''
|
||||
'Chapters list visible, current chapter: {chapterName}': ''
|
||||
'Chapters list hidden, current chapter: {chapterName}': ''
|
||||
|
||||
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: 'سەرەتا نوێترینەکان'
|
||||
View {replyCount} replies: ''
|
||||
# Context: View 10 Replies, View 1 Reply, View 1 Reply from Owner, View 2 Replies from Owner and others
|
||||
View: 'بینین'
|
||||
Hide: 'شاردنەوە'
|
||||
Replies: 'وەڵامدانەوەکان'
|
||||
Show More Replies: 'پیشاندانی وەڵامدانەوەی زۆرتر'
|
||||
Reply: 'وەڵامدانەوە'
|
||||
From {channelName}: 'لە {channelName}ەوە'
|
||||
And others: 'هی تر'
|
||||
There are no comments available for this video: ''
|
||||
Load More Comments: ''
|
||||
No more comments available: ''
|
||||
Pinned by: ''
|
||||
Member: 'ئەندام'
|
||||
Subscribed: ''
|
||||
Hearted: ''
|
||||
Up Next: ''
|
||||
|
||||
#Tooltips
|
||||
Tooltips:
|
||||
General Settings:
|
||||
Preferred API Backend: ''
|
||||
Fallback to Non-Preferred Backend on Failure: ''
|
||||
Thumbnail Preference: ''
|
||||
Invidious Instance: ''
|
||||
Region for Trending: ''
|
||||
External Link Handling: |
|
||||
Player Settings:
|
||||
Force Local Backend for Legacy Formats: ''
|
||||
Proxy Videos Through Invidious: ''
|
||||
Default Video Format: ''
|
||||
Allow DASH AV1 formats: ''
|
||||
Scroll Playback Rate Over Video Player: ''
|
||||
Skip by Scrolling Over Video Player: ''
|
||||
External Player Settings:
|
||||
External Player: ''
|
||||
Custom External Player Executable: ''
|
||||
Ignore Warnings: ''
|
||||
Custom External Player Arguments: ''
|
||||
DefaultCustomArgumentsTemplate: "(بنەڕەت: '{defaultCustomArguments}')"
|
||||
Distraction Free Settings:
|
||||
Hide Channels: ''
|
||||
Hide Subscriptions Live: ''
|
||||
Subscription Settings:
|
||||
Fetch Feeds from RSS: ''
|
||||
Fetch Automatically: ''
|
||||
Privacy Settings:
|
||||
Remove Video Meta Files: ''
|
||||
Experimental Settings:
|
||||
Replace HTTP Cache: ''
|
||||
SponsorBlock Settings:
|
||||
UseDeArrowTitles: ''
|
||||
|
||||
# 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: ''
|
||||
Unknown YouTube url type, cannot be opened in app: ''
|
||||
Hashtags have not yet been implemented, try again later: ''
|
||||
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: 'لێدانی ڤیدیۆی پێشوو'
|
||||
Playlist will not pause when current video is finished: ''
|
||||
Playlist will pause when current video is finished: ''
|
||||
Playing Next Video Interval: ''
|
||||
Canceled next video autoplay: ''
|
||||
|
||||
Default Invidious instance has been set to {instance}: ''
|
||||
Default Invidious instance has been cleared: ''
|
||||
'The playlist has ended. Enable loop to continue playing': ''
|
||||
Age Restricted:
|
||||
This {videoOrPlaylist} is age restricted: ''
|
||||
Type:
|
||||
Channel: 'کەناڵ'
|
||||
Video: 'ڤیدیۆ'
|
||||
External link opening has been disabled in the general settings: ''
|
||||
Downloading has completed: ''
|
||||
Starting download: ''
|
||||
Downloading failed: ''
|
||||
Screenshot Success: ''
|
||||
Screenshot Error: ''
|
||||
|
||||
Hashtag:
|
||||
Hashtag: ''
|
||||
This hashtag does not currently have any videos: ''
|
||||
Yes: 'بەڵێ'
|
||||
No: 'نەخێر'
|
||||
Ok: 'باشە'
|
||||
Go to page: بڕۆ بۆ {page}
|
|
@ -346,7 +346,7 @@ Settings:
|
|||
Hide Chapters: Skrýt kapitoly
|
||||
Hide Upcoming Premieres: Skrýt nadcházející premiéry
|
||||
Hide Channels: Skrýt videa z kanálů
|
||||
Hide Channels Placeholder: Název nebo ID kanálu
|
||||
Hide Channels Placeholder: ID kanálu
|
||||
Display Titles Without Excessive Capitalisation: Zobrazit názvy bez nadměrného
|
||||
použití velkých písmen
|
||||
Hide Featured Channels: Skrýt doporučené kanály
|
||||
|
@ -367,6 +367,12 @@ Settings:
|
|||
Hide Profile Pictures in Comments: Skrýt profilové obrázky v komentářích
|
||||
Blur Thumbnails: Rozmazat náhledy
|
||||
Hide Subscriptions Community: Skrýt komunitu odběratelů
|
||||
Hide Channels Invalid: Zadané ID kanálu je neplatné
|
||||
Hide Channels Disabled Message: Některé kanály byly zablokovány pomocí ID a nebyly
|
||||
zpracovány. Při aktualizaci těchto ID je funkce zablokována
|
||||
Hide Channels Already Exists: ID kanálu již existuje
|
||||
Hide Channels API Error: Chyba při načítání uživatele se zadaným ID. Zkontrolujte
|
||||
prosím, zda je zadané ID správné.
|
||||
Data Settings:
|
||||
Data Settings: 'Nastavení dat'
|
||||
Select Import Type: 'Vybrat typ importu'
|
||||
|
@ -606,6 +612,8 @@ Profile:
|
|||
Profile Filter: Filtr profilu
|
||||
Profile Settings: Nastavení profilu
|
||||
Toggle Profile List: Přepnout seznam profilů
|
||||
Open Profile Dropdown: Otevřít rozbalovací nabídku profilu
|
||||
Close Profile Dropdown: Zavřít rozbalovací nabídku profilu
|
||||
Channel:
|
||||
Subscribe: 'Odebírat'
|
||||
Unsubscribe: 'Zrušit odběr'
|
||||
|
@ -800,6 +808,8 @@ Video:
|
|||
'Live Chat is unavailable for this stream. It may have been disabled by the uploader.': Živý
|
||||
chat není pro tento stream k dispozici. Je možné, že byl vypnut nahrávajícím.
|
||||
Pause on Current Video: Pozastavit na současném videu
|
||||
Unhide Channel: Zobrazit kanál
|
||||
Hide Channel: Skrýt kanál
|
||||
Videos:
|
||||
#& Sort By
|
||||
Sort By:
|
||||
|
@ -946,10 +956,9 @@ Tooltips:
|
|||
Replace HTTP Cache: Zakáže mezipaměť HTTP Electronu a povolí vlastní paměťovou
|
||||
mezipaměť pro obrázky v paměti. Povede to ke zvýšenému využití RAM.
|
||||
Distraction Free Settings:
|
||||
Hide Channels: Zadejte název nebo ID kanálu pro skrytí všech videí, playlistů
|
||||
a samotného kanálu před zobrazením ve vyhledávání, trendech, nejpopulárnějších
|
||||
a doporučených. Zadaný název kanálu se musí zcela shodovat a rozlišují se v
|
||||
něm velká a malá písmena.
|
||||
Hide Channels: Zadejte ID kanálu pro skrytí všech videí, playlistů a samotného
|
||||
kanálu před zobrazením ve vyhledávání, trendech, nejpopulárnějších a doporučených.
|
||||
Zadané ID kanálu se musí zcela shodovat a rozlišují se v něm velká a malá písmena.
|
||||
Hide Subscriptions Live: Toto nastavení je nadřazeno nastavením celé aplikace
|
||||
„{appWideSetting}“ v části „{subsection}“ v části „{settingsSection}“
|
||||
SponsorBlock Settings:
|
||||
|
@ -1031,3 +1040,6 @@ Playlist will pause when current video is finished: Po přehrání aktuálního
|
|||
playlist pozastaven
|
||||
Playlist will not pause when current video is finished: Po přehrání aktuálního videa
|
||||
nebude playlist pozastaven
|
||||
Channel Hidden: Kanál {channel} přidán do filtru kanálů
|
||||
Go to page: Přejít na {page}
|
||||
Channel Unhidden: Kanál {channel} odebrán z filtrů kanálů
|
||||
|
|
|
@ -427,7 +427,7 @@ Settings:
|
|||
Hide Chapters: Kapitel ausblenden
|
||||
Hide Upcoming Premieres: Anstehende Premieren ausblenden
|
||||
Hide Channels: Videos aus Kanälen ausblenden
|
||||
Hide Channels Placeholder: Kanalname oder Kanal-ID
|
||||
Hide Channels Placeholder: Kanal-ID
|
||||
Display Titles Without Excessive Capitalisation: Titel ohne übermäßige Großschreibung
|
||||
anzeigen
|
||||
Hide Channel Playlists: Kanal-Wiedergabelisten ausblenden
|
||||
|
@ -448,6 +448,12 @@ Settings:
|
|||
Blur Thumbnails: Vorschaubilder unscharf machen
|
||||
Hide Profile Pictures in Comments: Profilbilder in den Kommentaren ausblenden
|
||||
Hide Subscriptions Community: Abo-Gemeinschaft ausblenden
|
||||
Hide Channels Invalid: Kanal-ID war ungültig
|
||||
Hide Channels Disabled Message: Einige Kanäle wurden mit ID gesperrt und nicht
|
||||
verarbeitet. Die Funktion wird blockiert, während diese IDs aktualisieren
|
||||
Hide Channels Already Exists: Kanal-ID bereits vorhanden
|
||||
Hide Channels API Error: Fehler beim Abrufen von Benutzern mit der bereitgestellten
|
||||
ID. Bitte überprüfen Sie erneut, ob der Ausweis korrekt ist.
|
||||
The app needs to restart for changes to take effect. Restart and apply change?: Die
|
||||
App muss neu gestartet werden, damit die Änderungen wirksam werden. Neu starten
|
||||
und Änderung übernehmen?
|
||||
|
@ -1021,11 +1027,10 @@ Tooltips:
|
|||
und aktiviert einen benutzerdefinierten In-Memory-Image-Cache. Dies führt zu
|
||||
einer erhöhten RAM-Auslastung.
|
||||
Distraction Free Settings:
|
||||
Hide Channels: Gebe einen Kanalnamen oder eine Kanal-ID ein, um zu verhindern,
|
||||
dass alle Videos, Wiedergabelisten und der Kanal selbst in der Suche, den Trends,
|
||||
den beliebtesten und den empfohlenen Videos angezeigt werden. Der eingegebene
|
||||
Kanalname muss vollständig übereinstimmen und es wird zwischen Groß- und Kleinschreibung
|
||||
unterschieden.
|
||||
Hide Channels: Gebe eine Kanal-ID ein, um zu verhindern, dass alle Videos, Wiedergabelisten
|
||||
und der Kanal selbst in der Suche, den Trends, den beliebtesten und den empfohlenen
|
||||
Videos angezeigt werden. Die eingegebene Kanal-ID muss vollständig übereinstimmen
|
||||
und es wird zwischen Groß- und Kleinschreibung unterschieden.
|
||||
Hide Subscriptions Live: Diese Einstellung wird durch die App-weite Einstellung
|
||||
„{appWideSetting}“ im Abschnitt „{subsection}“ der „{settingsSection}“ außer
|
||||
Kraft gesetzt
|
||||
|
|
|
@ -161,6 +161,7 @@ Settings:
|
|||
Middle: 'Μέση'
|
||||
End: 'Τέλος'
|
||||
Hidden: Κρυμμένο
|
||||
Blur: ''
|
||||
'Invidious Instance (Default is https://invidious.snopyta.org)': 'Διακομιστής
|
||||
Invidious (προεπιλογή https://invidious.snopyta.org)'
|
||||
Region for Trending: 'Περιοχή που καθορίζει την καρτέλα των τάσεων'
|
||||
|
|
|
@ -31,6 +31,7 @@ Close: Close
|
|||
Back: Back
|
||||
Forward: Forward
|
||||
Open New Window: Open New Window
|
||||
Go to page: Go to {page}
|
||||
|
||||
Version {versionNumber} is now available! Click for more details: Version {versionNumber} is now available! Click
|
||||
for more details
|
||||
|
@ -149,6 +150,7 @@ History:
|
|||
Settings:
|
||||
# On Settings Page
|
||||
Settings: Settings
|
||||
Expand All Settings Sections: Expand All Settings Sections
|
||||
The app needs to restart for changes to take effect. Restart and apply change?: The
|
||||
app needs to restart for changes to take effect. Restart and apply change?
|
||||
General Settings:
|
||||
|
@ -341,6 +343,7 @@ Settings:
|
|||
Fetch Feeds from RSS: Fetch Feeds from RSS
|
||||
Manage Subscriptions: Manage Subscriptions
|
||||
Fetch Automatically: Fetch Feed Automatically
|
||||
Only Show Latest Video for Each Channel: Only Show Latest Video for Each Channel
|
||||
Distraction Free Settings:
|
||||
Distraction Free Settings: Distraction Free Settings
|
||||
Sections:
|
||||
|
@ -368,7 +371,11 @@ Settings:
|
|||
Hide Sharing Actions: Hide Sharing Actions
|
||||
Hide Chapters: Hide Chapters
|
||||
Hide Channels: Hide Videos From Channels
|
||||
Hide Channels Placeholder: Channel Name or ID
|
||||
Hide Channels Disabled Message: Some channels were blocked using ID and weren't processed. Feature is blocked while those IDs are updating
|
||||
Hide Channels Placeholder: Channel ID
|
||||
Hide Channels Invalid: Channel ID provided was invalid
|
||||
Hide Channels API Error: Error retrieving user with the ID provided. Please check again if the ID is correct.
|
||||
Hide Channels Already Exists: Channel ID already exists
|
||||
Hide Featured Channels: Hide Featured Channels
|
||||
Hide Channel Playlists: Hide Channel Playlists
|
||||
Hide Channel Community: Hide Channel Community
|
||||
|
@ -516,6 +523,9 @@ Profile:
|
|||
Profile Manager: Profile Manager
|
||||
Create New Profile: Create New Profile
|
||||
Edit Profile: Edit Profile
|
||||
Edit Profile Name: Edit Profile Name
|
||||
Create Profile Name: Create Profile Name
|
||||
Profile Name: Profile Name
|
||||
Color Picker: Color Picker
|
||||
Custom Color: Custom Color
|
||||
Profile Preview: Profile Preview
|
||||
|
@ -550,6 +560,8 @@ Profile:
|
|||
Are you sure you want to delete the selected channels? This will not delete the channel from any other profile.: Are
|
||||
you sure you want to delete the selected channels? This will not delete the channel
|
||||
from any other profile.
|
||||
Close Profile Dropdown: Close Profile Dropdown
|
||||
Open Profile Dropdown: Open Profile Dropdown
|
||||
#On Channel Page
|
||||
Channel:
|
||||
Subscribe: Subscribe
|
||||
|
@ -627,6 +639,8 @@ Video:
|
|||
Copy YouTube Channel Link: Copy YouTube Channel Link
|
||||
Open Channel in Invidious: Open Channel in Invidious
|
||||
Copy Invidious Channel Link: Copy Invidious Channel Link
|
||||
Hide Channel: Hide Channel
|
||||
Unhide Channel: Show Channel
|
||||
Views: Views
|
||||
Loop Playlist: Loop Playlist
|
||||
Shuffle Playlist: Shuffle Playlist
|
||||
|
@ -872,8 +886,8 @@ Tooltips:
|
|||
you want to be passed to the external player.
|
||||
DefaultCustomArgumentsTemplate: "(Default: '{defaultCustomArguments}')"
|
||||
Distraction Free Settings:
|
||||
Hide Channels: Enter a channel name or channel ID to hide all videos, playlists and the channel itself from appearing in search, trending, most popular and recommended.
|
||||
The channel name entered must be a complete match and is case sensitive.
|
||||
Hide Channels: Enter a channel ID to hide all videos, playlists and the channel itself from appearing in search, trending, most popular and recommended.
|
||||
The channel ID entered must be a complete match and is case sensitive.
|
||||
Hide Subscriptions Live: 'This setting is overridden by the app-wide "{appWideSetting}" setting, in the "{subsection}" section of the "{settingsSection}"'
|
||||
Subscription Settings:
|
||||
Fetch Feeds from RSS: When enabled, FreeTube will use RSS instead of its default
|
||||
|
@ -930,6 +944,8 @@ Starting download: 'Starting download of "{videoTitle}"'
|
|||
Downloading failed: 'There was an issue downloading "{videoTitle}"'
|
||||
Screenshot Success: Saved screenshot as "{filePath}"
|
||||
Screenshot Error: Screenshot failed. {error}
|
||||
Channel Hidden: '{channel} added to channel filter'
|
||||
Channel Unhidden: '{channel} removed from channel filter'
|
||||
|
||||
Hashtag:
|
||||
Hashtag: Hashtag
|
||||
|
|
|
@ -427,7 +427,7 @@ Settings:
|
|||
Hide Sharing Actions: Hide sharing actions
|
||||
Hide Chapters: Hide chapters
|
||||
Hide Channels: Hide videos from channels
|
||||
Hide Channels Placeholder: Channel name or ID
|
||||
Hide Channels Placeholder: Channel ID
|
||||
Display Titles Without Excessive Capitalisation: Display Titles Without Excessive
|
||||
Capitalisation
|
||||
Hide Featured Channels: Hide featured channels
|
||||
|
@ -447,6 +447,12 @@ Settings:
|
|||
Hide Subscriptions Videos: Hide subscriptions videos
|
||||
Hide Subscriptions Community: Hide subscriptions community
|
||||
Hide Profile Pictures in Comments: Hide profile pictures in comments
|
||||
Hide Channels Invalid: Channel ID provided was invalid
|
||||
Hide Channels Disabled Message: Some channels were blocked using ID and weren't
|
||||
processed. Feature is blocked while those IDs are updating
|
||||
Hide Channels Already Exists: Channel ID already exists
|
||||
Hide Channels API Error: Error retrieving user with the ID provided. Please check
|
||||
again if the ID is correct.
|
||||
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?
|
||||
|
@ -957,9 +963,9 @@ Tooltips:
|
|||
Replace HTTP Cache: Disables Electron's disk-based HTTP cache and enables a custom
|
||||
in-memory image cache. Will lead to increased RAM usage.
|
||||
Distraction Free Settings:
|
||||
Hide Channels: Enter a channel name or channel ID to hide all videos, playlists
|
||||
and the channel itself from appearing in search, trending, most popular and
|
||||
recommended. The channel name entered must be a complete match and is case sensitive.
|
||||
Hide Channels: Enter a channel ID to hide all videos, playlists and the channel
|
||||
itself from appearing in search, trending, most popular and recommended. The
|
||||
channel ID entered must be a complete match and is case sensitive.
|
||||
Hide Subscriptions Live: This setting is overridden by the app-wide ‘{appWideSetting}’
|
||||
setting, in the ‘{subsection}’ section of the ‘{settingsSection}’
|
||||
SponsorBlock Settings:
|
||||
|
|
|
@ -324,6 +324,8 @@ Settings:
|
|||
Fetch Feeds from RSS: 'Recuperar suministros desde RSS'
|
||||
Manage Subscriptions: 'Gestionar suscripciones'
|
||||
Fetch Automatically: Obtener los feed automáticamente
|
||||
Only Show Latest Video for Each Channel: Mostrar solo los últimos vídeos de cada
|
||||
canal
|
||||
Data Settings:
|
||||
Data Settings: 'Datos'
|
||||
Select Import Type: 'Seleccionar tipo de importación'
|
||||
|
@ -420,7 +422,7 @@ Settings:
|
|||
Hide Chapters: Ocultar los capítulos
|
||||
Hide Upcoming Premieres: Ocultar los próximos estrenos
|
||||
Hide Channels: Ocultar los vídeos de los canales
|
||||
Hide Channels Placeholder: Nombre o ID del canal
|
||||
Hide Channels Placeholder: ID del canal
|
||||
Display Titles Without Excessive Capitalisation: Mostrar títulos sin demasiadas
|
||||
mayúsculas
|
||||
Hide Featured Channels: Ocultar los canales recomendados
|
||||
|
@ -441,6 +443,12 @@ Settings:
|
|||
Hide Profile Pictures in Comments: Ocultar las fotos del perfil en los comentarios
|
||||
Blur Thumbnails: Difuminar las miniaturas
|
||||
Hide Subscriptions Community: Ocultar las suscripciones a la comunidad
|
||||
Hide Channels Invalid: El ID del canal proporcionado no es válido
|
||||
Hide Channels Disabled Message: Algunos canales se bloquearon por ID y no se procesaron.
|
||||
La función está bloqueada mientras se actualizan esos ID
|
||||
Hide Channels Already Exists: El ID del canal ya existe
|
||||
Hide Channels API Error: Error al recuperar el usuario con el ID proporcionado.
|
||||
Por favor, compruebe de nuevo si el ID es correcto.
|
||||
The app needs to restart for changes to take effect. Restart and apply change?: ¿Quieres
|
||||
reiniciar FreeTube ahora para aplicar los cambios?
|
||||
Proxy Settings:
|
||||
|
@ -512,6 +520,7 @@ Settings:
|
|||
Enter Password To Unlock: Introduce la contraseña para desbloquear los ajustes
|
||||
Password Incorrect: Contraseña incorrecta
|
||||
Unlock: Desbloquear
|
||||
Expand All Settings Sections: Expandir todas las secciones de los ajustes
|
||||
About:
|
||||
#On About page
|
||||
About: 'Acerca de'
|
||||
|
@ -614,6 +623,11 @@ Profile:
|
|||
Profile Filter: Filtro de perfil
|
||||
Profile Settings: Ajustes del perfil
|
||||
Toggle Profile List: Alternar la lista de los perfiles
|
||||
Open Profile Dropdown: Abrir el desplegable del Perfil
|
||||
Close Profile Dropdown: Cerrar el desplegable del Perfil
|
||||
Profile Name: Nombre del perfil
|
||||
Edit Profile Name: Editar el nombre del perfil
|
||||
Create Profile Name: Crear un nombre para el perfil
|
||||
Channel:
|
||||
Subscribe: 'Suscribirse'
|
||||
Unsubscribe: 'Anular suscripción'
|
||||
|
@ -821,6 +835,8 @@ Video:
|
|||
chat en vivo no está disponible para esta transmisión. Tal vez estaba deshabilitado
|
||||
antes de la retransmisión.
|
||||
Pause on Current Video: Pausa en el vídeo actual
|
||||
Unhide Channel: Mostrar el canal
|
||||
Hide Channel: Ocultar el canal
|
||||
Videos:
|
||||
#& Sort By
|
||||
Sort By:
|
||||
|
@ -996,10 +1012,10 @@ Tooltips:
|
|||
una caché para la imagen en la memoria personalizada. Esto aumentará el uso
|
||||
de la memoria RAM.
|
||||
Distraction Free Settings:
|
||||
Hide Channels: Ingresa un nombre del canal o un ID del canal para ocultar todos
|
||||
los videos, listas de reproducción y el propio canal para que no aparezcan en
|
||||
la búsqueda, tendencias, más populares y recomendados. El nombre del canal ingresado
|
||||
debe ser una coincidencia completa y distinguir entre mayúsculas y minúsculas.
|
||||
Hide Channels: Introduzca un ID del canal para ocultar todos los vídeos, listas
|
||||
de reproducción y el propio canal para que no aparezcan en las búsquedas, tendencias,
|
||||
más populares y recomendados. El ID del canal introducido debe coincidir completamente
|
||||
y se debe distinguir entre mayúsculas y minúsculas.
|
||||
Hide Subscriptions Live: Esta configuración se reemplaza por la configuración
|
||||
«{appWideSetting}» de toda la aplicación, en la sección «{subsection}» de «{settingsSection}»
|
||||
SponsorBlock Settings:
|
||||
|
@ -1066,3 +1082,6 @@ Playlist will pause when current video is finished: La lista de reproducción se
|
|||
cuando termine el vídeo actual
|
||||
Playlist will not pause when current video is finished: La lista de reproducción no
|
||||
se detendrá cuando termine el vídeo actual
|
||||
Go to page: Ir a la {page}
|
||||
Channel Hidden: '{channel} añadido al filtro de canales'
|
||||
Channel Unhidden: '{channel} eliminado del filtro de canales'
|
||||
|
|
|
@ -391,7 +391,7 @@ Settings:
|
|||
Hide Upcoming Premieres: Peida tulevased esilinastused
|
||||
Hide Chapters: Peida peatükid
|
||||
Hide Channels: Peida kanalites leiduvad videod
|
||||
Hide Channels Placeholder: Kanali nimi või tunnus
|
||||
Hide Channels Placeholder: Kanali tunnus
|
||||
Display Titles Without Excessive Capitalisation: Näita pealkirju ilma liigsete
|
||||
suurtähtedeta
|
||||
Sections:
|
||||
|
@ -412,6 +412,13 @@ Settings:
|
|||
Hide Profile Pictures in Comments: Peida kommentaaride profiilipildid
|
||||
Blur Thumbnails: Hägusta pisipildid
|
||||
Hide Subscriptions Community: Peida tellijate loend
|
||||
Hide Channels Invalid: Sisestatud kanali tunnus on vigane
|
||||
Hide Channels Disabled Message: Mõned kanalid on blokeeritud nende tunnuste alusel
|
||||
ja nende andmeid ei töödelda. See funktsionaalsus pole kasutusel, kui nendes
|
||||
kanalites toimub muutusi
|
||||
Hide Channels Already Exists: Selline kanali tunnus juba on sul kirjas
|
||||
Hide Channels API Error: Selle kanali tunnus alusel andmete või kasutaja andmete
|
||||
laadimine ei õnnestunud. Palun kontrolli, et kanali tunnus oleks õige.
|
||||
Proxy Settings:
|
||||
Error getting network information. Is your proxy configured properly?: Võrguteavet
|
||||
ei õnnestu leida. Kas sa oled puhverserveri ikka korralikult seadistanud?
|
||||
|
@ -757,6 +764,8 @@ Video:
|
|||
'Live Chat is unavailable for this stream. It may have been disabled by the uploader.': Otsevestlus
|
||||
pole selle videovoo puhul saadaval. Võib-olla on üleslaadija vestluse keelanud.
|
||||
Pause on Current Video: Peata hetkel esitatav video
|
||||
Unhide Channel: Näita kanalit
|
||||
Hide Channel: Peida kanal
|
||||
Videos:
|
||||
#& Sort By
|
||||
Sort By:
|
||||
|
@ -919,10 +928,10 @@ Tooltips:
|
|||
Skip by Scrolling Over Video Player: MPV-stiilis video läbilappamiseks kasuta
|
||||
hiire ratast.
|
||||
Distraction Free Settings:
|
||||
Hide Channels: Sisesta kanali nimi või kanali ID, et kõik videod, esitusloendid
|
||||
ja kanal ise ei oleks nähtav otsingus, soovitatavate videote, populaarsete videote
|
||||
ja populaarsust koguvate videote vaates. Sisestatud kanali nimi peab otsingule
|
||||
vastama täielikult ja on tõstutundlik.
|
||||
Hide Channels: Sisesta kanali tunnus, et kõik videod, esitusloendid ja kanal ise
|
||||
ei oleks nähtav otsingus, soovitatavate videote, populaarsete videote ja populaarsust
|
||||
koguvate videote vaates. Sisestatud kanali tunnus peab otsingule vastama täpselt
|
||||
ja on tõstutundlik.
|
||||
Hide Subscriptions Live: Selle seadistuse tühistab rakenduseülene „{appWideSetting}“
|
||||
seadistus „{subsection}“/„{settingsSection}“
|
||||
Experimental Settings:
|
||||
|
@ -984,3 +993,6 @@ Playlist will pause when current video is finished: Hetkel mängiva video lõppe
|
|||
esitusloendi esitamine peatub
|
||||
Playlist will not pause when current video is finished: Hetkel mängiva video lõppemisel
|
||||
esitusloendi esitamine jätkub
|
||||
Channel Hidden: '{channel} on lisatud kanalite filtrisse'
|
||||
Go to page: 'Ava leht: {page}'
|
||||
Channel Unhidden: '{channel} on eemaldatud kanalite filtrist'
|
||||
|
|
|
@ -439,7 +439,7 @@ Settings:
|
|||
Hide Chapters: Masquer les chapitres
|
||||
Hide Upcoming Premieres: Cacher les avant-premières à venir
|
||||
Hide Channels: Masquer les vidéos des chaînes
|
||||
Hide Channels Placeholder: Nom ou identifiant de la chaîne
|
||||
Hide Channels Placeholder: Identifiant de la chaîne
|
||||
Display Titles Without Excessive Capitalisation: Afficher les titres sans majuscules
|
||||
excessives
|
||||
Hide Channel Playlists: Masquer les listes de lecture des chaînes
|
||||
|
@ -460,6 +460,13 @@ Settings:
|
|||
Blur Thumbnails: Flouter les miniatures
|
||||
Hide Profile Pictures in Comments: Masquer les photos de profil dans les commentaires
|
||||
Hide Subscriptions Community: Masquer les communautés abonnées
|
||||
Hide Channels Invalid: L'identifiant de la chaîne fourni n'est pas valide
|
||||
Hide Channels Disabled Message: Certaines chaînes ont été bloquées à l'aide d'un
|
||||
identifiant et n'ont pas été traitées. La fonctionnalité est bloquée tant que
|
||||
ces identifiants sont mis à jour
|
||||
Hide Channels Already Exists: L'identifiant de la chaîne existe déjà
|
||||
Hide Channels API Error: Erreur de récupération de l'utilisateur portant l'identifiant
|
||||
fourni. Veuillez vérifier à nouveau si l'ID est correct.
|
||||
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 ?
|
||||
|
@ -811,6 +818,8 @@ Video:
|
|||
chat en direct n'est pas disponible pour ce flux. Il a peut-être été désactivé
|
||||
par le téléchargeur.
|
||||
Pause on Current Video: Pause sur la vidéo en cours
|
||||
Hide Channel: Cacher la chaîne
|
||||
Unhide Channel: Rétablir la chaîne
|
||||
Videos:
|
||||
#& Sort By
|
||||
Sort By:
|
||||
|
@ -956,6 +965,8 @@ Profile:
|
|||
Profile Filter: Filtre de profil
|
||||
Profile Settings: Paramètres du profil
|
||||
Toggle Profile List: Afficher la liste des profils
|
||||
Open Profile Dropdown: Ouvrir la liste déroulante du profil
|
||||
Close Profile Dropdown: Fermer la liste déroulante du profil
|
||||
The playlist has been reversed: La liste de lecture a été inversée
|
||||
A new blog is now available, {blogTitle}. Click to view more: Un nouveau billet est
|
||||
maintenant disponible, {blogTitle}. Cliquez pour en savoir plus
|
||||
|
@ -1038,11 +1049,10 @@ Tooltips:
|
|||
un cache d'image personnalisé en mémoire. Ceci entraînera une augmentation de
|
||||
l'utilisation de la mémoire vive.
|
||||
Distraction Free Settings:
|
||||
Hide Channels: Entrez un nom de chaîne ou un identifiant de chaîne pour empêcher
|
||||
toutes les vidéos, les listes de lecture et la chaîne elle-même d'apparaître
|
||||
dans les recherches, dans les catégories Tendances, Plus populaires et Recommandés.
|
||||
Le nom de la chaîne entré doit correspondre exactement et est sensible à la
|
||||
casse.
|
||||
Hide Channels: Entrez un identifiant de chaîne pour empêcher toutes les vidéos,
|
||||
les listes de lecture et la chaîne elle-même d'apparaître dans les recherches,
|
||||
dans les catégories Tendances, Plus populaires et Recommandés. L'identifiant
|
||||
de la chaîne entré doit correspondre exactement et est sensible aux majuscules.
|
||||
Hide Subscriptions Live: Ce paramètre est remplacé par le paramètre « {appWideSetting} »
|
||||
applicable à l'ensemble de l'application, dans la section « {subsection} » de
|
||||
la section « {settingsSection} »
|
||||
|
@ -1112,3 +1122,6 @@ Playlist will pause when current video is finished: La liste de lecture se met e
|
|||
pause lorsque la vidéo en cours est terminée
|
||||
Playlist will not pause when current video is finished: La liste de lecture ne se
|
||||
met pas en pause lorsque la vidéo en cours est terminée
|
||||
Go to page: Aller à {page}
|
||||
Channel Hidden: '{channel} ajouté au filtre de chaîne'
|
||||
Channel Unhidden: '{channel} retiré du filtre de chaîne'
|
||||
|
|
|
@ -155,6 +155,7 @@ Settings:
|
|||
Middle: 'Sredina'
|
||||
End: 'Kraj'
|
||||
Hidden: Skriveno
|
||||
Blur: Neoštro
|
||||
'Invidious Instance (Default is https://invidious.snopyta.org)': 'Invidious primjerak
|
||||
(standardno se koristi https://invidious.snopyta.org)'
|
||||
Region for Trending: 'Regija za videa u trendu'
|
||||
|
@ -418,7 +419,7 @@ Settings:
|
|||
Hide Chapters: Sakrij poglavlja
|
||||
Hide Upcoming Premieres: Sakrij nadolazeće premijere
|
||||
Hide Channels: Sakrij videa iz kanala
|
||||
Hide Channels Placeholder: Ime kanala ili ID
|
||||
Hide Channels Placeholder: ID kanala
|
||||
Display Titles Without Excessive Capitalisation: Prikaži naslove bez pretjeranog
|
||||
korištenja velikih slova
|
||||
Hide Featured Channels: Sakrij istaknute kanale
|
||||
|
@ -439,6 +440,12 @@ Settings:
|
|||
Blur Thumbnails: Zamuti minijature
|
||||
Hide Profile Pictures in Comments: Sakrij slike profila u komentarima
|
||||
Hide Subscriptions Community: Sakrij pretplate zajednice
|
||||
Hide Channels Invalid: Navedeni ID kanala nije bio ispravan
|
||||
Hide Channels Disabled Message: Neki su kanali blokirani upotrebom ID-a i nisu
|
||||
obrađeni. Funkcija je blokirana tijekom aktualiziranje tih ID-ova
|
||||
Hide Channels Already Exists: ID kanala već postoji
|
||||
Hide Channels API Error: Greška pri dohvaćanju korisnika s navedenim ID-om. Ponovo
|
||||
provjeri točnost ID-a.
|
||||
The app needs to restart for changes to take effect. Restart and apply change?: Promjene
|
||||
će se primijeniti nakon ponovnog pokeretanja programa. Ponovo pokrenuti program?
|
||||
Proxy Settings:
|
||||
|
@ -987,9 +994,9 @@ Tooltips:
|
|||
Replace HTTP Cache: Deaktivira Electronovu HTTP predmemoriju temeljenu na disku
|
||||
i aktivira prilagođenu predmemoriju slika u memoriji. Povećava korištenje RAM-a.
|
||||
Distraction Free Settings:
|
||||
Hide Channels: Upiši ime kanala ili ID kanala za skrivanje svih videa, zbirki
|
||||
kao i samog kanala u pretrazi, trendovima popularnim i preporučenim. Upisano
|
||||
ime kanala se mora potpuno poklapati i razlikuje velika i mala slova.
|
||||
Hide Channels: Upiši ID kanala za skrivanje svih videa, zbirki kao i samog kanala
|
||||
u prikazu pretrage, trendova, najpopularniji i preporučeni. Upisani ID kanala
|
||||
se mora potpuno poklapati i razlikuje velika i mala slova.
|
||||
Hide Subscriptions Live: Ovu postavku nadjačava aplikacijska postavka „{appWideSetting}”,
|
||||
u odjeljku „{subsection}” u „{settingsSection}”
|
||||
SponsorBlock Settings:
|
||||
|
|
|
@ -168,6 +168,7 @@ Settings:
|
|||
Middle: 'Középső'
|
||||
End: 'Vég'
|
||||
Hidden: Rejtett
|
||||
Blur: Elhomályosítás
|
||||
'Invidious Instance (Default is https://invidious.snopyta.org)': 'Invidious példány
|
||||
(Alapértelmezés: https://invidious.snopyta.org)'
|
||||
Region for Trending: 'Népszerű területe'
|
||||
|
@ -427,7 +428,7 @@ Settings:
|
|||
Hide Chapters: Fejezetek elrejtése
|
||||
Hide Upcoming Premieres: Közelgő első előadások elrejtése
|
||||
Hide Channels: Videók elrejtése a csatornákból
|
||||
Hide Channels Placeholder: Csatorna neve vagy azonosítója
|
||||
Hide Channels Placeholder: Csatornaazonosító
|
||||
Display Titles Without Excessive Capitalisation: Címek megjelenítése túlzott nagybetűk
|
||||
nélkül
|
||||
Hide Featured Channels: Kiemelt csatornák elrejtése
|
||||
|
@ -448,6 +449,13 @@ Settings:
|
|||
Blur Thumbnails: Indexkép elhomályosítása
|
||||
Hide Profile Pictures in Comments: Profilképek elrejtése a megjegyzésekben
|
||||
Hide Subscriptions Community: Közösségi feliratkozások elrejtése
|
||||
Hide Channels Invalid: Érvénytelen a megadott csatornaazonosító
|
||||
Hide Channels Disabled Message: Egyes csatornákat letiltottak az azonosító használatával,
|
||||
és nem dolgozták fel őket. A jellemző le van tiltva az azonosítók frissítése
|
||||
közben
|
||||
Hide Channels Already Exists: Már létezik a csatornaazonosító
|
||||
Hide Channels API Error: Hiba történt a megadott azonosítóval rendelkező felhasználó
|
||||
lekérésekor. Kérjük, ellenőrizze még egyszer, hogy helyes-e az azonosító.
|
||||
The app needs to restart for changes to take effect. Restart and apply change?: Az
|
||||
alkalmazásnak újra kell indulnia, hogy a változtatások életbe lépjenek. Indítsa
|
||||
újra és alkalmazza a módosítást?
|
||||
|
@ -989,10 +997,10 @@ Tooltips:
|
|||
Replace HTTP Cache: Letiltja az Electron lemez alapú HTTP-gyorsítótárát, és engedélyezi
|
||||
az egyéni memórián belüli képgyorsítótárat. Megnövekedett RAM-használathoz vezet.
|
||||
Distraction Free Settings:
|
||||
Hide Channels: Adja meg a csatorna nevét vagy csatornaazonosítóját, hogy elrejtse
|
||||
az összes videót, lejátszási listát és magát a csatornát, hogy ne jelenjen meg
|
||||
a keresésben, illetve a felkapott, legnépszerűbb és legajánlottabb. A megadott
|
||||
csatornanévnek teljes egyezésnek kell lennie, és megkülönbözteti a kis- és nagybetűket.
|
||||
Hide Channels: Csatornaazonosító megadása, hogy elrejtse az összes videót, lejátszási
|
||||
listát és magát a csatornát, nehogy megjelenjen a keresésben, a felkapott, legnépszerűbb
|
||||
és legnépszerűbb. A megadott csatornaazonosítónak teljes egyezésnek kell lennie,
|
||||
és megkülönbözteti a kis- és nagybetűket.
|
||||
Hide Subscriptions Live: Ezt a beállítást felülírja az alkalmazásszintű „{appWideSetting}”
|
||||
beállítás a(z) „{settingsSection}” „{subsection}” szakaszában
|
||||
SponsorBlock Settings:
|
||||
|
|
|
@ -331,6 +331,8 @@ Settings:
|
|||
How do I import my subscriptions?: 'Come importo le mie iscrizioni?'
|
||||
Fetch Feeds from RSS: Scarica gli aggiornamenti dai flussi RSS
|
||||
Fetch Automatically: Recupera i feed automaticamente
|
||||
Only Show Latest Video for Each Channel: Mostra solo il video più recente per
|
||||
ciascun canale
|
||||
Advanced Settings:
|
||||
Advanced Settings: 'Impostazioni Avanzate'
|
||||
Enable Debug Mode (Prints data to the console): 'Abilità modalità Sviluppatore
|
||||
|
@ -428,7 +430,7 @@ Settings:
|
|||
Hide Chapters: Nascondi i capitoli
|
||||
Hide Upcoming Premieres: Nascondi le prossime Première
|
||||
Hide Channels: Nascondi i video dai canali
|
||||
Hide Channels Placeholder: Nome o ID del canale
|
||||
Hide Channels Placeholder: ID del canale
|
||||
Display Titles Without Excessive Capitalisation: Visualizza i titoli senza un
|
||||
uso eccessivo di maiuscole
|
||||
Hide Featured Channels: Nascondi i canali in evidenza
|
||||
|
@ -449,6 +451,13 @@ Settings:
|
|||
Hide Profile Pictures in Comments: Nascondi le immagini del profilo nei commenti
|
||||
Blur Thumbnails: Miniature sfocate
|
||||
Hide Subscriptions Community: Nascondi la comunità di iscritti
|
||||
Hide Channels Invalid: L'ID canale fornito non è valido
|
||||
Hide Channels Disabled Message: Alcuni canali sono stati bloccati utilizzando
|
||||
l'ID e non sono stati elaborati. La funzionalità è bloccata durante l'aggiornamento
|
||||
di tali ID
|
||||
Hide Channels Already Exists: L'ID canale esiste già
|
||||
Hide Channels API Error: Errore durante il recupero dell'utente con l'ID fornito.
|
||||
Controlla di nuovo se l'ID è corretto.
|
||||
The app needs to restart for changes to take effect. Restart and apply change?: L'app
|
||||
deve essere riavviata affinché le modifiche abbiano effetto. Riavviare e applicare
|
||||
la modifica?
|
||||
|
@ -508,10 +517,10 @@ Settings:
|
|||
Experimental Settings:
|
||||
Replace HTTP Cache: Sostituisci la cache HTTP
|
||||
Experimental Settings: Impostazioni sperimentali
|
||||
Warning: Queste impostazioni sono sperimentali, causano arresti anomali se abilitate.
|
||||
Warning: Queste Impostazioni sono sperimentali, causano arresti anomali se abilitate.
|
||||
Si consiglia prima di fare un backup. Utilizzare a proprio rischio!
|
||||
Password Dialog:
|
||||
Enter Password To Unlock: Inserisci la password per sbloccare le impostazioni
|
||||
Enter Password To Unlock: Inserisci la password per sbloccare le Impostazioni
|
||||
Password Incorrect: Password non corretta
|
||||
Password: Password
|
||||
Unlock: Sblocca
|
||||
|
@ -519,8 +528,9 @@ Settings:
|
|||
Set Password: Imposta password
|
||||
Password Settings: Impostazioni password
|
||||
Set Password To Prevent Access: Imposta una password per impedire l'accesso alle
|
||||
impostazioni
|
||||
Impostazioni
|
||||
Remove Password: Rimuovi password
|
||||
Expand All Settings Sections: Espandi tutte le sezioni delle Impostazioni
|
||||
About:
|
||||
#On About page
|
||||
About: 'Informazioni'
|
||||
|
@ -787,6 +797,8 @@ Video:
|
|||
chat dal vivo non è disponibile per questo video. Potrebbe essere stata disattivata
|
||||
dall'autore del caricamento.
|
||||
Pause on Current Video: Pausa sul video attuale
|
||||
Unhide Channel: Mostra canale
|
||||
Hide Channel: Nascondi canale
|
||||
Videos:
|
||||
#& Sort By
|
||||
Sort By:
|
||||
|
@ -935,6 +947,11 @@ Profile:
|
|||
Profile Filter: Filtro del profilo
|
||||
Profile Settings: Impostazioni dei profili
|
||||
Toggle Profile List: Attiva/disattiva elenco profili
|
||||
Open Profile Dropdown: Apri il menu a discesa del profilo
|
||||
Close Profile Dropdown: Chiudi il menu a discesa del profilo
|
||||
Profile Name: Nome del profilo
|
||||
Edit Profile Name: Modifica il nome del profilo
|
||||
Create Profile Name: Crea un nome per il profilo
|
||||
This video is unavailable because of missing formats. This can happen due to country unavailability.: Questo
|
||||
video non è disponibile a causa di alcuni formati mancanti. Questo può succedere
|
||||
in caso di mancata disponibilità della nazione.
|
||||
|
@ -955,7 +972,7 @@ Tooltips:
|
|||
o indietro per controllare la velocità di riproduzione. Tieni premuto il tasto
|
||||
CTRL (Command su Mac) e clicca con il tasto sinistro del mouse per tornare rapidamente
|
||||
alla velocità di riproduzione predefinita (1x a meno che non sia stata cambiata
|
||||
nelle impostazioni).
|
||||
nelle Impostazioni).
|
||||
Skip by Scrolling Over Video Player: Usa la rotella di scorrimento per saltare
|
||||
il video, in stile MPV.
|
||||
Allow DASH AV1 formats: I formati DASH AV1 possono avere un aspetto migliore dei
|
||||
|
@ -994,7 +1011,7 @@ Tooltips:
|
|||
un percorso personalizzato può essere impostato qui.
|
||||
External Player: Scegliendo un lettore esterno sarà visualizzata sulla miniatura
|
||||
un'icona per aprire il video nel lettore esterno (se la playlist lo supporta).
|
||||
Attenzione, le impostazioni Invidious non influiscono sui lettori esterni.
|
||||
Attenzione, le Impostazioni Invidious non influiscono sui lettori esterni.
|
||||
DefaultCustomArgumentsTemplate: '(Predefinito: {defaultCustomArguments})'
|
||||
Privacy Settings:
|
||||
Remove Video Meta Files: Se abilitato, quando chiuderai la pagina di riproduzione
|
||||
|
@ -1005,9 +1022,9 @@ Tooltips:
|
|||
una cache di immagini in memoria personalizzata. Comporta un aumento dell'utilizzo
|
||||
della RAM.
|
||||
Distraction Free Settings:
|
||||
Hide Channels: Inserisci il nome o l'ID di un canale per impedire che tutti i
|
||||
video, le playlist e il canale stesso vengano visualizzati nelle ricerche, tendenze,
|
||||
più popolari e consigliati. Il nome del canale inserito deve avere una corrispondenza
|
||||
Hide Channels: Inserisci l'ID di un canale per impedire che tutti i video, le
|
||||
playlist e il canale stesso vengano visualizzati nelle ricerche, tendenze, più
|
||||
popolari e consigliati. Il nome del canale inserito deve avere una corrispondenza
|
||||
completa e fa distinzione tra maiuscole e minuscole.
|
||||
Hide Subscriptions Live: Questa impostazione è sovrascritta dall'impostazione
|
||||
"{appWideSetting}" a livello di app, nella sezione "{subsection}" di "{settingsSection}"
|
||||
|
@ -1031,7 +1048,7 @@ Unknown YouTube url type, cannot be opened in app: Tipo di URL di YouTube sconos
|
|||
Search Bar:
|
||||
Clear Input: Pulisci ricerca
|
||||
External link opening has been disabled in the general settings: L'apertura dei link
|
||||
esterni è stata disabilitata nelle impostazioni generali
|
||||
esterni è stata disabilitata nelle Impostazioni generali
|
||||
Are you sure you want to open this link?: Sei sicuro di voler aprire questo link?
|
||||
Downloading has completed: 'Il download di "{videoTitle}" è terminato'
|
||||
Starting download: Avvio del download di "{videoTitle}"
|
||||
|
@ -1075,3 +1092,6 @@ Playlist will pause when current video is finished: La playlist verrà messa in
|
|||
al termine del video attuale
|
||||
Playlist will not pause when current video is finished: La playlist non verrà messa
|
||||
in pausa al termine del video attuale
|
||||
Channel Hidden: '{channel} aggiunto al filtro canali'
|
||||
Go to page: Vai a {page}
|
||||
Channel Unhidden: '{channel} rimosso dal filtro canali'
|
||||
|
|
|
@ -39,7 +39,7 @@ Global:
|
|||
Counts:
|
||||
Video Count: 1 件の動画 | {count} 件の動画
|
||||
Subscriber Count: 1 登録者 | {count} 登録者
|
||||
View Count: 1 回表示 | {count} 回表示
|
||||
View Count: 1 回視聴 | {count} 回視聴
|
||||
Watching Count: 1 人が視聴中 | {count} 人が視聴中
|
||||
Channel Count: 1 チャンネル | {count} チャンネル
|
||||
Search / Go to URL: '検索 / URL の表示'
|
||||
|
@ -145,6 +145,7 @@ Settings:
|
|||
Middle: '中間'
|
||||
End: '終了'
|
||||
Hidden: 非表示
|
||||
Blur: ぼかし
|
||||
'Invidious Instance (Default is https://invidious.snopyta.org)': '接続先の Invidious
|
||||
サーバー(初期値は https://invidious.snopyta.org)'
|
||||
Region for Trending: '急上昇の地域設定'
|
||||
|
@ -387,7 +388,7 @@ Settings:
|
|||
Hide Chapters: チャプターの非表示
|
||||
Hide Upcoming Premieres: 今後のプレミア公開を非表示
|
||||
Hide Channels: チャンネルから動画を非表示
|
||||
Hide Channels Placeholder: チャネル名または ID
|
||||
Hide Channels Placeholder: チャンネル ID
|
||||
Display Titles Without Excessive Capitalisation: 英語の大文字化を控えたタイトル表示
|
||||
Hide Channel Playlists: チャンネル再生リストの非表示
|
||||
Hide Channel Community: チャンネル コミュニティの非表示
|
||||
|
@ -407,6 +408,10 @@ Settings:
|
|||
Hide Profile Pictures in Comments: コメントでプロフィール写真を隠す
|
||||
Blur Thumbnails: サムネイルをぼかす
|
||||
Hide Subscriptions Community: チャンネルの購読者リストの非公開
|
||||
Hide Channels Invalid: 提供チャンネル ID 無効
|
||||
Hide Channels Disabled Message: 一部のチャンネルが ID を使用してブロックされ、処理されませんでした。これらの ID が更新されている間、機能はブロックされます
|
||||
Hide Channels Already Exists: 既に存在するチャンネル ID
|
||||
Hide Channels API Error: 提供された ID のユーザーを取得できませんでした。ID が正しいかどうかをもう一度確認してください。
|
||||
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?: ネットワーク情報の取得中にエラーが発生しました。プロキシーを正しく設定してますか?
|
||||
|
@ -460,7 +465,8 @@ Settings:
|
|||
Experimental Settings:
|
||||
Replace HTTP Cache: HTTP キャッシュの置換
|
||||
Experimental Settings: 実験中の設定
|
||||
Warning: この設定項目は実験中であるため、有効にするとクラッシュすることがあります。必ずバックアップを作成してください。使用は自己責任でお願いします!
|
||||
Warning:
|
||||
これらの設定は実験的なものであり、有効にするとアプリのクラッシュを引き起こす恐れがあります。バックアップをとっておくことを強くお勧めします。自己責任で使用してください!
|
||||
Password Settings:
|
||||
Password Settings: パスワード設定
|
||||
Set Password To Prevent Access: 設定の変更にはパスワードを設定とする
|
||||
|
@ -886,8 +892,8 @@ Tooltips:
|
|||
Replace HTTP Cache: Electron のディスクに基づく HTTP キャッシュを無効化し、メモリ内で独自の画像キャッシュを使用します。このことにより
|
||||
RAM の使用率は増加します。
|
||||
Distraction Free Settings:
|
||||
Hide Channels: チャンネル名またはチャンネル ID
|
||||
を入力すると、すべてのビデオ、再生リスト、チャンネル自体が検索や人気、およびおすすめに表示されなくなります。入力するチャンネル名は、大文字と小文字を区別するので完全に一致させてください。
|
||||
Hide Channels: チャンネル ID を入力すると、すべてのビデオ、再生リスト、チャンネル自体が検索や人気、およびおすすめに表示されなくなります。入力するチャンネル
|
||||
ID は、大文字と小文字を区別するので完全に一致させてください。
|
||||
Hide Subscriptions Live: この設定は、アプリ全体の "{appWideSetting}" 設定により上書きされます。"{settingsSection}"
|
||||
項目の "{subsection}" にあります
|
||||
SponsorBlock Settings:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Put the name of your locale in the same language
|
||||
Locale Name: 'kur-ckb'
|
||||
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.': >-
|
||||
|
@ -7,20 +7,20 @@ FreeTube: 'فریتیوب'
|
|||
رویداوە.
|
||||
|
||||
# Webkit Menu Bar
|
||||
File: 'فایل'
|
||||
Quit: 'چونەدەرەوە'
|
||||
Edit: 'دەستکاریکردن'
|
||||
Undo: 'گەڕانەوە'
|
||||
File: 'پەڕگە'
|
||||
Quit: 'دەرچوون'
|
||||
Edit: 'دەستکاری'
|
||||
Undo: 'پووچکردنەوە'
|
||||
Redo: 'هێنانەوە'
|
||||
Cut: 'بڕین'
|
||||
Copy: 'کۆپی'
|
||||
Paste: 'پەیست'
|
||||
Copy: 'لەبەرگرتنەوە'
|
||||
Paste: 'لکاندن'
|
||||
Delete: 'سڕینەوە'
|
||||
Select all: 'دیاریکردنی هەمووی'
|
||||
Reload: 'دوبارە دابەزاندن'
|
||||
Force Reload: 'دوباری دابەزاندی بەهێز'
|
||||
Toggle Developer Tools: 'ئەدەواتەکانی دیڤیڵۆپەر بەردەست بخە'
|
||||
Actual size: 'گەورەیی راستی'
|
||||
Reload: 'بارکردنەوە'
|
||||
Force Reload: 'بارکردنەوەی بەزۆر'
|
||||
Toggle Developer Tools: 'زامنی ئامرازەکانی گەشەپێدەر'
|
||||
Actual size: 'قەبارەی ڕاستەقینە'
|
||||
Zoom in: 'زووم کردنە ناوەوە'
|
||||
Zoom out: 'زووم کردنە دەرەوە'
|
||||
Toggle fullscreen: 'شاشەکەت پرکەرەوە'
|
||||
|
@ -30,9 +30,9 @@ Close: 'داخستن'
|
|||
Back: 'گەڕانەوە'
|
||||
Forward: 'چونەپێشەوە'
|
||||
|
||||
Version {versionNumber} is now available! Click for more details: 'ڤێرژنی {versionNumber}
|
||||
ئێستا بەردەستە! کلیک بکە بۆ زانیاری زیاتر'
|
||||
Download From Site: 'دایبەزێنە لە سایتەکەوە'
|
||||
Version {versionNumber} is now available! Click for more details: 'ئێستا وەشانی {versionNumber}
|
||||
بەردەستە..بۆ زانیاری زۆرتر کرتە بکە'
|
||||
Download From Site: 'لە وێبگەوە دایگرە'
|
||||
A new blog is now available, {blogTitle}. Click to view more: 'بڵۆگێکی نوێ بەردەستە،
|
||||
{blogTitle}. کلیک بکە بۆ بینینی زیاتر'
|
||||
|
||||
|
@ -190,3 +190,8 @@ Profile:
|
|||
Removed {profile} from your profiles: 'سڕاوەتەوە لە پرۆفایلەکانت {profile}'
|
||||
Channel:
|
||||
Playlists: {}
|
||||
New Window: پەنجەرەی نوێ
|
||||
Go to page: بڕۆ بۆ {page}
|
||||
Preferences: هەڵبژاردەکان
|
||||
Are you sure you want to open this link?: دڵنیایت دەتەوێت ئەم بەستەرە بکەیتەوە؟
|
||||
Open New Window: کردنەوەی پەنجەرەیەکی نوێ
|
||||
|
|
|
@ -426,7 +426,7 @@ Settings:
|
|||
Hide Chapters: Schowaj rozdziały
|
||||
Hide Upcoming Premieres: Schowaj nadchodzące premiery
|
||||
Hide Channels: Schowaj filmy z kanałów
|
||||
Hide Channels Placeholder: Nazwa albo ID kanału
|
||||
Hide Channels Placeholder: ID kanału
|
||||
Display Titles Without Excessive Capitalisation: Wyświetlaj tytuły bez nadmiernych
|
||||
wielkich liter
|
||||
Hide Channel Community: Schowaj społeczność kanału
|
||||
|
@ -447,6 +447,13 @@ Settings:
|
|||
Hide Profile Pictures in Comments: Nie pokazuj zdjęć profilowych w komentarzach
|
||||
Blur Thumbnails: Rozmazuj miniaturki
|
||||
Hide Subscriptions Community: Schowaj „Społeczność” kanałów
|
||||
Hide Channels Invalid: Podane ID kanału jest niepoprawne
|
||||
Hide Channels Disabled Message: Niektóre z kanałów nie zostały przetworzone, ponieważ
|
||||
zostały zablokowane po swoim ID. Funkcja jest zablokowana, aż do momentu, w
|
||||
którym zostaną one zaktualizowane.
|
||||
Hide Channels Already Exists: To ID kanału już jest
|
||||
Hide Channels API Error: Nie udało się wyszukać użytkownika po podanym ID. Proszę
|
||||
sprawdzić, czy wpisane ID jest poprawne.
|
||||
The app needs to restart for changes to take effect. Restart and apply change?: Aplikacja
|
||||
musi zostać ponownie uruchomiona, aby zmiany zostały wprowadzone. Uruchomić ponownie
|
||||
i zastosować zmiany?
|
||||
|
@ -791,6 +798,8 @@ Video:
|
|||
na żywo jest nie dostępny dla tej transmisji. Być może został on wyłączony przez
|
||||
osobę wstawiającą.
|
||||
Pause on Current Video: Zatrzymaj po tym filmie
|
||||
Unhide Channel: Pokaż kanał
|
||||
Hide Channel: Ukryj kanał
|
||||
Videos:
|
||||
#& Sort By
|
||||
Sort By:
|
||||
|
@ -932,6 +941,8 @@ Profile:
|
|||
Profile Filter: Filtr profilu
|
||||
Profile Settings: Ustawienia profilu
|
||||
Toggle Profile List: Włącz/wyłącz listę profili
|
||||
Open Profile Dropdown: Otwórz rozwijane menu profilu
|
||||
Close Profile Dropdown: Zamknij rozwijane menu profilu
|
||||
The playlist has been reversed: Playlista została odwrócona
|
||||
A new blog is now available, {blogTitle}. Click to view more: 'Nowy wpis na blogu
|
||||
jest dostępny, {blogTitle}. Kliknij, aby zobaczyć więcej'
|
||||
|
@ -1006,10 +1017,10 @@ Tooltips:
|
|||
Electrona i włącza własny obraz pamięci podręcznej wewnątrz pamięci RAM. Spowoduje
|
||||
to większe użycie pamięci RAM.
|
||||
Distraction Free Settings:
|
||||
Hide Channels: Wprowadź nazwę albo ID kanału, aby schować wszystkie filmy i playlisty
|
||||
tego kanału, oraz sam kanał z wyszukiwań, z zakładek „Na czasie” i „Popularne”
|
||||
oraz z polecanych. Nazwa kanału musi być dokładnym dopasowaniem, z uwzględnieniem
|
||||
wielkości liter.
|
||||
Hide Channels: Wprowadź ID kanału, aby schować wszystkie filmy i playlisty tego
|
||||
kanału, oraz sam kanał z wyszukiwań, z zakładek „Na czasie” i „Popularne” oraz
|
||||
z polecanych. ID kanału musi być dokładnym dopasowaniem, z uwzględnieniem wielkości
|
||||
liter.
|
||||
Hide Subscriptions Live: Ta opcja została nadpisana opcją ogólną „{appWideSetting}”
|
||||
z podgrupy „{subsection}” grupy „{settingsSection}”
|
||||
SponsorBlock Settings:
|
||||
|
@ -1079,3 +1090,6 @@ Playlist will pause when current video is finished: Playlista zatrzyma się, gdy
|
|||
film się zakończy
|
||||
Playlist will not pause when current video is finished: Playlista nie zatrzyma się,
|
||||
gdy obecny film się zakończy
|
||||
Channel Hidden: '{channel} dodany do filtra kanałów'
|
||||
Go to page: Idź do {page}
|
||||
Channel Unhidden: '{channel} usunięty z filtra kanału'
|
||||
|
|
|
@ -452,7 +452,7 @@ Settings:
|
|||
Hide Chapters: Ocultar capítulos
|
||||
Hide Upcoming Premieres: Ocultar próximas estreias
|
||||
Hide Channels: Ocultar vídeos dos canais
|
||||
Hide Channels Placeholder: Nome ou ID do canal
|
||||
Hide Channels Placeholder: ID do canal
|
||||
Display Titles Without Excessive Capitalisation: Mostrar títulos sem maiúsculas
|
||||
em excesso
|
||||
Hide Featured Channels: Ocultar canais em destaque
|
||||
|
@ -473,6 +473,13 @@ Settings:
|
|||
Hide Profile Pictures in Comments: Ocultar imagens de perfil nos comentários
|
||||
Blur Thumbnails: Desfocar miniaturas
|
||||
Hide Subscriptions Community: Ocultar subscrições de comunidades
|
||||
Hide Channels Invalid: O ID do canal não é válido
|
||||
Hide Channels Disabled Message: Alguns canais foram bloqueados e não foram processados.
|
||||
A funcionalidade está bloqueada enquanto estiver a ocorrer a atualização das
|
||||
ID.
|
||||
Hide Channels Already Exists: Esta ID já existe
|
||||
Hide Channels API Error: Não foi possível obter o utilizador através do ID. Verifique
|
||||
se a ID indicada é válida.
|
||||
External Player Settings:
|
||||
Custom External Player Arguments: Argumentos do reprodutor externo
|
||||
Custom External Player Executable: Executável do reprodutor externo
|
||||
|
@ -1002,9 +1009,9 @@ Tooltips:
|
|||
Replace HTTP Cache: Desativa a cache HTTP Electron e ativa uma cache de imagem
|
||||
na memória personalizada. Implica o aumento da utilização de memória RAM.
|
||||
Distraction Free Settings:
|
||||
Hide Channels: Introduza o nome ou ID de um canal para impedir que os vídeos,
|
||||
listas de reprodução e o próprio canal apareçam na pesquisa, tendências, mais
|
||||
populares e recomendados. O nome do canal introduzido tem que ser uma correspondência
|
||||
Hide Channels: Introduza o ID de um canal para impedir que os vídeos, listas de
|
||||
reprodução e o próprio canal apareçam na pesquisa, tendências, mais populares
|
||||
e recomendados. O nome do canal introduzido tem que ser uma correspondência
|
||||
exata e diferencia maiúsculas de minúsculas.
|
||||
Hide Subscriptions Live: Esta definição é substituída pela definição global "{appWideSetting}",
|
||||
existente na secção "{subsection}" de "{settingsSection}"
|
||||
|
|
|
@ -419,7 +419,7 @@ Settings:
|
|||
Hide Video Description: Скрыть описание видео
|
||||
Hide Chapters: Скрыть разделы
|
||||
Hide Upcoming Premieres: Скрыть предстоящие премьеры
|
||||
Hide Channels Placeholder: Название или идентификатор канала
|
||||
Hide Channels Placeholder: Идентификатор канала
|
||||
Hide Channels: Скрыть видео с каналов
|
||||
Display Titles Without Excessive Capitalisation: Отображать заголовки без чрезмерного
|
||||
использования заглавных букв
|
||||
|
@ -439,6 +439,13 @@ Settings:
|
|||
Hide Channel Podcasts: Скрыть звукопередачи канала
|
||||
Hide Subscriptions Shorts: Скрыть короткие видео из подписок
|
||||
Hide Profile Pictures in Comments: Скрыть изображения профилей в комментариях
|
||||
Hide Channels Invalid: Указанный идентификатор канала недействителен
|
||||
Hide Subscriptions Community: Скрыть сообщество подписок
|
||||
Hide Channels Disabled Message: Некоторые каналы были заблокированы по идентификатору
|
||||
и не были обработаны. Функция заблокирована, пока эти идентификаторы обновляются
|
||||
Hide Channels Already Exists: Идентификатор канала уже существует
|
||||
Hide Channels API Error: Ошибка при получении пользователя с предоставленным идентификатором.
|
||||
Пожалуйста, проверьте еще раз, правильно ли указан идентификатор.
|
||||
The app needs to restart for changes to take effect. Restart and apply change?: Чтобы
|
||||
изменения вступили в силу, необходимо перезапустить приложение. Перезапустить
|
||||
и применить изменения?
|
||||
|
@ -994,10 +1001,9 @@ Tooltips:
|
|||
Replace HTTP Cache: Отключает дисковый HTTP-кэш Electron и включает пользовательский
|
||||
кэш изображений в памяти. Приведёт к увеличению использования оперативной памяти.
|
||||
Distraction Free Settings:
|
||||
Hide Channels: Введите название канала или его идентификатор, чтобы скрыть все
|
||||
видео, подборки и сам канал от показа в поиске, трендах, наиболее просматриваемых
|
||||
и желательных. Введённое название канала должно полностью совпадать и учитывать
|
||||
регистр.
|
||||
Hide Channels: Введите идентификатор канала, чтобы скрыть все видео, плейлисты
|
||||
и сам канал от появления в результатах поиска, трендах, самых популярных и рекомендуемых.
|
||||
Введенный идентификатор канала должен полностью совпадать и чувствителен к регистру.
|
||||
Hide Subscriptions Live: Эта настройка переопределена общей настройкой «{appWideSetting}»,
|
||||
в подразделе «{subsection}» раздела «{settingsSection}»
|
||||
SponsorBlock Settings:
|
||||
|
|
|
@ -7,17 +7,17 @@ FreeTube: 'FreeTube'
|
|||
# Webkit Menu Bar
|
||||
File: 'Súbor'
|
||||
Quit: 'Ukončiť'
|
||||
Edit: 'Upravovať'
|
||||
Edit: 'Upraviť'
|
||||
Undo: 'Vrátiť späť'
|
||||
Redo: 'Znovu vykonať'
|
||||
Cut: 'Strih'
|
||||
Cut: 'Vystrihnúť'
|
||||
Copy: 'Kopírovať'
|
||||
Paste: 'Vložiť'
|
||||
Delete: 'Vymazať'
|
||||
Select all: 'Vybrať všetko'
|
||||
Reload: 'Obnoviť'
|
||||
Force Reload: 'Vynútiť obnovenie'
|
||||
Toggle Developer Tools: 'Spínať nástroje pre vývojárov'
|
||||
Toggle Developer Tools: 'Prepnúť vývojárske nástroje'
|
||||
Actual size: 'Skutočná veľkosť'
|
||||
Zoom in: 'Priblížiť'
|
||||
Zoom out: 'Oddialiť'
|
||||
|
@ -25,8 +25,8 @@ Toggle fullscreen: 'Prepnúť na celú obrazovku'
|
|||
Window: 'Okno'
|
||||
Minimize: 'Minimalizovať'
|
||||
Close: 'Zavrieť'
|
||||
Back: 'Späť'
|
||||
Forward: 'Vpred'
|
||||
Back: 'Dozadu'
|
||||
Forward: 'Dopredu'
|
||||
|
||||
# Global
|
||||
# Anything shared among components / views should be put here
|
||||
|
@ -34,16 +34,23 @@ Global:
|
|||
Videos: 'Videá'
|
||||
|
||||
# Search Bar
|
||||
Search / Go to URL: 'Hľadať / Ísť na adresu URL'
|
||||
Counts:
|
||||
Video Count: 1 video | {count} videí
|
||||
Subscriber Count: 1 odberateľ | {count} odberateľov
|
||||
Channel Count: 1 kanál | {count} kanálov
|
||||
Live: Naživo
|
||||
Community: Komunita
|
||||
Shorts: Shorts
|
||||
Search / Go to URL: 'Hľadať / Ísť na URL adresu'
|
||||
# In Filter Button
|
||||
Search Filters:
|
||||
Search Filters: 'Vyhľadávacie filtre'
|
||||
Sort By:
|
||||
Sort By: 'Triediť podľa'
|
||||
Most Relevant: 'Najrelevantnejšie'
|
||||
Most Relevant: 'Najvhodnejšie'
|
||||
Rating: 'Hodnotenie'
|
||||
Upload Date: 'Dátum nahratia'
|
||||
View Count: 'Počet zhliadnutí'
|
||||
View Count: 'Počet zobrazení'
|
||||
Time:
|
||||
Time: 'Čas'
|
||||
Any Time: 'Kedykoľvek'
|
||||
|
@ -53,37 +60,36 @@ Search Filters:
|
|||
This Month: 'Tento mesiac'
|
||||
This Year: 'Tento rok'
|
||||
Type:
|
||||
Type: 'Typ'
|
||||
All Types: 'Všetky druhy'
|
||||
Type: 'Druh'
|
||||
All Types: 'Všetky'
|
||||
Videos: 'Videá'
|
||||
Channels: 'Kanály'
|
||||
#& Playlists
|
||||
Movies: Filmy
|
||||
Duration:
|
||||
Duration: 'Trvanie'
|
||||
All Durations: 'Všetky trvania'
|
||||
All Durations: 'Všetky'
|
||||
Short (< 4 minutes): 'Krátke (menej ako 4 minúty)'
|
||||
Long (> 20 minutes): 'Dlhé (viac ako 20 minút)'
|
||||
# On Search Page
|
||||
Medium (4 - 20 minutes): Stredné (4 až 20 minút)
|
||||
Search Results: 'Výsledky vyhľadávania'
|
||||
Fetching results. Please wait: 'Načítavajú sa výsledky. Prosím čakajte'
|
||||
Fetching results. Please wait: 'Načítavam výsledky. Prosím čakajte'
|
||||
Fetch more results: 'Načítať viac výsledkov'
|
||||
# Sidebar
|
||||
There are no more results for this search: Pre toto hľadanie nie sú k dispozícii
|
||||
žiadne ďalšie výsledky
|
||||
There are no more results for this search: Pre toto hľadanie nie sú ďalšie výsledky
|
||||
Subscriptions:
|
||||
# On Subscriptions Page
|
||||
Subscriptions: 'Odbery'
|
||||
Latest Subscriptions: 'Najnovšie Odbery'
|
||||
Latest Subscriptions: 'Najnovšie odbery'
|
||||
'Your Subscription list is currently empty. Start adding subscriptions to see them here.': 'Váš
|
||||
zoznam odberov je momentálne prázdny. Začnite pridávať odbery aby sa tu zobrazili.'
|
||||
zoznam odberov je prázdny. Začnite pridávať odbery aby sa tu zobrazili.'
|
||||
'Getting Subscriptions. Please wait.': 'Získavanie odberov. Prosím čakajte.'
|
||||
Load More Videos: Načítať viac videí
|
||||
Refresh Subscriptions: Načítať odbery znovu
|
||||
'Getting Subscriptions. Please wait.': Nahrávam odbery, prosím počkajte.
|
||||
Refresh Subscriptions: Obnoviť odbery
|
||||
'Getting Subscriptions. Please wait.': Sťahujem odbery, prosím čakajte.
|
||||
This profile has a large number of subscriptions. Forcing RSS to avoid rate limiting: Tento
|
||||
profil má mnoho odberateľov. Vždy použite RSS, aby ste obišli limit
|
||||
profil má priveľa odberov. Vynucujem RSS na obídenie limitov
|
||||
Trending:
|
||||
Trending: 'Trendy'
|
||||
Default: Predvolené
|
||||
|
@ -91,30 +97,30 @@ Trending:
|
|||
Movies: Filmy
|
||||
Trending Tabs: Karty trendov
|
||||
Gaming: Hry
|
||||
Most Popular: 'Najpopulárnejšie'
|
||||
Playlists: 'Zoznamy'
|
||||
Most Popular: 'Populárne'
|
||||
Playlists: 'Playlist'
|
||||
User Playlists:
|
||||
Your Playlists: 'Vaše zoznamy'
|
||||
Your Playlists: 'Tvoj playlist'
|
||||
Your saved videos are empty. Click on the save button on the corner of a video to have it listed here: Vaše
|
||||
uložené videá sú prázdne. Kliknutím na tlačidlo uložiť v rohu videa ho tu zobrazíte
|
||||
Playlist Message: Táto stránka neodráža plne funkčné zoznamy videí. Uvádza iba zoznam
|
||||
videí, ktoré ste uložili alebo zaradili medzi obľúbené. Po dokončení práce sa
|
||||
všetky videá, ktoré sa tu nachádzajú, migrujú do zoznamu „Obľúbené“.
|
||||
Playlist Message: Zatiaľ tu nie sú plne funkčné playlisty. Sú tu len videá, ktoré
|
||||
ste uložili medzi obľúbené. Keď playlisty plne implementujeme do aplikácie, presunú
|
||||
sa do „Obľúbené“.
|
||||
History:
|
||||
# On History Page
|
||||
History: 'História'
|
||||
Watch History: 'História pozeraných videí'
|
||||
Watch History: 'Pozreté videá'
|
||||
Your history list is currently empty.: 'Váša história je momentálne prázdna.'
|
||||
Settings:
|
||||
# On Settings Page
|
||||
Settings: 'Nastavenia'
|
||||
General Settings:
|
||||
General Settings: 'Všeobecné nastavenia'
|
||||
Fallback to Non-Preferred Backend on Failure: 'Záloha k nepreferovanému backendu
|
||||
pri poruche'
|
||||
Enable Search Suggestions: 'Povoliť návrhy na vyhľadávanie'
|
||||
Fallback to Non-Preferred Backend on Failure: 'Pri chybe prepnúť na alternatívny
|
||||
backend'
|
||||
Enable Search Suggestions: 'Zapnúť návrhy vyhľadávania'
|
||||
Default Landing Page: 'Predvolená vstupná stránka'
|
||||
Locale Preference: 'Predvoľba miestneho nastavenia'
|
||||
Locale Preference: 'Nastaviť jazyk'
|
||||
Preferred API Backend:
|
||||
Preferred API Backend: 'Preferované API Backend'
|
||||
Local API: 'Lokálne API'
|
||||
|
@ -133,10 +139,10 @@ Settings:
|
|||
(Predvolená je https://invidious.snopyta.org)'
|
||||
Region for Trending: 'Región pre trendy'
|
||||
#! List countries
|
||||
Check for Latest Blog Posts: Skontrolovať najnovšie príspevky na blogu
|
||||
Check for Updates: Skontrolovať aktualizácie
|
||||
Check for Latest Blog Posts: Kontrolovať najnovšie príspevky na blogu
|
||||
Check for Updates: Kontrolovať aktualizácie
|
||||
View all Invidious instance information: Zobraziť všetko info o Invidious inštancií
|
||||
System Default: Určené Systémom
|
||||
System Default: Podľa systému
|
||||
Set Current Instance as Default: Nastaviť Aktuálne vybranú inštanciu ako predvolenú
|
||||
External Link Handling:
|
||||
External Link Handling: Spracovanie externých odkazov
|
||||
|
@ -325,7 +331,7 @@ Settings:
|
|||
Search cache has been cleared: Vyrovnávacia pamäť vyhľadávania bola vymazaná
|
||||
Automatically Remove Video Meta Files: Automaticky Odstrániť Metasúbory Videa
|
||||
The app needs to restart for changes to take effect. Restart and apply change?: Aplikácia
|
||||
požaduje reštartovanie aby sa prejavili zmeny. Reštartovať a aplikovať zmeny?
|
||||
požaduje reštart, aby sa zmeny prejavili. Reštartovať a aplikovať zmeny?
|
||||
Proxy Settings:
|
||||
Error getting network information. Is your proxy configured properly?: Chyba pri
|
||||
získavaní informácií o sieti. Je váš server proxy správne nakonfigurovaný?
|
||||
|
@ -465,8 +471,8 @@ Video:
|
|||
momentálne podporovaný.'
|
||||
'Chat is disabled or the Live Stream has ended.': 'Chat je zakázaný alebo sa priamy
|
||||
prenos skončil.'
|
||||
Live chat is enabled. Chat messages will appear here once sent.: 'Live chat je
|
||||
povolený. Po odoslaní sa tu zobrazia četové správy.'
|
||||
Live chat is enabled. Chat messages will appear here once sent.: 'Live chat je povolený.
|
||||
Po odoslaní sa tu zobrazia četové správy.'
|
||||
'Live Chat is currently not supported with the Invidious API. A direct connection to YouTube is required.': 'Live
|
||||
Chat momentálne nie je podporovaný pomocou API Invidious. Vyžaduje sa priame pripojenie
|
||||
k službe YouTube.'
|
||||
|
@ -528,7 +534,7 @@ Video:
|
|||
Video has been saved: Video uložené
|
||||
Save Video: Uložiť Video
|
||||
Sponsor Block category:
|
||||
music offtopic: hudba ostatné
|
||||
music offtopic: Hudba offtopic
|
||||
interaction: Interakcia
|
||||
self-promotion: Samopropagácia
|
||||
outro: Záver
|
||||
|
@ -692,9 +698,9 @@ Tooltips:
|
|||
Preferred API Backend: Vyberte backend, ktorý FreeTube používa na získavanie údajov.
|
||||
Lokálne API je zabudovaný extraktor. Invidious API vyžaduje na pripojenie server
|
||||
Invidious.
|
||||
External Link Handling: "Vyberte predvolené správanie pri kliknutí na odkaz, ktorý\
|
||||
\ nemožno otvoriť vo FreeTube.\nV predvolenom nastavení FreeTube otvorí odkaz,\
|
||||
\ na ktorý ste klikli, vo vašom predvolenom prehliadači.\n"
|
||||
External Link Handling: "Vyberte predvolené správanie pri kliknutí na odkaz, ktorý
|
||||
nemožno otvoriť vo FreeTube.\nV predvolenom nastavení FreeTube otvorí odkaz,
|
||||
na ktorý ste klikli, vo vašom predvolenom prehliadači.\n"
|
||||
Privacy Settings:
|
||||
Remove Video Meta Files: Ak je povolené, FreeTube po zatvorení stránky prezerania
|
||||
automaticky odstráni metasúbory vytvorené počas prehrávania videa.
|
||||
|
@ -751,12 +757,12 @@ Profile:
|
|||
Profile Select: Vyberte profil
|
||||
Profile Filter: Filter Profilov
|
||||
Profile Settings: Nastavenia profilu
|
||||
A new blog is now available, {blogTitle}. Click to view more: 'Nový príspevok na blogu
|
||||
je k dispozícií, {blogTitle}. Klikni pre viac informácií'
|
||||
Download From Site: Stiahnuť zo stránky
|
||||
Version {versionNumber} is now available! Click for more details: Je k dispozícií
|
||||
verzia {versionNumber} ! Klikni pre viac informácií
|
||||
Locale Name: slovenčina
|
||||
A new blog is now available, {blogTitle}. Click to view more: 'Dostupný nový článok
|
||||
na blogu: {blogTitle}. Klikni pre viac informácií'
|
||||
Download From Site: Stiahnuť z webu
|
||||
Version {versionNumber} is now available! Click for more details: Verzia {versionNumber}
|
||||
je k dispozícii! Klikni pre viac informácií
|
||||
Locale Name: Slovenčina
|
||||
Playing Next Video Interval: Prehrávanie ďalšieho videa za chvíľu. Kliknutím zrušíte.
|
||||
| Prehráva sa ďalšie video o {nextVideoInterval} sekundu. Kliknutím zrušíte. | Prehráva
|
||||
sa ďalšie video o {nextVideoInterval} sekúnd. Kliknutím zrušíte.
|
||||
|
@ -765,11 +771,11 @@ Hashtags have not yet been implemented, try again later: Neznámy typ adresy URL
|
|||
v aplikácii sa nedá otvoriť
|
||||
Unknown YouTube url type, cannot be opened in app: Neznámy typ adresy URL YouTube,
|
||||
v aplikácii sa nedá otvoriť
|
||||
Open New Window: Otvoriť Nové Okno
|
||||
Open New Window: Otvoriť nové okno
|
||||
Default Invidious instance has been set to {instance}: Predvolená Invidious inštancia
|
||||
bola nastavená na {instance}
|
||||
Search Bar:
|
||||
Clear Input: Čistý vstup
|
||||
Clear Input: Vymazať
|
||||
Default Invidious instance has been cleared: Predvolená Invidious inštancia bola vymazaná
|
||||
External link opening has been disabled in the general settings: V nastaveniach bolo
|
||||
vypnuté otváranie odkazov v externých aplikáciach
|
||||
|
@ -777,7 +783,7 @@ Are you sure you want to open this link?: Naozaj chcete otvoriť tento odkaz?
|
|||
New Window: Nové okno
|
||||
Channels:
|
||||
Channels: Kanály
|
||||
Title: List kanálov
|
||||
Count: '{číslo} kanál(e) sa nenašli.'
|
||||
Search bar placeholder: Vyhľadávať kanále
|
||||
Preferences: Preferencie
|
||||
Title: Zoznam kanálov
|
||||
Count: 'Nájdených {number} kanálov.'
|
||||
Search bar placeholder: Vyhľadať kanály
|
||||
Preferences: Predvoľby
|
||||
|
|
|
@ -333,6 +333,8 @@ Settings:
|
|||
Fetch Feeds from RSS: 'Прикупи фидове из RSS-а'
|
||||
Manage Subscriptions: 'Управљање праћењима'
|
||||
Fetch Automatically: Аутоматски прикупи фид
|
||||
Only Show Latest Video for Each Channel: Прикажи само најновији видео снимак за
|
||||
сваки канал
|
||||
Distraction Free Settings:
|
||||
Distraction Free Settings: 'Подешавања „Без ометања“'
|
||||
Hide Video Views: 'Сакриј прегледе видео снимка'
|
||||
|
@ -346,7 +348,7 @@ Settings:
|
|||
Hide Live Chat: 'Сакриј ћаскање уживо'
|
||||
Hide Active Subscriptions: 'Сакриј активна праћења'
|
||||
Blur Thumbnails: Замагли сличице
|
||||
Hide Channels Placeholder: Назив канала или ID канала
|
||||
Hide Channels Placeholder: ID канала
|
||||
Hide Video Description: Сакриј опис видео снимка
|
||||
Hide Chapters: Сакриј поглавља
|
||||
Sections:
|
||||
|
@ -373,6 +375,12 @@ Settings:
|
|||
Hide Profile Pictures in Comments: Сакриј слике профила у коментарима
|
||||
Hide Upcoming Premieres: Сакриј предстојеће премијере
|
||||
Hide Channel Releases: Сакриј издања канала
|
||||
Hide Channels Invalid: Наведени ID канала је неважећи
|
||||
Hide Channels Disabled Message: Неки канали су блокирани помоћу ID-а и нису обрађени.
|
||||
Функција је блокирана док се ти ID-ови ажурирају
|
||||
Hide Channels Already Exists: ID канала већ постоји
|
||||
Hide Channels API Error: Грешка при преузимању корисника са наведеним ID-ом. Проверите
|
||||
поново да ли је ID тачан.
|
||||
Data Settings:
|
||||
Data Settings: 'Подешавања података'
|
||||
Select Import Type: 'Избор врсте увоза'
|
||||
|
@ -486,6 +494,7 @@ Settings:
|
|||
Password: Лозинка
|
||||
Enter Password To Unlock: Унесите лозинку да бисте откључали подешавања
|
||||
Unlock: Откључај
|
||||
Expand All Settings Sections: Прошири све одељке подешавања
|
||||
About:
|
||||
#On About page
|
||||
About: 'О апликацији'
|
||||
|
@ -560,6 +569,11 @@ Profile:
|
|||
#On Channel Page
|
||||
Profile Settings: Подешавања профила
|
||||
Toggle Profile List: Укључи листу профила
|
||||
Open Profile Dropdown: Отвори падајући мени профила
|
||||
Close Profile Dropdown: Затвори падајући мени профила
|
||||
Profile Name: Име профила
|
||||
Edit Profile Name: Измени име профила
|
||||
Create Profile Name: Направи име профила
|
||||
Channel:
|
||||
Subscriber: 'Пратилац'
|
||||
Subscribers: 'Пратиоци'
|
||||
|
@ -755,6 +769,8 @@ Video:
|
|||
YouTube-ом.
|
||||
'Live Chat is unavailable for this stream. It may have been disabled by the uploader.': Ћаскање
|
||||
уживо није доступно за овај стрим. Можда га је онемогућио аутор.
|
||||
Unhide Channel: Прикажи канал
|
||||
Hide Channel: Сакриј канал
|
||||
Tooltips:
|
||||
Subscription Settings:
|
||||
Fetch Feeds from RSS: 'Када је омогућено, FreeTube ће користити RSS уместо свог
|
||||
|
@ -796,7 +812,7 @@ Tooltips:
|
|||
Distraction Free Settings:
|
||||
Hide Channels: Унесите назив канала или ID канала да бисте сакрили све видео снимке,
|
||||
плејлисте и сам канал, да се не појављују у претрази, у тренду, најпопуларнијима
|
||||
и препорученима. Назив канала који сте унели мора се потпуно подударати и разликовати
|
||||
и препорученима. ID канала који сте унели мора се потпуно подударати и разликовати
|
||||
велика и мала слова.
|
||||
Hide Subscriptions Live: Ово подешавање је замењено подешавањем за целу апликацију
|
||||
„{appWideSetting}“ у одељку „{subsection}“ у „{settingsSection}“
|
||||
|
@ -976,3 +992,6 @@ Screenshot Error: Снимак екрана није успео. {error}
|
|||
Downloading has completed: „{videoTitle}“ је завршио преузимање
|
||||
Loop is now enabled: Понављање је сада омогућено
|
||||
Downloading failed: Дошло је до проблема при преузимању „{videoTitle}“
|
||||
Channel Hidden: '{channel} је додат на филтер канала'
|
||||
Go to page: Иди на {page}
|
||||
Channel Unhidden: '{channel} је уклоњен из филтера канала'
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue