mirror of
https://github.com/TeamPiped/Piped.git
synced 2024-08-30 18:43:17 +00:00
Preferences + Format code.
This commit is contained in:
parent
e853cb9840
commit
4891c57bbd
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,7 +2,6 @@
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
@ -1,24 +1,29 @@
|
||||
# piped
|
||||
|
||||
## Project setup
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
|
24
src/App.vue
24
src/App.vue
@ -1,11 +1,23 @@
|
||||
<template>
|
||||
<div class="uk-container uk-container-expand uk-light uk-height-viewport" style="background:#0b0e0f">
|
||||
<nav class="uk-navbar-container uk-container-expand uk-light" style="background:#0b0e0f" uk-navbar>
|
||||
<div
|
||||
class="uk-container uk-container-expand uk-light uk-height-viewport"
|
||||
style="background: #0b0e0f"
|
||||
>
|
||||
<nav
|
||||
class="uk-navbar-container uk-container-expand uk-light"
|
||||
style="background: #0b0e0f"
|
||||
uk-navbar
|
||||
>
|
||||
<div class="uk-navbar-left">
|
||||
<router-link class="uk-navbar-item uk-logo uk-text-bold" to="/">Piped</router-link>
|
||||
<router-link class="uk-navbar-item uk-logo uk-text-bold" to="/"
|
||||
>Piped</router-link
|
||||
>
|
||||
</div>
|
||||
<div class="uk-navbar-right">
|
||||
<ul class="uk-navbar-nav">
|
||||
<li>
|
||||
<router-link to="/preferences">Preferences</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/login">Login</router-link>
|
||||
</li>
|
||||
@ -23,11 +35,9 @@
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -1,3 +1,3 @@
|
||||
export default {
|
||||
BASE_URL: 'https://pipedapi.kavin.rocks',
|
||||
BASE_URL: localStorage.getItem("instance") || 'https://pipedapi.kavin.rocks',
|
||||
}
|
@ -1,18 +1,28 @@
|
||||
<template>
|
||||
<div v-if="channel">
|
||||
<h1 class="uk-text-center"><img v-bind:src="channel.avatarUrl">{{ channel.name }}</h1>
|
||||
<img v-bind:src="channel.bannerUrl" style="width: 100%">
|
||||
<h1 class="uk-text-center">
|
||||
<img v-bind:src="channel.avatarUrl" />{{ channel.name }}
|
||||
</h1>
|
||||
<img v-bind:src="channel.bannerUrl" style="width: 100%" />
|
||||
<p v-html="this.channel.description.replaceAll('\n', '<br>')"></p>
|
||||
|
||||
<hr>
|
||||
<hr />
|
||||
|
||||
<div class="uk-grid-small" style="width: 100%" uk-grid="parallax: 0">
|
||||
<div style="width: 288px" v-bind:key="item.url" v-for="item in this.channel.relatedStreams">
|
||||
<router-link class="uk-link-muted" style="height: 100px" v-bind:to="item.url || '/'">
|
||||
<img style="width: 100%" v-bind:src="item.thumbnail">
|
||||
<div
|
||||
style="width: 288px"
|
||||
v-bind:key="item.url"
|
||||
v-for="item in this.channel.relatedStreams"
|
||||
>
|
||||
<router-link
|
||||
class="uk-link-muted"
|
||||
style="height: 100px"
|
||||
v-bind:to="item.url || '/'"
|
||||
>
|
||||
<img style="width: 100%" v-bind:src="item.thumbnail" />
|
||||
<a>{{ item.title }}</a>
|
||||
</router-link>
|
||||
<br>
|
||||
<br />
|
||||
<a>{{ timeFormat(item.duration) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -25,27 +35,31 @@ import Constants from "@/Constants.js";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
channel: null,
|
||||
}
|
||||
channel: null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getChannelData()
|
||||
this.getChannelData();
|
||||
},
|
||||
methods: {
|
||||
async fetchChannel() {
|
||||
return await (
|
||||
await fetch(Constants.BASE_URL + "/channels/" + this.$route.params.channelId)
|
||||
await fetch(
|
||||
Constants.BASE_URL +
|
||||
"/channels/" +
|
||||
this.$route.params.channelId
|
||||
)
|
||||
).json();
|
||||
},
|
||||
async getChannelData() {
|
||||
this.fetchChannel().then(data => this.channel = data)
|
||||
.then(() => document.title = this.channel.name + " - Piped")
|
||||
this.fetchChannel()
|
||||
.then(data => (this.channel = data))
|
||||
.then(() => (document.title = this.channel.name + " - Piped"));
|
||||
},
|
||||
timeFormat(duration) {
|
||||
|
||||
var pad = function(num, size) {
|
||||
return ('000' + num).slice(size * -1);
|
||||
}
|
||||
return ("000" + num).slice(size * -1);
|
||||
};
|
||||
|
||||
var time = parseFloat(duration).toFixed(3),
|
||||
hours = Math.floor(time / 60 / 60),
|
||||
@ -54,14 +68,12 @@ export default {
|
||||
|
||||
var str = "";
|
||||
|
||||
if (hours > 0)
|
||||
str += pad(hours, 2) + ":"
|
||||
if (hours > 0) str += pad(hours, 2) + ":";
|
||||
|
||||
str += pad(minutes, 2) + ':' + pad(seconds, 2)
|
||||
str += pad(minutes, 2) + ":" + pad(seconds, 2);
|
||||
|
||||
return str;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
79
src/components/Preferences.vue
Normal file
79
src/components/Preferences.vue
Normal file
@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<table class="uk-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Instance Name</th>
|
||||
<th>Instance Locations</th>
|
||||
<th>Has CDN?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-bind:key="instance.name" v-for="instance in instances">
|
||||
<tr>
|
||||
<td>{{ instance.name }}</td>
|
||||
<td>{{ instance.locations }}</td>
|
||||
<td>{{ instance.cdn }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<select
|
||||
class="uk-select"
|
||||
v-model="selectedInstance"
|
||||
@change="onChange($event)"
|
||||
>
|
||||
<option
|
||||
v-bind:key="instance.name"
|
||||
v-for="instance in instances"
|
||||
v-bind:value="instance.apiurl"
|
||||
>
|
||||
{{ instance.name }}
|
||||
</option>
|
||||
</select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
selectedInstance: null,
|
||||
instances: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
fetch(
|
||||
"https://raw.githubusercontent.com/wiki/TeamPiped/Piped-Frontend/Instances.md"
|
||||
)
|
||||
.then(resp => resp.text())
|
||||
.then(body => {
|
||||
var skipped = 0;
|
||||
const lines = body.split("\n");
|
||||
lines.map(line => {
|
||||
const split = line.split("|");
|
||||
if (split.length == 4) {
|
||||
if (skipped < 2) {
|
||||
skipped++;
|
||||
return;
|
||||
}
|
||||
this.instances.push({
|
||||
name: split[0].trim(),
|
||||
apiurl: split[1].trim(),
|
||||
locations: split[2].trim(),
|
||||
cdn: split[3].trim()
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (localStorage)
|
||||
this.selectedInstance =
|
||||
localStorage.getItem("instance") ||
|
||||
"https://pipedapi.kavin.rocks";
|
||||
},
|
||||
methods: {
|
||||
onChange() {
|
||||
if (localStorage)
|
||||
localStorage.setItem("instance", this.selectedInstance);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -1,45 +1,58 @@
|
||||
<template>
|
||||
<h1 class="uk-text-bold uk-text-center">Trending</h1>
|
||||
|
||||
<hr>
|
||||
<hr />
|
||||
|
||||
<div class="uk-grid-collapse" style="width: 100%" uk-grid="parallax: 0">
|
||||
<div class="uk-tile-default" style="width: 300px; background: #0b0e0f" v-bind:key="video.url" v-for="video in videos">
|
||||
<div class="uk-card uk-card-default uk-card-body uk-grid-match uk-text-secondary" style="background: #0b0e0f">
|
||||
<router-link class="uk-text-emphasis" v-bind:to="video.url || '/'">
|
||||
<div
|
||||
class="uk-tile-default"
|
||||
style="width: 300px; background: #0b0e0f"
|
||||
v-bind:key="video.url"
|
||||
v-for="video in videos"
|
||||
>
|
||||
<div
|
||||
class="uk-card uk-card-default uk-card-body uk-grid-match uk-text-secondary"
|
||||
style="background: #0b0e0f"
|
||||
>
|
||||
<router-link
|
||||
class="uk-text-emphasis"
|
||||
v-bind:to="video.url || '/'"
|
||||
>
|
||||
<p>{{ video.title }}</p>
|
||||
<img style="width: 100%" v-bind:src="video.thumbnail" />
|
||||
</router-link>
|
||||
<router-link class="uk-link-muted" v-bind:to="video.uploaderUrl || '/'">
|
||||
<router-link
|
||||
class="uk-link-muted"
|
||||
v-bind:to="video.uploaderUrl || '/'"
|
||||
>
|
||||
<p>{{ video.uploaderName }}</p>
|
||||
</router-link>
|
||||
<font-awesome-icon icon="eye"></font-awesome-icon> {{ video.views }} views
|
||||
<font-awesome-icon icon="eye"></font-awesome-icon>
|
||||
{{ video.views }} views
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Constants from '@/Constants.js'
|
||||
import Constants from "@/Constants.js";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
videos: [],
|
||||
videos: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
||||
document.title = "Trending - Piped";
|
||||
console.log(Constants.BASE_URL);
|
||||
|
||||
this.fetchTrending().then(videos => this.videos = videos)
|
||||
this.fetchTrending().then(videos => (this.videos = videos));
|
||||
},
|
||||
methods: {
|
||||
async fetchTrending() {
|
||||
return await (
|
||||
await fetch(Constants.BASE_URL + "/trending")
|
||||
).json();
|
||||
}
|
||||
return await (await fetch(Constants.BASE_URL + "/trending")).json();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -1,7 +1,11 @@
|
||||
<template>
|
||||
<div class="uk-container uk-container-xlarge">
|
||||
<h1 class="uk-text-bold">{{ video.title }}</h1>
|
||||
<video controls ref="player" class="video-js preview-player-dimensions "></video>
|
||||
<video
|
||||
controls
|
||||
ref="player"
|
||||
class="video-js preview-player-dimensions"
|
||||
></video>
|
||||
|
||||
<img :src="video.uploaderAvatar" />
|
||||
<router-link class="uk-text-bold" v-bind:to="video.uploaderUrl || '/'">
|
||||
@ -15,27 +19,37 @@
|
||||
{{ video.dislikes }}
|
||||
</p>
|
||||
<p>
|
||||
<font-awesome-icon icon="eye"></font-awesome-icon> {{ video.views }} views
|
||||
<font-awesome-icon icon="eye"></font-awesome-icon>
|
||||
{{ video.views }} views
|
||||
</p>
|
||||
<p class="uk-light" v-html="video.description"></p>
|
||||
<a v-if="sponsors && sponsors.segments">Sponsors Segments: {{ sponsors.segments.length }}</a>
|
||||
<a v-if="sponsors && sponsors.segments"
|
||||
>Sponsors Segments: {{ sponsors.segments.length }}</a
|
||||
>
|
||||
|
||||
<hr>
|
||||
<hr />
|
||||
|
||||
<div class="uk-tile-default uk-text-secondary" style="background: #0b0e0f; width: 300px" v-bind:key="related.url" v-for="related in video.relatedStreams">
|
||||
<div
|
||||
class="uk-tile-default uk-text-secondary"
|
||||
style="background: #0b0e0f; width: 300px"
|
||||
v-bind:key="related.url"
|
||||
v-for="related in video.relatedStreams"
|
||||
>
|
||||
<router-link class="uk-link-muted" v-bind:to="related.url">
|
||||
<p class="uk-text-emphasis">{{ related.title }}</p>
|
||||
<img style="width: 100%" v-bind:src="related.thumbnail" />
|
||||
</router-link>
|
||||
<p>
|
||||
<router-link class="uk-link-muted" v-bind:to="related.uploaderUrl || '/'">
|
||||
<router-link
|
||||
class="uk-link-muted"
|
||||
v-bind:to="related.uploaderUrl || '/'"
|
||||
>
|
||||
<p>{{ related.uploaderName }}</p>
|
||||
</router-link>
|
||||
<font-awesome-icon icon="eye"></font-awesome-icon>
|
||||
{{ related.views }} views
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -52,11 +66,11 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
video: {
|
||||
title: "Loading...",
|
||||
title: "Loading..."
|
||||
},
|
||||
player: null,
|
||||
audioplayer: null,
|
||||
sponsors: null,
|
||||
sponsors: null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@ -68,37 +82,45 @@ export default {
|
||||
this.player.dispose();
|
||||
}
|
||||
if (this.audioplayer) {
|
||||
this.audioplayer.pause()
|
||||
this.audioplayer.pause();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"$route.query.v": function(v) {
|
||||
if (v) {
|
||||
if (this.audioplayer)
|
||||
this.audioplayer.pause()
|
||||
if (this.audioplayer) this.audioplayer.pause();
|
||||
this.getVideoData();
|
||||
this.getSponsors();
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchVideo() {
|
||||
return await (
|
||||
await fetch(Constants.BASE_URL + "/streams/" + this.$route.query.v)
|
||||
await fetch(
|
||||
Constants.BASE_URL + "/streams/" + this.$route.query.v
|
||||
)
|
||||
).json();
|
||||
},
|
||||
async fetchSponsors() {
|
||||
return await (
|
||||
await fetch(Constants.BASE_URL + "/sponsors/" + this.$route.query.v + "?category=[\"sponsor\",\"interaction\",\"selfpromo\",\"music_offtopic\"]")
|
||||
await fetch(
|
||||
Constants.BASE_URL +
|
||||
"/sponsors/" +
|
||||
this.$route.query.v +
|
||||
'?category=["sponsor","interaction","selfpromo","music_offtopic"]'
|
||||
)
|
||||
).json();
|
||||
},
|
||||
async getVideoData() {
|
||||
this.fetchVideo()
|
||||
.then((data) => (this.video = data))
|
||||
.then(data => (this.video = data))
|
||||
.then(() => {
|
||||
document.title = this.video.title + " - Piped";
|
||||
|
||||
this.video.description = this.video.description.replaceAll("http://www.youtube.com", "").replaceAll("https://www.youtube.com", "")
|
||||
this.video.description = this.video.description
|
||||
.replaceAll("http://www.youtube.com", "")
|
||||
.replaceAll("https://www.youtube.com", "");
|
||||
|
||||
const options = {
|
||||
autoplay: false,
|
||||
@ -110,22 +132,23 @@ export default {
|
||||
"volumePanel",
|
||||
"qualitySelector",
|
||||
"captionsButton",
|
||||
"fullscreenToggle",
|
||||
],
|
||||
"fullscreenToggle"
|
||||
]
|
||||
},
|
||||
responsive: false,
|
||||
aspectRatio: '16:9'
|
||||
aspectRatio: "16:9"
|
||||
};
|
||||
|
||||
const noPrevPlayer = !this.player
|
||||
const noPrevPlayer = !this.player;
|
||||
|
||||
if (noPrevPlayer) this.player = videojs(this.$refs.player, options);
|
||||
if (noPrevPlayer)
|
||||
this.player = videojs(this.$refs.player, options);
|
||||
|
||||
this.player.hotkeys({
|
||||
volumeStep: 0.1,
|
||||
seekStep: 5,
|
||||
enableModifiersForNumbers: false,
|
||||
enableHoverScroll: true,
|
||||
enableHoverScroll: true
|
||||
});
|
||||
|
||||
this.player.poster(this.video.thumbnailUrl);
|
||||
@ -133,96 +156,110 @@ export default {
|
||||
var src = [];
|
||||
|
||||
if (this.video.livestream) {
|
||||
|
||||
src.push({
|
||||
src: this.video.hls,
|
||||
type: 'application/x-mpegURL'
|
||||
})
|
||||
|
||||
type: "application/x-mpegURL"
|
||||
});
|
||||
} else {
|
||||
this.video.videoStreams.map((stream) =>
|
||||
this.video.videoStreams.map(stream =>
|
||||
src.push({
|
||||
src: stream.url,
|
||||
type: stream.mimeType,
|
||||
label: stream.quality,
|
||||
label: stream.quality
|
||||
})
|
||||
);
|
||||
|
||||
this.video.audioStreams.map((stream) =>
|
||||
this.video.audioStreams.map(stream =>
|
||||
src.push({
|
||||
src: stream.url,
|
||||
type: stream.mimeType,
|
||||
label: stream.quality,
|
||||
label: stream.quality
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this.audioplayer = new Audio((this.video.audioStreams.slice(-1)[0].url));
|
||||
this.audioplayer = new Audio(
|
||||
this.video.audioStreams.slice(-1)[0].url
|
||||
);
|
||||
|
||||
this.player.src(src);
|
||||
|
||||
if (noPrevPlayer) {
|
||||
this.player.on('timeupdate', () => {
|
||||
this.player.on("timeupdate", () => {
|
||||
if (this.sponsors && this.sponsors.segments) {
|
||||
const time = this.player.currentTime()
|
||||
const time = this.player.currentTime();
|
||||
this.sponsors.segments.map(segment => {
|
||||
if (!segment.skipped) {
|
||||
const end = segment.segment[1]
|
||||
if (time >= segment.segment[0] && time < end) {
|
||||
this.player.currentTime(end)
|
||||
this.audioplayer.currentTime = end
|
||||
segment.skipped = true
|
||||
return
|
||||
const end = segment.segment[1];
|
||||
if (
|
||||
time >= segment.segment[0] &&
|
||||
time < end
|
||||
) {
|
||||
this.player.currentTime(end);
|
||||
this.audioplayer.currentTime = end;
|
||||
segment.skipped = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (this.audioplayer) {
|
||||
|
||||
const delay = this.audioplayer.currentTime - this.player.currentTime()
|
||||
const delay =
|
||||
this.audioplayer.currentTime -
|
||||
this.player.currentTime();
|
||||
|
||||
if (Math.abs(delay) > 0.1) {
|
||||
this.audioplayer.currentTime = this.player.currentTime() - delay
|
||||
this.audioplayer.currentTime =
|
||||
this.player.currentTime() - delay;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.player.on('play', () => {
|
||||
this.audioplayer.play()
|
||||
this.player.on("play", () => {
|
||||
this.audioplayer.play();
|
||||
});
|
||||
|
||||
this.player.on('pause', () => {
|
||||
this.audioplayer.currentTime = this.player.currentTime()
|
||||
this.audioplayer.pause()
|
||||
this.player.on("pause", () => {
|
||||
this.audioplayer.currentTime = this.player.currentTime();
|
||||
this.audioplayer.pause();
|
||||
});
|
||||
|
||||
this.player.on('volumechange', () => {
|
||||
this.audioplayer.volume = this.player.volume()
|
||||
})
|
||||
|
||||
this.player.on("volumechange", () => {
|
||||
this.audioplayer.volume = this.player.volume();
|
||||
});
|
||||
}
|
||||
|
||||
if (!noPrevPlayer)
|
||||
this.player.remoteTextTracks().map(track => this.player.removeRemoteTextTrack(track));
|
||||
this.player
|
||||
.remoteTextTracks()
|
||||
.map(track =>
|
||||
this.player.removeRemoteTextTrack(track)
|
||||
);
|
||||
|
||||
this.video.subtitles.map(subtitle => {
|
||||
this.player.addRemoteTextTrack({
|
||||
this.player.addRemoteTextTrack(
|
||||
{
|
||||
kind: "captions",
|
||||
src: subtitle.url.replace("fmt=ttml", "fmt=vtt"),
|
||||
src: subtitle.url.replace(
|
||||
"fmt=ttml",
|
||||
"fmt=vtt"
|
||||
),
|
||||
label: "Track",
|
||||
type: "captions/captions.vtt"
|
||||
}, false).mode = "showing"
|
||||
})
|
||||
},
|
||||
false
|
||||
).mode = "showing";
|
||||
});
|
||||
|
||||
//const parent = this.player.el().querySelector(".vjs-progress-holder")
|
||||
//TODO: Add sponsors on seekbar: https://github.com/ajayyy/SponsorBlock/blob/e39de9fd852adb9196e0358ed827ad38d9933e29/src/js-components/previewBar.ts#L12
|
||||
|
||||
});
|
||||
},
|
||||
async getSponsors() {
|
||||
this.fetchSponsors().then((data) => (this.sponsors = data));
|
||||
},
|
||||
},
|
||||
this.fetchSponsors().then(data => (this.sponsors = data));
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Watch from '../components/WatchVideo.vue'
|
||||
import Trending from '../components/TrendingPage.vue'
|
||||
import Channel from '../components/Channel.vue'
|
||||
import Preferences from '../components/Preferences.vue'
|
||||
|
||||
const routes = [{
|
||||
path: '/watch',
|
||||
@ -15,6 +16,10 @@ const routes = [{
|
||||
path: '/channel/:channelId',
|
||||
name: 'Channel',
|
||||
component: Channel
|
||||
}, {
|
||||
path: '/preferences',
|
||||
name: 'Preferences',
|
||||
component: Preferences
|
||||
}]
|
||||
|
||||
const router = createRouter({
|
||||
|
Loading…
Reference in New Issue
Block a user