mirror of
https://github.com/FreeTubeApp/FreeTube
synced 2024-11-22 01:45:40 +01:00
Compare commits
32 Commits
69d64aa55f
...
6ea2b7afc6
Author | SHA1 | Date | |
---|---|---|---|
|
6ea2b7afc6 | ||
|
dbe57a4caa | ||
|
8b1fe4268a | ||
|
a968655d4a | ||
|
8c73360e41 | ||
|
7d932b936c | ||
|
73626f66cd | ||
|
fd74dc97e6 | ||
|
1a96092d6f | ||
|
fe8440de24 | ||
|
f1225cfc97 | ||
|
19c7a247c5 | ||
|
07e2775098 | ||
|
7981b05223 | ||
|
3ea5b1465d | ||
|
22fed89c3f | ||
|
1ff0682c3c | ||
|
14dc80e007 | ||
|
e68c534e6c | ||
|
62e7b439db | ||
|
7b8d36023e | ||
|
94f0463c4d | ||
|
91ceacd89d | ||
|
cc87017290 | ||
|
a3d375ba8e | ||
|
d1a7b84bf3 | ||
|
a9fd50227e | ||
|
9d9a769139 | ||
|
55261487f9 | ||
|
ae4dedaeed | ||
|
c219926c5e | ||
|
d8b149c24d |
@ -4,6 +4,7 @@ import { mapActions, mapMutations } from 'vuex'
|
||||
import FtButton from '../ft-button/ft-button.vue'
|
||||
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
|
||||
import FtPrompt from '../ft-prompt/ft-prompt.vue'
|
||||
import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue'
|
||||
import { MAIN_PROFILE_ID } from '../../../constants'
|
||||
|
||||
import { calculateColorLuminance, getRandomColor } from '../../helpers/colors'
|
||||
@ -27,7 +28,8 @@ export default defineComponent({
|
||||
'ft-settings-section': FtSettingsSection,
|
||||
'ft-button': FtButton,
|
||||
'ft-flex-box': FtFlexBox,
|
||||
'ft-prompt': FtPrompt
|
||||
'ft-prompt': FtPrompt,
|
||||
'ft-toggle-switch': FtToggleSwitch,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
@ -38,7 +40,9 @@ export default defineComponent({
|
||||
'youtube',
|
||||
'youtubeold',
|
||||
'newpipe'
|
||||
]
|
||||
],
|
||||
|
||||
shouldExportPlaylistForOlderVersions: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -1015,6 +1019,55 @@ export default defineComponent({
|
||||
await this.promptAndWriteToFile(options, JSON.stringify(this.allPlaylists), 'All playlists has been successfully exported')
|
||||
},
|
||||
|
||||
exportPlaylistsForOlderVersionsSometimes: function () {
|
||||
if (this.shouldExportPlaylistForOlderVersions) {
|
||||
this.exportPlaylistsForOlderVersions()
|
||||
} else {
|
||||
this.exportPlaylists()
|
||||
}
|
||||
},
|
||||
|
||||
exportPlaylistsForOlderVersions: async function () {
|
||||
const dateStr = getTodayDateStrLocalTimezone()
|
||||
const exportFileName = 'freetube-playlists-as-single-favorites-playlist-' + dateStr + '.db'
|
||||
|
||||
const options = {
|
||||
defaultPath: exportFileName,
|
||||
filters: [
|
||||
{
|
||||
name: 'Database File',
|
||||
extensions: ['db']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const favoritesPlaylistData = {
|
||||
playlistName: 'Favorites',
|
||||
protected: true,
|
||||
videos: [],
|
||||
}
|
||||
|
||||
this.allPlaylists.forEach((playlist) => {
|
||||
playlist.videos.forEach((video) => {
|
||||
const videoAlreadyAdded = favoritesPlaylistData.videos.some((v) => {
|
||||
return v.videoId === video.videoId
|
||||
})
|
||||
if (videoAlreadyAdded) { return }
|
||||
|
||||
favoritesPlaylistData.videos.push(
|
||||
Object.assign({
|
||||
// The "required" keys during import (but actually unused) in older versions
|
||||
isLive: false,
|
||||
paid: false,
|
||||
published: '',
|
||||
}, video)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
await this.promptAndWriteToFile(options, JSON.stringify([favoritesPlaylistData]), 'All playlists has been successfully exported')
|
||||
},
|
||||
|
||||
convertOldFreeTubeFormatToNew(oldData) {
|
||||
const convertedData = []
|
||||
for (const channel of oldData) {
|
||||
|
@ -42,7 +42,17 @@
|
||||
/>
|
||||
<ft-button
|
||||
:label="$t('Settings.Data Settings.Export Playlists')"
|
||||
@click="exportPlaylists"
|
||||
@click="exportPlaylistsForOlderVersionsSometimes"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<ft-flex-box>
|
||||
<ft-toggle-switch
|
||||
:label="$t('Settings.Data Settings.Export Playlists For Older FreeTube Versions.Label')"
|
||||
:compact="true"
|
||||
:default-value="shouldExportPlaylistForOlderVersions"
|
||||
:tooltip="$t('Settings.Data Settings.Export Playlists For Older FreeTube Versions.Tooltip')"
|
||||
:tooltip-allow-newlines="true"
|
||||
@change="shouldExportPlaylistForOlderVersions = !shouldExportPlaylistForOlderVersions"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<ft-prompt
|
||||
|
@ -5,7 +5,7 @@ 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 { checkYoutubeId, findChannelTagInfo } from '../../helpers/channels'
|
||||
import { checkYoutubeChannelId, findChannelTagInfo } from '../../helpers/channels'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PlayerSettings',
|
||||
@ -152,7 +152,7 @@ export default defineComponent({
|
||||
showToast(this.$t('Settings.Distraction Free Settings.Hide Channels Already Exists'))
|
||||
},
|
||||
validateChannelId: function (text) {
|
||||
return checkYoutubeId(text)
|
||||
return checkYoutubeChannelId(text)
|
||||
},
|
||||
findChannelTagInfo: async function (text) {
|
||||
return await findChannelTagInfo(text, this.backendOptions)
|
||||
@ -167,7 +167,7 @@ export default defineComponent({
|
||||
if (tag.invalid) continue
|
||||
|
||||
// process if no preferred name and is possibly a YouTube ID
|
||||
if (tag.preferredName === '' && checkYoutubeId(tag.name)) {
|
||||
if (tag.preferredName === '' && checkYoutubeChannelId(tag.name)) {
|
||||
this.channelHiderDisabled = true
|
||||
|
||||
const { preferredName, icon, iconHref, invalidId } = await this.findChannelTagInfo(tag.name)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,10 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isActiveProfile: function (profile) {
|
||||
return profile._id === this.activeProfile._id
|
||||
},
|
||||
|
||||
toggleProfileList: function () {
|
||||
this.profileListShown = !this.profileListShown
|
||||
|
||||
|
@ -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)"
|
||||
|
@ -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,54 @@ export default defineComponent({
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
hideProfileDropdownToggle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
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 +80,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 +108,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 +136,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 +151,34 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isProfileDropdownEnabled && !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" />
|
||||
|
@ -30,7 +30,11 @@ export default defineComponent({
|
||||
tooltipPosition: {
|
||||
type: String,
|
||||
default: 'bottom-left'
|
||||
}
|
||||
},
|
||||
tooltipAllowNewlines: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
|
@ -29,6 +29,7 @@
|
||||
class="selectTooltip"
|
||||
:position="tooltipPosition"
|
||||
:tooltip="tooltip"
|
||||
:allow-newlines="tooltipAllowNewlines"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -92,6 +92,16 @@
|
||||
transform: translate(calc(-50% * var(--horizontal-directionality-coefficient)), 1em);
|
||||
}
|
||||
|
||||
.text.allowNewlines {
|
||||
white-space: pre-wrap;
|
||||
text-align: start;
|
||||
inline-size: 55vw;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1100px) {
|
||||
inline-size: 40vw;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
@ -13,7 +13,11 @@ export default defineComponent({
|
||||
tooltip: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
allowNewlines: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
const id = `ft-tooltip-${++idCounter}`
|
||||
|
@ -10,11 +10,13 @@
|
||||
<p
|
||||
:id="id"
|
||||
class="text"
|
||||
:class="position"
|
||||
:class="{
|
||||
[position]: true,
|
||||
allowNewlines,
|
||||
}"
|
||||
role="tooltip"
|
||||
>
|
||||
{{ tooltip }}
|
||||
</p>
|
||||
v-text="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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: {
|
||||
@ -74,6 +78,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 () {
|
||||
@ -181,6 +193,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"
|
||||
|
@ -43,7 +43,7 @@ async function findChannelById(id, backendOptions) {
|
||||
* @returns {Promise<{icon: string, iconHref: string, preferredName: string} | { invalidId: boolean }>}
|
||||
*/
|
||||
export async function findChannelTagInfo(id, backendOptions) {
|
||||
if (!/UC\S{22}/.test(id)) return { invalidId: true }
|
||||
if (!checkYoutubeChannelId(id)) return { invalidId: true }
|
||||
try {
|
||||
const channel = await findChannelById(id, backendOptions)
|
||||
if (!process.env.IS_ELECTRON || backendOptions.preference === 'invidious') {
|
||||
@ -71,6 +71,6 @@ export async function findChannelTagInfo(id, backendOptions) {
|
||||
* @param {string} id
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function checkYoutubeId(id) {
|
||||
return /UC\S{22}/.test(id)
|
||||
export function checkYoutubeChannelId(id) {
|
||||
return /^UC[\w-]{22}$/.test(id)
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
// to avoid code conflict and duplicate entries
|
||||
import {
|
||||
faAngleDown,
|
||||
faAngleUp,
|
||||
faArrowDown,
|
||||
faArrowLeft,
|
||||
faArrowRight,
|
||||
@ -86,6 +87,7 @@ Vue.config.productionTip = process.env.NODE_ENV === 'development'
|
||||
library.add(
|
||||
// solid icons
|
||||
faAngleDown,
|
||||
faAngleUp,
|
||||
faArrowDown,
|
||||
faArrowLeft,
|
||||
faArrowRight,
|
||||
|
@ -69,6 +69,7 @@
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.subscribeButton {
|
||||
|
@ -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,7 +53,6 @@
|
||||
class="unsubscribeContainer"
|
||||
>
|
||||
<ft-subscribe-button
|
||||
class="btn"
|
||||
:channel-id="channel.id"
|
||||
:channel-name="channel.name"
|
||||
:channel-thumbnail="channel.thumbnail"
|
||||
|
@ -811,6 +811,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:
|
||||
@ -1037,3 +1039,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} من مرشح القناة
|
||||
|
@ -3,7 +3,7 @@ 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: 'پەڕگە'
|
||||
@ -32,10 +32,11 @@ 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: ''
|
||||
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
|
||||
@ -44,27 +45,27 @@ Global:
|
||||
Videos: 'ڤیدیۆکان'
|
||||
Shorts: ''
|
||||
Live: 'ڕاستەوخۆ'
|
||||
Community: ''
|
||||
Community: 'کۆمەڵگە'
|
||||
Counts:
|
||||
Video Count: '١ ڤیدیۆ | {count} ڤیدیۆ'
|
||||
Channel Count: '١ کەناڵ | {count} کەناڵ'
|
||||
Subscriber Count: '١ بەشداربوو | {count} بەشداربوو'
|
||||
View Count: 'بینینەک | {count} بینین'
|
||||
Watching Count: ''
|
||||
Watching Count: '١ تەمەشاکردن | {count} تەمەشاکردن'
|
||||
|
||||
# Search Bar
|
||||
Search / Go to URL: ''
|
||||
Search / Go to URL: 'گەڕان/ بڕۆ بۆ ئرڵ'
|
||||
Search Bar:
|
||||
Clear Input: ''
|
||||
# In Filter Button
|
||||
Search Filters:
|
||||
Search Filters: ''
|
||||
Search Filters: 'پاڵفتەکردنی گەڕان'
|
||||
Sort By:
|
||||
Sort By: 'ڕیزکردن بە'
|
||||
Most Relevant: ''
|
||||
Rating: 'هەڵسەنگاندن'
|
||||
Upload Date: 'ڕێکەوتی بارکردن'
|
||||
View Count: ''
|
||||
View Count: 'ژمارەی بینین'
|
||||
Time:
|
||||
Time: 'کات'
|
||||
Any Time: 'هەر کاتێک'
|
||||
@ -87,10 +88,10 @@ Search Filters:
|
||||
Medium (4 - 20 minutes): 'ناوەند (٤ - ٢٠ خولەک)'
|
||||
Long (> 20 minutes): 'درێژ (> ٢٠ خولەک)'
|
||||
# On Search Page
|
||||
Search Results: ''
|
||||
Search Results: 'ئەنجامەکانی گەڕان'
|
||||
Fetching results. Please wait: ''
|
||||
Fetch more results: ''
|
||||
There are no more results for this search: ''
|
||||
There are no more results for this search: 'ئەنجامەکی تر نییە بۆ ئەم گەڕانە'
|
||||
# Sidebar
|
||||
Subscriptions:
|
||||
# On Subscriptions Page
|
||||
@ -121,26 +122,27 @@ Channels:
|
||||
Unsubscribe Prompt: ''
|
||||
Trending:
|
||||
Trending: ''
|
||||
Default: ''
|
||||
Default: 'بنەڕەت'
|
||||
Music: 'مۆسیقا'
|
||||
Gaming: 'یاری'
|
||||
Movies: 'فیلم'
|
||||
Trending Tabs: ''
|
||||
Most Popular: 'باوترین'
|
||||
Playlists: ''
|
||||
Playlists: 'پێڕستی لێدانەکان'
|
||||
User Playlists:
|
||||
Your 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: ''
|
||||
Search bar placeholder: 'لەناو پێڕستی لێدان بگەڕێ'
|
||||
History:
|
||||
# On History Page
|
||||
History: 'مێژوو'
|
||||
Watch History: 'مێژووی تەمەشاکردن'
|
||||
Your history list is currently empty.: ''
|
||||
Empty Search Message: ''
|
||||
Search bar placeholder: ""
|
||||
Your history list is currently empty.: 'ئێستا لیستەی مێژووت بەتاڵە.'
|
||||
Empty Search Message: 'هیچ ڤیدیۆیەک لە مێژووت نەدۆزرایەوە کە بەرانبەری گەڕانەکەت
|
||||
بێت'
|
||||
Search bar placeholder: "لەناو مێژوو بگەڕێ"
|
||||
Settings:
|
||||
# On Settings Page
|
||||
Settings: 'ڕێکخستنەکان'
|
||||
@ -150,22 +152,22 @@ Settings:
|
||||
Check for Updates: ''
|
||||
Check for Latest Blog Posts: ''
|
||||
Fallback to Non-Preferred Backend on Failure: ''
|
||||
Enable Search Suggestions: ''
|
||||
Enable Search Suggestions: 'کاراکردنی پێشنیارەکانی گەڕان'
|
||||
Default Landing Page: ''
|
||||
Locale Preference: ''
|
||||
System Default: ''
|
||||
System Default: 'بنەڕەتی سیستەم'
|
||||
Preferred API Backend:
|
||||
Preferred API Backend: ''
|
||||
Local API: ''
|
||||
Invidious API: ''
|
||||
Video View Type:
|
||||
Video View Type: ''
|
||||
Grid: ''
|
||||
List: 'پێڕست'
|
||||
Grid: 'خانەخانە'
|
||||
List: 'لیستە'
|
||||
Thumbnail Preference:
|
||||
Thumbnail Preference: ''
|
||||
Default: ''
|
||||
Beginning: ''
|
||||
Default: 'بنەڕەت'
|
||||
Beginning: 'سەرەتا'
|
||||
Middle: 'ناوەڕاست'
|
||||
End: 'کۆتایی'
|
||||
Hidden: 'شاراوە'
|
||||
@ -193,10 +195,10 @@ Settings:
|
||||
Hide Side Bar Labels: ''
|
||||
Hide FreeTube Header Logo: ''
|
||||
Base Theme:
|
||||
Base Theme: ''
|
||||
Base Theme: 'ڕووکاری بنچینە'
|
||||
Black: 'ڕەش'
|
||||
Dark: 'تاریک'
|
||||
System Default: ''
|
||||
System Default: 'بنەڕەتی سیستەم'
|
||||
Light: 'ڕووناک'
|
||||
Dracula: ''
|
||||
Catppuccin Mocha: ''
|
||||
@ -208,11 +210,11 @@ Settings:
|
||||
Pink: 'پەمبە'
|
||||
Purple: 'وەنەوشەیی'
|
||||
Deep Purple: 'وەنەوشەیی تۆخ'
|
||||
Indigo: ''
|
||||
Indigo: 'نیلی'
|
||||
Blue: 'شین'
|
||||
Light Blue: 'شینی ئاڵ'
|
||||
Cyan: ''
|
||||
Teal: ''
|
||||
Cyan: 'شینی تۆخ'
|
||||
Teal: 'شەدری'
|
||||
Green: 'کەسک'
|
||||
Light Green: 'کەسکی ئاڵ'
|
||||
Lime: ''
|
||||
@ -247,11 +249,11 @@ Settings:
|
||||
Player Settings: 'ڕێکخستنەکانی لێدەر'
|
||||
Force Local Backend for Legacy Formats: ''
|
||||
Play Next Video: 'لێدانی ڤیدیۆی دواتر'
|
||||
Turn on Subtitles by Default: ''
|
||||
Turn on Subtitles by Default: 'هەڵکردنی بنەڕەتی ژێرنووس'
|
||||
Autoplay Videos: 'خۆلێدانی ڤیدیۆ'
|
||||
Proxy Videos Through Invidious: ''
|
||||
Autoplay Playlists: ''
|
||||
Enable Theatre Mode by Default: ''
|
||||
Autoplay Playlists: 'خۆلێدانی پێڕستی لێدان'
|
||||
Enable Theatre Mode by Default: 'کاراکردنی بنەڕەتیی شێوازی شانۆیی'
|
||||
Scroll Volume Over Video Player: ''
|
||||
Scroll Playback Rate Over Video Player: ''
|
||||
Skip by Scrolling Over Video Player: ''
|
||||
@ -269,23 +271,23 @@ Settings:
|
||||
Legacy Formats: ''
|
||||
Audio Formats: ''
|
||||
Default Quality:
|
||||
Default Quality: ''
|
||||
Default Quality: 'جۆرایەتی بنەڕەت'
|
||||
Auto: 'خۆکار'
|
||||
144p: ''
|
||||
240p: ''
|
||||
360p: ''
|
||||
480p: ''
|
||||
720p: ''
|
||||
1080p: ''
|
||||
1440p: ''
|
||||
4k: ''
|
||||
8k: ''
|
||||
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: ''
|
||||
Ask Path: 'بۆ بوخچەی پاشەکەوت بپرسە'
|
||||
Folder Label: ''
|
||||
Folder Button: 'دیاریکردنی بوخچە'
|
||||
File Name Label: ''
|
||||
@ -313,9 +315,10 @@ Settings:
|
||||
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 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:
|
||||
@ -329,17 +332,17 @@ Settings:
|
||||
Sections:
|
||||
Side Bar: ''
|
||||
Subscriptions Page: ''
|
||||
Channel Page: ''
|
||||
Watch Page: ''
|
||||
General: ''
|
||||
Hide Video Views: ''
|
||||
Channel Page: 'پەڕەی کەناڵ'
|
||||
Watch Page: 'پەڕەی تەمەشاکردن'
|
||||
General: 'گشتی'
|
||||
Hide Video Views: 'شاردنەوەی ژمارەی بینراوەکانی ڤیدیۆ'
|
||||
Hide Video Likes And Dislikes: ''
|
||||
Hide Channel Subscribers: ''
|
||||
Hide Comment Likes: ''
|
||||
Hide Recommended Videos: ''
|
||||
Hide Recommended Videos: 'شاردنەوەی ڤیدیۆ پێشنیازکراوەکان'
|
||||
Hide Trending Videos: ''
|
||||
Hide Popular Videos: ''
|
||||
Hide Playlists: ''
|
||||
Hide Playlists: 'شاردنەوەی پێڕستی لێدان'
|
||||
Hide Live Chat: ''
|
||||
Hide Active Subscriptions: ''
|
||||
Hide Video Description: ''
|
||||
@ -367,29 +370,30 @@ Settings:
|
||||
Hide Subscriptions Live: ''
|
||||
Hide Subscriptions Community: ''
|
||||
Data Settings:
|
||||
Data Settings: ''
|
||||
Select Import Type: ''
|
||||
Select Export Type: ''
|
||||
Data Settings: 'ڕێکخستنەکانی دراوە'
|
||||
Select Import Type: 'دیاریکردنی جۆری هاوردە'
|
||||
Select Export Type: 'دیاریکردنی جۆری هەناردە'
|
||||
Import Subscriptions: ''
|
||||
Subscription File: ''
|
||||
History File: ''
|
||||
Playlist 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: ''
|
||||
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: ''
|
||||
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: ''
|
||||
@ -403,18 +407,19 @@ Settings:
|
||||
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: ''
|
||||
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: ''
|
||||
@ -427,15 +432,15 @@ Settings:
|
||||
Auto Skip: ''
|
||||
Show In Seek Bar: ''
|
||||
Prompt To Skip: ''
|
||||
Do Nothing: ''
|
||||
Do Nothing: 'هیچ مەکە'
|
||||
Category Color: ''
|
||||
Parental Control Settings:
|
||||
Parental Control Settings: ''
|
||||
Hide Unsubscribe Button: ''
|
||||
Show Family Friendly Only: ''
|
||||
Hide Search Bar: ''
|
||||
Hide Search Bar: 'شاردنەوەی میلی گەڕان'
|
||||
Download Settings:
|
||||
Download Settings: ''
|
||||
Download Settings: 'ڕێکخستنەکانی داگرتن'
|
||||
Ask Download Path: ''
|
||||
Choose Path: ''
|
||||
Download Behavior: ''
|
||||
@ -446,50 +451,50 @@ Settings:
|
||||
Warning: ''
|
||||
Replace HTTP Cache: ''
|
||||
Password Dialog:
|
||||
Password: ''
|
||||
Enter Password To Unlock: ''
|
||||
Password Incorrect: ''
|
||||
Unlock: ''
|
||||
Password: 'تێپەڕەوشە'
|
||||
Enter Password To Unlock: 'تێپەڕەوشە بنووسە بۆ کردنەوەی کڵۆمی ڕێکخستنەکان'
|
||||
Password Incorrect: 'تێپەڕەوشەی نادرووست'
|
||||
Unlock: 'کردنەوەی کڵۆم'
|
||||
Password Settings:
|
||||
Password Settings: ''
|
||||
Password Settings: 'ڕێکخستنەکانی تێپەڕەوشە'
|
||||
Set Password To Prevent Access: ''
|
||||
Set Password: ''
|
||||
Remove Password: ''
|
||||
Set Password: 'دانانی تێپەڕەوشە'
|
||||
Remove Password: 'لادانی تێپەڕەوشە'
|
||||
About:
|
||||
#On About page
|
||||
About: ''
|
||||
About: 'دەربارە'
|
||||
Beta: ''
|
||||
Source code: ''
|
||||
Licensed under the AGPLv3: ''
|
||||
View License: ''
|
||||
Source code: 'کۆدی سەرچاوە'
|
||||
Licensed under the AGPLv3: 'مۆڵەتی وەشانی سێیەمی AGPL هەیە'
|
||||
View License: 'بینینی مۆڵەت'
|
||||
Downloads / Changelog: ''
|
||||
GitHub releases: ''
|
||||
Help: ''
|
||||
FreeTube Wiki: ''
|
||||
FAQ: ''
|
||||
Help: 'یارمەتی'
|
||||
FreeTube Wiki: 'ویکی فریتیوب'
|
||||
FAQ: 'پرسیارە دووبارەکان'
|
||||
Discussions: ''
|
||||
Report a problem: ''
|
||||
Report a problem: 'سکاڵا لە کێشەیەک بکە'
|
||||
GitHub issues: ''
|
||||
Please check for duplicates before posting: ''
|
||||
Website: ''
|
||||
Blog: ''
|
||||
Email: ''
|
||||
Mastodon: ''
|
||||
Website: 'وێبگە'
|
||||
Blog: 'بلۆگ'
|
||||
Email: 'ئیمێڵ'
|
||||
Mastodon: 'ماستادۆن'
|
||||
Chat on Matrix: ''
|
||||
Please read the: ''
|
||||
room rules: ''
|
||||
Translate: ''
|
||||
Translate: 'وەرگێڕان'
|
||||
Credits: ''
|
||||
FreeTube is made possible by: ''
|
||||
these people and projects: ''
|
||||
Donate: ''
|
||||
Donate: 'بەخشین'
|
||||
|
||||
Profile:
|
||||
Profile Settings: ''
|
||||
Toggle Profile List: ''
|
||||
Profile Select: ''
|
||||
Profile Filter: ''
|
||||
All Channels: ''
|
||||
All Channels: 'هەموو کەناڵەکان'
|
||||
Profile Manager: ''
|
||||
Create New Profile: ''
|
||||
Edit Profile: ''
|
||||
@ -512,12 +517,12 @@ Profile:
|
||||
'{profile} is now the active profile': ''
|
||||
Subscription List: ''
|
||||
Other Channels: ''
|
||||
'{number} selected': ''
|
||||
Select All: ''
|
||||
Select None: ''
|
||||
Delete Selected: ''
|
||||
'{number} selected': '{number} دیاریکراوە'
|
||||
Select All: 'دیاریکردنی هەموویان'
|
||||
Select None: 'دیاری نەکردنی هیچیان'
|
||||
Delete Selected: 'سڕینەوەی دیاریکراوەکان'
|
||||
Add Selected To Profile: ''
|
||||
No channel(s) have been selected: ''
|
||||
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.
|
||||
: ''
|
||||
@ -531,78 +536,82 @@ Channel:
|
||||
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: ''
|
||||
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: ''
|
||||
Videos: 'ڤیدیۆکان'
|
||||
This channel does not currently have any videos: 'ئەم کەناڵە ئێستا هیچ ڤیدیۆیەکی
|
||||
نییە'
|
||||
Sort Types:
|
||||
Newest: ''
|
||||
Oldest: ''
|
||||
Newest: 'نوێترین'
|
||||
Oldest: 'کۆنترین'
|
||||
Most Popular: ''
|
||||
Shorts:
|
||||
This channel does not currently have any shorts: ''
|
||||
Live:
|
||||
Live: ''
|
||||
Live: 'ڕاستەوخۆ'
|
||||
This channel does not currently have any live streams: ''
|
||||
Playlists:
|
||||
Playlists: ''
|
||||
This channel does not currently have any playlists: ''
|
||||
Playlists: 'پێڕستی لێدان'
|
||||
This channel does not currently have any playlists: 'ئەم کەناڵە ئێستا هیچ پێڕستێکی
|
||||
لێدانی نییە'
|
||||
Sort Types:
|
||||
Last Video Added: ''
|
||||
Newest: ''
|
||||
Oldest: ''
|
||||
Last Video Added: 'دوایین ڤیدیۆ زیادکراوەکان'
|
||||
Newest: 'نوێترین'
|
||||
Oldest: 'کۆنترین'
|
||||
Podcasts:
|
||||
Podcasts: ''
|
||||
This channel does not currently have any podcasts: ''
|
||||
Podcasts: 'پۆدکاستەکان'
|
||||
This channel does not currently have any podcasts: 'ئەم کەناڵە ئێستا هیچ پۆدکاستێکی
|
||||
نییە'
|
||||
Releases:
|
||||
Releases: ''
|
||||
This channel does not currently have any releases: ''
|
||||
About:
|
||||
About: ''
|
||||
Channel Description: ''
|
||||
About: 'دەربارە'
|
||||
Channel Description: 'پێناسی کەناڵ'
|
||||
Tags:
|
||||
Tags: ''
|
||||
Search for: ''
|
||||
Details: ''
|
||||
Details: 'وردەکاری'
|
||||
Joined: ''
|
||||
Location: ''
|
||||
Featured Channels: ''
|
||||
Community:
|
||||
This channel currently does not have any posts: ''
|
||||
votes: ''
|
||||
votes: '{votes} دەنگ'
|
||||
Reveal Answers: ''
|
||||
Hide 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: ''
|
||||
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 YouTube: 'کردنەوەی کەناڵ لە یوتیوب'
|
||||
Copy YouTube Channel Link: 'لەبەرگرتنەوەی بەستەری کەناڵی یوتیوب'
|
||||
Open Channel in Invidious: ''
|
||||
Copy Invidious Channel Link: ''
|
||||
Views: ''
|
||||
Views: 'بینراو'
|
||||
Loop Playlist: ''
|
||||
Shuffle Playlist: ''
|
||||
Reverse Playlist: ''
|
||||
Play Next Video: ''
|
||||
Play Previous Video: ''
|
||||
Play Next Video: 'لێدانی ڤیدیۆی دواتر'
|
||||
Play Previous Video: 'لێدانی ڤیدیۆی پێشوو'
|
||||
Pause on Current Video: ''
|
||||
Watched: ''
|
||||
Autoplay: ''
|
||||
Watched: 'تەمەشاکراو'
|
||||
Autoplay: 'خۆلێدان'
|
||||
Starting soon, please refresh the page to check again: ''
|
||||
# As in a Live Video
|
||||
Premieres on: ''
|
||||
@ -619,14 +628,14 @@ Video:
|
||||
'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: ''
|
||||
Download Video: 'داگرتنی ڤیدیۆ'
|
||||
video only: 'تەنیا ڤیدیۆ'
|
||||
audio only: 'تەنیا دەنگ'
|
||||
Audio:
|
||||
Low: ''
|
||||
Medium: ''
|
||||
High: ''
|
||||
Best: ''
|
||||
Low: 'نزم'
|
||||
Medium: 'مامناوەند'
|
||||
High: 'بەرز'
|
||||
Best: 'باشترین'
|
||||
Published:
|
||||
Jan: ''
|
||||
Feb: ''
|
||||
@ -640,24 +649,24 @@ Video:
|
||||
Oct: ''
|
||||
Nov: ''
|
||||
Dec: ''
|
||||
Second: ''
|
||||
Seconds: ''
|
||||
Minute: ''
|
||||
Minutes: ''
|
||||
Hour: ''
|
||||
Hours: ''
|
||||
Day: ''
|
||||
Days: ''
|
||||
Week: ''
|
||||
Weeks: ''
|
||||
Month: ''
|
||||
Months: ''
|
||||
Year: ''
|
||||
Years: ''
|
||||
Second: 'چرکە'
|
||||
Seconds: 'چرکە'
|
||||
Minute: 'خولەک'
|
||||
Minutes: 'خولەک'
|
||||
Hour: 'کاتژمێر'
|
||||
Hours: 'کاتژمێر'
|
||||
Day: 'ڕۆژ'
|
||||
Days: 'ڕۆژ'
|
||||
Week: 'هەفتە'
|
||||
Weeks: 'هەفتە'
|
||||
Month: 'مانگ'
|
||||
Months: 'مانگ'
|
||||
Year: 'ساڵ'
|
||||
Years: 'ساڵ'
|
||||
Ago: ''
|
||||
Upcoming: ''
|
||||
In less than a minute: ''
|
||||
Published on: ''
|
||||
Published on: 'بڵاوکرایەوە لە'
|
||||
Streamed on: ''
|
||||
Started streaming on: ''
|
||||
translated from English: ''
|
||||
@ -674,8 +683,8 @@ Video:
|
||||
filler: ''
|
||||
External Player:
|
||||
OpenInTemplate: ''
|
||||
video: ''
|
||||
playlist: ''
|
||||
video: 'ڤیدیۆ'
|
||||
playlist: 'پێڕستی لێدان'
|
||||
OpeningTemplate: ''
|
||||
UnsupportedActionTemplate: ''
|
||||
Unsupported Actions:
|
||||
@ -698,20 +707,22 @@ Video:
|
||||
Dropped / Total Frames: ''
|
||||
Mimetype: ''
|
||||
#& Videos
|
||||
Unhide Channel: پیشاندانی کەناڵ
|
||||
Hide Channel: شاردنەوەی کەناڵ
|
||||
Videos:
|
||||
#& Sort By
|
||||
Sort By:
|
||||
Newest: ''
|
||||
Oldest: ''
|
||||
Newest: 'نوێترین'
|
||||
Oldest: 'کۆنترین'
|
||||
#& Most Popular
|
||||
#& Playlists
|
||||
Playlist:
|
||||
#& About
|
||||
Playlist: ''
|
||||
Playlist: 'پێڕستی لێدان'
|
||||
View Full Playlist: ''
|
||||
Videos: ''
|
||||
View: ''
|
||||
Views: ''
|
||||
Videos: 'ڤیدیۆکان'
|
||||
View: 'بینراو'
|
||||
Views: 'بینراو'
|
||||
Last Updated On: ''
|
||||
|
||||
# On Video Watch Page
|
||||
@ -726,12 +737,12 @@ Change Format:
|
||||
Dash formats are not available for this video: ''
|
||||
Audio formats are not available for this video: ''
|
||||
Share:
|
||||
Share Video: ''
|
||||
Share Channel: ''
|
||||
Share Playlist: ''
|
||||
Share Video: 'هاوبەشکردنی ڤیدیۆ'
|
||||
Share Channel: 'هاوبەشکردنی کەناڵ'
|
||||
Share Playlist: 'هاوبەشکردنی پێڕستی لێدان'
|
||||
Include Timestamp: ''
|
||||
Copy Link: ''
|
||||
Open Link: ''
|
||||
Copy Link: 'لەبەرگرتنەوەی بەستەر'
|
||||
Open Link: 'کردنەوەی بەستەر'
|
||||
Copy Embed: ''
|
||||
Open Embed: ''
|
||||
# On Click
|
||||
@ -750,7 +761,7 @@ Chapters:
|
||||
'Chapters list visible, current chapter: {chapterName}': ''
|
||||
'Chapters list hidden, current chapter: {chapterName}': ''
|
||||
|
||||
Mini Player: ''
|
||||
Mini Player: 'لێدەری گچکە'
|
||||
Comments:
|
||||
Comments: ''
|
||||
Click to View Comments: ''
|
||||
@ -758,23 +769,23 @@ Comments:
|
||||
There are no more comments for this video: ''
|
||||
Show Comments: ''
|
||||
Hide Comments: ''
|
||||
Sort by: ''
|
||||
Sort by: 'ڕیزکردن بە'
|
||||
Top comments: ''
|
||||
Newest first: ''
|
||||
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}: ''
|
||||
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: ''
|
||||
Member: 'ئەندام'
|
||||
Subscribed: ''
|
||||
Hearted: ''
|
||||
Up Next: ''
|
||||
@ -800,7 +811,7 @@ Tooltips:
|
||||
Custom External Player Executable: ''
|
||||
Ignore Warnings: ''
|
||||
Custom External Player Arguments: ''
|
||||
DefaultCustomArgumentsTemplate: ""
|
||||
DefaultCustomArgumentsTemplate: "(بنەڕەت: '{defaultCustomArguments}')"
|
||||
Distraction Free Settings:
|
||||
Hide Channels: ''
|
||||
Hide Subscriptions Live: ''
|
||||
@ -828,8 +839,8 @@ Loop is now enabled: ''
|
||||
Shuffle is now disabled: ''
|
||||
Shuffle is now enabled: ''
|
||||
The playlist has been reversed: ''
|
||||
Playing Next Video: ''
|
||||
Playing Previous Video: ''
|
||||
Playing Next Video: 'لێدانی ڤیدیۆی دواتر'
|
||||
Playing Previous Video: 'لێدانی ڤیدیۆی پێشوو'
|
||||
Playlist will not pause when current video is finished: ''
|
||||
Playlist will pause when current video is finished: ''
|
||||
Playing Next Video Interval: ''
|
||||
@ -841,8 +852,8 @@ Default Invidious instance has been cleared: ''
|
||||
Age Restricted:
|
||||
This {videoOrPlaylist} is age restricted: ''
|
||||
Type:
|
||||
Channel: ''
|
||||
Video: ''
|
||||
Channel: 'کەناڵ'
|
||||
Video: 'ڤیدیۆ'
|
||||
External link opening has been disabled in the general settings: ''
|
||||
Downloading has completed: ''
|
||||
Starting download: ''
|
||||
@ -853,6 +864,7 @@ Screenshot Error: ''
|
||||
Hashtag:
|
||||
Hashtag: ''
|
||||
This hashtag does not currently have any videos: ''
|
||||
Yes: ''
|
||||
No: ''
|
||||
Ok: ''
|
||||
Yes: 'بەڵێ'
|
||||
No: 'نەخێر'
|
||||
Ok: 'باشە'
|
||||
Go to page: بڕۆ بۆ {page}
|
||||
|
@ -806,6 +806,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:
|
||||
@ -1036,3 +1038,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ů
|
||||
|
@ -470,6 +470,15 @@ Settings:
|
||||
Export History: Export History
|
||||
Import Playlists: Import Playlists
|
||||
Export Playlists: Export Playlists
|
||||
Export Playlists For Older FreeTube Versions:
|
||||
Label: Export Playlists For Older FreeTube Versions
|
||||
# |- = Keep newlines, No newline at end
|
||||
Tooltip: |-
|
||||
This option exports videos from all playlists into one playlist named 'Favorites'.
|
||||
How to export & import videos in playlists for an older version of FreeTube:
|
||||
1. Export your playlists with this option enabled.
|
||||
2. Delete all of your existing playlists using the Remove All Playlists option under Privacy Settings.
|
||||
3. Launch the older version of FreeTube and import the exported playlists."
|
||||
Profile object has insufficient data, skipping item: Profile object has insufficient
|
||||
data, skipping item
|
||||
All subscriptions and profiles have been successfully imported: All subscriptions
|
||||
@ -624,6 +633,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
|
||||
|
@ -620,6 +620,8 @@ 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
|
||||
Channel:
|
||||
Subscribe: 'Suscribirse'
|
||||
Unsubscribe: 'Anular suscripción'
|
||||
@ -827,6 +829,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:
|
||||
@ -1073,3 +1077,5 @@ Playlist will pause when current video is finished: La lista de reproducción se
|
||||
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'
|
||||
|
@ -764,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:
|
||||
@ -991,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'
|
||||
|
@ -794,6 +794,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:
|
||||
@ -942,6 +944,8 @@ 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
|
||||
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.
|
||||
@ -1082,3 +1086,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'
|
||||
|
@ -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: کردنەوەی پەنجەرەیەکی نوێ
|
||||
|
@ -798,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:
|
||||
@ -939,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'
|
||||
@ -1086,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'
|
||||
|
@ -761,6 +761,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 уместо свог
|
||||
@ -982,3 +984,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} је уклоњен из филтера канала'
|
||||
|
@ -713,6 +713,8 @@ Video:
|
||||
Upcoming: 即将到来
|
||||
'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:
|
||||
@ -936,3 +938,5 @@ Hashtag:
|
||||
Playlist will pause when current video is finished: 当前视频播完后播放列表会暂停
|
||||
Playlist will not pause when current video is finished: 当前视频播完后播放列表不会暂停
|
||||
Go to page: 转到页{page}
|
||||
Channel Hidden: '{channel} 频道已添加到频道过滤器'
|
||||
Channel Unhidden: 从频道过滤器删除了{channel} 频道
|
||||
|
@ -131,7 +131,7 @@ Settings:
|
||||
Preferred API Backend:
|
||||
Preferred API Backend: '偏好API伺服器'
|
||||
Local API: '本機 API'
|
||||
Invidious API: 'Invidious API(應用程式介面)'
|
||||
Invidious API: 'Invidious API'
|
||||
Video View Type:
|
||||
Video View Type: '影片觀看類別'
|
||||
Grid: '網格'
|
||||
@ -141,7 +141,7 @@ Settings:
|
||||
Default: '預設'
|
||||
Beginning: '片頭'
|
||||
Middle: '中間'
|
||||
End: '結尾'
|
||||
End: '片尾'
|
||||
Hidden: 隱藏
|
||||
Blur: 模糊
|
||||
'Invidious Instance (Default is https://invidious.snopyta.org)': 'Invidious實例(預設為
|
||||
@ -410,7 +410,7 @@ Settings:
|
||||
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?: 此變更需要重啟讓修改生效。重啟並且套用變更?
|
||||
The app needs to restart for changes to take effect. Restart and apply change?: 必須重新啟動應用程式以生效。重新啟動並套用變更嗎?
|
||||
Proxy Settings:
|
||||
Error getting network information. Is your proxy configured properly?: 取得網路資訊時發生錯誤。您的代理伺服器設定正確嗎?
|
||||
City: 城市
|
||||
@ -590,12 +590,12 @@ Channel:
|
||||
Releases: 發布
|
||||
This channel does not currently have any releases: 此頻道目前沒有任何發布
|
||||
Video:
|
||||
Open in YouTube: '在YouTube中開啟'
|
||||
Copy YouTube Link: '複製YouTube連結'
|
||||
Open YouTube Embedded Player: '開啟YouTube內嵌播放器'
|
||||
Copy YouTube Embedded Player Link: '複製YouTube內嵌播放器連結'
|
||||
Open in Invidious: '在Invidious中開啟'
|
||||
Copy Invidious Link: '複製Invidious連結'
|
||||
Open in YouTube: '在 YouTube 中開啟'
|
||||
Copy YouTube Link: '複製 YouTube 連結'
|
||||
Open YouTube Embedded Player: '開啟 YouTube 內嵌播放器'
|
||||
Copy YouTube Embedded Player Link: '複製 YouTube 內嵌播放器連結'
|
||||
Open in Invidious: '在 Invidious 中開啟'
|
||||
Copy Invidious Link: '複製 Invidious 連結'
|
||||
Views: '觀看'
|
||||
Watched: '已觀看'
|
||||
# As in a Live Video
|
||||
@ -661,10 +661,10 @@ Video:
|
||||
audio only: 僅音訊
|
||||
video only: 僅影片
|
||||
Download Video: 下載影片
|
||||
Copy Invidious Channel Link: 複製Invidious頻道連結
|
||||
Open Channel in Invidious: 在Invidious開啟頻道
|
||||
Copy YouTube Channel Link: 複製YouTube頻道連結
|
||||
Open Channel in YouTube: 在YouTube開啟頻道
|
||||
Copy Invidious Channel Link: 複製 Invidious 頻道連結
|
||||
Open Channel in Invidious: 在 Invidious 開啟頻道
|
||||
Copy YouTube Channel Link: 複製 YouTube 頻道連結
|
||||
Open Channel in YouTube: 在 YouTube 開啟頻道
|
||||
Started streaming on: '開始直播時間'
|
||||
Streamed on: 直播於
|
||||
Video has been removed from your saved list: 影片已從您的播放清單移除
|
||||
@ -722,6 +722,8 @@ Video:
|
||||
Upcoming: 即將到來
|
||||
'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:
|
||||
@ -944,3 +946,6 @@ Hashtag:
|
||||
This hashtag does not currently have any videos: 此標籤目前沒有任何影片
|
||||
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} 已從頻道過濾條件移除'
|
||||
|
Loading…
Reference in New Issue
Block a user