Preferences + Format code.

This commit is contained in:
FireMasterK 2020-11-17 10:45:35 +05:30
parent e853cb9840
commit 4891c57bbd
11 changed files with 379 additions and 219 deletions

1
.gitignore vendored
View File

@ -2,7 +2,6 @@
node_modules
/dist
# local env files
.env.local
.env.*.local

View File

@ -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/).

View File

@ -1,33 +1,43 @@
<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-navbar-left">
<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="/login">Login</router-link>
</li>
<li>
<router-link to="/feed">Feed</router-link>
</li>
</ul>
</div>
</nav>
<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
>
</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>
<li>
<router-link to="/feed">Feed</router-link>
</li>
</ul>
</div>
</nav>
<router-view />
</div>
<router-view />
</div>
</template>
<script>
export default {
data() {
return {
}
return {};
}
}
};
</script>
<style>

View File

@ -1,3 +1,3 @@
export default {
BASE_URL: 'https://pipedapi.kavin.rocks',
}
BASE_URL: localStorage.getItem("instance") || 'https://pipedapi.kavin.rocks',
}

View File

@ -1,22 +1,32 @@
<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%">
<p v-html="this.channel.description.replaceAll('\n', '<br>')"></p>
<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%" />
<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">
<a>{{ item.title }}</a>
</router-link>
<br>
<a>{{ timeFormat(item.duration) }}</a>
<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" />
<a>{{ item.title }}</a>
</router-link>
<br />
<a>{{ timeFormat(item.duration) }}</a>
</div>
</div>
</div>
</div>
</template>
<script>
@ -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);
}
var pad = function(num, size) {
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>

View 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>

View File

@ -1,45 +1,58 @@
<template>
<h1 class="uk-text-bold uk-text-center">Trending</h1>
<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 || '/'">
<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 || '/'">
<p>{{ video.uploaderName }}</p>
</router-link>
<font-awesome-icon icon="eye"></font-awesome-icon> {{ video.views }} views
</div>
</div>
</div>
</template>
<script>
import Constants from '@/Constants.js'
export default {
data() {
return {
videos: [],
};
},
mounted() {
document.title = "Trending - Piped";
this.fetchTrending().then(videos => this.videos = videos)
},
methods: {
async fetchTrending() {
return await (
await fetch(Constants.BASE_URL + "/trending")
).json();
}
}
}
</script>
<template>
<h1 class="uk-text-bold uk-text-center">Trending</h1>
<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 || '/'"
>
<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 || '/'"
>
<p>{{ video.uploaderName }}</p>
</router-link>
<font-awesome-icon icon="eye"></font-awesome-icon>
{{ video.views }} views
</div>
</div>
</div>
</template>
<script>
import Constants from "@/Constants.js";
export default {
data() {
return {
videos: []
};
},
mounted() {
document.title = "Trending - Piped";
console.log(Constants.BASE_URL);
this.fetchTrending().then(videos => (this.videos = videos));
},
methods: {
async fetchTrending() {
return await (await fetch(Constants.BASE_URL + "/trending")).json();
}
}
};
</script>

View File

@ -1,42 +1,56 @@
<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>
<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>
<img :src="video.uploaderAvatar" />
<router-link class="uk-text-bold" v-bind:to="video.uploaderUrl || '/'">
<a>{{ video.uploader }}</a>
</router-link>
<p class="uk-dark">
<font-awesome-icon icon="thumbs-down"></font-awesome-icon>
{{ video.likes }}
<font-awesome-icon icon="thumbs-up"></font-awesome-icon>
{{ video.dislikes }}
</p>
<p>
<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>
<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">
<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" />
<img :src="video.uploaderAvatar" />
<router-link class="uk-text-bold" v-bind:to="video.uploaderUrl || '/'">
<a>{{ video.uploader }}</a>
</router-link>
<p>
<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>
<p class="uk-dark">
<font-awesome-icon icon="thumbs-down"></font-awesome-icon>
{{ video.likes }}
<font-awesome-icon icon="thumbs-up"></font-awesome-icon>
{{ video.dislikes }}
</p>
<p>
<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
>
<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"
>
<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 || '/'"
>
<p>{{ related.uploaderName }}</p>
</router-link>
<font-awesome-icon icon="eye"></font-awesome-icon>
{{ related.views }} views
</p>
</div>
</div>
</template>
<script>
@ -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) {
"$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,
})
);
type: stream.mimeType,
label: stream.quality
})
);
this.video.audioStreams.map((stream) =>
src.push({
src: stream.url,
type: stream.mimeType,
label: stream.quality,
this.video.audioStreams.map(stream =>
src.push({
src: stream.url,
type: stream.mimeType,
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({
kind: "captions",
src: subtitle.url.replace("fmt=ttml", "fmt=vtt"),
label: "Track",
type: "captions/captions.vtt"
}, false).mode = "showing"
})
this.player.addRemoteTextTrack(
{
kind: "captions",
src: subtitle.url.replace(
"fmt=ttml",
"fmt=vtt"
),
label: "Track",
type: "captions/captions.vtt"
},
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>

View File

@ -1,18 +1,18 @@
import { createApp } from 'vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faThumbsUp, faThumbsDown, faEye } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
library.add(faThumbsUp, faThumbsDown, faEye)
require("uikit/src/less/uikit.less")
require("uikit/dist/js/uikit.min.js")
import router from '@/router/router'
import App from './App.vue'
import './registerServiceWorker'
const app = createApp(App)
app.use(router)
app.component('font-awesome-icon', FontAwesomeIcon)
app.mount('#app')
import { createApp } from 'vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faThumbsUp, faThumbsDown, faEye } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
library.add(faThumbsUp, faThumbsDown, faEye)
require("uikit/src/less/uikit.less")
require("uikit/dist/js/uikit.min.js")
import router from '@/router/router'
import App from './App.vue'
import './registerServiceWorker'
const app = createApp(App)
app.use(router)
app.component('font-awesome-icon', FontAwesomeIcon)
app.mount('#app')

View File

@ -21,7 +21,7 @@ if (process.env.NODE_ENV === 'production') {
},
updated() {
console.log('New content is available; please refresh.')
caches.keys().then(function(names) {
caches.keys().then(function (names) {
for (let name of names) caches.delete(name);
})
},

View File

@ -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({
@ -22,4 +27,4 @@ const router = createRouter({
routes
})
export default router
export default router