Update from Main

This commit is contained in:
Dragos 2022-12-18 13:01:27 +02:00 committed by GitHub
parent 3444c96118
commit fa9381a6d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 266 additions and 75 deletions

View File

@ -36,6 +36,7 @@
> >
<font-awesome-icon icon="rss" /> <font-awesome-icon icon="rss" />
</a> </a>
<WatchOnYouTubeButton :link="`https://youtube.com/channel/${this.channel.id}`" />
<p>|</p> <p>|</p>
<button <button
v-for="(tab, index) in tabs" v-for="(tab, index) in tabs"
@ -75,11 +76,13 @@
<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";
export default { export default {
components: { components: {
ErrorHandler, ErrorHandler,
ContentItem, ContentItem,
WatchOnYouTubeButton,
}, },
data() { data() {
return { return {
@ -201,23 +204,23 @@ export default {
}, },
}); });
} else { } else {
this.handleLocalSubscriptions(this.channel.id); if (!this.handleLocalSubscriptions(this.channel.id)) return;
} }
this.subscribed = !this.subscribed; this.subscribed = !this.subscribed;
}, },
getTranslatedTabName(tabName) { getTranslatedTabName(tabName) {
let translatedTabName = tabName; let translatedTabName = tabName;
switch (tabName) { switch (tabName) {
case "Livestreams": case "livestreams":
translatedTabName = this.$t("titles.livestreams"); translatedTabName = this.$t("titles.livestreams");
break; break;
case "Playlists": case "playlists":
translatedTabName = this.$t("titles.playlists"); translatedTabName = this.$t("titles.playlists");
break; break;
case "Channels": case "channels":
translatedTabName = this.$t("titles.channels"); translatedTabName = this.$t("titles.channels");
break; break;
case "Shorts": case "shorts":
translatedTabName = this.$t("video.shorts"); translatedTabName = this.$t("video.shorts");
break; break;
default: default:

View File

@ -4,7 +4,7 @@
<font-awesome-icon :icon="['fab', 'github']" /> <font-awesome-icon :icon="['fab', 'github']" />
<span v-t="'actions.source_code'" /> <span v-t="'actions.source_code'" />
</a> </a>
<a href="https://piped-docs.kavin.rocks/" target="_blank"> <a href="https://docs.piped.video/" target="_blank">
<font-awesome-icon :icon="['fa', 'book']" /> <font-awesome-icon :icon="['fa', 'book']" />
<span v-t="'actions.documentation'" /> <span v-t="'actions.documentation'" />
</a> </a>

View File

@ -159,7 +159,11 @@ export default {
: [...new Set((this.getLocalSubscriptions() ?? []).concat(newChannels))]; : [...new Set((this.getLocalSubscriptions() ?? []).concat(newChannels))];
// Sort for better cache hits // Sort for better cache hits
subscriptions.sort(); subscriptions.sort();
try {
localStorage.setItem("localSubscriptions", JSON.stringify(subscriptions)); localStorage.setItem("localSubscriptions", JSON.stringify(subscriptions));
} catch (e) {
alert(this.$t("info.local_storage"));
}
}, },
}, },
}; };

View File

