Remove things I don't care about and update

This commit is contained in:
root
2021-07-21 15:09:57 +05:30
parent fa87544163
commit dfc650bea6
11 changed files with 123 additions and 502 deletions
+6 -22
View File
@@ -20,13 +20,7 @@
<v-spacer></v-spacer>
<v-responsive max-width="720">
<v-text-field
dense
flat
hide-details
rounded
solo-inverted
></v-text-field>
<SearchMenu />
</v-responsive>
</v-container>
</v-app-bar>
@@ -38,8 +32,13 @@
</template>
<script>
import SearchMenu from '@/routes/SearchMenu'
export default {
name: 'App',
components: {
SearchMenu
},
data: () => ({
links: [
{
@@ -47,21 +46,6 @@ export default {
name: 'Preferences',
to: '/preferences'
},
{
id: 'login',
name: 'Login',
to: '/login'
},
{
id: 'register',
name: 'Register',
to: '/register'
},
{
id: 'feed',
name: 'Feed',
to: '/feed'
},
{
id: 'trending',
name: 'Trending',
-20
View File
@@ -37,26 +37,6 @@ const routes = [
name: 'Channel',
component: () => import('@/routes/Channel')
},
{
path: '/login',
name: 'Login',
component: () => import('@/routes/LoginPage')
},
{
path: '/register',
name: 'Register',
component: () => import('@/routes/RegisterPage')
},
{
path: '/feed',
name: 'Feed',
component: () => import('@/routes/FeedPage')
},
{
path: '/import',
name: 'Import',
component: () => import('@/routes/ImportPage')
},
{
path: '/:videoId([a-zA-Z0-9_-]{11})',
component: () => import('@/components/VideoRedirect')
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<ErrorHandler v-if="channel && channel.error" :message="channel.message" :error="channel.error" />
<div v-else-if="channel" v-show="!channel.error">
<div v-else-if="channel">
<h1 class="uk-text-center"><img height="48" width="48" v-bind:src="channel.avatarUrl" />{{ channel.name }}</h1>
<img v-if="channel.bannerUrl" v-bind:src="channel.bannerUrl" style="width: 100%" loading="lazy" />
<p style="white-space: pre-wrap">{{ channel.description }}</p>
-86
View File
@@ -1,86 +0,0 @@
<template>
<v-container fluid>
<h1 class="uk-text-bold uk-text-center">Feed</h1>
<small>You can import subscriptions from <router-link to="/import">here</router-link>.</small>
<div class="uk-align-right">
<a :href="getRssUrl"><font-awesome-icon icon="rss"></font-awesome-icon></a>
</div>
<hr />
<div class="uk-grid-xl" uk-grid="parallax: 0">
<div
:style="[{ background: backgroundColor }]"
class="uk-width-1-2 uk-width-1-3@s uk-width-1-4@m uk-width-1-5@l uk-width-1-6@xl"
v-bind:key="video.url"
v-for="video in videos"
>
<div class="uk-text-secondary" :style="[{ background: backgroundColor }]">
<router-link class="uk-text-emphasis" v-bind:to="'/watch?v=' + video.id">
<img style="width: 100%" v-bind:src="video.thumbnail" alt="thumbnail" loading="lazy" />
<p>{{ video.title }}</p>
</router-link>
<div>
<div>
<router-link class="uk-link-muted" :to="'/channel/' + video.uploader_id">
<a>{{ video.uploader }}</a>
</router-link>
<br />
</div>
</div>
<b class="uk-text-small uk-align-left">
<div v-if="video.views >= 0">
<font-awesome-icon icon="eye"></font-awesome-icon>
{{ numberFormat(video.views) }} views
<br />
</div>
<div>
{{ timeAgo(video.uploaded) }}
</div>
</b>
<div class="uk-align-right">
<b class="uk-text-small">{{ timeFormat(video.duration) }}</b>
<br />
<router-link :to="'/watch?v=' + video.id + '&listen=1'">
<font-awesome-icon icon="headphones"></font-awesome-icon>
</router-link>
</div>
</div>
</div>
</div>
</v-container>
</template>
<script>
import { LibPiped } from '@/tools/libpiped'
export default {
data () {
return {
videos: []
}
},
mounted () {
// reimplement using vue-meta
// document.title = 'Feed - Piped'
this.fetchFeed().then(videos => (this.videos = videos))
},
methods: {
async fetchFeed () {
return LibPiped.fetchJson('/feed')
}
},
computed: {
getRssUrl () {
return LibPiped.apiUrl() + '/feed/rss?authToken=' + LibPiped.getAuthToken()
}
}
}
</script>
-129
View File
@@ -1,129 +0,0 @@
<template>
<div class="uk-vertical-align uk-text-center uk-height-1-1 ">
<form class="uk-panel uk-panel-box">
<div class="uk-form-row">
<input type="file" @change="fileChange" ref="fileSelector" />
</div>
<div class="uk-form-row">
<b>Selected Subscriptions: {{ selectedSubscriptions }}</b>
</div>
<div class="uk-form-row">
<a
class="uk-width-1-1 uk-button uk-button-primary uk-button-large uk-width-auto"
style="background: #222"
@click="handleImport"
>Import</a
>
</div>
</form>
<br />
<b>Importing Subscriptions from YouTube</b>
<br />
<div>
Open
<a href="https://takeout.google.com/takeout/custom/youtube">takeout.google.com/takeout/custom/youtube</a>
<br />
In "Select data to include", click on "All YouTube data included" and select only "subscriptions".
<br />
Create the export and download the zip file.
<br />
Extract subscriptions.json from the zip file.
<br />
Select and import the file above.
</div>
<br />
<b>Importing Subscriptions from Invidious</b>
<br />
<div>
Open
<a href="https://invidio.us/data_control">invidiou.us/data_control</a>
<br />
Click on any of the export options.
<br />
Select and import the file above.
</div>
<br />
<b>Importing Subscriptions from NewPipe</b>
<br />
<div>
Go to the Feed tab.
<br />
Click on the arrow on where it says "Subscriptions".
<br />
Save the file somewhere.
<br />
Select and import the file above.
</div>
</div>
</template>
<script>
export default {
data () {
return {
subscriptions: []
}
},
computed: {
selectedSubscriptions () {
return this.subscriptions.length
}
},
activated () {
if (!this.authenticated) this.$router.push('/login')
},
methods: {
fileChange () {
this.$refs.fileSelector.files[0].text().then(text => {
this.subscriptions = []
// Invidious
if (text.indexOf('opml') !== -1) {
const parser = new DOMParser()
const xmlDoc = parser.parseFromString(text, 'text/xml')
xmlDoc.querySelectorAll('outline[xmlUrl]').forEach(item => {
const url = item.getAttribute('xmlUrl')
const id = url.substr(-24)
this.subscriptions.push(id)
})
}
// NewPipe
if (text.indexOf('app_version') !== -1) {
const json = JSON.parse(text)
json.subscriptions
.filter(item => item.service_id === 0)
.forEach(item => {
const url = item.url
const id = url.substr(-24)
this.subscriptions.push(id)
})
}
// Invidious JSON
if (text.indexOf('thin_mode') !== -1) {
const json = JSON.parse(text)
this.subscriptions = json.subscriptions
}
// Google Takeout
if (text.indexOf('contentDetails') !== -1) {
const json = JSON.parse(text)
json.forEach(item => {
const id = item.snippet.resourceId.channelId
this.subscriptions.push(id)
})
}
})
},
handleImport () {
this.fetchJson(this.apiUrl() + '/import', null, {
method: 'POST',
headers: {
Authorization: this.getAuthToken()
},
body: JSON.stringify(this.subscriptions)
}).then(json => {
if (json.message === 'ok') window.location = '/feed'
})
}
}
}
</script>
-66
View File
@@ -1,66 +0,0 @@
<template>
<div class="uk-vertical-align uk-text-center uk-height-1-1 ">
<form class="uk-panel uk-panel-box">
<div class="uk-form-row">
<input
class="uk-width-1-1 uk-form-large uk-input uk-width-auto"
type="text"
v-model="username"
autocomplete="username"
placeholder="Username"
/>
</div>
<div class="uk-form-row">
<input
class="uk-width-1-1 uk-form-large uk-input uk-width-auto"
type="password"
v-model="password"
autocomplete="password"
placeholder="Password"
/>
</div>
<div class="uk-form-row">
<a
class="uk-width-1-1 uk-button uk-button-primary uk-button-large uk-width-auto"
style="background: #222"
@click="login"
>Login</a
>
</div>
</form>
</div>
</template>
<script>
export default {
data () {
return {
username: null,
password: null
}
},
mounted () {
// TODO: Add Server Side check
if (this.getAuthToken()) {
this.$router.push('/')
}
},
methods: {
login () {
console.log('authToken' + this.hashCode(this.apiUrl()))
this.fetchJson(this.apiUrl() + '/login', null, {
method: 'POST',
body: JSON.stringify({
username: this.username,
password: this.password
})
}).then(resp => {
if (resp.token) {
this.setPreference('authToken' + this.hashCode(this.apiUrl()), resp.token)
window.location = '/' // done to bypass cache
} else alert(resp.error)
})
}
}
}
</script>
+57 -9
View File
@@ -1,6 +1,52 @@
<template>
<ErrorHandler v-if="playlist && playlist.error" :message="playlist.message" :error="playlist.error" />
<v-container fluid v-else-if="playlist">
<v-row>
<v-col md="8" offset-md="2">
<v-card>
<v-img v-if="playlist.avatarUrl" :src="playlist.avatarUrl" />
<v-card-title class="display-1">
{{ playlist.name }}
</v-card-title>
<v-card-text>
<router-link v-if="playlist.uploaderUrl" :to="playlist.uploaderUrl" custom v-slot="{ navigate }">
<div
style="justify-items: center; align-items: center; vertical-align: center; display: flex; cursor: pointer"
@click="navigate" @keypress.enter="navigate" role="link"
>
<div>
<v-img :src="playlist.uploaderAvatar" height="48" width="48" class="rounded-circle" />
</div>
<div class="text-h5 ml-4">
{{ playlist.uploader }}
</div>
<div class="ml-4">
<v-btn
v-if="$store.state.isAuthenticated"
@click="subscribeHandler"
color="primary"
>
{{ subscribed ? "Unsubscribe" : "Subscribe" }}
</v-btn>
</div>
</div>
</router-link>
<h5 class="text-h5 ml-16">{{ playlist.videos }} Videos</h5>
</v-card-text>
<v-card-actions>
<v-btn icon x-large link :href="getRssUrl"><v-icon x-large>mdi-rss-box</v-icon></v-btn>
</v-card-actions>
</v-card>
<v-row v-for="(chunk, chunkId) in chunkedByFour" :key="chunkId" class="mt-4">
<v-col md="3" v-for="video in chunk" :key="video.url">
<VideoItem :video="video" />
</v-col>
</v-row>
</v-col>
</v-row>
</v-container>
<div v-else-if="playlist" v-show="!playlist.error">
<h1 class="uk-text-center">
<img v-bind:src="playlist.avatarUrl" height="48" width="48" loading="lazy" />
@@ -38,6 +84,8 @@
import ErrorHandler from '@/components/ErrorHandler.vue'
import VideoItem from '@/components/VideoItem.vue'
import { chunk as _chunk } from 'lodash-es'
export default {
data () {
return {
@@ -47,20 +95,20 @@ export default {
mounted () {
this.getPlaylistData()
},
activated () {
window.addEventListener('scroll', this.handleScroll)
},
deactivated () {
window.removeEventListener('scroll', this.handleScroll)
},
computed: {
getRssUrl: _this => {
return _this.apiUrl() + '/rss/playlists/' + _this.$route.query.list
getRssUrl () {
return this.$store.getters.apiUrl + '/rss/playlists/' + this.$route.query.list
},
chunkedByFour () {
return _chunk(this.playlist.relatedStreams, 4)
}
},
methods: {
async fetchPlaylist () {
return await await this.fetchJson(this.apiUrl() + '/playlists/' + this.$route.query.list)
return this.$store.dispatch('fetchJson', {
path: '/playlists/' + this.$route.query.list
})
},
async getPlaylistData () {
this.fetchPlaylist()
-66
View File
@@ -1,66 +0,0 @@
<template>
<div class="uk-vertical-align uk-text-center uk-height-1-1 ">
<form class="uk-panel uk-panel-box">
<div class="uk-form-row">
<input
class="uk-width-1-1 uk-form-large uk-input uk-width-auto"
type="text"
v-model="username"
autocomplete="username"
placeholder="Username"
/>
</div>
<div class="uk-form-row">
<input
class="uk-width-1-1 uk-form-large uk-input uk-width-auto"
type="password"
v-model="password"
autocomplete="password"
placeholder="Password"
/>
</div>
<div class="uk-form-row">
<a
class="uk-width-1-1 uk-button uk-button-primary uk-button-large uk-width-auto"
style="background: #222"
@click="register"
>Register</a
>
</div>
</form>
</div>
</template>
<script>
export default {
data () {
return {
username: null,
password: null
}
},
mounted () {
// TODO: Add Server Side check
if (this.getAuthToken()) {
this.$router.push('/')
}
},
methods: {
register () {
console.log('authToken' + this.hashCode(this.apiUrl()))
this.fetchJson(this.apiUrl() + '/register', null, {
method: 'POST',
body: JSON.stringify({
username: this.username,
password: this.password
})
}).then(resp => {
if (resp.token) {
this.setPreference('authToken' + this.hashCode(this.apiUrl()), resp.token)
window.location = '/' // done to bypass cache
} else alert(resp.error)
})
}
}
}
</script>
+58
View File
@@ -0,0 +1,58 @@
<template>
<v-autocomplete
v-model="select"
:loading="requestInProgress"
:items="searchSuggestions"
:search-input.sync="searchText"
cache-items
class="mx-4"
flat
hide-no-data
hide-details
label="What are you looking for?"
solo-inverted
></v-autocomplete>
</template>
<script>
export default {
name: 'SearchMenu',
data () {
return {
selected: 0,
select: '',
searchText: '',
requestInProgress: false,
searchSuggestions: []
}
},
watch: {
searchText (val) {
if (val && val !== this.select) {
this.refreshSuggestions()
}
},
select (val) {
this.$router.push({
name: 'SearchResults',
query: { search_query: val }
})
}
},
methods: {
async refreshSuggestions () {
this.requestInProgress = true
this.searchSuggestions = await this.$store.dispatch('fetchJson', {
path: '/suggestions',
params: {
query: this.searchText
}
})
this.searchSuggestions.unshift(this.searchText)
this.requestInProgress = false
}
}
}
</script>
+1 -1
View File
@@ -97,7 +97,7 @@ export default {
},
methods: {
async fetchResults () {
return await await this.fetchJson(this.apiUrl() + '/search', {
return this.fetchJson(this.apiUrl() + '/search', {
q: this.$route.query.search_query,
filter: this.selectedFilter
})
-102
View File
@@ -1,102 +0,0 @@
<template>
<div
class="uk-position-absolute uk-panel uk-box-shadow-large suggestions-container"
:style="[{ background: secondaryBackgroundColor }]"
>
<ul class="uk-list uk-margin-remove uk-text-secondary">
<li
v-for="(suggestion, i) in searchSuggestions"
:key="i"
:style="[selected === i ? { background: secondaryForegroundColor } : {}]"
@mouseover="onMouseOver(i)"
@mousedown.stop="onClick(i)"
class="uk-margin-remove suggestion"
>
{{ suggestion }}
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
searchText: String
},
data () {
return {
selected: 0,
searchSuggestions: []
}
},
methods: {
onKeyUp (e) {
if (e.key === 'ArrowUp') {
if (this.selected <= 0) {
this.setSelected(this.searchSuggestions.length - 1)
} else {
this.setSelected(this.selected - 1)
}
e.preventDefault()
} else if (e.key === 'ArrowDown') {
if (this.selected >= this.searchSuggestions.length - 1) {
this.setSelected(0)
} else {
this.setSelected(this.selected + 1)
}
e.preventDefault()
} else {
this.refreshSuggestions()
}
},
async refreshSuggestions () {
this.searchSuggestions = await this.fetchJson(this.apiUrl() + '/suggestions', {
query: this.searchText
})
this.searchSuggestions.unshift(this.searchText)
this.setSelected(0)
},
onMouseOver (i) {
if (i !== this.selected) {
this.selected = i
}
},
onClick (i) {
this.setSelected(i)
this.$router.push({
name: 'SearchResults',
query: { search_query: this.searchSuggestions[i] }
})
},
setSelected (val) {
this.selected = val
this.$emit('searchchange', this.searchSuggestions[this.selected])
}
}
}
</script>
<style>
.suggestions-container {
left: 50%;
transform: translateX(-50%);
max-width: 640px;
width: 100%;
box-sizing: border-box;
padding: 5px 0;
z-index: 10;
}
.suggestion {
padding: 4px 15px;
}
@media screen and (max-width: 959px) {
.suggestions-container {
max-width: calc(100% - 60px);
}
}
@media screen and (max-width: 639px) {
.suggestions-container {
max-width: calc(100% - 30px);
}
}
</style>