Merge remote-tracking branch 'origin/master' into toast

This commit is contained in:
Kavin 2023-03-17 04:13:04 +00:00
commit defefed9ed
No known key found for this signature in database
GPG Key ID: 49451E4482CC5BCD
37 changed files with 1176 additions and 729 deletions

View File

@ -143,6 +143,7 @@ Contributions in any other form are also welcomed.
- [Hyperpipe](https://codeberg.org/Hyperpipe/Hyperpipe) - an alternative privacy respecting frontend for YouTube Music. - [Hyperpipe](https://codeberg.org/Hyperpipe/Hyperpipe) - an alternative privacy respecting frontend for YouTube Music.
- [Musicale](https://github.com/Bellisario/musicale) - an alternative to YouTube Music, with style. - [Musicale](https://github.com/Bellisario/musicale) - an alternative to YouTube Music, with style.
- [ytify](https://github.com/n-ce/ytify) - a complementary minimal audio streaming frontend for YouTube. - [ytify](https://github.com/n-ce/ytify) - a complementary minimal audio streaming frontend for YouTube.
- [PsTube](https://github.com/prateekmedia/pstube) - Watch and download videos without ads
## YourKit ## YourKit

View File

@ -2,6 +2,7 @@ server {
listen 80; listen 80;
listen [::]:80; listen [::]:80;
server_name localhost; server_name localhost;
error_log off;
location / { location / {
root /usr/share/nginx/html; root /usr/share/nginx/html;

View File

@ -7,6 +7,7 @@
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<link title="Piped" type="application/opensearchdescription+xml" rel="search" href="/opensearch.xml" /> <link title="Piped" type="application/opensearchdescription+xml" rel="search" href="/opensearch.xml" />
<title>Piped</title> <title>Piped</title>
<meta name="theme-color" content="#0f0f0f">
<meta property="og:title" content="Piped" /> <meta property="og:title" content="Piped" />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:image" content="/img/icons/favicon-32x32.png" /> <meta property="og:image" content="/img/icons/favicon-32x32.png" />

View File

@ -14,11 +14,11 @@
"@fortawesome/free-solid-svg-icons": "6.3.0", "@fortawesome/free-solid-svg-icons": "6.3.0",
"@fortawesome/vue-fontawesome": "3.0.3", "@fortawesome/vue-fontawesome": "3.0.3",
"buffer": "6.0.3", "buffer": "6.0.3",
"dompurify": "3.0.0", "dompurify": "3.0.1",
"hotkeys-js": "3.10.1", "hotkeys-js": "3.10.1",
"javascript-time-ago": "2.5.9", "javascript-time-ago": "2.5.9",
"mux.js": "6.3.0", "mux.js": "6.3.0",
"shaka-player": "4.3.4", "shaka-player": "4.3.5",
"stream-browserify": "3.0.0", "stream-browserify": "3.0.0",
"vue": "3.2.47", "vue": "3.2.47",
"vue-i18n": "9.2.2", "vue-i18n": "9.2.2",
@ -26,22 +26,22 @@
"xml-js": "1.6.11" "xml-js": "1.6.11"
}, },
"devDependencies": { "devDependencies": {
"@iconify/json": "2.2.27", "@iconify/json": "2.2.35",
"@intlify/vite-plugin-vue-i18n": "6.0.3", "@intlify/vite-plugin-vue-i18n": "6.0.3",
"@unocss/preset-icons": "0.50.0", "@unocss/preset-icons": "0.50.6",
"@unocss/preset-web-fonts": "0.50.0", "@unocss/preset-web-fonts": "0.50.6",
"@unocss/transformer-directives": "0.50.0", "@unocss/transformer-directives": "0.50.6",
"@unocss/transformer-variant-group": "0.50.0", "@unocss/transformer-variant-group": "0.50.6",
"@vitejs/plugin-legacy": "4.0.1", "@vitejs/plugin-legacy": "4.0.2",
"@vitejs/plugin-vue": "4.0.0", "@vitejs/plugin-vue": "4.1.0",
"@vue/compiler-sfc": "3.2.47", "@vue/compiler-sfc": "3.2.47",
"eslint": "8.34.0", "eslint": "8.36.0",
"eslint-config-prettier": "8.6.0", "eslint-config-prettier": "8.7.0",
"eslint-plugin-prettier": "4.2.1", "eslint-plugin-prettier": "4.2.1",
"eslint-plugin-vue": "9.9.0", "eslint-plugin-vue": "9.9.0",
"prettier": "2.8.4", "prettier": "2.8.4",
"unocss": "0.50.0", "unocss": "0.50.6",
"vite": "4.1.4", "vite": "4.2.0",
"vite-plugin-eslint": "1.8.1", "vite-plugin-eslint": "1.8.1",
"vite-plugin-pwa": "0.14.4" "vite-plugin-pwa": "0.14.4"
}, },

View File

@ -1,12 +1,13 @@
<template> <template>
<div class="w-full min-h-screen px-1vw py-5 reset" :class="[theme]"> <div class="flex flex-col w-full min-h-screen px-1vw py-5 reset" :class="[theme]">
<div class="flex-1">
<NavBar /> <NavBar />
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
<keep-alive :max="5"> <keep-alive :max="5">
<component :is="Component" :key="$route.fullPath" /> <component :is="Component" :key="$route.fullPath" />
</keep-alive> </keep-alive>
</router-view> </router-view>
</div>
<FooterComponent /> <FooterComponent />
</div> </div>
@ -34,6 +35,14 @@ export default {
if (themePref == "auto") this.theme = darkModePreference.matches ? "dark" : "light"; if (themePref == "auto") this.theme = darkModePreference.matches ? "dark" : "light";
else this.theme = themePref; else this.theme = themePref;
// Change title bar color based on user's theme
const themeColor = document.querySelector("meta[name='theme-color']");
if (this.theme === "light") {
themeColor.setAttribute("content", "#FFF");
} else {
themeColor.setAttribute("content", "#0F0F0F");
}
// Used for the scrollbar // Used for the scrollbar
const root = document.querySelector(":root"); const root = document.querySelector(":root");
this.theme == "dark" ? root.classList.add("dark") : root.classList.remove("dark"); this.theme == "dark" ? root.classList.add("dark") : root.classList.remove("dark");

View File

@ -1,18 +1,23 @@
<template> <template>
<ErrorHandler v-if="channel && channel.error" :message="channel.message" :error="channel.error" /> <ErrorHandler v-if="channel && channel.error" :message="channel.message" :error="channel.error" />
<div v-if="channel" v-show="!channel.error"> <LoadingIndicatorPage :show-content="channel != null && !channel.error">
<div class="flex justify-center place-items-center"> <img
v-if="channel.bannerUrl"
:src="channel.bannerUrl"
class="w-full py-1.5 h-30 md:h-50 object-cover"
loading="lazy"
/>
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="flex place-items-center">
<img height="48" width="48" class="rounded-full m-1" :src="channel.avatarUrl" /> <img height="48" width="48" class="rounded-full m-1" :src="channel.avatarUrl" />
<h1 v-text="channel.name" /> <div class="flex gap-1 items-center">
<font-awesome-icon class="ml-1.5 !text-3xl" v-if="channel.verified" icon="check" /> <h1 v-text="channel.name" class="!text-xl" />
<font-awesome-icon class="!text-xl" v-if="channel.verified" icon="check" />
</div>
</div> </div>
<img v-if="channel.bannerUrl" :src="channel.bannerUrl" class="w-full pb-1.5" loading="lazy" />
<!-- eslint-disable-next-line vue/no-v-html -->
<p class="whitespace-pre-wrap">
<span v-html="purifyHTML(rewriteDescription(channel.description))" />
</p>
<div class="flex gap-2">
<button <button
class="btn" class="btn"
@click="subscribeHandler" @click="subscribeHandler"
@ -30,14 +35,30 @@
v-if="channel.id" v-if="channel.id"
:href="`${apiUrl()}/feed/unauthenticated/rss?channels=${channel.id}`" :href="`${apiUrl()}/feed/unauthenticated/rss?channels=${channel.id}`"
target="_blank" target="_blank"
class="btn flex-col mx-3" class="btn flex-col"
> >
<font-awesome-icon icon="rss" /> <font-awesome-icon icon="rss" />
</a> </a>
</div>
</div>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-if="channel.description" class="whitespace-pre-wrap py-2 mx-1">
<span v-if="fullDescription" v-html="purifyHTML(rewriteDescription(channel.description))" />
<span v-html="purifyHTML(rewriteDescription(channel.description.slice(0, 100)))" v-else />
<span v-if="channel.description.length > 100 && !fullDescription">...</span>
<button
v-if="channel.description.length > 100"
class="hover:underline font-semibold text-neutral-500 block whitespace-normal"
@click="fullDescription = !fullDescription"
>
[{{ fullDescription ? $t("actions.show_less") : $t("actions.show_more") }}]
</button>
</div>
<WatchOnYouTubeButton :link="`https://youtube.com/channel/${this.channel.id}`" /> <WatchOnYouTubeButton :link="`https://youtube.com/channel/${this.channel.id}`" />
<div class="flex mt-4 mb-2"> <div class="flex my-2 mx-1">
<button <button
v-for="(tab, index) in tabs" v-for="(tab, index) in tabs"
:key="tab.name" :key="tab.name"
@ -61,19 +82,21 @@
hide-channel hide-channel
/> />
</div> </div>
</div> </LoadingIndicatorPage>
</template> </template>
<script> <script>
import ErrorHandler from "./ErrorHandler.vue"; import ErrorHandler from "./ErrorHandler.vue";
import ContentItem from "./ContentItem.vue"; import ContentItem from "./ContentItem.vue";
import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue"; import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
export default { export default {
components: { components: {
ErrorHandler, ErrorHandler,
ContentItem, ContentItem,
WatchOnYouTubeButton, WatchOnYouTubeButton,
LoadingIndicatorPage,
}, },
data() { data() {
return { return {
@ -82,6 +105,7 @@ export default {
tabs: [], tabs: [],
selectedTab: 0, selectedTab: 0,
contentItems: [], contentItems: [],
fullDescription: false,
}; };
}, },
mounted() { mounted() {
@ -121,7 +145,9 @@ export default {
}); });
}, },
async fetchChannel() { async fetchChannel() {
const url = this.apiUrl() + "/" + this.$route.params.path + "/" + this.$route.params.channelId; const url = this.$route.path.includes("@")
? this.apiUrl() + "/c/" + this.$route.params.channelId
: this.apiUrl() + "/" + this.$route.params.path + "/" + this.$route.params.channelId;
return await this.fetchJson(url); return await this.fetchJson(url);
}, },
async getChannelData() { async getChannelData() {

View File

@ -24,27 +24,29 @@
<hr /> <hr />
<div class="video-grid"> <LoadingIndicatorPage :show-content="videosStore != null" class="video-grid">
<template v-for="video in videos" :key="video.url"> <template v-for="video in videos" :key="video.url">
<VideoItem v-if="shouldShowVideo(video)" :is-feed="true" :item="video" /> <VideoItem v-if="shouldShowVideo(video)" :is-feed="true" :item="video" />
</template> </template>
</div> </LoadingIndicatorPage>
</template> </template>
<script> <script>
import VideoItem from "./VideoItem.vue"; import VideoItem from "./VideoItem.vue";
import SortingSelector from "./SortingSelector.vue"; import SortingSelector from "./SortingSelector.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
export default { export default {
components: { components: {
VideoItem, VideoItem,
SortingSelector, SortingSelector,
LoadingIndicatorPage,
}, },
data() { data() {
return { return {
currentVideoCount: 0, currentVideoCount: 0,
videoStep: 100, videoStep: 100,
videosStore: [], videosStore: null,
videos: [], videos: [],
availableFilters: ["all", "shorts", "videos"], availableFilters: ["all", "shorts", "videos"],
selectedFilter: "all", selectedFilter: "all",

View File

@ -0,0 +1,55 @@
<template>
<div v-if="!showContent" class="flex min-h-[75vh] w-full justify-center items-center">
<span id="spinner" />
</div>
<div v-else>
<slot />
</div>
</template>
<style>
#spinner:after {
--spinner-color: #000;
}
.dark #spinner:after {
--spinner-color: #fff;
}
#spinner {
display: inline-block;
width: 70px;
height: 70px;
}
#spinner:after {
content: " ";
display: block;
width: 54px;
height: 54px;
margin: 8px;
border-radius: 50%;
border: 4px solid var(--spinner-color);
border-color: var(--spinner-color) transparent var(--spinner-color) transparent;
animation: spinner 1.2s linear infinite;
}
@keyframes spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
<script>
export default {
props: {
showContent: {
type: Boolean,
required: true,
},
},
};
</script>

View File

@ -1,7 +1,7 @@
<template> <template>
<ErrorHandler v-if="playlist && playlist.error" :message="playlist.message" :error="playlist.error" /> <ErrorHandler v-if="playlist && playlist.error" :message="playlist.message" :error="playlist.error" />
<div v-if="playlist" v-show="!playlist.error"> <LoadingIndicatorPage :show-content="playlist" v-show="!playlist.error">
<h1 class="text-center my-4" v-text="playlist.name" /> <h1 class="text-center my-4" v-text="playlist.name" />
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
@ -46,11 +46,12 @@
width="168" width="168"
/> />
</div> </div>
</div> </LoadingIndicatorPage>
</template> </template>
<script> <script>
import ErrorHandler from "./ErrorHandler.vue"; import ErrorHandler from "./ErrorHandler.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
import VideoItem from "./VideoItem.vue"; import VideoItem from "./VideoItem.vue";
import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue"; import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue";
@ -59,6 +60,7 @@ export default {
ErrorHandler, ErrorHandler,
VideoItem, VideoItem,
WatchOnYouTubeButton, WatchOnYouTubeButton,
LoadingIndicatorPage,
}, },
data() { data() {
return { return {
@ -88,7 +90,7 @@ export default {
}, },
}).then(json => { }).then(json => {
if (json.error) alert(json.error); if (json.error) alert(json.error);
else if (json.filter(playlist => playlist.id === playlistId).length > 0) this.admin = true; else if (json.some(playlist => playlist.id === playlistId)) this.admin = true;
}); });
this.isPlaylistBookmarked(); this.isPlaylistBookmarked();
}, },

View File

@ -376,6 +376,7 @@ export default {
languages: [ languages: [
{ code: "ar", name: "Arabic" }, { code: "ar", name: "Arabic" },
{ code: "az", name: "Azərbaycan" }, { code: "az", name: "Azərbaycan" },
{ code: "bg", name: "Български" },
{ code: "bn", name: "বাংলা" }, { code: "bn", name: "বাংলা" },
{ code: "bs", name: "Bosanski" }, { code: "bs", name: "Bosanski" },
{ code: "ca", name: "Català" }, { code: "ca", name: "Català" },
@ -436,7 +437,7 @@ export default {
this.fetchJson("https://piped-instances.kavin.rocks/").then(resp => { this.fetchJson("https://piped-instances.kavin.rocks/").then(resp => {
this.instances = resp; this.instances = resp;
if (this.instances.filter(instance => instance.api_url == this.apiUrl()).length == 0) if (!this.instances.some(instance => instance.api_url == this.apiUrl()))
this.instances.push({ this.instances.push({
name: "Custom Instance", name: "Custom Instance",
api_url: this.apiUrl(), api_url: this.apiUrl(),
@ -615,4 +616,10 @@ export default {
.pref { .pref {
@apply flex justify-between items-center my-2 mx-[15vw] lt-md:mx-[2vw]; @apply flex justify-between items-center my-2 mx-[15vw] lt-md:mx-[2vw];
} }
.pref:nth-child(odd) {
@apply bg-gray-200;
}
.dark .pref:nth-child(odd) {
@apply bg-dark-800;
}
</style> </style>

View File

@ -18,19 +18,21 @@
</i18n-t> </i18n-t>
</div> </div>
<div v-if="results" class="video-grid"> <LoadingIndicatorPage :show-content="results != null && results.items?.length" class="video-grid">
<template v-for="result in results.items" :key="result.url"> <template v-for="result in results.items" :key="result.url">
<ContentItem :item="result" height="94" width="168" /> <ContentItem :item="result" height="94" width="168" />
</template> </template>
</div> </LoadingIndicatorPage>
</template> </template>
<script> <script>
import ContentItem from "./ContentItem.vue"; import ContentItem from "./ContentItem.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
export default { export default {
components: { components: {
ContentItem, ContentItem,
LoadingIndicatorPage,
}, },
data() { data() {
return { return {

View File

@ -3,17 +3,19 @@
<hr /> <hr />
<div class="video-grid"> <LoadingIndicatorPage :show-content="videos.length != 0" class="video-grid">
<VideoItem v-for="video in videos" :key="video.url" :item="video" height="118" width="210" /> <VideoItem v-for="video in videos" :key="video.url" :item="video" height="118" width="210" />
</div> </LoadingIndicatorPage>
</template> </template>
<script> <script>
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
import VideoItem from "./VideoItem.vue"; import VideoItem from "./VideoItem.vue";
export default { export default {
components: { components: {
VideoItem, VideoItem,
LoadingIndicatorPage,
}, },
data() { data() {
return { return {

View File

@ -12,10 +12,10 @@
> >
<div class="w-full"> <div class="w-full">
<img <img
class="w-full aspect-video" class="w-full aspect-video object-contain"
:src="item.thumbnail" :src="item.thumbnail"
:alt="item.title" :alt="item.title"
:class="{ 'shorts-img': item.isShort }" :class="{ 'shorts-img': item.isShort, 'opacity-75': item.watched }"
loading="lazy" loading="lazy"
/> />
<!-- progress bar --> <!-- progress bar -->

View File

@ -92,7 +92,7 @@ export default {
this.hotkeysPromise.then(() => { this.hotkeysPromise.then(() => {
var self = this; var self = this;
this.$hotkeys( this.$hotkeys(
"f,m,j,k,l,c,space,up,down,left,right,0,1,2,3,4,5,6,7,8,9,shift+n,shift+,,shift+.,return", "f,m,j,k,l,c,space,up,down,left,right,0,1,2,3,4,5,6,7,8,9,shift+n,shift+,,shift+.,return,.,,",
function (e, handler) { function (e, handler) {
const videoEl = self.$refs.videoEl; const videoEl = self.$refs.videoEl;
switch (handler.key) { switch (handler.key) {
@ -191,6 +191,14 @@ export default {
case "return": case "return":
self.skipSegment(videoEl); self.skipSegment(videoEl);
break; break;
case ".":
videoEl.currentTime += 0.04;
e.preventDefault();
break;
case ",":
videoEl.currentTime -= 0.04;
e.preventDefault();
break;
} }
}, },
); );
@ -206,6 +214,8 @@ export default {
}, },
methods: { methods: {
async loadVideo() { async loadVideo() {
this.updateSponsors();
const component = this; const component = this;
const videoEl = this.$refs.videoEl; const videoEl = this.$refs.videoEl;
@ -263,9 +273,7 @@ export default {
const MseSupport = window.MediaSource !== undefined; const MseSupport = window.MediaSource !== undefined;
const lbry = this.getPreferenceBoolean("disableLBRY", false) const lbry = null;
? null
: this.video.videoStreams.filter(stream => stream.quality === "LBRY")[0];
var uri; var uri;
var mime; var mime;
@ -275,9 +283,10 @@ export default {
mime = "application/x-mpegURL"; mime = "application/x-mpegURL";
} else if (this.video.audioStreams.length > 0 && !lbry && MseSupport) { } else if (this.video.audioStreams.length > 0 && !lbry && MseSupport) {
if (!this.video.dash) { if (!this.video.dash) {
const dash = ( const dash = (await import("../utils/DashUtils.js")).generate_dash_file_from_formats(
await import("@/utils/DashUtils.js").then(mod => mod.default) streams,
).generate_dash_file_from_formats(streams, this.video.duration); this.video.duration,
);
uri = "data:application/dash+xml;charset=utf-8;base64," + btoa(dash); uri = "data:application/dash+xml;charset=utf-8;base64," + btoa(dash);
} else { } else {
@ -313,7 +322,7 @@ export default {
uri = this.video.hls; uri = this.video.hls;
mime = "application/x-mpegURL"; mime = "application/x-mpegURL";
} else { } else {
uri = this.video.videoStreams.filter(stream => stream.codec == null).slice(-1)[0].url; uri = this.video.videoStreams.findLast(stream => stream.codec == null).url;
mime = "video/mp4"; mime = "video/mp4";
} }
@ -363,6 +372,9 @@ export default {
else this.setPlayerAttrs(this.$player, videoEl, uri, mime, this.$shaka); else this.setPlayerAttrs(this.$player, videoEl, uri, mime, this.$shaka);
if (noPrevPlayer) { if (noPrevPlayer) {
videoEl.addEventListener("loadeddata", () => {
if (document.pictureInPictureElement) videoEl.requestPictureInPicture();
});
videoEl.addEventListener("timeupdate", () => { videoEl.addEventListener("timeupdate", () => {
const time = videoEl.currentTime; const time = videoEl.currentTime;
this.$emit("timeupdate", time); this.$emit("timeupdate", time);
@ -647,22 +659,7 @@ export default {
if (markers) markers.style.background = `linear-gradient(${array.join(",")})`; if (markers) markers.style.background = `linear-gradient(${array.join(",")})`;
}, },
destroy(hotkeys) { updateSponsors() {
if (this.$ui) {
this.$ui.destroy();
this.$ui = undefined;
this.$player = undefined;
}
if (this.$player) {
this.$player.destroy();
this.$player = undefined;
}
if (hotkeys) this.$hotkeys?.unbind();
this.$refs.container?.querySelectorAll("div").forEach(node => node.remove());
},
},
watch: {
sponsors() {
const skipOptions = this.getPreferenceJSON("skipOptions", {}); const skipOptions = this.getPreferenceJSON("skipOptions", {});
this.sponsors?.segments?.forEach(segment => { this.sponsors?.segments?.forEach(segment => {
const option = skipOptions[segment.category]; const option = skipOptions[segment.category];
@ -674,6 +671,19 @@ export default {
}); });
} }
}, },
destroy(hotkeys) {
if (this.$ui && !document.pictureInPictureElement) {
this.$ui.destroy();
this.$ui = undefined;
this.$player = undefined;
}
if (this.$player) {
this.$player.destroy();
if (!document.pictureInPictureElement) this.$player = undefined;
}
if (hotkeys) this.$hotkeys?.unbind();
this.$refs.container?.querySelectorAll("div").forEach(node => node.remove());
},
}, },
}; };
</script> </script>

View File

@ -10,7 +10,7 @@
/> />
</div> </div>
<div v-if="video && !isEmbed" class="w-full"> <LoadingIndicatorPage :show-content="video && !isEmbed" class="w-full">
<ErrorHandler v-if="video && video.error" :message="video.message" :error="video.error" /> <ErrorHandler v-if="video && video.error" :message="video.message" :error="video.error" />
<Transition> <Transition>
<ToastComponent v-if="shouldShowToast" @dismissed="dismiss"> <ToastComponent v-if="shouldShowToast" @dismissed="dismiss">
@ -20,6 +20,7 @@
<div v-show="!video.error"> <div v-show="!video.error">
<div :class="isMobile ? 'flex-col' : 'flex'"> <div :class="isMobile ? 'flex-col' : 'flex'">
<keep-alive>
<VideoPlayer <VideoPlayer
ref="videoPlayer" ref="videoPlayer"
:video="video" :video="video"
@ -29,6 +30,7 @@
@timeupdate="onTimeUpdate" @timeupdate="onTimeUpdate"
@ended="onVideoEnded" @ended="onVideoEnded"
/> />
</keep-alive>
<ChaptersBar <ChaptersBar
:mobileLayout="isMobile" :mobileLayout="isMobile"
v-if="video?.chapters?.length > 0 && showChapters" v-if="video?.chapters?.length > 0 && showChapters"
@ -219,7 +221,7 @@
<hr class="sm:hidden" /> <hr class="sm:hidden" />
</div> </div>
</div> </div>
</div> </LoadingIndicatorPage>
</template> </template>
<script> <script>
@ -232,6 +234,7 @@ import PlaylistAddModal from "./PlaylistAddModal.vue";
import ShareModal from "./ShareModal.vue"; import ShareModal from "./ShareModal.vue";
import PlaylistVideos from "./PlaylistVideos.vue"; import PlaylistVideos from "./PlaylistVideos.vue";
import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue"; import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
import ToastComponent from "./ToastComponent.vue"; import ToastComponent from "./ToastComponent.vue";
export default { export default {
@ -246,14 +249,13 @@ export default {
ShareModal, ShareModal,
PlaylistVideos, PlaylistVideos,
WatchOnYouTubeButton, WatchOnYouTubeButton,
LoadingIndicatorPage,
ToastComponent, ToastComponent,
}, },
data() { data() {
const smallViewQuery = window.matchMedia("(max-width: 640px)"); const smallViewQuery = window.matchMedia("(max-width: 640px)");
return { return {
video: { video: null,
title: "Loading...",
},
playlistId: null, playlistId: null,
playlist: null, playlist: null,
index: null, index: null,
@ -361,7 +363,7 @@ export default {
this.showDesc = !this.getPreferenceBoolean("minimizeDescription", false); this.showDesc = !this.getPreferenceBoolean("minimizeDescription", false);
this.showRecs = !this.getPreferenceBoolean("minimizeRecommendations", false); this.showRecs = !this.getPreferenceBoolean("minimizeRecommendations", false);
this.showChapters = !this.getPreferenceBoolean("minimizeChapters", false); this.showChapters = !this.getPreferenceBoolean("minimizeChapters", false);
if (this.video.duration) { if (this.video?.duration) {
document.title = this.video.title + " - Piped"; document.title = this.video.title + " - Piped";
this.$refs.videoPlayer.loadVideo(); this.$refs.videoPlayer.loadVideo();
} }

View File

@ -4,10 +4,10 @@
"login": "تسجيل الدخول", "login": "تسجيل الدخول",
"register": "إنشاء حساب", "register": "إنشاء حساب",
"preferences": "الإعدادات", "preferences": "الإعدادات",
"history": "تاريخ التصفح", "history": "سجل المشاهدة",
"subscriptions": "الاشتراكات", "subscriptions": "الاشتراكات",
"playlists": "قوائم التشغيل", "playlists": "قوائم التشغيل",
"feed": "التغذية", "feed": "محتوى الاشتراكات",
"account": "الحساب", "account": "الحساب",
"instance": "الخادم", "instance": "الخادم",
"player": "المشغل", "player": "المشغل",
@ -127,7 +127,8 @@
"skip_button_only": "إظهار زر التخطي", "skip_button_only": "إظهار زر التخطي",
"skip_automatically": "تلقائيا", "skip_automatically": "تلقائيا",
"min_segment_length": "الحد الأدنى لطول الفصل (بالثواني)", "min_segment_length": "الحد الأدنى لطول الفصل (بالثواني)",
"skip_segment": "تخطي الجزء" "skip_segment": "تخطي الجزء",
"show_less": "عرض أقل"
}, },
"video": { "video": {
"sponsor_segments": "المقاطع الإعلانية", "sponsor_segments": "المقاطع الإعلانية",

168
src/locales/bg.json Normal file
View File

@ -0,0 +1,168 @@
{
"titles": {
"channels": "Канали",
"login": "Вход",
"register": "Регистрация",
"feed": "Абонаменти",
"history": "История",
"playlists": "Плейлисти",
"instance": "Инстанция",
"player": "Плейър",
"livestreams": "Излъчвания на живо",
"bookmarks": "Отметки",
"trending": "Набиращи популярност",
"account": "Профил",
"preferences": "Настройки",
"subscriptions": "Абонаменти"
},
"actions": {
"most_recent": "Най-скорошен",
"unsubscribe": "Отписване - {count}",
"uses_api_from": "Използва API от ",
"skip_sponsors": "Пропускане на спонсори",
"skip_preview": "Пропускане на преглед/обобщение",
"skip_self_promo": "Пропускане на самореклама/неплатена реклама",
"min_segment_length": "Минимална дължина на сегмента (в секунди)",
"default_quality": "Качество по подразбиране",
"minimize_comments_default": "Минимизиране на коментарите по подразбиране",
"subscribe": "Абониране - {count}",
"view_subscriptions": "Преглед на абонаменти",
"sort_by": "Сортиране по:",
"least_recent": "Най-малко скорошен",
"channel_name_asc": "Име на канал (А-Я)",
"channel_name_desc": "Име на канал (Я-А)",
"back": "Назад",
"enable_sponsorblock": "Активиране на SponsorBlock",
"skip_button_only": "Показване на бутона за пропускане",
"skip_automatically": "Автоматично",
"skip_intro": "Пропускане на прекъсване/въвеждаща анимация",
"skip_outro": "Пропускане на крайни карти/надписи",
"skip_interaction": "Пропускане на напомняне за абониране",
"skip_non_music": "Попускане Немузикален раздел в музика",
"skip_highlight": "Пропускане на видео акцент",
"show_markers": "Показване на маркери в плейъра",
"skip_segment": "Пропускане на сегмент",
"theme": "Тема",
"auto": "Автоматично",
"dark": "Тъмна",
"light": "Светла",
"autoplay_video": "Автоматично пускане на видео",
"audio_only": "Само аудио",
"buffering_goal": "Буфериране (в секунди)",
"country_selection": "Избор на държава",
"default_homepage": "Начална страница по подразбиране",
"minimize_description_default": "Минимизиране на описанието по подразбиране",
"store_watch_history": "Запазване на историята на гледане",
"language_selection": "Избор на език",
"instances_list": "Списък на инстанциите",
"enabled_codecs": "Разрешени кодеци (множество)",
"instance_selection": "Избор на инстанция",
"show_more": "Покажи повече",
"yes": "Да",
"no": "Не",
"export_to_json": "Експорт в JSON",
"import_from_json": "Импорт от JSON/CSV",
"loop_this_video": "Повтаряне на това видео",
"auto_play_next_video": "Автоматично пускане на следващото видео",
"donations": "Дарения за разработка",
"minimize_comments": "Минимизиране на коментарите",
"show_comments": "Показване на коментарите",
"show_description": "Показване на описание",
"search": "Търси",
"minimize_description": "Минимизиране на описание",
"filter": "Филтър",
"clear_history": "Изчистване на историята",
"minimize_recommendations": "Минимизиране на препоръчани",
"show_recommendations": "Показване на препоръчани",
"view_ssl_score": "Преглед на SSL резултат",
"loading": "Зареждане...",
"hide_replies": "Скрий отговорите",
"load_more_replies": "Зареди още отговори",
"remove_from_playlist": "Премахване от плейлист",
"create_playlist": "Създаване на плейлист",
"reset_preferences": "Нулиране на настройките",
"with_timecode": "Сподели с текущото време",
"piped_link": "Piped връзка",
"documentation": "Документация",
"delete_account": "Изтрий акаунта",
"download_as_txt": "Изтегляне като .txt",
"share": "Сподели",
"follow_link": "Последвай връзката",
"add_to_playlist": "Добави към плейлист",
"delete_playlist_video_confirm": "Да се премахне ли видеото от плейлиста?",
"show_watch_on_youtube": "Показване на бутона \"Гледай в YouTube\"",
"source_code": "Изходен код",
"minimize_chapters_default": "Минимизиране на разделите по подразбиране",
"minimize_recommendations_default": "Минимизиране на препоръчани по подразбиране",
"show_chapters": "Раздели",
"logout": "Отписване от това устройство",
"clone_playlist": "Клониране на плейлист",
"clone_playlist_success": "Успешно клониране!",
"backup_preferences": "Архивиране на настройките",
"rename_playlist": "Преименуване на плейлиста",
"new_playlist_name": "Ново име на плейлиста",
"back_to_home": "Обратно към начална страница",
"status_page": "Статус",
"copy_link": "Копирай връзката",
"time_code": "Текущо време (в секунди)",
"reply_count": "{count} отговора",
"restore_preferences": "Възстановяване на настройките",
"invalidate_session": "Отписване от всички устройства",
"different_auth_instance": "Използване на различна инстанция за удостоверяване",
"store_search_history": "Запазване на историята на търсене",
"instance_auth_selection": "Избор на инстанция за удостоверяване",
"confirm_reset_preferences": "Сигурни ли сте, че искате да нулирате настройките?",
"hide_watched": "Скриване на гледани видеоклипове в Абонаменти"
},
"player": {
"watch_on": "Гледай в {0}"
},
"login": {
"username": "Потребителско име",
"password": "Парола"
},
"video": {
"videos": "Видеоклипове",
"views": "{views} показвания",
"chapters": "Раздели",
"all": "Всички",
"watched": "Гледани",
"category": "Категория"
},
"preferences": {
"version": "Версия",
"registered_users": "Регистрирани потребители",
"instance_locations": "Местоположения на инстанция",
"instance_name": "Име на инстанция",
"has_cdn": "Има ли CDN?",
"up_to_date": "Актуален?",
"ssl_score": "SSL резултат"
},
"comment": {
"disabled": "Коментарите са деактивирани.",
"pinned_by": "Фиксиран от {author}",
"loading": "Коментарите се зареждат...",
"user_disabled": "Коментарите са деактивирани в настройките."
},
"search": {
"did_you_mean": "Имахте предвид: {0}?",
"all": "YouTube: Всички",
"videos": "YouTube: Видеоклипове",
"channels": "YouTube: Канали",
"playlists": "YouTube: Плейлисти",
"music_songs": "YT Music: Песни",
"music_videos": "YT Music: Видеоклипове",
"music_albums": "YT Music: Албуми",
"music_playlists": "YT Music: Плейлисти"
},
"subscriptions": {
"subscribed_channels_count": "Абониран за: {0}"
},
"info": {
"page_not_found": "Страницата не е намерена",
"copied": "Копирано!",
"cannot_copy": "Не може да се копира!",
"local_storage": "Това действие изисква localStorage, разрешени ли са бисквитките?",
"register_no_email_note": "Използването на имейл като потребителско име не се препоръчва. Продължете все пак?"
}
}

View File

@ -104,7 +104,11 @@
"show_watch_on_youtube": "Schaltfläche „Auf YouTube ansehen“ anzeigen", "show_watch_on_youtube": "Schaltfläche „Auf YouTube ansehen“ anzeigen",
"with_playlist": "Mit Wiedergabeliste teilen", "with_playlist": "Mit Wiedergabeliste teilen",
"playlist_bookmarked": "Markiert", "playlist_bookmarked": "Markiert",
"bookmark_playlist": "Lesezeichen" "bookmark_playlist": "Lesezeichen",
"skip_segment": "Segment überspringen",
"skip_automatically": "Automatisch",
"min_segment_length": "Minimale Segmentlänge (in Sekunden)",
"skip_button_only": "Überspringen-Schaltfläche anzeigen"
}, },
"player": { "player": {
"watch_on": "Auf {0} ansehen" "watch_on": "Auf {0} ansehen"
@ -134,7 +138,8 @@
"live": "{0} Live", "live": "{0} Live",
"chapters": "Kapitel", "chapters": "Kapitel",
"shorts": "Shorts", "shorts": "Shorts",
"all": "Alle" "all": "Alle",
"category": "Kategorie"
}, },
"preferences": { "preferences": {
"ssl_score": "SSL-Bewertung", "ssl_score": "SSL-Bewertung",

View File

@ -129,7 +129,9 @@
"with_playlist": "Share with playlist", "with_playlist": "Share with playlist",
"bookmark_playlist": "Bookmark", "bookmark_playlist": "Bookmark",
"playlist_bookmarked": "Bookmarked", "playlist_bookmarked": "Bookmarked",
"dismiss": "Dismiss" "dismiss": "Dismiss",
"show_more": "Show more",
"show_less": "Show less"
}, },
"comment": { "comment": {
"pinned_by": "Pinned by {author}", "pinned_by": "Pinned by {author}",

View File

@ -42,7 +42,7 @@
"instance_selection": "Selección de instancias", "instance_selection": "Selección de instancias",
"enabled_codecs": "Códecs habilitados (múltiples)", "enabled_codecs": "Códecs habilitados (múltiples)",
"instances_list": "Lista de instancias", "instances_list": "Lista de instancias",
"language_selection": "Selección de lenguajes", "language_selection": "Selección de idioma",
"store_watch_history": "Recordar historial de visualización", "store_watch_history": "Recordar historial de visualización",
"minimize_description_default": "Minimizar la descripción por defecto", "minimize_description_default": "Minimizar la descripción por defecto",
"show_comments": "Mostrar comentarios", "show_comments": "Mostrar comentarios",

View File

@ -120,7 +120,11 @@
"no_valid_playlists": "Le fichier ne contient pas de listes de lecture valides !", "no_valid_playlists": "Le fichier ne contient pas de listes de lecture valides !",
"bookmark_playlist": "Marque-page", "bookmark_playlist": "Marque-page",
"playlist_bookmarked": "Dans les marque-pages", "playlist_bookmarked": "Dans les marque-pages",
"with_playlist": "Partager avec la liste de lecture" "with_playlist": "Partager avec la liste de lecture",
"skip_button_only": "Afficher le bouton de saut",
"skip_automatically": "Automatiquement",
"min_segment_length": "Longueur minimale du segment (en secondes)",
"skip_segment": "Sauter le segment"
}, },
"player": { "player": {
"watch_on": "Regarder sur {0}" "watch_on": "Regarder sur {0}"
@ -134,7 +138,8 @@
"chapters": "Chapitres", "chapters": "Chapitres",
"live": "{0} en direct", "live": "{0} en direct",
"shorts": "Courtes", "shorts": "Courtes",
"all": "Tout" "all": "Tout",
"category": "Catégorie"
}, },
"preferences": { "preferences": {
"ssl_score": "Score SSL", "ssl_score": "Score SSL",

View File

@ -53,7 +53,7 @@
"instances_list": "רשימת עותקים", "instances_list": "רשימת עותקים",
"enabled_codecs": "מפענחים פעילים (מגוון)", "enabled_codecs": "מפענחים פעילים (מגוון)",
"instance_selection": "בחירת עותק", "instance_selection": "בחירת עותק",
"show_more": "להציג עוד", "show_more": "להציג יותר",
"yes": "כן", "yes": "כן",
"no": "לא", "no": "לא",
"export_to_json": "ייצוא ל־JSON", "export_to_json": "ייצוא ל־JSON",
@ -127,7 +127,8 @@
"skip_button_only": "הצגת כפתור דילוג", "skip_button_only": "הצגת כפתור דילוג",
"min_segment_length": "אורך מקטע מזערי (בשניות)", "min_segment_length": "אורך מקטע מזערי (בשניות)",
"skip_segment": "דילוג על מקטע", "skip_segment": "דילוג על מקטע",
"skip_automatically": "אוטומטית" "skip_automatically": "אוטומטית",
"show_less": "להציג פחות"
}, },
"comment": { "comment": {
"pinned_by": "ננעץ על ידי {author}", "pinned_by": "ננעץ על ידי {author}",

View File

@ -8,7 +8,8 @@
"chapters": "Poglavlja", "chapters": "Poglavlja",
"live": "{0} uživo", "live": "{0} uživo",
"shorts": "Kratka videa", "shorts": "Kratka videa",
"all": "Sva" "all": "Sva",
"category": "Kategorija"
}, },
"preferences": { "preferences": {
"ssl_score": "SSL ocjena", "ssl_score": "SSL ocjena",
@ -92,7 +93,7 @@
"select_playlist": "Odaberi popis snimaka", "select_playlist": "Odaberi popis snimaka",
"please_select_playlist": "Odaberi popis snimaka", "please_select_playlist": "Odaberi popis snimaka",
"delete_playlist_video_confirm": "Ukloniti video iz popisa snimaka?", "delete_playlist_video_confirm": "Ukloniti video iz popisa snimaka?",
"show_markers": "Prikaži oznake na Pokretaču", "show_markers": "Prikaži oznake na playeru",
"delete_account": "Izbriši račun", "delete_account": "Izbriši račun",
"logout": "Odjavi se s ovog uređaja", "logout": "Odjavi se s ovog uređaja",
"minimize_recommendations_default": "Standardno sakrij preporuke", "minimize_recommendations_default": "Standardno sakrij preporuke",
@ -130,7 +131,11 @@
"no_valid_playlists": "Datoteka ne sadrži ispravne popise snimaka!", "no_valid_playlists": "Datoteka ne sadrži ispravne popise snimaka!",
"with_playlist": "Dijeli s popisom snimaka", "with_playlist": "Dijeli s popisom snimaka",
"playlist_bookmarked": "Zabilježeno", "playlist_bookmarked": "Zabilježeno",
"bookmark_playlist": "Zabilježi" "bookmark_playlist": "Zabilježi",
"skip_button_only": "Prikaži gumb za preskakanje",
"skip_automatically": "Automatski",
"skip_segment": "Preskoči segment",
"min_segment_length": "Najmanja duljina segmenta (u sekundama)"
}, },
"player": { "player": {
"watch_on": "Gledaj na {0}" "watch_on": "Gledaj na {0}"
@ -146,7 +151,7 @@
"playlists": "Popisi snimaka", "playlists": "Popisi snimaka",
"account": "Račun", "account": "Račun",
"instance": "Instanca", "instance": "Instanca",
"player": "Pokretač", "player": "Player",
"channels": "Kanali", "channels": "Kanali",
"livestreams": "Prijenosi uživo", "livestreams": "Prijenosi uživo",
"bookmarks": "Zabilješke" "bookmarks": "Zabilješke"

View File

@ -138,7 +138,8 @@
"live": "{0} Diretta", "live": "{0} Diretta",
"chapters": "Capitoli", "chapters": "Capitoli",
"shorts": "Short", "shorts": "Short",
"all": "Tutti" "all": "Tutti",
"category": "Categoria"
}, },
"preferences": { "preferences": {
"ssl_score": "Valutazione SSL", "ssl_score": "Valutazione SSL",

View File

@ -31,11 +31,11 @@
"uses_api_from": "API使用元 ", "uses_api_from": "API使用元 ",
"enable_sponsorblock": "SponsorBlock を有効化", "enable_sponsorblock": "SponsorBlock を有効化",
"skip_sponsors": "広告をスキップ", "skip_sponsors": "広告をスキップ",
"skip_intro": "休止時間/イントロ画面をスキップ", "skip_intro": "休止時間/導入アニメをスキップ",
"skip_outro": "終了画面/クレジットをスキップ", "skip_outro": "終了シーン/クレジットをスキップ",
"skip_preview": "プレビュー/要約をスキップ", "skip_preview": "プレビュー/要約をスキップ",
"skip_interaction": "チャンネル登録など操作を求める自己宣伝をスキップ", "skip_interaction": "チャンネル登録など操作を求める自己宣伝をスキップ",
"skip_self_promo": "無償/自己プロモーションをスキップ", "skip_self_promo": "無報酬/自己の宣伝をスキップ",
"skip_non_music": "音楽: 非音楽部分をスキップ", "skip_non_music": "音楽: 非音楽部分をスキップ",
"theme": "テーマ", "theme": "テーマ",
"auto": "自動", "auto": "自動",
@ -43,12 +43,12 @@
"light": "ライト", "light": "ライト",
"autoplay_video": "動画を自動再生", "autoplay_video": "動画を自動再生",
"audio_only": "音声のみ", "audio_only": "音声のみ",
"default_quality": "デフォルトの画質", "default_quality": "標準の画質",
"buffering_goal": "バッファリング目標値 (秒)", "buffering_goal": "バッファリング目標値 (秒)",
"country_selection": "国の選択", "country_selection": "国の選択",
"default_homepage": "ホームに表示するページ", "default_homepage": "ホームに表示するページ",
"show_comments": "コメントを表示", "show_comments": "コメントを表示",
"minimize_description_default": "デフォルトで詳細を最小化する", "minimize_description_default": "最初から説明を最小化",
"store_watch_history": "再生履歴を保存する", "store_watch_history": "再生履歴を保存する",
"language_selection": "言語の選択", "language_selection": "言語の選択",
"instances_list": "インスタンス一覧", "instances_list": "インスタンス一覧",
@ -68,16 +68,16 @@
"show_recommendations": "おすすめを見る", "show_recommendations": "おすすめを見る",
"disable_lbry": "ストリーミングのLBRYを無効化", "disable_lbry": "ストリーミングのLBRYを無効化",
"enable_lbry_proxy": "LBRYプロキシをオン", "enable_lbry_proxy": "LBRYプロキシをオン",
"view_ssl_score": "SSLスコアを見る", "view_ssl_score": "SSLの評価を表示",
"search": "検索", "search": "検索",
"filter": "フィルター", "filter": "フィルター",
"loading": "読み込み中…", "loading": "読み込み中…",
"clear_history": "再生履歴を削除", "clear_history": "再生履歴を削除",
"hide_replies": "返信を非表示", "hide_replies": "返信を非表示",
"load_more_replies": "返信をもっと見る", "load_more_replies": "返信をもっと見る",
"skip_filler_tangent": "無関係なコンテンツをスキップ", "skip_filler_tangent": "無関係な談話をスキップ",
"skip_highlight": "ハイライトをスキップ", "skip_highlight": "ハイライトをスキップ",
"add_to_playlist": "再生リストに追加する", "add_to_playlist": "再生リストに追加",
"create_playlist": "再生リストを作成", "create_playlist": "再生リストを作成",
"remove_from_playlist": "再生リストから削除", "remove_from_playlist": "再生リストから削除",
"delete_playlist_video_confirm": "再生リストからこの動画を削除しますか?", "delete_playlist_video_confirm": "再生リストからこの動画を削除しますか?",
@ -98,9 +98,9 @@
"different_auth_instance": "認証に別のインスタンスを使う", "different_auth_instance": "認証に別のインスタンスを使う",
"download_as_txt": ".txtでダウンロード", "download_as_txt": ".txtでダウンロード",
"logout": "このデバイスでログアウト", "logout": "このデバイスでログアウト",
"minimize_recommendations_default": "デフォルトでおすすめを最小化する", "minimize_recommendations_default": "最初からおすすめを最小化",
"hide_watched": "再生済みの動画をフィードに表示しない", "hide_watched": "再生済みの動画をフィードに表示しない",
"minimize_chapters_default": "デフォルトでチャプターを最小化する", "minimize_chapters_default": "最初からチャプターを最小化",
"show_watch_on_youtube": "「YouTubeで見る」ボタンを表示する", "show_watch_on_youtube": "「YouTubeで見る」ボタンを表示する",
"invalidate_session": "すべてのデバイスでログアウトする", "invalidate_session": "すべてのデバイスでログアウトする",
"instance_auth_selection": "認証インスタンスの選択", "instance_auth_selection": "認証インスタンスの選択",
@ -119,11 +119,15 @@
"follow_link": "リンクに従う", "follow_link": "リンクに従う",
"reply_count": "{count} 件の返信", "reply_count": "{count} 件の返信",
"clone_playlist": "再生リストを複製", "clone_playlist": "再生リストを複製",
"minimize_comments_default": "デフォルトでコメントを最小化する", "minimize_comments_default": "最初からコメントを最小化",
"no_valid_playlists": "ファイルに有効な再生リストが含まれていません。", "no_valid_playlists": "このファイルは有効な再生リストではありません!",
"playlist_bookmarked": "ブックマーク完了", "playlist_bookmarked": "ブックマーク完了",
"bookmark_playlist": "ブックマーク", "bookmark_playlist": "ブックマーク",
"with_playlist": "再生リストで共有" "with_playlist": "再生リストで共有",
"skip_automatically": "自動",
"skip_button_only": "スキップボタン表示",
"skip_segment": "ここをスキップ",
"min_segment_length": "最小の区切りの長さ (秒)"
}, },
"comment": { "comment": {
"pinned_by": "{author} によって固定", "pinned_by": "{author} によって固定",
@ -135,8 +139,8 @@
"instance_name": "インスタンス名", "instance_name": "インスタンス名",
"instance_locations": "インスタンスの場所", "instance_locations": "インスタンスの場所",
"has_cdn": "CDNの有無", "has_cdn": "CDNの有無",
"ssl_score": "SSLスコア", "ssl_score": "SSLの評価",
"registered_users": "登録済みユーザー", "registered_users": "登録ユーザー",
"version": "バージョン", "version": "バージョン",
"up_to_date": "最新?" "up_to_date": "最新?"
}, },
@ -153,7 +157,8 @@
"chapters": "チャプター", "chapters": "チャプター",
"live": "{0} ライブ配信", "live": "{0} ライブ配信",
"shorts": "ショート", "shorts": "ショート",
"all": "すべて" "all": "すべて",
"category": "分類"
}, },
"search": { "search": {
"did_you_mean": "もしかして: {0}", "did_you_mean": "もしかして: {0}",

View File

@ -58,15 +58,15 @@
"remove_from_playlist": "Uit Afspeellijst Verwijderen", "remove_from_playlist": "Uit Afspeellijst Verwijderen",
"select_playlist": "Selecteer een Afspeellijst", "select_playlist": "Selecteer een Afspeellijst",
"delete_playlist_confirm": "Deze afspeellijst verwijderen?", "delete_playlist_confirm": "Deze afspeellijst verwijderen?",
"please_select_playlist": "Kies een afspeellijst a.u.b.", "please_select_playlist": "Selecteer een afspeellijst alsjeblief",
"instance_selection": "Instantie Selectie", "instance_selection": "Instantie Selectie",
"import_from_json": "Importeren uit JSON/CSV", "import_from_json": "Importeren uit JSON/CSV",
"clear_history": "Geschiedenis Wissen", "clear_history": "Geschiedenis Wissen",
"load_more_replies": "Laad meer Antwoorden", "load_more_replies": "Laad meer Antwoorden",
"delete_playlist_video_confirm": "Video van playlist verwijderen?", "delete_playlist_video_confirm": "Video uit deze afspeellijst verwijderen?",
"create_playlist": "Afspeellijst Maken", "create_playlist": "Afspeellijst Maken",
"delete_playlist": "Afspeellijst Verwijderen", "delete_playlist": "Afspeellijst Verwijderen",
"show_markers": "Toon markeringen op Speler", "show_markers": "Laat markeringen op speler zien",
"store_search_history": "Zoekgeschiedenis Opslaan", "store_search_history": "Zoekgeschiedenis Opslaan",
"minimize_chapters_default": "Hoofdstukken Standaard Minimaliseren", "minimize_chapters_default": "Hoofdstukken Standaard Minimaliseren",
"show_watch_on_youtube": "Toon Bekijk op YouTube knop", "show_watch_on_youtube": "Toon Bekijk op YouTube knop",
@ -77,8 +77,8 @@
"copy_link": "Link kopiëren", "copy_link": "Link kopiëren",
"hide_watched": "Verberg bekeken video's in de feed", "hide_watched": "Verberg bekeken video's in de feed",
"minimize_comments": "Opmerkingen minimaliseren", "minimize_comments": "Opmerkingen minimaliseren",
"instance_auth_selection": "Autenticatie Instantie Selectie", "instance_auth_selection": "Selectie authenticatie-instantie",
"clone_playlist": "Afspeellijst klonen", "clone_playlist": "Afspeellijst dupliceren",
"download_as_txt": "Downloaden als .txt", "download_as_txt": "Downloaden als .txt",
"rename_playlist": "Afspeellijst hernoemen", "rename_playlist": "Afspeellijst hernoemen",
"new_playlist_name": "Nieuwe afspeellijstnaam", "new_playlist_name": "Nieuwe afspeellijstnaam",
@ -91,20 +91,24 @@
"instance_donations": "Instantie donaties", "instance_donations": "Instantie donaties",
"reply_count": "{count} antwoorden", "reply_count": "{count} antwoorden",
"no_valid_playlists": "Het bestand bevat geen geldige afspeellijsten!", "no_valid_playlists": "Het bestand bevat geen geldige afspeellijsten!",
"clone_playlist_success": "Succesvol gekloond!", "clone_playlist_success": "Dupliceren gelukt!",
"reset_preferences": "Voorkeuren opnieuw instellen", "reset_preferences": "Voorkeuren herstellen",
"back_to_home": "Terug naar de start", "back_to_home": "Terug naar de start",
"minimize_comments_default": "Opmerkingen Standaard Minimaliseren", "minimize_comments_default": "Opmerkingen Standaard Minimaliseren",
"delete_account": "Account Verwijderen", "delete_account": "Account Verwijderen",
"logout": "Uitloggen van dit apparaat", "logout": "Uitloggen op dit apparaat",
"minimize_recommendations_default": "Aanbevelingen Standaard Minimaliseren", "minimize_recommendations_default": "Aanbevelingen Standaard Minimaliseren",
"confirm_reset_preferences": "Weet u zeker dat u uw voorkeuren opnieuw wilt instellen?", "confirm_reset_preferences": "Weet u zeker dat u uw voorkeuren opnieuw wilt instellen?",
"backup_preferences": "Back-up voorkeuren", "backup_preferences": "Back-up voorkeuren",
"invalidate_session": "Alle apparaten afmelden", "invalidate_session": "Uitloggen op alle apparaten",
"different_auth_instance": "Gebruik een andere instantie voor authenticatie", "different_auth_instance": "Gebruik een andere instantie voor authenticatie",
"with_playlist": "Delen met afspeellijst", "with_playlist": "Delen met afspeellijst",
"playlist_bookmarked": "Bladwijzer gemaakt", "playlist_bookmarked": "Bladwijzer gemaakt",
"bookmark_playlist": "Bladwijzer" "bookmark_playlist": "Bladwijzer",
"skip_automatically": "Automatisch",
"skip_button_only": "toon de overslaan knop",
"min_segment_length": "Minimale segmentlengte (in seconden)",
"skip_segment": "segment overslaan"
}, },
"titles": { "titles": {
"register": "Registreren", "register": "Registreren",
@ -113,9 +117,9 @@
"preferences": "Voorkeuren", "preferences": "Voorkeuren",
"history": "Geschiedenis", "history": "Geschiedenis",
"subscriptions": "Abonnementen", "subscriptions": "Abonnementen",
"trending": "Trending", "trending": "populair",
"playlists": "Afspeellijsten", "playlists": "Afspeellijsten",
"account": "Account", "account": "profiel",
"instance": "Instantie", "instance": "Instantie",
"player": "Speler", "player": "Speler",
"livestreams": "Livestreams", "livestreams": "Livestreams",
@ -148,7 +152,9 @@
"sponsor_segments": "Sponsorsegmenten", "sponsor_segments": "Sponsorsegmenten",
"ratings_disabled": "Beoordelingen Uitgeschakeld", "ratings_disabled": "Beoordelingen Uitgeschakeld",
"live": "{0} Live", "live": "{0} Live",
"shorts": "Shorts" "shorts": "Shorts",
"category": "Categorie",
"all": "Alle"
}, },
"preferences": { "preferences": {
"has_cdn": "Heeft CDN?", "has_cdn": "Heeft CDN?",
@ -161,8 +167,8 @@
}, },
"comment": { "comment": {
"pinned_by": "Vastgemaakt door {author}", "pinned_by": "Vastgemaakt door {author}",
"user_disabled": "Reacties zijn uitgeschakeld in de instellingen.", "user_disabled": "Opmerkingen zijn uitgeschakeld in de instellingen.",
"loading": "Reacties laden...", "loading": "Opmerkingen laden...",
"disabled": "Reacties zijn uitgeschakeld door de uploader." "disabled": "Reacties zijn uitgeschakeld door de uploader."
}, },
"info": { "info": {
@ -170,7 +176,8 @@
"copied": "Gekopieerd!", "copied": "Gekopieerd!",
"cannot_copy": "Kan niet kopiëren!", "cannot_copy": "Kan niet kopiëren!",
"page_not_found": "Pagina niet gevonden", "page_not_found": "Pagina niet gevonden",
"local_storage": "Deze actie vereist lokale opslag, zijn cookies ingeschakeld?" "local_storage": "Deze actie vereist lokale opslag, zijn cookies ingeschakeld?",
"register_no_email_note": "Een e-mailadres als gebruikersnaam gebruiken wordt afgeraden. Toch doorgaan?"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Geabonneerd op: {0}" "subscribed_channels_count": "Geabonneerd op: {0}"

View File

@ -144,7 +144,8 @@
"ratings_disabled": "ମୂଲ୍ୟାୟନ ଅକ୍ଷମ ହୋଇଛି", "ratings_disabled": "ମୂଲ୍ୟାୟନ ଅକ୍ଷମ ହୋଇଛି",
"chapters": "ଅଧ୍ୟାୟ ଗୁଡ଼ିକ", "chapters": "ଅଧ୍ୟାୟ ଗୁଡ଼ିକ",
"live": "{0} ସିଧାପ୍ରସାରଣ", "live": "{0} ସିଧାପ୍ରସାରଣ",
"all": "ସମସ୍ତ" "all": "ସମସ୍ତ",
"category": "ବର୍ଗ"
}, },
"search": { "search": {
"did_you_mean": "ଆପଣ କହିବାକୁ ଚାହୁଁଛନ୍ତି କି: {0}?", "did_you_mean": "ଆପଣ କହିବାକୁ ଚାହୁଁଛନ୍ତି କି: {0}?",

View File

@ -157,7 +157,8 @@
"chapters": "Rozdziały", "chapters": "Rozdziały",
"live": "{0} Na żywo", "live": "{0} Na żywo",
"shorts": "Krótkie wideo", "shorts": "Krótkie wideo",
"all": "Wszystkie" "all": "Wszystkie",
"category": "Kategoria"
}, },
"search": { "search": {
"did_you_mean": "Czy chodziło ci o: {0}?", "did_you_mean": "Czy chodziło ci o: {0}?",

View File

@ -100,7 +100,15 @@
"minimize_recommendations": "Ascunde Recomandări", "minimize_recommendations": "Ascunde Recomandări",
"yes": "Da", "yes": "Da",
"show_comments": "Arată Comentarii", "show_comments": "Arată Comentarii",
"show_description": "Arată Descriere" "show_description": "Arată Descriere",
"bookmark_playlist": "Marcaj",
"no_valid_playlists": "Fișierul nu conține playlist-uri valide!",
"skip_automatically": "Automat",
"min_segment_length": "Lungimea minimă a segmentului (în secunde)",
"skip_segment": "Sări segmentul",
"skip_button_only": "Afișează butonul de săritură",
"with_playlist": "Distribuie cu playlist",
"playlist_bookmarked": "Marcat"
}, },
"preferences": { "preferences": {
"ssl_score": "Scor SSL", "ssl_score": "Scor SSL",
@ -125,7 +133,9 @@
"sponsor_segments": "Segmente Sponsori", "sponsor_segments": "Segmente Sponsori",
"ratings_disabled": "Like-uri dezactivate", "ratings_disabled": "Like-uri dezactivate",
"live": "{0} Live", "live": "{0} Live",
"videos": "Video-uri" "videos": "Video-uri",
"category": "Categorie",
"all": "Tot"
}, },
"login": { "login": {
"username": "Nume User", "username": "Nume User",
@ -146,7 +156,9 @@
"cannot_copy": "Nu se poate copia!", "cannot_copy": "Nu se poate copia!",
"preferences_note": "Sfat: preferințele sunt salvate in memoria locala a browserului tău. Ștergând datele browserului le ștergi si pe ele.", "preferences_note": "Sfat: preferințele sunt salvate in memoria locala a browserului tău. Ștergând datele browserului le ștergi si pe ele.",
"page_not_found": "Pagină negăsită", "page_not_found": "Pagină negăsită",
"copied": "S-a copiat!" "copied": "S-a copiat!",
"register_no_email_note": "Utilizarea unui e-mail ca nume de utilizator nu este recomandată. Continui oricum?",
"local_storage": "Această acțiune necesită localStorage, sunt activate cookie-urile?"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Abonat la: {0}" "subscribed_channels_count": "Abonat la: {0}"
@ -164,7 +176,8 @@
"livestreams": "Live-uri", "livestreams": "Live-uri",
"channels": "Canale", "channels": "Canale",
"preferences": "Preferințe", "preferences": "Preferințe",
"player": "Player" "player": "Player",
"bookmarks": "Marcaje"
}, },
"player": { "player": {
"watch_on": "Vezi pe {0}" "watch_on": "Vezi pe {0}"

View File

@ -5,8 +5,8 @@
"register": "Регистрация", "register": "Регистрация",
"feed": "Подписки", "feed": "Подписки",
"preferences": "Настройки", "preferences": "Настройки",
"history": "История просмотров", "history": "История",
"subscriptions": "Ваши подписки", "subscriptions": "Подписки",
"playlists": "Плейлисты", "playlists": "Плейлисты",
"account": "Аккаунт", "account": "Аккаунт",
"player": "Плеер", "player": "Плеер",
@ -28,7 +28,7 @@
"channel_name_asc": "Имя канала (А-Я)", "channel_name_asc": "Имя канала (А-Я)",
"channel_name_desc": "Имя канала (Я-А)", "channel_name_desc": "Имя канала (Я-А)",
"back": "Назад", "back": "Назад",
"uses_api_from": "Использовать API, предоставляемое ", "uses_api_from": "Использовать API ",
"enable_sponsorblock": "Включить Sponsorblock", "enable_sponsorblock": "Включить Sponsorblock",
"skip_sponsors": "Пропускать спонсорскую рекламу", "skip_sponsors": "Пропускать спонсорскую рекламу",
"skip_intro": "Пропускать заставку/интро", "skip_intro": "Пропускать заставку/интро",
@ -67,7 +67,7 @@
"minimize_recommendations": "Свернуть рекомендации", "minimize_recommendations": "Свернуть рекомендации",
"show_recommendations": "Показать рекомендации", "show_recommendations": "Показать рекомендации",
"disable_lbry": "Отключить LBRY для стриминга", "disable_lbry": "Отключить LBRY для стриминга",
"enable_lbry_proxy": "Проксировать видео с LBRY", "enable_lbry_proxy": "Проксировать видео для LBRY",
"view_ssl_score": "Посмотреть настройки SSL", "view_ssl_score": "Посмотреть настройки SSL",
"search": "Поиск", "search": "Поиск",
"filter": "Фильтр", "filter": "Фильтр",
@ -85,42 +85,42 @@
"select_playlist": "Выбрать плейлист", "select_playlist": "Выбрать плейлист",
"delete_playlist_confirm": "Удалить этот плейлист?", "delete_playlist_confirm": "Удалить этот плейлист?",
"delete_playlist_video_confirm": "Удалить видео из плейлиста?", "delete_playlist_video_confirm": "Удалить видео из плейлиста?",
"show_markers": "Показать Mаркеры Hа Проигрывателе", "show_markers": "Показать маркеры на проигрывателе",
"delete_account": "Удалить аккаунт", "delete_account": "Удалить аккаунт",
"logout": "Выйти из этого устройства", "logout": "Выйти из этого устройства",
"download_as_txt": "Скачать как .txt", "download_as_txt": "Скачать как .txt",
"minimize_recommendations_default": "Скрыть Рекомендации по умолчанию", "minimize_recommendations_default": "Скрыть Рекомендации по умолчанию",
"invalidate_session": "Выйти из всех устройств", "invalidate_session": "Выйти из всех устройств",
"different_auth_instance": "Использовать другие средства аутентификации", "different_auth_instance": "Использовать другое зеркало для аутентификации",
"instance_auth_selection": "Выбор средств аутентификации", "instance_auth_selection": "Выбор зеркала аутентификации",
"clone_playlist": "Клонировать плейлист", "clone_playlist": "Клонировать плейлист",
"clone_playlist_success": "Клонирование прошло успешно!", "clone_playlist_success": "Успешно клонировано!",
"show_chapters": "Части", "show_chapters": "Главы",
"rename_playlist": "Переименовать плейлист", "rename_playlist": "Переименовать плейлист",
"new_playlist_name": "Новое название плейлиста", "new_playlist_name": "Новое название плейлиста",
"share": "Поделиться", "share": "Поделиться",
"with_timecode": "Поделиться с отметкой времени", "with_timecode": "Поделиться с таймкодом",
"piped_link": "Ссылка Piped", "piped_link": "Ссылка Piped",
"follow_link": "Ссылка подписки", "follow_link": "Перейти по ссылке",
"copy_link": "Скопировать ссылку", "copy_link": "Скопировать ссылку",
"time_code": "Тайм-код (в секундах)", "time_code": "Таймкод (в секундах)",
"reset_preferences": "Сбросить настройки", "reset_preferences": "Сбросить настройки",
"confirm_reset_preferences": "Вы уверены, что хотите сбросить настройки?", "confirm_reset_preferences": "Вы уверены, что хотите сбросить настройки?",
"backup_preferences": "Бэкап настроек", "backup_preferences": "Бэкап настроек",
"restore_preferences": "Восстановить настройки", "restore_preferences": "Восстановить настройки",
"back_to_home": "Вернутся на главную", "back_to_home": "Назад на главную",
"store_search_history": "Хранить историю поиска", "store_search_history": "Хранить историю поиска",
"hide_watched": "Скрыть просмотренные видео в ленте", "hide_watched": "Скрыть просмотренные видео в ленте",
"status_page": "Статус", "status_page": "Статус",
"source_code": "Исходный код", "source_code": "Исходный код",
"documentation": "Пожертвования сервера", "documentation": "Документация",
"instance_donations": "Пожертвования сервера", "instance_donations": "Пожертвования зеркала",
"reply_count": "{count} ответов", "reply_count": "{count} ответов",
"minimize_comments_default": "Сворачивать комментарии по умолчанию", "minimize_comments_default": "Сворачивать комментарии по умолчанию",
"minimize_comments": "Свернуть комментарии", "minimize_comments": "Свернуть комментарии",
"show_watch_on_youtube": "Показать кнопку Смотреть на YouTube", "show_watch_on_youtube": "Показать кнопку Смотреть на YouTube",
"minimize_chapters_default": "Скрывать главы по умолчанию", "minimize_chapters_default": "Скрывать главы по умолчанию",
"no_valid_playlists": "Файл не содержит действительных списков воспроизведения!", "no_valid_playlists": "Файл не содержит действующих плейлистов!",
"with_playlist": "Поделиться с плейлистом", "with_playlist": "Поделиться с плейлистом",
"bookmark_playlist": "Закладка", "bookmark_playlist": "Закладка",
"playlist_bookmarked": "В закладках", "playlist_bookmarked": "В закладках",
@ -130,14 +130,14 @@
"skip_segment": "Пропустить сегмент" "skip_segment": "Пропустить сегмент"
}, },
"comment": { "comment": {
"pinned_by": "Прикреплено пользователем {author}", "pinned_by": "Закреплено пользователем {author}",
"loading": "Загрузка комментариев...", "loading": "Загрузка комментариев...",
"user_disabled": "Комментарии отключены в настройках.", "user_disabled": "Комментарии отключены в настройках.",
"disabled": "Коментарии отключены автором." "disabled": "Комментарии отключены автором."
}, },
"preferences": { "preferences": {
"instance_name": "Название", "instance_name": "Имя зеркала",
"instance_locations": "Местоположение", "instance_locations": "Местоположения зеркала",
"has_cdn": "Имеется CDN?", "has_cdn": "Имеется CDN?",
"ssl_score": "Оценка настроек SSL", "ssl_score": "Оценка настроек SSL",
"registered_users": "Зарегистрировано пользователей", "registered_users": "Зарегистрировано пользователей",
@ -145,7 +145,7 @@
"up_to_date": "Версия актуальна?" "up_to_date": "Версия актуальна?"
}, },
"login": { "login": {
"username": "Аккаунт на Piped", "username": "Имя пользователя",
"password": "Пароль" "password": "Пароль"
}, },
"video": { "video": {
@ -157,7 +157,8 @@
"live": "{0} В эфире", "live": "{0} В эфире",
"chapters": "Содержание", "chapters": "Содержание",
"shorts": "Shorts", "shorts": "Shorts",
"all": "Все" "all": "Все",
"category": "Категория"
}, },
"search": { "search": {
"did_you_mean": "Может быть вы имели в виду: {0}?", "did_you_mean": "Может быть вы имели в виду: {0}?",
@ -174,11 +175,11 @@
"subscribed_channels_count": "Подписан на: {0}" "subscribed_channels_count": "Подписан на: {0}"
}, },
"info": { "info": {
"preferences_note": "Примечание: настройки сохранены в локальном хранилище браузера. При удалении данных браузера они будут удалены.", "preferences_note": "Примечание: настройки сохранены в локальном хранилище браузера. Удаление данных вашего браузера сбросит их.",
"copied": "Скопировано!", "copied": "Скопировано!",
"cannot_copy": "Не получилось скопировать!", "cannot_copy": "Не удалось скопировать!",
"page_not_found": "Страница не найдена", "page_not_found": "Страница не найдена",
"local_storage": "Это действие требует локального хранилища (localStorage), разрешены ли файлы cookie?", "local_storage": "Это действие требует разрешения localStorage, включены ли cookie-файлы?",
"register_no_email_note": "Использование электронной почты в качестве имени пользователя не рекомендуется. Продолжить?" "register_no_email_note": "Использование электронной почты в качестве имени пользователя не рекомендуется. Продолжить?"
} }
} }

View File

@ -157,7 +157,8 @@
"shorts": "කෙටි වීඩියෝ", "shorts": "කෙටි වීඩියෝ",
"ratings_disabled": "ශ්‍රේණිගත කිරීම් අබල කර ඇත", "ratings_disabled": "ශ්‍රේණිගත කිරීම් අබල කර ඇත",
"live": "{0} සජීවී", "live": "{0} සජීවී",
"all": "සියල්ල" "all": "සියල්ල",
"category": "කාණ්ඩය"
}, },
"search": { "search": {
"did_you_mean": "ඔබ අදහස් කළේ: {0}?", "did_you_mean": "ඔබ අදහස් කළේ: {0}?",

View File

@ -45,7 +45,7 @@
"export_to_json": "JSON Olarak Dışa Aktar", "export_to_json": "JSON Olarak Dışa Aktar",
"no": "Hayır", "no": "Hayır",
"yes": "Evet", "yes": "Evet",
"show_more": "Daha Fazla Göster", "show_more": "Daha fazla göster",
"instance_selection": "Örnek Seçimi", "instance_selection": "Örnek Seçimi",
"loading": "Yükleniyor...", "loading": "Yükleniyor...",
"filter": "Filtrele", "filter": "Filtrele",
@ -108,7 +108,8 @@
"min_segment_length": "En Küçük Bölüm Uzunluğu (saniye cinsinden)", "min_segment_length": "En Küçük Bölüm Uzunluğu (saniye cinsinden)",
"skip_segment": "Bölümü Atla", "skip_segment": "Bölümü Atla",
"skip_button_only": "Atla düğmesini göster", "skip_button_only": "Atla düğmesini göster",
"skip_automatically": "Otomatik olarak" "skip_automatically": "Otomatik olarak",
"show_less": "Daha az göster"
}, },
"player": { "player": {
"watch_on": "{0} Üzerinde İzle" "watch_on": "{0} Üzerinde İzle"

View File

@ -28,7 +28,7 @@
"show_comments": "Hiển thị bình luận", "show_comments": "Hiển thị bình luận",
"store_watch_history": "Lịch sử xem trên cửa hàng", "store_watch_history": "Lịch sử xem trên cửa hàng",
"language_selection": "Lựa chọn ngôn ngữ", "language_selection": "Lựa chọn ngôn ngữ",
"instances_list": "Danh sách phiên bản", "instances_list": "Danh sách instance",
"show_more": "Hiện thị nhiều hơn", "show_more": "Hiện thị nhiều hơn",
"import_from_json": "Nhập từ JSON/CSV", "import_from_json": "Nhập từ JSON/CSV",
"loop_this_video": "Lặp lại video này", "loop_this_video": "Lặp lại video này",
@ -53,7 +53,7 @@
"light": "Sáng", "light": "Sáng",
"audio_only": "Chỉ có âm thanh", "audio_only": "Chỉ có âm thanh",
"minimize_description_default": "Thu nhỏ mô tả theo mặc định", "minimize_description_default": "Thu nhỏ mô tả theo mặc định",
"instance_selection": "Lựa chọn phiên bản", "instance_selection": "Lựa chọn instance",
"yes": "Có", "yes": "Có",
"enabled_codecs": "Các codec được bật (Nhiều)", "enabled_codecs": "Các codec được bật (Nhiều)",
"export_to_json": "Xuất định dạng JSON", "export_to_json": "Xuất định dạng JSON",
@ -78,7 +78,8 @@
"minimize_comments": "Thu nhỏ bình luận", "minimize_comments": "Thu nhỏ bình luận",
"reply_count": "{count} phản hồi", "reply_count": "{count} phản hồi",
"status_page": "Trạng thái", "status_page": "Trạng thái",
"new_playlist_name": "Tên danh sách phát mới" "new_playlist_name": "Tên danh sách phát mới",
"skip_automatically": "Tự động"
}, },
"titles": { "titles": {
"register": "Đăng ký", "register": "Đăng ký",
@ -92,7 +93,8 @@
"account": "Tài khoản", "account": "Tài khoản",
"channels": "Kênh", "channels": "Kênh",
"instance": "Instance", "instance": "Instance",
"player": "Trình phát video" "player": "Trình phát video",
"livestreams": "Phát sóng trực tiếp"
}, },
"player": { "player": {
"watch_on": "Xem trên {0}" "watch_on": "Xem trên {0}"
@ -104,8 +106,8 @@
"disabled": "Bình luận đã bị tắt bởi người đăng video." "disabled": "Bình luận đã bị tắt bởi người đăng video."
}, },
"preferences": { "preferences": {
"instance_name": "Tên phiên bản", "instance_name": "Tên instance",
"instance_locations": "Vị trí phiên bản", "instance_locations": "Vị trí instance",
"has_cdn": "Có CDN?", "has_cdn": "Có CDN?",
"registered_users": "Người dùng đã đăng ký", "registered_users": "Người dùng đã đăng ký",
"version": "Phiên bản", "version": "Phiên bản",
@ -124,7 +126,8 @@
"live": "{0} Trực tiếp", "live": "{0} Trực tiếp",
"chapters": "Chương", "chapters": "Chương",
"videos": "Video", "videos": "Video",
"shorts": "Shorts" "shorts": "Shorts",
"all": "Tất cả"
}, },
"search": { "search": {
"did_you_mean": "Ý của bạn là: {0}?", "did_you_mean": "Ý của bạn là: {0}?",

View File

@ -44,7 +44,7 @@
"least_recent": "最早的", "least_recent": "最早的",
"most_recent": "最新的", "most_recent": "最新的",
"sort_by": "排序:", "sort_by": "排序:",
"view_subscriptions": "查看订阅", "view_subscriptions": "查看订阅列表",
"unsubscribe": "取消订阅 - {count}", "unsubscribe": "取消订阅 - {count}",
"subscribe": "订阅 - {count}", "subscribe": "订阅 - {count}",
"loading": "正在加载...", "loading": "正在加载...",
@ -108,7 +108,8 @@
"skip_automatically": "自动", "skip_automatically": "自动",
"min_segment_length": "最小分段长度(以秒为单位)", "min_segment_length": "最小分段长度(以秒为单位)",
"skip_segment": "跳过分段", "skip_segment": "跳过分段",
"skip_button_only": "显示跳过按钮" "skip_button_only": "显示跳过按钮",
"show_less": "显示更少"
}, },
"video": { "video": {
"sponsor_segments": "赞助商部分", "sponsor_segments": "赞助商部分",
@ -141,8 +142,8 @@
"watch_on": "在 {0} 观看" "watch_on": "在 {0} 观看"
}, },
"titles": { "titles": {
"feed": "RSS 订阅源", "feed": "订阅流",
"subscriptions": "订阅", "subscriptions": "订阅列表",
"history": "历史", "history": "历史",
"preferences": "设置", "preferences": "设置",
"register": "注册", "register": "注册",

View File

@ -27,7 +27,7 @@ const routes = [
component: () => import("../components/PlaylistPage.vue"), component: () => import("../components/PlaylistPage.vue"),
}, },
{ {
path: "/:path(v|w|embed|shorts|watch)/:v?", path: "/:path(v|w|embed|live|shorts|watch)/:v?",
name: "WatchVideo", name: "WatchVideo",
component: () => import("../components/WatchVideo.vue"), component: () => import("../components/WatchVideo.vue"),
}, },
@ -41,6 +41,11 @@ const routes = [
name: "Channel", name: "Channel",
component: () => import("../components/ChannelPage.vue"), component: () => import("../components/ChannelPage.vue"),
}, },
{
path: "/@:channelId",
name: "Channel handle",
component: () => import("../components/ChannelPage.vue"),
},
{ {
path: "/login", path: "/login",
name: "Login", name: "Login",

View File

@ -4,12 +4,12 @@ import { Buffer } from "buffer";
window.Buffer = Buffer; window.Buffer = Buffer;
import { json2xml } from "xml-js"; import { json2xml } from "xml-js";
const DashUtils = { export function generate_dash_file_from_formats(VideoFormats, VideoLength) {
generate_dash_file_from_formats(VideoFormats, VideoLength) { const generatedJSON = generate_xmljs_json_from_data(VideoFormats, VideoLength);
const generatedJSON = this.generate_xmljs_json_from_data(VideoFormats, VideoLength);
return json2xml(generatedJSON); return json2xml(generatedJSON);
}, }
generate_xmljs_json_from_data(VideoFormatArray, VideoLength) {
function generate_xmljs_json_from_data(VideoFormatArray, VideoLength) {
const convertJSON = { const convertJSON = {
declaration: { declaration: {
attributes: { attributes: {
@ -32,22 +32,26 @@ const DashUtils = {
{ {
type: "element", type: "element",
name: "Period", name: "Period",
elements: this.generate_adaptation_set(VideoFormatArray), elements: generate_adaptation_set(VideoFormatArray),
}, },
], ],
}, },
], ],
}; };
return convertJSON; return convertJSON;
}, }
generate_adaptation_set(VideoFormatArray) {
function generate_adaptation_set(VideoFormatArray) {
const adaptationSets = []; const adaptationSets = [];
let mimeAudioObjs = []; let mimeAudioObjs = [];
VideoFormatArray.forEach(videoFormat => { VideoFormatArray.forEach(videoFormat => {
// the dual formats should not be used // the dual formats should not be used
if (videoFormat.mimeType.indexOf("video") != -1 && !videoFormat.videoOnly) { if (
(videoFormat.mimeType.includes("video") && !videoFormat.videoOnly) ||
videoFormat.mimeType.includes("application")
) {
return; return;
} }
@ -97,17 +101,18 @@ const DashUtils = {
for (var i = 0; i < mimeAudioObj.videoFormats.length; i++) { for (var i = 0; i < mimeAudioObj.videoFormats.length; i++) {
const videoFormat = mimeAudioObj.videoFormats[i]; const videoFormat = mimeAudioObj.videoFormats[i];
if (isVideoFormat) { if (isVideoFormat) {
adapSet.elements.push(this.generate_representation_video(videoFormat)); adapSet.elements.push(generate_representation_video(videoFormat));
} else { } else {
adapSet.elements.push(this.generate_representation_audio(videoFormat)); adapSet.elements.push(generate_representation_audio(videoFormat));
} }
} }
adaptationSets.push(adapSet); adaptationSets.push(adapSet);
}); });
return adaptationSets; return adaptationSets;
}, }
generate_representation_audio(Format) {
function generate_representation_audio(Format) {
const representation = { const representation = {
type: "element", type: "element",
name: "Representation", name: "Representation",
@ -154,8 +159,9 @@ const DashUtils = {
], ],
}; };
return representation; return representation;
}, }
generate_representation_video(Format) {
function generate_representation_video(Format) {
const representation = { const representation = {
type: "element", type: "element",
name: "Representation", name: "Representation",
@ -198,7 +204,4 @@ const DashUtils = {
], ],
}; };
return representation; return representation;
}, }
};
export default DashUtils;

799
yarn.lock

File diff suppressed because it is too large Load Diff