@ -14,7 +14,7 @@
<div> <div>
<strong v-text="`${playlist.videos} ${$t('video.videos')}`" /> <strong v-text="`${playlist.videos} ${$t('video.videos')}`" />
<br /> <br />
<button class="btn mr-1" v-if="authenticated && !isPipedPlaylist" @click="clonePlaylist"> <button class="btn mr-1 ml-2" v-if="authenticated && !isPipedPlaylist" @click="clonePlaylist">
{{ $t("actions.clone_playlist") }}<font-awesome-icon class="ml-3" icon="clone" /> {{ $t("actions.clone_playlist") }}<font-awesome-icon class="ml-3" icon="clone" />
</button> </button>
<button class="btn mr-1" @click="downloadPlaylistAsTxt"> <button class="btn mr-1" @click="downloadPlaylistAsTxt">
@ -23,6 +23,7 @@
<a class="btn" :href="getRssUrl"> <a class="btn" :href="getRssUrl">
<font-awesome-icon icon="rss" /> <font-awesome-icon icon="rss" />
</a> </a>
<WatchOnYouTubeButton :link="`https://www.youtube.com/playlist?list=${this.$route.query.list}`" />
</div> </div>
</div> </div>
@ -47,11 +48,13 @@
<script> <script>
import ErrorHandler from "./ErrorHandler.vue"; import ErrorHandler from "./ErrorHandler.vue";
import VideoItem from "./VideoItem.vue"; import VideoItem from "./VideoItem.vue";
import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue";
export default { export default {
components: { components: {
ErrorHandler, ErrorHandler,
VideoItem, VideoItem,
WatchOnYouTubeButton,
}, },
data() { data() {
return { return {
@ -137,7 +140,7 @@ export default {
downloadPlaylistAsTxt() { downloadPlaylistAsTxt() {
var data = ""; var data = "";
this.playlist.relatedStreams.forEach(element => { this.playlist.relatedStreams.forEach(element => {
data += "https://piped.kavin.rocks" + element.url + "\n"; data += "https://piped.video" + element.url + "\n";
}); });
this.download(data, this.playlist.name + ".txt", "text/plain"); this.download(data, this.playlist.name + ".txt", "text/plain");
}, },

View File

@ -3,8 +3,18 @@
<hr /> <hr />
<div v-if="authenticated"> <div>
<button v-t="'actions.create_playlist'" class="btn" @click="createPlaylist" /> <div class="flex">
<button v-t="'actions.create_playlist'" class="btn mr-2" @click="onCreatePlaylist" />
<button
v-if="this.playlists.length > 0"
v-t="'actions.export_to_json'"
class="btn"
@click="exportPlaylists"
/>
<input id="fileSelector" ref="fileSelector" type="file" class="display-none" @change="importPlaylists" />
<label for="fileSelector" v-t="'actions.import_from_json'" class="btn ml-2" role="button" />
</div>
<div class="video-grid"> <div class="video-grid">
<div v-for="playlist in playlists" :key="playlist.id" class="efy_trans_filter"> <div v-for="playlist in playlists" :key="playlist.id" class="efy_trans_filter">
@ -36,34 +46,18 @@
</div> </div>
</div> </div>
</div> </div>
<div v-else class="text-center h-[65vh] flex flex-col justify-center items-center">
<h1 v-t="'actions.not_logged_in'"></h1>
<div class="flex mt-100 items-center children:(mx-30)">
<button @click="showLoginModal = true" v-t="'titles.account'"></button>
<a class="btn h-min!" href="/" v-t="'actions.back_to_home'"></a>
</div>
</div>
<br />
<LoginModal v-if="showLoginModal" @close="showLoginModal = !showLoginModal" />
</template> </template>
<script> <script>
import LoginModal from "./LoginModal.vue";
export default { export default {
components: {
LoginModal,
},
data() { data() {
return { return {
playlists: [], playlists: [],
showLoginModal: false,
}; };
}, },
mounted() { mounted() {
if (this.authenticated) this.fetchPlaylists(); if (this.authenticated) this.fetchPlaylists();
else this.showLoginModal = true; else this.$router.push("/login");
}, },
activated() { activated() {
document.title = this.$t("titles.playlists") + " - Piped"; document.title = this.$t("titles.playlists") + " - Piped";
@ -119,10 +113,16 @@ export default {
else this.playlists = this.playlists.filter(playlist => playlist.id !== id); else this.playlists = this.playlists.filter(playlist => playlist.id !== id);
}); });
}, },
createPlaylist() { onCreatePlaylist() {
const name = prompt(this.$t("actions.create_playlist")); const name = prompt(this.$t("actions.create_playlist"));
if (name) if (!name) return;
this.fetchJson(this.authApiUrl() + "/user/playlists/create", null, { this.createPlaylist(name).then(json => {
if (json.error) alert(json.error);
else this.fetchPlaylists();
});
},
async createPlaylist(name) {
let json = await this.fetchJson(this.authApiUrl() + "/user/playlists/create", null, {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
name: name, name: name,
@ -131,9 +131,71 @@ export default {
Authorization: this.getAuthToken(), Authorization: this.getAuthToken(),
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}).then(json => { });
if (json.error) alert(json.error); return json;
else this.fetchPlaylists(); },
async exportPlaylists() {
if (!this.playlists) return;
let json = {
format: "Piped",
version: 1,
playlists: [],
};
let tasks = [];
for (var i = 0; i < this.playlists.length; i++) {
tasks.push(this.fetchPlaylistJson(this.playlists[i].id));
}
json.playlists = await Promise.all(tasks);
this.download(JSON.stringify(json), "playlists.json", "application/json");
},
async fetchPlaylistJson(playlistId) {
let playlist = await this.fetchJson(this.authApiUrl() + "/playlists/" + playlistId);
let playlistJson = {
name: playlist.name,
// possible other types: history, watch later, ...
type: "playlist",
// as Invidious supports public and private playlists
visibility: "private",
// list of the videos, starting with "https://youtube.com" to clarify that those are YT videos
videos: [],
};
for (var i = 0; i < playlist.relatedStreams.length; i++) {
playlistJson.videos.push("https://youtube.com" + playlist.relatedStreams[i].url);
}
return playlistJson;
},
async importPlaylists() {
const file = this.$refs.fileSelector.files[0];
let text = await file.text();
let playlists = JSON.parse(text).playlists;
if (!playlists.length) {
alert(this.$t("actions.no_valid_playlists"));
return;
}
let tasks = [];
for (var i = 0; i < playlists.length; i++) {
tasks.push(this.createPlaylistWithVideos(playlists[i]));
}
await Promise.all(tasks);
window.location.reload();
},
async createPlaylistWithVideos(playlist) {
let newPlaylist = await this.createPlaylist(playlist.name);
console.log(newPlaylist);
let videoIds = playlist.videos.map(url => url.substr(-11));
await this.addVideosToPlaylist(newPlaylist.playlistId, videoIds);
},
async addVideosToPlaylist(playlistId, videoIds) {
await this.fetchJson(this.authApiUrl() + "/user/playlists/add", null, {
method: "POST",
body: JSON.stringify({
playlistId: playlistId,
videoIds: videoIds,
}),
headers: {
Authorization: this.getAuthToken(),
"Content-Type": "application/json",
},
}); });
}, },
}, },

View File

@ -192,6 +192,36 @@
@change="onChange($event)" @change="onChange($event)"
/> />
</label> </label>
<label class="pref" for="chkMinimizeChapters">
<strong v-t="'actions.minimize_chapters_default'" />
<input
id="chkMinimizeChapters"
v-model="minimizeChapters"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkShowWatchOnYouTube">
<strong v-t="'actions.show_watch_on_youtube'" />
<input
id="chkShowWatchOnYouTube"
v-model="showWatchOnYouTube"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkStoreSearchHistory">
<strong v-t="'actions.store_search_history'" />
<input
id="chkStoreSearchHistory"
v-model="searchHistory"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkStoreWatchHistory"> <label class="pref" for="chkStoreWatchHistory">
<strong v-t="'actions.store_watch_history'" /> <strong v-t="'actions.store_watch_history'" />
<input <input
@ -202,6 +232,16 @@
@change="onChange($event)" @change="onChange($event)"
/> />
</label> </label>
<label v-if="watchHistory" class="pref" for="chkHideWatched">
<strong v-t="'actions.hide_watched'" />
<input
id="chkHideWatched"
v-model="hideWatched"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="ddlEnabledCodecs"> <label class="pref" for="ddlEnabledCodecs">
<strong v-t="'actions.enabled_codecs'" /> <strong v-t="'actions.enabled_codecs'" />
<select <select
@ -419,6 +459,8 @@ export default {
minimizeComments: false, minimizeComments: false,
minimizeDescription: false, minimizeDescription: false,
minimizeRecommendations: false, minimizeRecommendations: false,
minimizeChapters: false,
showWatchOnYouTube: false,
watchHistory: false, watchHistory: false,
searchHistory: false, searchHistory: false,
hideWatched: false, hideWatched: false,
@ -444,6 +486,7 @@ export default {
{ code: "hi", name: "हिंदी" }, { code: "hi", name: "हिंदी" },
{ code: "id", name: "Indonesia" }, { code: "id", name: "Indonesia" },
{ code: "is", name: "Íslenska" }, { code: "is", name: "Íslenska" },
{ code: "kab", name: "Taqbaylit" },
{ code: "hr", name: "Hrvatski" }, { code: "hr", name: "Hrvatski" },
{ code: "it", name: "Italiano" }, { code: "it", name: "Italiano" },
{ code: "ja", name: "日本語" }, { code: "ja", name: "日本語" },
@ -452,10 +495,12 @@ export default {
{ code: "ml", name: "മലയാളം" }, { code: "ml", name: "മലയാളം" },
{ code: "nb_NO", name: "Norwegian Bokmål" }, { code: "nb_NO", name: "Norwegian Bokmål" },
{ code: "nl", name: "Nederlands" }, { code: "nl", name: "Nederlands" },
{ code: "or", name: "ଓଡ଼ିଆ" },
{ code: "pl", name: "Polski" }, { code: "pl", name: "Polski" },
{ code: "pt", name: "Português" }, { code: "pt", name: "Português" },
{ code: "pt_PT", name: "Português (Portugal)" }, { code: "pt_PT", name: "Português (Portugal)" },
{ code: "pt_BR", name: "Português (Brasil)" }, { code: "pt_BR", name: "Português (Brasil)" },
{ code: "ro", name: "Română" },
{ code: "ru", name: "Русский" }, { code: "ru", name: "Русский" },
{ code: "sr", name: "Српски" }, { code: "sr", name: "Српски" },
{ code: "sv", name: "Svenska" }, { code: "sv", name: "Svenska" },
@ -555,9 +600,11 @@ export default {
this.minimizeComments = this.getPreferenceBoolean("minimizeComments", false); this.minimizeComments = this.getPreferenceBoolean("minimizeComments", false);
this.minimizeDescription = this.getPreferenceBoolean("minimizeDescription", false); this.minimizeDescription = this.getPreferenceBoolean("minimizeDescription", false);
this.minimizeRecommendations = this.getPreferenceBoolean("minimizeRecommendations", false); this.minimizeRecommendations = this.getPreferenceBoolean("minimizeRecommendations", false);
this.minimizeChapters = this.getPreferenceBoolean("minimizeChapters", false);
this.showWatchOnYouTube = this.getPreferenceBoolean("showWatchOnYouTube", false);
this.watchHistory = this.getPreferenceBoolean("watchHistory", false); this.watchHistory = this.getPreferenceBoolean("watchHistory", false);
this.searchHistory = this.getPreferenceBoolean("searchHistory", false); this.searchHistory = this.getPreferenceBoolean("searchHistory", false);
this.selectedLanguage = this.getPreferenceString("hl", await this.defaultLangage); this.selectedLanguage = this.getPreferenceString("hl", await this.defaultLanguage);
this.enabledCodecs = this.getPreferenceString("enabledCodecs", "vp9,avc").split(","); this.enabledCodecs = this.getPreferenceString("enabledCodecs", "vp9,avc").split(",");
this.disableLBRY = this.getPreferenceBoolean("disableLBRY", false); this.disableLBRY = this.getPreferenceBoolean("disableLBRY", false);
this.proxyLBRY = this.getPreferenceBoolean("proxyLBRY", false); this.proxyLBRY = this.getPreferenceBoolean("proxyLBRY", false);
@ -581,8 +628,8 @@ export default {
if ( if (
this.getPreferenceString("theme", "dark") !== this.selectedTheme || this.getPreferenceString("theme", "dark") !== this.selectedTheme ||
this.getPreferenceBoolean("watchHistory", false) != this.watchHistory || this.getPreferenceBoolean("watchHistory", false) != this.watchHistory ||
this.getPreferenceString("hl", await this.defaultLangage) !== this.selectedLanguage || this.getPreferenceString("hl", await this.defaultLanguage) !== this.selectedLanguage ||
this.getPreferenceString("enabledCodecs", "av1,vp9,avc") !== this.enabledCodecs.join(",") this.getPreferenceString("enabledCodecs", "vp9,avc") !== this.enabledCodecs.join(",")
) )
shouldReload = true; shouldReload = true;
@ -614,6 +661,8 @@ export default {
localStorage.setItem("minimizeComments", this.minimizeComments); localStorage.setItem("minimizeComments", this.minimizeComments);
localStorage.setItem("minimizeDescription", this.minimizeDescription); localStorage.setItem("minimizeDescription", this.minimizeDescription);
localStorage.setItem("minimizeRecommendations", this.minimizeRecommendations); localStorage.setItem("minimizeRecommendations", this.minimizeRecommendations);
localStorage.setItem("minimizeChapters", this.minimizeChapters);
localStorage.setItem("showWatchOnYouTube", this.showWatchOnYouTube);
localStorage.setItem("watchHistory", this.watchHistory); localStorage.setItem("watchHistory", this.watchHistory);
localStorage.setItem("searchHistory", this.searchHistory); localStorage.setItem("searchHistory", this.searchHistory);
if (!this.searchHistory) localStorage.removeItem("search_history"); if (!this.searchHistory) localStorage.removeItem("search_history");

View File

@ -66,8 +66,8 @@ export default {
} }
}, },
onChange() { onChange() {
this.setPreference("shareWithTimeCode", this.withTimeCode); this.setPreference("shareWithTimeCode", this.withTimeCode, true);
this.setPreference("shareAsPipedLink", this.pipedLink); this.setPreference("shareAsPipedLink", this.pipedLink, true);
}, },
}, },
computed: { computed: {

View File

@ -9,27 +9,38 @@
<i18n-t keypath="subscriptions.subscribed_channels_count">{{ subscriptions.length }}</i18n-t> <i18n-t keypath="subscriptions.subscribed_channels_count">{{ subscriptions.length }}</i18n-t>
</div> </div>
<hr /> <hr />
<!-- Subscriptions card list -->
<div class="grid"> <div class="pp-subs-cards">
<div class="mb-3" v-for="subscription in subscriptions" :key="subscription.url"> <!-- channel info card -->
<div class="flex justify-center place-items-center"> <div efy_card class="pp-subs-card" v-for="subscription in subscriptions" :key="subscription.url">
<div class="w-full flex justify-between items-center">
<router-link :to="subscription.url" class="pp-import-channel flex font-bold"> <router-link :to="subscription.url" class="pp-import-channel flex font-bold">
<img :src="subscription.avatar" width="48" height="48" /> <img :src="subscription.avatar" width="48" height="48" />
<span class="mx-2" v-text="subscription.name" /> <span class="mx-2" v-text="subscription.name" />
</router-link> </router-link>
<!-- (un)subscribe btn -->
<button <button
class="btn w-min"
@click="handleButton(subscription)" @click="handleButton(subscription)"
v-t="`actions.${subscription.subscribed ? 'unsubscribe' : 'subscribe'}`" v-t="`actions.${subscription.subscribed ? 'unsubscribe' : 'subscribe'}`"
/> />
</div> </div>
</div> </div>
</div>
</div>
<br />
</template> </template>
<style>
.pp-subs-cards {
display: grid;
gap: var(--efy_gap);
grid-template-columns: repeat(auto-fill, minmax(240rem, 1fr));
}
.pp-subs-card :is(a, span) {
-webkit-text-fill-color: var(--efy_text) !important;
}
.pp-subs-card button {
margin-bottom: 0;
width: 100%;
}
</style>
<script> <script>
export default { export default {
data() { data() {

View File

@ -17,6 +17,7 @@ export default {
}; };
}, },
mounted() { mounted() {
if (this.$route.path == "/" && this.getPreferenceString("homepage", "trending") == "feed") return;
let region = this.getPreferenceString("region", "US"); let region = this.getPreferenceString("region", "US");
this.fetchTrending(region).then(videos => { this.fetchTrending(region).then(videos => {

View File

@ -273,7 +273,11 @@ export default {
).generate_dash_file_from_formats(streams, this.video.duration); ).generate_dash_file_from_formats(streams, 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 uri = this.video.dash; } else {
const url = new URL(this.video.dash);
url.searchParams.set("rewrite", false);
uri = url.toString();
}
mime = "application/dash+xml"; mime = "application/dash+xml";
} else if (lbry) { } else if (lbry) {
uri = lbry.url; uri = lbry.url;
@ -372,13 +376,13 @@ export default {
}); });
videoEl.addEventListener("volumechange", () => { videoEl.addEventListener("volumechange", () => {
this.setPreference("volume", videoEl.volume); this.setPreference("volume", videoEl.volume, true);
}); });
videoEl.addEventListener("ratechange", e => { videoEl.addEventListener("ratechange", e => {
const rate = videoEl.playbackRate; const rate = videoEl.playbackRate;
if (rate > 0 && !isNaN(videoEl.duration) && !isNaN(videoEl.duration - e.timeStamp / 1000)) if (rate > 0 && !isNaN(videoEl.duration) && !isNaN(videoEl.duration - e.timeStamp / 1000))
this.setPreference("rate", rate); this.setPreference("rate", rate, true);
}); });
videoEl.addEventListener("ended", () => { videoEl.addEventListener("ended", () => {
@ -440,7 +444,14 @@ export default {
this.$ui = new shaka.ui.Overlay(localPlayer, this.$refs.container, videoEl); this.$ui = new shaka.ui.Overlay(localPlayer, this.$refs.container, videoEl);
const overflowMenuButtons = ["quality", "captions", "picture_in_picture", "playback_rate", "airplay"]; const overflowMenuButtons = [
"quality",
"language",
"captions",
"picture_in_picture",
"playback_rate",
"airplay",
];
if (this.isEmbed) { if (this.isEmbed) {
overflowMenuButtons.push("open_new_tab"); overflowMenuButtons.push("open_new_tab");
@ -480,22 +491,40 @@ export default {
if (qualityConds) this.$player.configure("abr.enabled", false); if (qualityConds) this.$player.configure("abr.enabled", false);
player.load(uri, 0, mime).then(() => { player.load(uri, 0, mime).then(() => {
const isSafari = window.navigator?.vendor?.includes("Apple");
if (!isSafari) {
// Set the audio language
const prefLang = this.getPreferenceString("hl", "en").substr(0, 2);
var lang = "en";
for (var l in player.getAudioLanguages()) {
if (l == prefLang) {
lang = l;
return;
}
}
player.selectAudioLanguage(lang);
}
if (qualityConds) { if (qualityConds) {
var leastDiff = Number.MAX_VALUE; var leastDiff = Number.MAX_VALUE;
var bestStream = null; var bestStream = null;
var bestAudio = 0; var bestAudio = 0;
const tracks = player
.getVariantTracks()
.filter(track => track.language == lang || track.language == "und");
// Choose the best audio stream // Choose the best audio stream
if (quality >= 480) if (quality >= 480)
player.getVariantTracks().forEach(track => { tracks.forEach(track => {
const audioBandwidth = track.audioBandwidth; const audioBandwidth = track.audioBandwidth;
if (audioBandwidth > bestAudio) bestAudio = audioBandwidth; if (audioBandwidth > bestAudio) bestAudio = audioBandwidth;
}); });
// Find best matching stream based on resolution and bitrate // Find best matching stream based on resolution and bitrate
player tracks
.getVariantTracks()
.sort((a, b) => a.bandwidth - b.bandwidth) .sort((a, b) => a.bandwidth - b.bandwidth)
.forEach(stream => { .forEach(stream => {
if (stream.audioBandwidth < bestAudio) return; if (stream.audioBandwidth < bestAudio) return;
@ -706,6 +735,7 @@ html .shaka-range-element:focus {
.material-icons-round, .material-icons-round,
.shaka-current-time { .shaka-current-time {
-webkit-text-fill-color: #fff !important; -webkit-text-fill-color: #fff !important;
filter: none !important;
opacity: 1 !important; opacity: 1 !important;
} }
.shaka-overflow-menu, .shaka-overflow-menu,

View File

@ -0,0 +1,24 @@
<script>
export default {
props: {
link: String,
},
};
</script>
<template>
<template v-if="this.getPreferenceBoolean('showWatchOnYouTube', false)">
<a :href="link" class="pp-watch-onyt-btn btn" role="button" :title="$t('player.watch_on') + 'YouTube'">
<font-awesome-icon class="mx-1.5" :icon="['fab', 'youtube']" />
</a>
</template>
</template>
<style>
.pp-watch-onyt-btn {
display: flex;
margin-left: var(--efy_gap0);
align-items: center;
gap: 5rem;
}
</style>

View File

@ -96,7 +96,8 @@
> >
<font-awesome-icon icon="rss" /> <font-awesome-icon icon="rss" />
</a> </a>
<!-- watch on youtube button --> <WatchOnYouTubeButton :link="`https://youtu.be/${getVideoId()}`" />
<!-- Share Dialog -->
<button class="btn" @click="showShareModal = !showShareModal"> <button class="btn" @click="showShareModal = !showShareModal">
<i18n-t class="lt-lg:hidden" keypath="actions.share" tag="strong"></i18n-t> <i18n-t class="lt-lg:hidden" keypath="actions.share" tag="strong"></i18n-t>
<font-awesome-icon class="mx-1.5 ml-1" icon="fa-share" /> <font-awesome-icon class="mx-1.5 ml-1" icon="fa-share" />
@ -212,6 +213,7 @@ import ChaptersBar from "./ChaptersBar.vue";
import PlaylistAddModal from "./PlaylistAddModal.vue"; 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";
export default { export default {
name: "App", name: "App",
@ -224,6 +226,7 @@ export default {
PlaylistAddModal, PlaylistAddModal,
ShareModal, ShareModal,
PlaylistVideos, PlaylistVideos,
WatchOnYouTubeButton,
}, },
data() { data() {
const smallViewQuery = window.matchMedia("(max-width: 640px)"); const smallViewQuery = window.matchMedia("(max-width: 640px)");
@ -330,6 +333,7 @@ export default {
this.showComments = !this.getPreferenceBoolean("minimizeComments", false); this.showComments = !this.getPreferenceBoolean("minimizeComments", false);
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);
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();
@ -368,7 +372,7 @@ export default {
return this.fetchJson(this.apiUrl() + "/comments/" + this.getVideoId()); return this.fetchJson(this.apiUrl() + "/comments/" + this.getVideoId());
}, },
onChange() { onChange() {
this.setPreference("autoplay", this.selectedAutoPlay); this.setPreference("autoplay", this.selectedAutoPlay, true);
}, },
async getVideoData() { async getVideoData() {
await this.fetchVideo() await this.fetchVideo()
@ -470,7 +474,7 @@ export default {
}, },
}); });
} else { } else {
this.handleLocalSubscriptions(this.channelId); if (!this.handleLocalSubscriptions(this.channelId)) return;
} }
this.subscribed = !this.subscribed; this.subscribed = !this.subscribed;
}, },