mirror of
https://github.com/TeamPiped/Piped.git
synced 2024-08-14 23:57:27 +00:00
feat: support for adding custom instances
This commit is contained in:
parent
d04fb453f6
commit
4d7390e244
4 changed files with 139 additions and 16 deletions
79
src/components/CustomInstanceModal.vue
Normal file
79
src/components/CustomInstanceModal.vue
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<template>
|
||||||
|
<ModalComponent @close="$emit('close')">
|
||||||
|
<h3 v-t="'titles.custom_instances'" class="my-4 font-bold" />
|
||||||
|
<hr />
|
||||||
|
<div class="text-center">
|
||||||
|
<div>
|
||||||
|
<div v-for="(customInstance, index) in customInstances" :key="customInstance.name">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span>{{ customInstance.name }} - {{ customInstance.api_url }}</span>
|
||||||
|
<span
|
||||||
|
class="i-fa6-solid:circle-minus cursor-pointer"
|
||||||
|
@click="removeInstance(customInstance, index)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form class="flex flex-col items-end gap-2">
|
||||||
|
<input v-model="name" class="input w-full" type="text" :placeholder="$t('preferences.instance_name')" />
|
||||||
|
<input
|
||||||
|
v-model="url"
|
||||||
|
class="input w-full"
|
||||||
|
type="text"
|
||||||
|
:placeholder="$t('preferences.api_url')"
|
||||||
|
@keyup.enter="addInstance"
|
||||||
|
/>
|
||||||
|
<button v-t="'actions.add'" class="btn w-min" @click.prevent="addInstance" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ModalComponent>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ModalComponent from "./ModalComponent.vue";
|
||||||
|
export default {
|
||||||
|
components: { ModalComponent },
|
||||||
|
emits: ["close"],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
customInstances: [],
|
||||||
|
name: "",
|
||||||
|
url: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.customInstances = this.getCustomInstances();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async addInstance() {
|
||||||
|
const newInstance = {
|
||||||
|
name: this.name,
|
||||||
|
api_url: this.url,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!newInstance.name || !newInstance.api_url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.isValidInstanceUrl(newInstance.api_url)) {
|
||||||
|
alert(this.$t("actions.invalid_url"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addCustomInstance(newInstance);
|
||||||
|
this.name = "";
|
||||||
|
this.url = "";
|
||||||
|
},
|
||||||
|
removeInstance(instance, index) {
|
||||||
|
this.customInstances.splice(index, 1);
|
||||||
|
|
||||||
|
this.removeCustomInstance(instance);
|
||||||
|
},
|
||||||
|
isValidInstanceUrl(str) {
|
||||||
|
var a = document.createElement("a");
|
||||||
|
a.href = str;
|
||||||
|
return a.host && a.host != window.location.host;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -313,6 +313,10 @@
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
<div class="pref">
|
||||||
|
<span v-t="'titles.custom_instances'" class="w-max" />
|
||||||
|
<button v-t="'actions.customize'" class="btn" @click="showCustomInstancesModal = true" />
|
||||||
|
</div>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<!-- options that are visible only when logged in -->
|
<!-- options that are visible only when logged in -->
|
||||||
|
@ -359,7 +363,7 @@
|
||||||
<th v-t="'preferences.ssl_score'" />
|
<th v-t="'preferences.ssl_score'" />
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody v-for="instance in instances" :key="instance.name">
|
<tbody v-for="instance in publicInstances" :key="instance.name">
|
||||||
<tr>
|
<tr>
|
||||||
<td v-text="instance.name" />
|
<td v-text="instance.name" />
|
||||||
<td v-text="instance.locations" />
|
<td v-text="instance.locations" />
|
||||||
|
@ -387,14 +391,23 @@
|
||||||
@close="showConfirmResetPrefsDialog = false"
|
@close="showConfirmResetPrefsDialog = false"
|
||||||
@confirm="resetPreferences()"
|
@confirm="resetPreferences()"
|
||||||
/>
|
/>
|
||||||
|
<CustomInstanceModal
|
||||||
|
v-if="showCustomInstancesModal"
|
||||||
|
@close="
|
||||||
|
showCustomInstancesModal = false;
|
||||||
|
fetchInstances();
|
||||||
|
"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import CountryMap from "@/utils/CountryMaps/en.json";
|
import CountryMap from "@/utils/CountryMaps/en.json";
|
||||||
import ConfirmModal from "./ConfirmModal.vue";
|
import ConfirmModal from "./ConfirmModal.vue";
|
||||||
|
import CustomInstanceModal from "./CustomInstanceModal.vue";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ConfirmModal,
|
ConfirmModal,
|
||||||
|
CustomInstanceModal,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -402,7 +415,8 @@ export default {
|
||||||
selectedInstance: null,
|
selectedInstance: null,
|
||||||
authInstance: false,
|
authInstance: false,
|
||||||
selectedAuthInstance: null,
|
selectedAuthInstance: null,
|
||||||
instances: [],
|
customInstances: [],
|
||||||
|
publicInstances: [],
|
||||||
sponsorBlock: true,
|
sponsorBlock: true,
|
||||||
skipOptions: new Map([
|
skipOptions: new Map([
|
||||||
["sponsor", { value: "auto", label: "actions.skip_sponsors" }],
|
["sponsor", { value: "auto", label: "actions.skip_sponsors" }],
|
||||||
|
@ -496,25 +510,21 @@ export default {
|
||||||
prefetchLimit: 2,
|
prefetchLimit: 2,
|
||||||
password: null,
|
password: null,
|
||||||
showConfirmResetPrefsDialog: false,
|
showConfirmResetPrefsDialog: false,
|
||||||
|
showCustomInstancesModal: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
instances() {
|
||||||
|
return [...this.publicInstances, ...this.customInstances];
|
||||||
|
},
|
||||||
|
},
|
||||||
activated() {
|
activated() {
|
||||||
document.title = this.$t("titles.preferences") + " - Piped";
|
document.title = this.$t("titles.preferences") + " - Piped";
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
if (Object.keys(this.$route.query).length > 0) this.$router.replace({ query: {} });
|
if (Object.keys(this.$route.query).length > 0) this.$router.replace({ query: {} });
|
||||||
|
|
||||||
this.fetchJson(import.meta.env.VITE_PIPED_INSTANCES).then(resp => {
|
this.fetchInstances();
|
||||||
this.instances = resp;
|
|
||||||
if (!this.instances.some(instance => instance.api_url == this.apiUrl()))
|
|
||||||
this.instances.push({
|
|
||||||
name: "Custom Instance",
|
|
||||||
api_url: this.apiUrl(),
|
|
||||||
locations: "Unknown",
|
|
||||||
cdn: false,
|
|
||||||
uptime_30d: 100,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.testLocalStorage) {
|
if (this.testLocalStorage) {
|
||||||
this.selectedInstance = this.getPreferenceString("instance", import.meta.env.VITE_PIPED_API);
|
this.selectedInstance = this.getPreferenceString("instance", import.meta.env.VITE_PIPED_API);
|
||||||
|
@ -633,6 +643,21 @@ export default {
|
||||||
if (shouldReload) window.location.reload();
|
if (shouldReload) window.location.reload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async fetchInstances() {
|
||||||
|
this.customInstances = this.getCustomInstances();
|
||||||
|
|
||||||
|
this.fetchJson(import.meta.env.VITE_PIPED_INSTANCES).then(resp => {
|
||||||
|
this.publicInstances = resp;
|
||||||
|
if (!this.publicInstances.some(instance => instance.api_url == this.apiUrl()))
|
||||||
|
this.publicInstances.push({
|
||||||
|
name: "Selected Instance",
|
||||||
|
api_url: this.apiUrl(),
|
||||||
|
locations: "Unknown",
|
||||||
|
cdn: false,
|
||||||
|
uptime_30d: 100,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
sslScore(url) {
|
sslScore(url) {
|
||||||
return "https://www.ssllabs.com/ssltest/analyze.html?d=" + new URL(url).host + "&latest";
|
return "https://www.ssllabs.com/ssltest/analyze.html?d=" + new URL(url).host + "&latest";
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
"albums": "Albums",
|
"albums": "Albums",
|
||||||
"bookmarks": "Bookmarks",
|
"bookmarks": "Bookmarks",
|
||||||
"channel_groups": "Channel groups",
|
"channel_groups": "Channel groups",
|
||||||
"dearrow": "DeArrow"
|
"dearrow": "DeArrow",
|
||||||
|
"custom_instances": "Custom instances"
|
||||||
},
|
},
|
||||||
"player": {
|
"player": {
|
||||||
"watch_on": "View on {0}",
|
"watch_on": "View on {0}",
|
||||||
|
@ -151,7 +152,10 @@
|
||||||
"generate_qrcode": "Generate QR Code",
|
"generate_qrcode": "Generate QR Code",
|
||||||
"download_frame": "Download frame",
|
"download_frame": "Download frame",
|
||||||
"add_to_group": "Add to group",
|
"add_to_group": "Add to group",
|
||||||
"concurrent_prefetch_limit": "Concurrent Stream Prefetch Limit"
|
"concurrent_prefetch_limit": "Concurrent Stream Prefetch Limit",
|
||||||
|
"customize": "Customize",
|
||||||
|
"invalid_url": "Invalid URL!",
|
||||||
|
"add": "Add"
|
||||||
},
|
},
|
||||||
"comment": {
|
"comment": {
|
||||||
"pinned_by": "Pinned by {author}",
|
"pinned_by": "Pinned by {author}",
|
||||||
|
@ -167,7 +171,8 @@
|
||||||
"version": "Version",
|
"version": "Version",
|
||||||
"up_to_date": "Up to date?",
|
"up_to_date": "Up to date?",
|
||||||
"ssl_score": "SSL Score",
|
"ssl_score": "SSL Score",
|
||||||
"uptime_30d": "Uptime (30d)"
|
"uptime_30d": "Uptime (30d)",
|
||||||
|
"api_url": "Api URL"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"username": "Username",
|
"username": "Username",
|
||||||
|
|
14
src/main.js
14
src/main.js
|
@ -553,6 +553,20 @@ const mixin = {
|
||||||
|
|
||||||
return !resp.error;
|
return !resp.error;
|
||||||
},
|
},
|
||||||
|
getCustomInstances() {
|
||||||
|
return JSON.parse(window.localStorage.getItem("customInstances")) ?? [];
|
||||||
|
},
|
||||||
|
addCustomInstance(instance) {
|
||||||
|
let customInstances = this.getCustomInstances();
|
||||||
|
customInstances.push(instance);
|
||||||
|
window.localStorage.setItem("customInstances", JSON.stringify(customInstances));
|
||||||
|
},
|
||||||
|
removeCustomInstance(instanceToDelete) {
|
||||||
|
let customInstances = this.getCustomInstances().filter(
|
||||||
|
instance => instance.api_url != instanceToDelete.api_url,
|
||||||
|
);
|
||||||
|
window.localStorage.setItem("customInstances", JSON.stringify(customInstances));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
authenticated(_this) {
|
authenticated(_this) {
|
||||||
|
|
Loading…
Reference in a new issue