Merge branch 'development' into piped-support

This commit is contained in:
ChunkyProgrammer 2023-08-29 17:27:55 -07:00
commit 533c9da3b7
28 changed files with 328 additions and 225 deletions

65
.github/issue-labeler.yml vendored Normal file
View File

@ -0,0 +1,65 @@
'B: visual':
- '(visual bug)'
'B: Unofficial Download':
- '(AUR|Chocolatey|FreeTubeCordova|PortableApps|winget|Scoop|Snapcraft|MPR|Nix)'
'B: keyboard control':
- '(keyboard control not working)'
'B: text/string':
- '(text/string issue)'
'B: content not loading':
- '(content not loading)'
'B: accessibility':
- '(accessibility issue)'
'B: usability':
- '(usability issue)'
'B: crash':
- '(causes crash)'
'B: feature stopped working':
- '(feature stopped working)'
'B: inconsistent behavior':
- '(inconsistent behavior)'
'B: data loss':
- '(data loss)'
'B: race condition':
- '(race condition)'
'B: API issue':
- '(API issue)'
'B: developer mode':
- '(only happens in developer mode)'
'E: improvement existing feature':
- '(improvement to existing feature)'
'E: new optional setting':
- '(new optional setting)'
'E: visual improvement':
- '(visual improvement)'
'E: display more information':
- '(display more information to user)'
'E: ease of use improvement':
- '(ease of use improvement)'
'E: support external software':
- '(support for external software)'
'E: new feature':
- '(new feature)'
'E: keyboard shortcut':
- '(new keyboard shortcut)'

View File

@ -1,104 +0,0 @@
name: "Set Issue Label and Assignee"
on:
issues:
types: [opened]
jobs:
label_issue:
runs-on: ubuntu-latest
steps:
- uses: Naturalclar/issue-action@v2.0.2
with:
title-or-body: "body"
parameters: >-
[
{
"keywords": ["visual bug"],
"labels": ["B: visual"]
},
{
"keywords": ["AUR", "Chocolatey", "FreeTubeCordova", "PortableApps", "winget", "Scoop", "Snapcraft", "MPR", "Nix"],
"labels": ["B: Unofficial Download"]
},
{
"keywords": ["keyboard control not working"],
"labels": ["B: keyboard control"]
},
{
"keywords": ["text/string issue"],
"labels": ["B: text/string"]
},
{
"keywords": ["content not loading"],
"labels": ["B: content not loading"]
},
{
"keywords": ["accessibility issue"],
"labels": ["B: accessibility"]
},
{
"keywords": ["usability issue"],
"labels": ["B: usability"]
},
{
"keywords": ["causes crash"],
"labels": ["B: crash"]
},
{
"keywords": ["feature stopped working"],
"labels": ["B: feature stopped working"]
},
{
"keywords": ["inconsistent behavior"],
"labels": ["B: inconsistent behavior"]
},
{
"keywords": ["data loss"],
"labels": ["B: data loss"]
},
{
"keywords": ["race condition"],
"labels": ["B: race condition"]
},
{
"keywords": ["API issue"],
"labels": ["B: API issue"]
},
{
"keywords": ["only happens in developer mode"],
"labels": ["B: developer mode"]
},
{
"keywords": ["improvement to existing feature"],
"labels": ["E: improvement existing feature"]
},
{
"keywords": ["new optional setting"],
"labels": ["E: new optional setting"]
},
{
"keywords": ["visual improvement"],
"labels": ["E: visual improvement"]
},
{
"keywords": ["display more information to user"],
"labels": ["E: display more information"]
},
{
"keywords": ["ease of use improvement"],
"labels": ["E: ease of use improvement"]
},
{
"keywords": ["support for external software"],
"labels": ["E: support external software"]
},
{
"keywords": ["new feature"],
"labels": ["E: new feature"]
},
{
"keywords": ["new keyboard shortcut"],
"labels": ["E: keyboard shortcut"]
}
]
github-token: "${{ secrets.GITHUB_TOKEN }}"

18
.github/workflows/label-issue.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: "Issue Labeler"
on:
issues:
types: [opened]
permissions:
issues: write
contents: read
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: github/issue-labeler@v3.2
with:
configuration-path: .github/issue-labeler.yml
enable-versioned-regex: 0
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -14,3 +14,4 @@ jobs:
- uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/pr-labeler.yml

View File

