mirror of
https://codeberg.org/Hyperpipe/Hyperpipe
synced 2025-06-27 20:58:01 +02:00
Switched to Composition API
This commit is contained in:
parent
75b0a9b319
commit
8ee03fa623
11 changed files with 705 additions and 647 deletions
|
@ -7,6 +7,12 @@ defineProps({
|
|||
default: '--art: linear-gradient(45deg, #88c0d0, #5e81ac);',
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['open-album']);
|
||||
|
||||
function onClick() {
|
||||
emit('open-album');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -20,16 +26,6 @@ defineProps({
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
onClick() {
|
||||
this.$emit('open-album');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card {
|
||||
min-height: 17rem;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import PlayBtn from './PlayBtn.vue';
|
||||
|
||||
defineProps(['title', 'desc', 'subs', 'thumbs', 'play']);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script setup>
|
||||
defineEmits(['update-search']);
|
||||
import SearchBar from '../components/SearchBar.vue';
|
||||
|
||||
defineEmits(['update-search']);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
<script setup>
|
||||
defineProps(['url', 'urls', 'show']);
|
||||
defineProps({
|
||||
url: String,
|
||||
urls: Array,
|
||||
show: Boolean,
|
||||
});
|
||||
defineEmits(['playthis']);
|
||||
</script>
|
||||
|
||||
|
@ -56,34 +60,4 @@ defineEmits(['playthis']);
|
|||
.pl-main {
|
||||
padding-left: 3rem;
|
||||
}
|
||||
|
||||
.bars-wrap {
|
||||
position: absolute;
|
||||
height: 1.5rem;
|
||||
width: 2rem;
|
||||
transform: rotateZ(180deg);
|
||||
}
|
||||
.bars {
|
||||
position: relative;
|
||||
height: 15%;
|
||||
width: calc(calc(100% / 3) - 0.2rem);
|
||||
margin-left: 0.1rem;
|
||||
background: var(--color-foreground);
|
||||
float: left;
|
||||
animation: heightc 1s ease infinite;
|
||||
}
|
||||
.bars:first-child {
|
||||
animation-delay: 0.25s;
|
||||
}
|
||||
.bars:nth-child(2) {
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
.bars:last-child {
|
||||
margin-left: none;
|
||||
}
|
||||
@keyframes heightc {
|
||||
50% {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,44 +1,141 @@
|
|||
<script setup>
|
||||
defineProps([
|
||||
'search',
|
||||
'songItems',
|
||||
'items'
|
||||
]);
|
||||
defineEmits([
|
||||
'get-song',
|
||||
import { reactive, watch } from 'vue';
|
||||
import PlayBtn from './PlayBtn.vue';
|
||||
import SongItem from './SongItem.vue';
|
||||
import AlbumItem from './AlbumItem.vue';
|
||||
|
||||
import { getJsonPiped, getPipedQuery } from '../scripts/fetch.js'
|
||||
|
||||
const props = defineProps(['search', 'songItems', 'items']);
|
||||
|
||||
const emit = defineEmits([
|
||||
'get-album',
|
||||
'get-artist',
|
||||
'lazy',
|
||||
'play-urls',
|
||||
'add-song',
|
||||
]);
|
||||
|
||||
const data = reactive({
|
||||
notes: null,
|
||||
albums: null,
|
||||
albumTitle: null,
|
||||
songs: null,
|
||||
recommendedArtists: null,
|
||||
});
|
||||
|
||||
function Reset() {
|
||||
for (let i in data) {
|
||||
data[i] = null
|
||||
}
|
||||
}
|
||||
|
||||
function playAlbum() {
|
||||
const urls = data.songs.items.map((item) => {
|
||||
return { url: item.url, title: item.title };
|
||||
});
|
||||
|
||||
emit('play-urls', urls);
|
||||
}
|
||||
|
||||
function getSearch(q) {
|
||||
if (q) {
|
||||
const pq = q.split(' ').join('+');
|
||||
|
||||
history.pushState({}, '', `/search/${pq + getPipedQuery()}`);
|
||||
|
||||
document.title = 'Search Results for ' + q;
|
||||
|
||||
getResults(pq);
|
||||
emit('lazy');
|
||||
} else {
|
||||
Reset();
|
||||
|
||||
history.pushState({}, '', '/');
|
||||
document.title = 'Hyperpipe';
|
||||
|
||||
console.log('No Search');
|
||||
}
|
||||
}
|
||||
|
||||
async function getResults(q) {
|
||||
const filters = ['music_songs', 'music_albums'];
|
||||
|
||||
for (let filter of filters) {
|
||||
|
||||
const json = await getJsonPiped(`/search?q=${q}&filter=${filter}`);
|
||||
|
||||
data[filter.split('_')[1]] = json;
|
||||
console.log(json, data);
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.search,
|
||||
(n) => {
|
||||
if (n) {
|
||||
n = n.replace(location.search || '', '');
|
||||
|
||||
console.log(n);
|
||||
getSearch(n);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.songItems,
|
||||
(i) => {
|
||||
console.log(i);
|
||||
|
||||
Reset();
|
||||
|
||||
data.songs = {}
|
||||
data.songs.items = i.items;
|
||||
data.albumTitle = i.title;
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.items,
|
||||
(itms) => {
|
||||
Reset();
|
||||
|
||||
for (let i in itms) {
|
||||
|
||||
data[i] = {}
|
||||
data[i].items = itms[i];
|
||||
|
||||
console.log(data[i]);
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="songs && songs.corrected" class="text-full">
|
||||
I Fixed your Typo, "<span class="caps">{{ songs.suggestion }}</span
|
||||
<div v-if="data.songs && data.songs.corrected" class="text-full">
|
||||
I Fixed your Typo, "<span class="caps">{{ data.songs.suggestion }}</span
|
||||
>"!!
|
||||
</div>
|
||||
|
||||
<div v-if="albumTitle" class="text-full flex">
|
||||
<div v-if="data.albumTitle" class="text-full flex">
|
||||
<PlayBtn @click="playAlbum" />
|
||||
<span>{{ albumTitle }}</span>
|
||||
<span>{{ data.albumTitle }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="songs && songs.items[0]" class="search-songs">
|
||||
<div v-if="data.songs && data.songs.items[0]" class="search-songs">
|
||||
<h2>Top Songs</h2>
|
||||
<div class="grid">
|
||||
<template v-for="song in songs.items">
|
||||
<template v-for="song in data.songs.items">
|
||||
<SongItem
|
||||
:author="song.uploaderName || ''"
|
||||
:title="song.title || song.name"
|
||||
:channel="song.uploaderUrl || ''"
|
||||
:play="song.url || '/watch?v=' + song.videoId"
|
||||
:play="song.url || '/watch?v=' + song.id"
|
||||
@open-song="
|
||||
$emit('get-song', {
|
||||
$emit('play-urls', [{
|
||||
url: song.url || '/watch?v=' + song.id,
|
||||
title: song.title || song.name,
|
||||
})
|
||||
}])
|
||||
"
|
||||
@get-artist="
|
||||
(e) => {
|
||||
|
@ -56,17 +153,18 @@ defineEmits([
|
|||
</template>
|
||||
</div>
|
||||
<a
|
||||
v-if="notes"
|
||||
@click.prevent="$emit('get-album', '/playlist?list=' + notes.items)"
|
||||
v-if="data.notes"
|
||||
@click.prevent="$emit('get-album', '/playlist?list=' + data.notes.items)"
|
||||
class="more"
|
||||
:href="'/playlist?list=' + data.notes.items"
|
||||
>See All</a
|
||||
>
|
||||
</div>
|
||||
|
||||
<div v-if="albums && albums.items[0]" class="search-albums">
|
||||
<div v-if="data.albums && data.albums.items[0]" class="search-albums">
|
||||
<h2>Albums</h2>
|
||||
<div class="grid-3">
|
||||
<template v-for="album in albums.items">
|
||||
<template v-for="album in data.albums.items">
|
||||
<AlbumItem
|
||||
:author="album.uploaderName || album.subtitle"
|
||||
:name="album.name || album.title"
|
||||
|
@ -81,11 +179,11 @@ defineEmits([
|
|||
</div>
|
||||
|
||||
<div
|
||||
v-if="recommendedArtists && recommendedArtists.items[0]"
|
||||
v-if="data.recommendedArtists && data.recommendedArtists.items[0]"
|
||||
class="search-artists">
|
||||
<h2>Similar Artists</h2>
|
||||
<div class="grid-3">
|
||||
<template v-for="artist in recommendedArtists.items">
|
||||
<template v-for="artist in data.recommendedArtists.items">
|
||||
<AlbumItem
|
||||
:author="artist.subtitle"
|
||||
:name="artist.title"
|
||||
|
@ -96,99 +194,6 @@ defineEmits([
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PlayBtn from './PlayBtn.vue';
|
||||
import SongItem from './SongItem.vue';
|
||||
import AlbumItem from './AlbumItem.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PlayBtn,
|
||||
SongItem,
|
||||
AlbumItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
songs: null,
|
||||
albums: null,
|
||||
recommendedArtists: null,
|
||||
albumTitle: null,
|
||||
notes: null,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
search(NewSearch) {
|
||||
console.log(NewSearch);
|
||||
this.getSearch(NewSearch);
|
||||
},
|
||||
songItems(i) {
|
||||
console.log(i);
|
||||
|
||||
this.Reset();
|
||||
|
||||
this.songs = {};
|
||||
this.songs.items = i.items;
|
||||
this.albumTitle = i.title;
|
||||
},
|
||||
items(itms) {
|
||||
this.Reset();
|
||||
|
||||
for (let i in itms) {
|
||||
this[i] = {};
|
||||
this[i].items = itms[i];
|
||||
|
||||
console.log(this[i]);
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
Reset() {
|
||||
this.notes = null;
|
||||
this.albums = null;
|
||||
this.albumTitle = null;
|
||||
this.songs = null;
|
||||
this.recommendedArtists = null;
|
||||
},
|
||||
playAlbum() {
|
||||
const urls = this.songs.items.map((item) => {
|
||||
return { url: item.url, title: item.title };
|
||||
});
|
||||
|
||||
this.$emit('play-urls', urls);
|
||||
},
|
||||
getSearch(q) {
|
||||
if (q) {
|
||||
history.pushState({}, '', `/search/${q}`);
|
||||
|
||||
document.title = 'Search Results for ' + q;
|
||||
|
||||
this.getResults(q.split(' ').join('+'));
|
||||
this.$emit('lazy');
|
||||
} else {
|
||||
this.Reset();
|
||||
history.pushState({}, '', '/');
|
||||
document.title = 'Hyperpipe';
|
||||
|
||||
console.log('No Search');
|
||||
}
|
||||
},
|
||||
getResults(q) {
|
||||
const filters = ['music_songs', 'music_albums'];
|
||||
|
||||
for (let filter of filters) {
|
||||
fetch(
|
||||
`https://pipedapi.kavin.rocks/search?q=${q}&filter=${filter}`,
|
||||
).then((res) => {
|
||||
res.json().then((json) => {
|
||||
this[filter.split('_')[1]] = json;
|
||||
console.log(json);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.search-albums,
|
||||
.search-songs,
|
||||
|
@ -196,11 +201,23 @@ export default {
|
|||
place-items: start center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.search-songs h2,
|
||||
.search-artists h2,
|
||||
.search-albums h2 {
|
||||
text-align: center;
|
||||
}
|
||||
.search-albums .grid-3,
|
||||
.search-artists .grid-3 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.search-artists {
|
||||
text-align: center;
|
||||
}
|
||||
.search-artists .bg-img {
|
||||
border-radius: 50%;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.text-full {
|
||||
padding: 1rem;
|
||||
font-size: 1.5rem;
|
||||
|
|
|
@ -1,11 +1,43 @@
|
|||
<script setup>
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
author: String,
|
||||
title: String,
|
||||
channel: String,
|
||||
play: String,
|
||||
});
|
||||
defineEmits(['get-artist']);
|
||||
|
||||
const emit = defineEmits(['get-artist', 'open-song']);
|
||||
|
||||
function openSong(el) {
|
||||
if (!el.classList.contains('ign')) {
|
||||
emit('open-song', props.play);
|
||||
}
|
||||
}
|
||||
|
||||
async function Share() {
|
||||
if ('share' in navigator) {
|
||||
const data = {
|
||||
title: `Listen to ${props.title} by ${props.author} on Hyperpipe`,
|
||||
url: location.origin + props.play,
|
||||
};
|
||||
|
||||
try {
|
||||
await navigator.share(data);
|
||||
console.log('Done Sharing!');
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
} else {
|
||||
navigator.clipboard.writeText(location.host + props.play).then(
|
||||
() => {
|
||||
console.log('Copied to Clipboard');
|
||||
},
|
||||
(err) => {
|
||||
console.log(err);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="song card flex pop" @click="openSong($event.target)">
|
||||
|
@ -35,42 +67,6 @@ defineEmits(['get-artist']);
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
openSong(el) {
|
||||
if (!el.classList.contains('ign')) {
|
||||
this.$emit('open-song', this.play);
|
||||
}
|
||||
},
|
||||
async Share() {
|
||||
if ('share' in navigator) {
|
||||
const data = {
|
||||
title: `Listen to ${this.title} by ${this.author} on Hyperpipe`,
|
||||
url: location.origin + this.play,
|
||||
};
|
||||
|
||||
try {
|
||||
await navigator.share(data);
|
||||
console.log('Done Sharing!');
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
} else {
|
||||
navigator.clipboard.writeText(location.host + this.play).then(
|
||||
() => {
|
||||
console.log('Copied to Clipboard');
|
||||
},
|
||||
(err) => {
|
||||
console.log(err);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card {
|
||||
margin: 1rem 0;
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
<script setup>
|
||||
const props = defineProps({
|
||||
defineProps({
|
||||
state: String,
|
||||
time: Number,
|
||||
show: Boolean,
|
||||
loop: Boolean,
|
||||
});
|
||||
defineEmits(['vol', 'play', 'list', 'loop']);
|
||||
|
||||
console.log(props);
|
||||
defineEmits(['vol', 'play', 'list', 'loop', 'change-time']);
|
||||
</script>
|
||||
<template>
|
||||
<div id="statusbar" class="flex">
|
||||
|
@ -19,9 +17,14 @@ console.log(props);
|
|||
|
||||
<div
|
||||
id="statusbar-progress"
|
||||
class="progress"
|
||||
:style="'--tw: ' + time + '%;'">
|
||||
<div class="progress-thumb"></div>
|
||||
class="range-wrap"
|
||||
:style="'--fw:' + time + '%;'">
|
||||
<input
|
||||
:value="time"
|
||||
type="range"
|
||||
name="statusbar-progress"
|
||||
max="100"
|
||||
@input="$emit('change-time', $event.target.value)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -77,38 +80,6 @@ console.log(props);
|
|||
.bi-infinity {
|
||||
font-size: 1.75rem !important;
|
||||
}
|
||||
.progress {
|
||||
--h: 0.25rem;
|
||||
--w: 5rem;
|
||||
--th: 100%;
|
||||
--tw: 75%;
|
||||
background: var(--color-border);
|
||||
height: var(--h);
|
||||
width: var(--w);
|
||||
transition: width 0.4s ease;
|
||||
}
|
||||
.progress.v {
|
||||
--th: 75%;
|
||||
--tw: 100%;
|
||||
--h: 5rem;
|
||||
--w: 0.25rem;
|
||||
transform: rotateZ(180deg);
|
||||
z-index: 999999;
|
||||
}
|
||||
.progress-thumb {
|
||||
width: var(--tw);
|
||||
height: var(--th);
|
||||
background: var(--color-foreground);
|
||||
overflow: hidden;
|
||||
transition: width 0.4s ease;
|
||||
}
|
||||
.progress-thumb:after {
|
||||
content: '';
|
||||
background-color: #fff;
|
||||
height: 0.5rem;
|
||||
width: 0.5rem;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.popup {
|
||||
--h: 6.5rem;
|
||||
--w: 1rem;
|
||||
|
@ -125,6 +96,7 @@ input[type='range'] {
|
|||
background: transparent;
|
||||
color: transparent;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
input[type='range']:focus {
|
||||
outline: none;
|
||||
|
@ -132,16 +104,49 @@ input[type='range']:focus {
|
|||
input[type='range']::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s ease;
|
||||
}
|
||||
input[type='range']:hover::-webkit-slider-thumb,
|
||||
#vol input[type='range']::-webkit-slider-thumb {
|
||||
background-color: var(--color-foreground);
|
||||
opacity: 1;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
border-radius: 50%;
|
||||
margin-top: -0.45rem;
|
||||
}
|
||||
input[type='range']::-webkit-slider-runnable-track {
|
||||
height: 0.1rem;
|
||||
height: 100%;
|
||||
background-color: var(--color-border);
|
||||
}
|
||||
#vol input[type='range']::-webkit-slider-runnable-track {
|
||||
height: 0.1rem;
|
||||
}
|
||||
.range-wrap {
|
||||
--fw: 0%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
height: 0.25rem;
|
||||
display: inline-block;
|
||||
}
|
||||
.range-wrap:before {
|
||||
content: '';
|
||||
width: var(--fw);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
height: 0.25rem;
|
||||
background-color: var(--color-foreground);
|
||||
transition: width 0.4s ease;
|
||||
}
|
||||
.range-wrap input[type='range'] {
|
||||
--w: 100%;
|
||||
--h: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
#statusbar-progress {
|
||||
width: 50vw;
|
||||
min-width: 200px;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue