Merge from main + Update efy submodule

This commit is contained in:
dragos-efy
2023-01-07 18:42:23 +02:00
parent 14d79e5cea
commit 2c043461ea
32 changed files with 594 additions and 380 deletions

View File

@@ -24,7 +24,7 @@
<font-awesome-icon class="ml-1" v-if="comment.hearted" icon="heart" />
</div>
</div>
<div class="whitespace-pre-wrap" v-html="urlify(comment.commentText)" />
<div class="whitespace-pre-wrap" v-html="purifyHTML(comment.commentText)" />
<template v-if="comment.repliesPage && (!loadingReplies || !showingReplies)">
<div @click="loadReplies" class="cursor-pointer">
<a v-text="`${$t('actions.reply_count', comment.replyCount)}`" />

View File

@@ -42,7 +42,7 @@ export default {
}
.modal-container {
@apply w-min m-auto min-w-[20vw] relative;
@apply w-300rem m-auto max-w-[100vw] relative;
}
.modal-container > button {

View File

@@ -1,10 +1,10 @@
<template>
<ModalComponent>
<h2 v-t="'actions.select_playlist'" />
<select class="select w-full mt-3" v-model="selectedPlaylist">
<h4 v-t="'actions.select_playlist'" class="mb-2" />
<select class="select w-full mb-2" v-model="selectedPlaylist">
<option v-for="playlist in playlists" :value="playlist.id" :key="playlist.id" v-text="playlist.name" />
</select>
<div class="flex justify-end mt-3">
<div class="flex justify-end">
<button
class="btn"
@click="handleClick(selectedPlaylist)"

View File

@@ -149,10 +149,7 @@ export default {
version: 1,
playlists: [],
};
let tasks = [];
for (var i = 0; i < this.playlists.length; i++) {
tasks.push(this.fetchPlaylistJson(this.playlists[i].id));
}
let tasks = this.playlists.map(playlist => this.fetchPlaylistJson(playlist.id));
json.playlists = await Promise.all(tasks);
this.download(JSON.stringify(json), "playlists.json", "application/json");
},
@@ -165,31 +162,44 @@ export default {
// 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: [],
videos: playlist.relatedStreams.map(stream => "https://youtube.com" + stream.url),
};
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) {
let tasks = [];
// list of playlists exported from Piped
if (text.includes("playlists")) {
let playlists = JSON.parse(text).playlists;
if (!playlists.length) {
alert(this.$t("actions.no_valid_playlists"));
return;
}
for (var i = 0; i < playlists.length; i++) {
tasks.push(this.createPlaylistWithVideos(playlists[i]));
}
// CSV from Google Takeout
} else if (file.name.slice(-4).toLowerCase() == ".csv") {
const lines = text.split("\n");
const playlist = {
name: lines[1].split(",")[4],
videos: lines
.slice(4, lines.length)
.filter(line => line != "")
.map(line => `https://youtube.com/watch?v=${line.split(",")[0]}`),
};
tasks.push(this.createPlaylistWithVideos(playlist));
} else {
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);
},

View File

@@ -1,19 +1,25 @@
<template>
<ModalComponent>
<h2 v-t="'actions.share'" />
<div class="flex justify-between mt-4">
<label v-t="'actions.with_timecode'" for="withTimeCode" />
<input id="withTimeCode" type="checkbox" v-model="withTimeCode" @change="onChange" />
</div>
<div class="flex justify-between mt-2">
<h4 v-t="'actions.share'" />
<div class="flex justify-between mt-2 mb-2">
<label v-t="'actions.piped_link'" />
<input type="checkbox" v-model="pipedLink" @change="onChange" />
</div>
<div class="flex justify-between mt-2">
<label v-t="'actions.time_code'" />
<input class="input w-300" type="text" v-model="timeStamp" />
<div v-if="this.hasPlaylist" class="flex justify-between">
<label v-t="'actions.with_playlist'" />
<input type="checkbox" v-model="withPlaylist" @change="onChange" />
</div>
<a :href="generatedLink" target="_blank"><h6 class="mb-2" v-text="generatedLink" /></a>
<div class="flex justify-between">
<label v-t="'actions.with_timecode'" for="withTimeCode" />
<input id="withTimeCode" type="checkbox" v-model="withTimeCode" @change="onChange" />
</div>
<div v-if="this.withTimeCode" class="flex justify-between mt-2" style="align-items: center">
<label v-t="'actions.time_code'" />
<input class="input w-300 mb-0rem" type="text" v-model="timeStamp" />
</div>
<a :href="generatedLink" target="_blank">
<h6 class="mb-2 mt-2" v-text="generatedLink" />
</a>
<div class="flex justify-end mt-4">
<button class="btn" style="margin-right: 15rem" v-t="'actions.follow_link'" @click="followLink()" />
<button class="btn" v-t="'actions.copy_link'" @click="copyLink()" />
@@ -34,6 +40,12 @@ export default {
type: Number,
required: true,
},
playlistId: {
type: String,
},
playlistIndex: {
type: Number,
},
},
components: {
ModalComponent,
@@ -42,13 +54,17 @@ export default {
return {
withTimeCode: true,
pipedLink: true,
withPlaylist: true,
timeStamp: null,
hasPlaylist: false,
};
},
mounted() {
this.timeStamp = parseInt(this.currentTime);
this.withTimeCode = this.getPreferenceBoolean("shareWithTimeCode", true);
this.pipedLink = this.getPreferenceBoolean("shareAsPipedLink", true);
this.withPlaylist = this.getPreferenceBoolean("shareWithPlaylist", true);
this.hasPlaylist = this.playlistId != undefined && !isNaN(this.playlistIndex);
},
methods: {
followLink() {
@@ -68,6 +84,7 @@ export default {
onChange() {
this.setPreference("shareWithTimeCode", this.withTimeCode, true);
this.setPreference("shareAsPipedLink", this.pipedLink, true);
this.setPreference("shareWithPlaylist", this.withPlaylist, true);
},
},
computed: {
@@ -77,6 +94,10 @@ export default {
: "https://youtu.be/" + this.videoId;
var url = new URL(baseUrl);
if (this.withTimeCode && this.timeStamp > 0) url.searchParams.append("t", this.timeStamp);
if (this.hasPlaylist && this.withPlaylist) {
url.searchParams.append("list", this.playlistId);
url.searchParams.append("index", this.playlistIndex);
}
return url.href;
},
},

View File

@@ -124,6 +124,8 @@
v-if="showShareModal"
:video-id="getVideoId()"
:current-time="currentTime"
:playlist-id="playlistId"
:playlist-index="index"
@close="showShareModal = !showShareModal"
/>
</div>
@@ -438,7 +440,10 @@ export default {
this.fetchSponsors().then(data => (this.sponsors = data));
},
async getComments() {
this.fetchComments().then(data => (this.comments = data));
this.fetchComments().then(data => {
this.rewriteComments(data.comments);
this.comments = data;
});
},
async fetchSubscribedStatus() {
if (!this.channelId) return;
@@ -461,6 +466,23 @@ export default {
this.subscribed = json.subscribed;
});
},
rewriteComments(data) {
data.forEach(comment => {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(comment.commentText, "text/html");
xmlDoc.querySelectorAll("a").forEach(elem => {
if (!elem.innerText.match(/(?:[\d]{1,2}:)?(?:[\d]{1,2}):(?:[\d]{1,2})/))
elem.outerHTML = elem.getAttribute("href");
});
comment.commentText = xmlDoc
.querySelector("body")
.innerHTML.replaceAll(/(?:http(?:s)?:\/\/)?(?:www\.)?youtube\.com(\/[/a-zA-Z0-9_?=&-]*)/gm, "$1")
.replaceAll(
/(?:http(?:s)?:\/\/)?(?:www\.)?youtu\.be\/(?:watch\?v=)?([/a-zA-Z0-9_?=&-]*)/gm,
"/watch?v=$1",
);
});
},
subscribeHandler() {
if (this.authenticated) {
this.fetchJson(this.authApiUrl() + (this.subscribed ? "/unsubscribe" : "/subscribe"), null, {
@@ -487,7 +509,8 @@ export default {
}).then(json => {
this.comments.nextpage = json.nextpage;
this.loading = false;
json.comments.map(comment => this.comments.comments.push(comment));
this.rewriteComments(json.comments);
this.comments.comments = this.comments.comments.concat(json.comments);
});
}
},