@ -63,7 +63,7 @@
"autolinker": "^4.0.0",
"electron-context-menu": "^3.6.1",
"lodash.debounce": "^4.0.8",
"marked": "^7.0.4",
"marked": "^7.0.5",
"path-browserify": "^1.0.1",
"process": "^0.11.10",
"video.js": "7.21.5",
@ -78,11 +78,11 @@
"vue-router": "^3.6.5",
"vue-tiny-slider": "^0.1.39",
"vuex": "^3.6.2",
"youtubei.js": "^6.0.0"
"youtubei.js": "^6.1.0"
},
"devDependencies": {
"@babel/core": "^7.22.10",
"@babel/eslint-parser": "^7.22.10",
"@babel/core": "^7.22.11",
"@babel/eslint-parser": "^7.22.11",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/preset-env": "^7.22.10",
"@double-great/stylelint-a11y": "^2.0.2",
@ -92,12 +92,12 @@
"css-minimizer-webpack-plugin": "^5.0.1",
"electron": "^22.3.18",
"electron-builder": "^24.6.3",
"eslint": "^8.47.0",
"eslint": "^8.48.0",
"eslint-config-prettier": "^9.0.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-jsonc": "^2.9.0",
"eslint-plugin-n": "^16.0.1",
"eslint-plugin-n": "^16.0.2",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-unicorn": "^48.0.1",

View File

@ -26,6 +26,8 @@
.flexBox {
display: block;
user-select: unset;
-webkit-user-select: unset;
}
#changeLogText {

View File

@ -2,4 +2,6 @@
display: flex;
flex-flow: row wrap;
justify-content: space-evenly;
user-select: none;
-webkit-user-select: none;
}

View File

@ -3,6 +3,8 @@
flex-flow: row wrap;
justify-content: space-evenly;
position: relative;
-webkit-user-select: none;
user-select: none;
}
@ -84,6 +86,7 @@
list-style-type: none;
position: absolute;
text-align: center;
-webkit-user-select: none;
user-select: none;
z-index: 3;

View File

@ -97,6 +97,8 @@
.ft-input-component ::-webkit-input-placeholder {
color: var(--tertiary-text-color);
user-select: none;
-webkit-user-select: none;
}
.forceTextColor .ft-input {

View File

@ -53,9 +53,8 @@
<p
v-if="listType !== 'grid'"
class="description"
>
{{ description }}
</p>
v-html="description"
/>
</div>
</div>
</template>

View File

@ -120,9 +120,8 @@
<p
v-if="listType !== 'grid' && appearance === 'result'"
class="description"
>
{{ description }}
</p>
v-html="description"
/>
</div>
</div>
</template>

View File

@ -29,6 +29,8 @@
line-height: 1em;
text-align: center;
padding: 17.5px 0;
user-select: none;
-webkit-user-select: none;
}
.profileName {

View File

@ -36,6 +36,8 @@
line-height: 1em;
text-align: center;
padding: 25px 0;
user-select: none;
-webkit-user-select: none;
}
@media only screen and (max-width: 680px) {

View File

@ -17,6 +17,8 @@
font-size: 20px;
line-height: 1em;
text-align: center;
user-select: none;
-webkit-user-select: none;
}
.profileList {

View File

@ -19,6 +19,7 @@ pure-checkbox input[type="checkbox"], .pure-radiobutton input[type="checkbox"],
position: relative;
padding-left: 2em;
vertical-align: middle;
-webkit-user-select: none;
user-select: none;
cursor: pointer;
display: block;

View File

@ -39,6 +39,8 @@
}
.sectionTitle {
-webkit-user-select: none;
user-select: none;
margin-left: 2%;
}

View File

@ -132,6 +132,12 @@ export default defineComponent({
selectedBitrate: '',
selectedMimeType: '',
selectedFPS: 0,
currentAdaptiveFormat: null,
autoQuality: '',
autoResolution: '',
autoBitrate: '',
autoMimeType: '',
autoFPS: 0,
using60Fps: false,
activeSourceList: [],
activeAdaptiveFormats: [],
@ -230,11 +236,11 @@ export default defineComponent({
return this.$store.getters.getSponsorBlockShowSkippedToast
},
displayVideoPlayButton: function() {
displayVideoPlayButton: function () {
return this.$store.getters.getDisplayVideoPlayButton
},
enterFullscreenOnDisplayRotate: function() {
enterFullscreenOnDisplayRotate: function () {
return this.$store.getters.getEnterFullscreenOnDisplayRotate
},
@ -315,23 +321,23 @@ export default defineComponent({
return playbackRates
},
enableScreenshot: function() {
enableScreenshot: function () {
return this.$store.getters.getEnableScreenshot
},
screenshotFormat: function() {
screenshotFormat: function () {
return this.$store.getters.getScreenshotFormat
},
screenshotQuality: function() {
screenshotQuality: function () {
return this.$store.getters.getScreenshotQuality
},
screenshotAskPath: function() {
screenshotAskPath: function () {
return this.$store.getters.getScreenshotAskPath
},
screenshotFolder: function() {
screenshotFolder: function () {
return this.$store.getters.getScreenshotFolderPath
},
@ -340,11 +346,11 @@ export default defineComponent({
}
},
watch: {
showStatsModal: function() {
showStatsModal: function () {
this.player.trigger(this.statsModalEventName)
},
enableScreenshot: function() {
enableScreenshot: function () {
this.toggleScreenshotButton()
}
},
@ -466,10 +472,41 @@ export default defineComponent({
}
})
const qualityLevels = this.player.qualityLevels()
// Catch quality changes and update auto labels
// Event will not fire if new auto resolution is same as previous manual
// eg. 1080p30 -> auto 1080p30
qualityLevels.on('change', ({ selectedIndex }) => {
if (this.selectedQuality === 'auto' || (this.selectedQuality === '' && this.defaultQuality === 'auto')) {
const newQualityLevel = qualityLevels[selectedIndex]
this.autoBitrate = newQualityLevel.bitrate
this.autoFPS = newQualityLevel.frameRate
this.autoResolution = `${newQualityLevel.width}x${newQualityLevel.height}`
let qualityLabel = ''
const adaptiveFormat = this.activeAdaptiveFormats.find((format) => {
return format.bitrate === newQualityLevel.bitrate
})
if (adaptiveFormat) {
this.autoMimeType = adaptiveFormat.mimeType
this.currentAdaptiveFormat = adaptiveFormat
qualityLabel = `auto ${adaptiveFormat.qualityLabel}`
} else {
qualityLabel = `auto ${newQualityLevel.height}p`
}
// Can be run before createDashQualitySelector is called
const qualityElement = document.getElementById('vjs-current-quality')
if (qualityElement !== null) {
qualityElement.innerText = qualityLabel
}
}
})
// disable any quality the isn't the default one, as soon as it gets added
// we don't need to disable any qualities for auto
if (this.useDash && this.defaultQuality !== 'auto') {
const qualityLevels = this.player.qualityLevels()
qualityLevels.on('addqualitylevel', ({ qualityLevel }) => {
qualityLevel.enabled = qualityLevel.bitrate === this.selectedBitrate
})
@ -1077,6 +1114,7 @@ export default defineComponent({
})
const selectedFormat = formatsToTest[0]
this.currentAdaptiveFormat = selectedFormat
this.selectedBitrate = selectedFormat.bitrate
this.selectedResolution = `${selectedFormat.width}x${selectedFormat.height}`
this.selectedFPS = selectedFormat.fps
@ -1136,7 +1174,10 @@ export default defineComponent({
const selectedQuality = bitrate === 'auto' ? 'auto' : qualityLabel
const qualityElement = document.getElementById('vjs-current-quality')
qualityElement.innerText = selectedQuality
// Include resolution of previous selection
// If new auto resolution is the same as the previous resolution
// the qualityLevels on 'change' event will not be called
qualityElement.innerText = (selectedQuality === 'auto') ? `auto ${this.currentAdaptiveFormat.qualityLabel}` : selectedQuality
this.selectedQuality = selectedQuality
if (selectedQuality !== 'auto') {
@ -1144,7 +1185,14 @@ export default defineComponent({
this.selectedFPS = adaptiveFormat.fps
this.selectedBitrate = adaptiveFormat.bitrate
this.selectedMimeType = adaptiveFormat.mimeType
this.currentAdaptiveFormat = adaptiveFormat
} else {
// Default auto values to use previous selected values in case the adaptive format doesn't change
this.autoResolution = this.selectedResolution
this.autoFPS = this.selectedFPS
this.autoBitrate = this.selectedBitrate
this.autoMimeType = this.selectedMimeType
this.selectedResolution = 'auto'
this.selectedFPS = 'auto'
this.selectedBitrate = 'auto'
@ -1428,7 +1476,7 @@ export default defineComponent({
toggleFullWindow()
}
createControlTextEl (button) {
createControlTextEl(button) {
// Add class name to button to be able to target it with CSS selector
button.classList.add('vjs-button-fullwindow')
button.title = 'Full Window'
@ -1446,7 +1494,7 @@ export default defineComponent({
videojs.registerComponent('fullWindowButton', fullWindowButton)
},
createToggleTheatreModeButton: function() {
createToggleTheatreModeButton: function () {
if (!this.theatrePossible) {
return
}
@ -1478,7 +1526,7 @@ export default defineComponent({
videojs.registerComponent('toggleTheatreModeButton', toggleTheatreModeButton)
},
toggleTheatreMode: function() {
toggleTheatreMode: function () {
if (!this.player.isFullscreen_) {
const toggleTheatreModeButton = document.getElementById('toggleTheatreModeButton')
if (!this.useTheatreMode) {
@ -1520,7 +1568,7 @@ export default defineComponent({
videojs.registerComponent('screenshotButton', screenshotButton)
},
toggleScreenshotButton: function() {
toggleScreenshotButton: function () {
const button = document.getElementById('screenshotButton').parentNode
if (this.enableScreenshot && this.format !== 'audio') {
button.classList.remove('vjs-hidden')
@ -1529,7 +1577,7 @@ export default defineComponent({
}
},
takeScreenshot: async function() {
takeScreenshot: async function () {
if (!this.enableScreenshot || this.format === 'audio') {
return
}
@ -1652,11 +1700,13 @@ export default defineComponent({
},
createDashQualitySelector: function (levels) {
const currentAdaptiveFormat = this.currentAdaptiveFormat
const adaptiveFormats = this.adaptiveFormats
const activeAdaptiveFormats = this.activeAdaptiveFormats
const setDashQualityLevel = this.setDashQualityLevel
const defaultQuality = this.defaultQuality
const defaultBitrate = this.selectedBitrate
const autoResolution = this.autoResolution
const VjsButton = videojs.getComponent('Button')
class dashQualitySelector extends VjsButton {
@ -1743,7 +1793,15 @@ export default defineComponent({
button.title = 'Select Quality'
button.innerHTML = beginningHtml + qualityHtml + endingHtml
button.querySelector('#vjs-current-quality').innerText = defaultIsAuto ? 'auto' : currentQualityLabel
let autoQualityLabel = 'auto'
if (currentAdaptiveFormat) {
autoQualityLabel += ` ${currentAdaptiveFormat.qualityLabel}`
} else if (autoResolution !== '') {
autoQualityLabel += ` ${autoResolution.split('x')[1]}p`
}
// For default auto, it may select a resolution before generating the quality buttons
button.querySelector('#vjs-current-quality').innerText = defaultIsAuto ? autoQualityLabel : currentQualityLabel
return button.children[0]
}
@ -1794,7 +1852,7 @@ export default defineComponent({
})
},
transformAndInsertCaptions: async function() {
transformAndInsertCaptions: async function () {
let captionList
if (this.captionHybridList[0] instanceof Promise) {
captionList = await Promise.all(this.captionHybridList)
@ -1814,7 +1872,7 @@ export default defineComponent({
}
},
toggleFullWindow: function() {
toggleFullWindow: function () {
if (!this.player.isFullscreen_) {
if (this.player.isFullWindow) {
this.player.removeClass('vjs-full-screen')
@ -1836,7 +1894,7 @@ export default defineComponent({
}
},
exitFullWindow: function() {
exitFullWindow: function () {
if (this.player.isFullWindow) {
this.player.isFullWindow = false
document.documentElement.style.overflow = this.player.docOrigOverflow
@ -1901,14 +1959,14 @@ export default defineComponent({
this.usingTouch = false
},
toggleShowStatsModal: function() {
toggleShowStatsModal: function () {
if (this.format !== 'dash') {
showToast(this.$t('Video.Stats.Video statistics are not available for legacy videos'))
} else {
this.showStatsModal = !this.showStatsModal
}
},
createStatsModal: function() {
createStatsModal: function () {
const ModalDialog = videojs.getComponent('ModalDialog')
this.statsModal = new ModalDialog(this.player, {
temporary: false,
@ -1927,12 +1985,12 @@ export default defineComponent({
this.showStatsModal = false
})
},
updateStatsContent: function() {
updateStatsContent: function () {
if (this.showStatsModal) {
this.statsModal.contentEl().innerHTML = this.getFormattedStats()
}
},
getFormattedStats: function() {
getFormattedStats: function () {
const currentVolume = this.player.muted() ? 0 : this.player.volume()
const volume = `${(currentVolume * 100).toFixed(0)}%`
const bandwidth = `${(this.playerStats.bandwidth / 1000).toFixed(2)}kbps`
@ -1940,18 +1998,20 @@ export default defineComponent({
const droppedFrames = this.playerStats.videoPlaybackQuality.droppedVideoFrames
const totalFrames = this.playerStats.videoPlaybackQuality.totalVideoFrames
const frames = `${droppedFrames} / ${totalFrames}`
const resolution = this.selectedResolution === 'auto' ? 'auto' : `${this.selectedResolution}@${this.selectedFPS}fps`
const resolution = this.selectedResolution === 'auto' ? `${this.autoResolution}@${this.autoFPS}fps (auto)` : `${this.selectedResolution}@${this.selectedFPS}fps`
const playerDimensions = `${this.playerStats.playerDimensions.width}x${this.playerStats.playerDimensions.height}`
const bitrate = this.selectedBitrate === 'auto' ? `${this.autoBitrate} (auto)` : this.selectedBitrate
const mimeType = this.selectedMimeType === 'auto' ? `${this.autoMimeType} (auto)` : this.selectedMimeType
const statsArray = [
[this.$t('Video.Stats.Video ID'), this.videoId],
[this.$t('Video.Stats.Resolution'), resolution],
[this.$t('Video.Stats.Player Dimensions'), playerDimensions],
[this.$t('Video.Stats.Bitrate'), this.selectedBitrate],
[this.$t('Video.Stats.Bitrate'), bitrate],
[this.$t('Video.Stats.Volume'), volume],
[this.$t('Video.Stats.Bandwidth'), bandwidth],
[this.$t('Video.Stats.Buffered'), buffered],
[this.$t('Video.Stats.Dropped / Total Frames'), frames],
[this.$t('Video.Stats.Mimetype'), this.selectedMimeType]
[this.$t('Video.Stats.Mimetype'), mimeType]
]
let listContentHTML = ''
@ -2180,7 +2240,7 @@ export default defineComponent({
}
},
stopPowerSaveBlocker: function() {
stopPowerSaveBlocker: function () {
if (process.env.IS_ELECTRON && this.powerSaveBlocker !== null) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.STOP_POWER_SAVE_BLOCKER, this.powerSaveBlocker)

View File

@ -12,6 +12,8 @@
transition-property: width;
transition-duration: 150ms;
transition-timing-function: ease-in-out;
user-select: none;
-webkit-user-select: none;
}
.inner {
@ -41,6 +43,7 @@
position: relative;
padding: 5px;
min-height: 35px;
-webkit-user-drag: none;
}
.moreOption {

View File

@ -61,6 +61,7 @@
color: gray;
opacity: 0.5;
pointer-events: none;
-webkit-user-select: none;
user-select: none;
}

View File

@ -594,6 +594,13 @@ export function getVideoParamsFromUrl(url) {
return paramsObject
}
},
// youtube.com/live
function () {
if (/^\/live\/[\w-]+$/.test(urlObject.pathname)) {
extractParams(urlObject.pathname.replace('/live/', ''))
return paramsObject
}
},
// cloudtube
function () {
if (/^cadence\.(gq|moe)$/.test(urlObject.host) && /^\/cloudtube\/video\/[\w-]+$/.test(urlObject.pathname)) {

View File

@ -5,6 +5,8 @@ import router from './router/index'
import store from './store/index'
import i18n from './i18n/index'
import { library } from '@fortawesome/fontawesome-svg-core'
// Please keep the list of constants sorted by name
// to avoid code conflict and duplicate entries
import {
faAngleDown,
faArrowDown,
@ -26,6 +28,8 @@ import {
faExchangeAlt,
faExclamationCircle,
faExternalLinkAlt,
faEye,
faEyeSlash,
faFileDownload,
faFileVideo,
faFilter,
@ -57,8 +61,6 @@ import {
faTimes,
faTimesCircle,
faUsers,
faEye,
faEyeSlash,
} from '@fortawesome/free-solid-svg-icons'
import {
faBitcoin,
@ -72,6 +74,8 @@ Vue.config.devtools = process.env.NODE_ENV === 'development'
Vue.config.performance = process.env.NODE_ENV === 'development'
Vue.config.productionTip = process.env.NODE_ENV === 'development'
// Please keep the list of constants sorted by name
// to avoid code conflict and duplicate entries
library.add(
// solid icons
faAngleDown,
@ -94,6 +98,8 @@ library.add(
faExchangeAlt,
faExclamationCircle,
faExternalLinkAlt,
faEye,
faEyeSlash,
faFileDownload,
faFileVideo,
faFilter,
@ -125,8 +131,6 @@ library.add(
faTimes,
faTimesCircle,
faUsers,
faEye,
faEyeSlash,
// brand icons
faGithub,

View File

@ -85,6 +85,8 @@ $watched-transition-duration: 0.5s;
.inner {
grid-column: 1;
grid-row: 1;
user-select: none;
-webkit-user-select: none;
}
.thumbnailLink {
@ -141,7 +143,6 @@ $watched-transition-duration: 0.5s;
justify-self: left;
margin-bottom: 4px;
margin-left: 4px;
opacity: $thumbnail-overlay-opacity;
}
.favoritesIcon {
@ -149,7 +150,7 @@ $watched-transition-duration: 0.5s;
justify-self: right;
margin-right: 3px;
margin-top: 3px;
opacity: $thumbnail-overlay-opacity;
height: fit-content;
}
.watchedProgressBar {
@ -297,12 +298,43 @@ $watched-transition-duration: 0.5s;
margin-top: 8px;
}
}
.favoritesIcon,
.externalPlayerIcon {
opacity: $thumbnail-overlay-opacity;
}
@media (hover: hover) {
&:hover .favoritesIcon,
&:hover .externalPlayerIcon,
&:focus-within .favoritesIcon,
&:focus-within .externalPlayerIcon {
visibility: visible;
opacity: $thumbnail-overlay-opacity;
}
&:hover .optionsButton,
&:focus-within .optionsButton {
visibility: visible;
opacity: 1;
}
.favoritesIcon,
.externalPlayerIcon,
.optionsButton {
visibility: none;
opacity: 0;
transition: visibility 0s, opacity 0.2s linear;
}
}
}
.videoWatched,
.live,
.upcoming {
text-transform: uppercase;
user-select: none;
-webkit-user-select: none;
}
// we use h3 for semantic reasons but don't want to keep the h3 style

View File

@ -678,10 +678,9 @@ body.vjs-full-window {
.vjs-quality-level-value {
width: 100%;
height: 100%;
}
.vjs-quality-level-value span {
line-height: 40px;
display: flex;
justify-content: center;
align-items: center;
}
.vjs-quality-level-value:hover + .vjs-quality-level-menu {
@ -1091,17 +1090,10 @@ body.vjs-full-window {
cursor: pointer;
padding: 0;
margin: 0 0.45em 0 0.45em;
/* iOS Safari */
-webkit-touch-callout: none;
/* Safari */
-webkit-user-select: none;
/* Konqueror HTML */
/* Firefox */
-moz-user-select: none;
/* Internet Explorer/Edge */
-ms-user-select: none;
/* Non-prefixed version, currently supported by Chrome and Opera */
user-select: none;
background-color: #73859f;
background-color: rgba(115, 133, 159, 0.5);
}

View File

@ -574,6 +574,7 @@ About:
View License: Lizenz einsehen
Licensed under the AGPLv3: Lizensiert unter der AGPLv3
Source code: Quellcode
Discussions: Diskussionen
Channel:
Subscriber: Abonnement
Subscribers: Abonnements
@ -622,6 +623,9 @@ Channel:
This channel currently does not have any posts: Dieser Kanal enthält derzeit keine
Beiträge
Community: Gemeinschaft
votes: '{votes} Stimmen'
Reveal Answers: Antworten aufzeigen
Hide Answers: Antworten verbergen
Live:
Live: Live
This channel does not currently have any live streams: Dieser Kanal hat derzeit

View File

@ -85,6 +85,9 @@ Subscriptions:
Disabled Automatic Fetching: شما دریافت خودکار اشتراک را غیرفعال کرده اید. اشتراک‌ها
را بازخوانی کنید تا آنها را در اینجا ببینید.
Empty Channels: کانال های مشترک شما در حال حاضر هیچ ویدیویی ندارند.
All Subscription Tabs Hidden: همه تب های اشتراک پنهان هستند. برای مشاهده محتوا در
اینجا، لطفاً برخی از برگه‌ها را در بخش '{subsection}' در '{settingsSection}' باز
کنید.
More: 'بیشتر'
Trending:
Trending: 'پربازدید ها'
@ -123,11 +126,12 @@ Settings:
General Settings: 'تنظیمات عمومی'
Check for Updates: 'بررسی برای به روزرسانی ها'
Check for Latest Blog Posts: 'بررسی آخرین مطالب وبلاگ'
Fallback to Non-Preferred Backend on Failure: 'بازگشت به پشتیبانی ترجیح نداده
شده موقع شکست'
Fallback to Non-Preferred Backend on Failure: 'همه تب های اشتراک پنهان هستند.
برای مشاهده محتوا در اینجا، لطفاً برخی از برگه‌ها را در بخش ''{subsection}''
در ''{settingsSection}'' باز کنید.'
Enable Search Suggestions: 'فعال کردن پیشنهادات جستجو'
Default Landing Page: 'صفحه اول پیشفرض'
Locale Preference: 'اولویت مکان'
Locale Preference: 'تغییر زبان سیستم'
System Default: 'پیشفرض سیستم'
Preferred API Backend:
Preferred API Backend: 'ای پی ای پشتیبان ترجیح داده شده'

View File

@ -601,9 +601,9 @@ Channel:
Community: Сообщество
This channel currently does not have any posts: На этом канале в настоящее время
нет никаких записей
votes: '{голоса} голоса'
Hide Answers: Скрыть ответ
Reveal Answers: Раскрыть ответ
votes: 'Голосов: {votes}'
Hide Answers: Скрыть ответы
Reveal Answers: Раскрыть ответы
Live:
Live: Трансляции
This channel does not currently have any live streams: На этом канале в настоящее
@ -904,7 +904,7 @@ Profile:
Subscription List: Список подписок
Profile Filter: Фильтр профилей
Profile Settings: Настройки профиля
Toggle Profile List: Переключать список профилей
Toggle Profile List: Переключить список профилей
The playlist has been reversed: Подборка была перевёрнута
A new blog is now available, {blogTitle}. Click to view more: Доступен новый блог
{blogTitle}. Нажмите здесь, чтобы посмотреть подробности

110
yarn.lock
View File

@ -33,31 +33,31 @@
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730"
integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==
"@babel/core@^7.22.10":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.10.tgz#aad442c7bcd1582252cb4576747ace35bc122f35"
integrity sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==
"@babel/core@^7.22.11":
version "7.22.11"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.11.tgz#8033acaa2aa24c3f814edaaa057f3ce0ba559c24"
integrity sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==
dependencies:
"@ampproject/remapping" "^2.2.0"
"@babel/code-frame" "^7.22.10"
"@babel/generator" "^7.22.10"
"@babel/helper-compilation-targets" "^7.22.10"
"@babel/helper-module-transforms" "^7.22.9"
"@babel/helpers" "^7.22.10"
"@babel/parser" "^7.22.10"
"@babel/helpers" "^7.22.11"
"@babel/parser" "^7.22.11"
"@babel/template" "^7.22.5"
"@babel/traverse" "^7.22.10"
"@babel/types" "^7.22.10"
"@babel/traverse" "^7.22.11"
"@babel/types" "^7.22.11"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
json5 "^2.2.2"
json5 "^2.2.3"
semver "^6.3.1"
"@babel/eslint-parser@^7.22.10":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.22.10.tgz#bfdf3d1b32ad573fe7c1c3447e0b485e3a41fd09"
integrity sha512-0J8DNPRXQRLeR9rPaUMM3fA+RbixjnVLe/MRMYCkp3hzgsSuxCHQ8NN8xQG1wIHKJ4a1DTROTvFJdW+B5/eOsg==
"@babel/eslint-parser@^7.22.11":
version "7.22.11"
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.22.11.tgz#cceb8c7989c241a16dd14e12a6cd725618f3f58b"
integrity sha512-YjOYZ3j7TjV8OhLW6NCtyg8G04uStATEUe5eiLuCZaXz2VSDQ3dsAtm2D+TuQyAqNMUK2WacGo0/uma9Pein1w==
dependencies:
"@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1"
eslint-visitor-keys "^2.1.0"
@ -356,14 +356,14 @@
"@babel/template" "^7.22.5"
"@babel/types" "^7.22.10"
"@babel/helpers@^7.22.10":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.10.tgz#ae6005c539dfbcb5cd71fb51bfc8a52ba63bc37a"
integrity sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==
"@babel/helpers@^7.22.11":
version "7.22.11"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.11.tgz#b02f5d5f2d7abc21ab59eeed80de410ba70b056a"
integrity sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==
dependencies:
"@babel/template" "^7.22.5"
"@babel/traverse" "^7.22.10"
"@babel/types" "^7.22.10"
"@babel/traverse" "^7.22.11"
"@babel/types" "^7.22.11"
"@babel/highlight@^7.22.10":
version "7.22.10"
@ -374,10 +374,10 @@
chalk "^2.4.2"
js-tokens "^4.0.0"
"@babel/parser@^7.18.4", "@babel/parser@^7.22.10", "@babel/parser@^7.22.5":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55"
integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==
"@babel/parser@^7.18.4", "@babel/parser@^7.22.11", "@babel/parser@^7.22.5":
version "7.22.11"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.11.tgz#becf8ee33aad2a35ed5607f521fe6e72a615f905"
integrity sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g==
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5":
version "7.22.5"
@ -1039,10 +1039,10 @@
"@babel/parser" "^7.22.5"
"@babel/types" "^7.22.5"
"@babel/traverse@^7.18.9", "@babel/traverse@^7.22.10", "@babel/traverse@^7.22.5":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.10.tgz#20252acb240e746d27c2e82b4484f199cf8141aa"
integrity sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==
"@babel/traverse@^7.18.9", "@babel/traverse@^7.22.11", "@babel/traverse@^7.22.5":
version "7.22.11"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.11.tgz#71ebb3af7a05ff97280b83f05f8865ac94b2027c"
integrity sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==
dependencies:
"@babel/code-frame" "^7.22.10"
"@babel/generator" "^7.22.10"
@ -1050,15 +1050,15 @@
"@babel/helper-function-name" "^7.22.5"
"@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.22.10"
"@babel/types" "^7.22.10"
"@babel/parser" "^7.22.11"
"@babel/types" "^7.22.11"
debug "^4.1.0"
globals "^11.1.0"
"@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.22.10", "@babel/types@^7.22.5", "@babel/types@^7.4.4":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03"
integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==
"@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.22.10", "@babel/types@^7.22.11", "@babel/types@^7.22.5", "@babel/types@^7.4.4":
version "7.22.11"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.11.tgz#0e65a6a1d4d9cbaa892b2213f6159485fe632ea2"
integrity sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==
dependencies:
"@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.5"
@ -1189,10 +1189,10 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@eslint/js@^8.47.0":
version "8.47.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.47.0.tgz#5478fdf443ff8158f9de171c704ae45308696c7d"
integrity sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==
"@eslint/js@8.48.0":
version "8.48.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.48.0.tgz#642633964e217905436033a2bd08bf322849b7fb"
integrity sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==
"@fortawesome/fontawesome-common-types@6.4.2":
version "6.4.2"
@ -3691,10 +3691,10 @@ eslint-plugin-jsonc@^2.9.0:
jsonc-eslint-parser "^2.0.4"
natural-compare "^1.4.0"
eslint-plugin-n@^16.0.1:
version "16.0.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-16.0.1.tgz#baa62bb3af52940a53ba15386348ad9b0b425ada"
integrity sha512-CDmHegJN0OF3L5cz5tATH84RPQm9kG+Yx39wIqIwPR2C0uhBGMWfbbOtetR83PQjjidA5aXMu+LEFw1jaSwvTA==
eslint-plugin-n@^16.0.2:
version "16.0.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-16.0.2.tgz#5b2c0ad8dd9b724244d30fad2cc49ff4308a2152"
integrity sha512-Y66uDfUNbBzypsr0kELWrIz+5skicECrLUqlWuXawNSLUq3ltGlCwu6phboYYOTSnoTdHgTLrc+5Ydo6KjzZog==
dependencies:
"@eslint-community/eslint-utils" "^4.4.0"
builtins "^5.0.1"
@ -3796,15 +3796,15 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
eslint@^8.47.0:
version "8.47.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.47.0.tgz#c95f9b935463fb4fad7005e626c7621052e90806"
integrity sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==
eslint@^8.48.0:
version "8.48.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.48.0.tgz#bf9998ba520063907ba7bfe4c480dc8be03c2155"
integrity sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==
dependencies:
"@eslint-community/eslint-utils" "^4.2.0"
"@eslint-community/regexpp" "^4.6.1"
"@eslint/eslintrc" "^2.1.2"
"@eslint/js" "^8.47.0"
"@eslint/js" "8.48.0"
"@humanwhocodes/config-array" "^0.11.10"
"@humanwhocodes/module-importer" "^1.0.1"
"@nodelib/fs.walk" "^1.2.8"
@ -5283,7 +5283,7 @@ json5@^1.0.1, json5@^1.0.2:
dependencies:
minimist "^1.2.0"
json5@^2.2.0, json5@^2.2.2:
json5@^2.2.0, json5@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
@ -5578,10 +5578,10 @@ map-obj@^4.1.0:
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a"
integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==
marked@^7.0.4:
version "7.0.4"
resolved "https://registry.yarnpkg.com/marked/-/marked-7.0.4.tgz#e2558ee2d535b9df6a27c6e282dc603a18388a6d"
integrity sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ==
marked@^7.0.5:
version "7.0.5"
resolved "https://registry.yarnpkg.com/marked/-/marked-7.0.5.tgz#8a9e4e3afb93b58fe9ee7608e67cc154eb15d508"
integrity sha512-lwNAFTfXgqpt/XvK17a/8wY9/q6fcSPZT1aP6QW0u74VwaJF/Z9KbRcX23sWE4tODM+AolJNcUtErTkgOeFP/Q==
matcher@^3.0.0:
version "3.0.0"
@ -8678,10 +8678,10 @@ yocto-queue@^1.0.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
youtubei.js@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/youtubei.js/-/youtubei.js-6.0.0.tgz#545b8744a826ef7bfdb21a7c930950e784a85659"
integrity sha512-GGQpSfBGlqcDav4UtMoUehJTuHjLOZBynLFJBSIRZVXFWRzPCIl/LF2GTOZnZJfutmi62VaXvjiz4pGvCJO5bg==
youtubei.js@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/youtubei.js/-/youtubei.js-6.1.0.tgz#da93dd4e73ac4ceedf7000883083c7376e5b1091"
integrity sha512-EJmPuQ1pLimrcp5nPDSeZHVthT7KJLxp9Z5sYmihXKn1ct8e7cntKaFnPvy5QsbjpipICpDpQEs+d+owDhjgIg==
dependencies:
jintr "^1.1.0"
tslib "^2.5.0"