mirror of
https://codeberg.org/Hyperpipe/Hyperpipe
synced 2025-06-27 12:48:01 +02:00
Switched to Pinia, Added Save for Albums
This commit is contained in:
parent
a43b0907e2
commit
f303f91108
25 changed files with 945 additions and 799 deletions
|
@ -75,6 +75,7 @@ You can reach out to me personally on:
|
||||||
- PeerJS -> [MIT][peer]
|
- PeerJS -> [MIT][peer]
|
||||||
- Bootstrap Icons -> [MIT][bi]
|
- Bootstrap Icons -> [MIT][bi]
|
||||||
- VueJS theme -> [MIT][vuetheme]
|
- VueJS theme -> [MIT][vuetheme]
|
||||||
|
- Dracula theme -> [MIT][dracula]
|
||||||
- Nord theme -> [MIT][nord]
|
- Nord theme -> [MIT][nord]
|
||||||
|
|
||||||
### Similar Projects
|
### Similar Projects
|
||||||
|
@ -95,3 +96,4 @@ You can reach out to me personally on:
|
||||||
[hls]: https://github.com/video-dev/hls.js/blob/master/LICENSE
|
[hls]: https://github.com/video-dev/hls.js/blob/master/LICENSE
|
||||||
[nord]: https://github.com/arcticicestudio/nord/blob/develop/LICENSE.md
|
[nord]: https://github.com/arcticicestudio/nord/blob/develop/LICENSE.md
|
||||||
[vuetheme]: https://github.com/vuejs/theme/blob/main/LICENSE
|
[vuetheme]: https://github.com/vuejs/theme/blob/main/LICENSE
|
||||||
|
[dracula]: https://github.com/dracula/dracula-theme/blob/master/LICENSE
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.svg" />
|
||||||
<link rel="preconnect" href="https://pipedapi.kavin.rocks" />
|
<link rel="preconnect" href="https://pipedapi.kavin.rocks" />
|
||||||
<link rel="preconnect" href="https://cdn.jsdelivr.net" />
|
<link rel="preconnect" href="https://cdn.jsdelivr.net" />
|
||||||
<link rel="preconnect" href="https://hyperpipeapi.onrender.com" />
|
<link rel="preconnect" href="https://hyperpipeapi.onrender.com" />
|
||||||
|
|
782
package-lock.json
generated
782
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -11,6 +11,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hls.js": "^1.1.5",
|
"hls.js": "^1.1.5",
|
||||||
|
"pinia": "^2.0.16",
|
||||||
"vue": "^3.2.31"
|
"vue": "^3.2.31"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
5
public/favicon.svg
Normal file
5
public/favicon.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#ebebeba3" class="bi bi-vinyl" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
|
||||||
|
<path d="M8 6a2 2 0 1 0 0 4 2 2 0 0 0 0-4zM4 8a4 4 0 1 1 8 0 4 4 0 0 1-8 0z"/>
|
||||||
|
<path d="M9 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 340 B |
|
@ -3,6 +3,12 @@
|
||||||
"short_name": "Hyperpipe",
|
"short_name": "Hyperpipe",
|
||||||
"start_url": "/",
|
"start_url": "/",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#fff",
|
"background_color": "#000",
|
||||||
"description": "Privacy respecting YouTube Music Frontend."
|
"description": "Privacy respecting YouTube Music Frontend.",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/favicon.svg",
|
||||||
|
"sizes": "any"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
386
src/App.vue
386
src/App.vue
|
@ -1,60 +1,39 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
/* Imports */
|
/* Imports */
|
||||||
import Hls from 'hls.js';
|
import Hls from 'hls.js';
|
||||||
import { ref, reactive, onBeforeMount, onMounted } from 'vue';
|
import { ref, watch, reactive, onBeforeMount, onMounted } from 'vue';
|
||||||
|
|
||||||
/* Components */
|
/* Components */
|
||||||
import NavBar from './components/NavBar.vue';
|
import NavBar from '@/components/NavBar.vue';
|
||||||
import StatusBar from './components/StatusBar.vue';
|
import StatusBar from '@/components/StatusBar.vue';
|
||||||
import NowPlaying from './components/NowPlaying.vue';
|
import NowPlaying from '@/components/NowPlaying.vue';
|
||||||
import Genres from './components/Genres.vue';
|
import Genres from '@/components/Genres.vue';
|
||||||
import Search from './components/Search.vue';
|
import Search from '@/components/Search.vue';
|
||||||
import NewPlaylist from './components/NewPlaylist.vue';
|
import NewPlaylist from '@/components/NewPlaylist.vue';
|
||||||
import Playlists from './components/Playlists.vue';
|
import Playlists from '@/components/Playlists.vue';
|
||||||
import Lyrics from './components/Lyrics.vue';
|
import Lyrics from '@/components/Lyrics.vue';
|
||||||
import Info from './components/Info.vue';
|
import Info from '@/components/Info.vue';
|
||||||
import Artist from './components/Artist.vue';
|
import Artist from '@/components/Artist.vue';
|
||||||
import Prefs from './components/Prefs.vue';
|
import Prefs from '@/components/Prefs.vue';
|
||||||
|
|
||||||
/* Composables */
|
/* Composables */
|
||||||
import { getJson, getJsonPiped } from './scripts/fetch.js';
|
import { getJson, getJsonPiped } from '@/scripts/fetch.js';
|
||||||
import { useLazyLoad, useStore } from './scripts/util.js';
|
import { useLazyLoad, useStore, useRoute } from '@/scripts/util.js';
|
||||||
import { useSetupDB, useUpdatePlaylist } from './scripts/db.js';
|
import { useSetupDB, useUpdatePlaylist } from '@/scripts/db.js';
|
||||||
|
|
||||||
/* Reactivity */
|
/* Stores */
|
||||||
const data = reactive({
|
import { useData, usePlayer } from '@/stores/player.js';
|
||||||
artUrl: '',
|
import { useResults, useArtist } from '@/stores/results.js';
|
||||||
cover: '',
|
import { useNav } from '@/stores/misc.js';
|
||||||
audioSrc: [],
|
|
||||||
url: '',
|
|
||||||
urls: [],
|
|
||||||
songItems: null,
|
|
||||||
items: {},
|
|
||||||
title: '',
|
|
||||||
description: '',
|
|
||||||
artist: '',
|
|
||||||
artistUrl: '',
|
|
||||||
state: 'play',
|
|
||||||
duration: 0,
|
|
||||||
time: 0,
|
|
||||||
showplaylist: false,
|
|
||||||
showlyrics: false,
|
|
||||||
showinfo: false,
|
|
||||||
loop: false,
|
|
||||||
lyrics: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const artist = reactive({
|
const store = useStore(),
|
||||||
playlistId: null,
|
data = useData(),
|
||||||
title: null,
|
player = usePlayer(),
|
||||||
description: null,
|
results = useResults(),
|
||||||
subscriberCount: 0,
|
artist = useArtist(),
|
||||||
thumbnails: [],
|
nav = useNav();
|
||||||
});
|
|
||||||
|
|
||||||
const search = ref(''),
|
const genreid = ref(''),
|
||||||
genreid = ref(''),
|
|
||||||
page = ref('home'),
|
|
||||||
path = ref(location.pathname);
|
path = ref(location.pathname);
|
||||||
|
|
||||||
const audio = ref(null);
|
const audio = ref(null);
|
||||||
|
@ -73,8 +52,8 @@ function parseUrl() {
|
||||||
getExplore();
|
getExplore();
|
||||||
break;
|
break;
|
||||||
case 'search':
|
case 'search':
|
||||||
search.value = loc[2];
|
nav.state.search = loc[2];
|
||||||
console.log(search.value);
|
console.log(nav.state.search);
|
||||||
break;
|
break;
|
||||||
case 'watch':
|
case 'watch':
|
||||||
getSong(loc[1] + location.search);
|
getSong(loc[1] + location.search);
|
||||||
|
@ -85,56 +64,44 @@ function parseUrl() {
|
||||||
console.log(loc[1]);
|
console.log(loc[1]);
|
||||||
break;
|
break;
|
||||||
case 'channel':
|
case 'channel':
|
||||||
getArtist(loc[1]);
|
getArtist(loc[2]);
|
||||||
console.log(loc[1]);
|
console.log(loc[2]);
|
||||||
break;
|
break;
|
||||||
case 'explore':
|
case 'explore':
|
||||||
genreid.value = loc[2];
|
genreid.value = loc[2];
|
||||||
page.value = 'genres';
|
nav.state.page = 'genres';
|
||||||
default:
|
default:
|
||||||
console.log(loc);
|
console.log(loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Toggle(e) {
|
|
||||||
console.log(e, data[e]);
|
|
||||||
data[e] = !data[e];
|
|
||||||
}
|
|
||||||
|
|
||||||
function timeUpdate(t) {
|
|
||||||
data.time = Math.floor((t / data.duration) * 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTime(t) {
|
function setTime(t) {
|
||||||
audio.value.currentTime = (t / 100) * data.duration;
|
audio.value.currentTime = (t / 100) * player.state.duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addSong(s) {
|
function addSong(s) {
|
||||||
data.urls.push(s);
|
data.state.urls.push(s);
|
||||||
|
|
||||||
const index = data.urls.map(s => s.url).indexOf(data.url);
|
const index = data.state.urls.map(s => s.url).indexOf(data.state.url);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(index == data.urls.length - 1 && data.time > 98) ||
|
(index == data.state.urls.length - 1 && player.state.time > 98) ||
|
||||||
data.urls.length == 1
|
data.state.urls.length == 1
|
||||||
) {
|
) {
|
||||||
console.log(true);
|
|
||||||
playNext();
|
playNext();
|
||||||
} else {
|
|
||||||
console.log(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(s, data.urls);
|
console.log(s, data.state.urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
function playThis(t) {
|
function playThis(t) {
|
||||||
const i = data.urls.indexOf(t);
|
const i = data.state.urls.indexOf(t);
|
||||||
getSong(data.urls[i].url);
|
getSong(data.state.urls[i].url);
|
||||||
}
|
}
|
||||||
|
|
||||||
function playList(a) {
|
function playList(a) {
|
||||||
data.urls = a;
|
data.state.urls = a;
|
||||||
getSong(data.urls[0].url);
|
getSong(data.state.urls[0].url);
|
||||||
}
|
}
|
||||||
|
|
||||||
function playNext(u) {
|
function playNext(u) {
|
||||||
|
@ -144,21 +111,21 @@ function playNext(u) {
|
||||||
|
|
||||||
audio.value.src = '';
|
audio.value.src = '';
|
||||||
|
|
||||||
const now = data.urls.filter(s => s.url === data.url)[0],
|
const now = data.state.urls.filter(s => s.url === data.state.url)[0],
|
||||||
i = data.urls.indexOf(now),
|
i = data.state.urls.indexOf(now),
|
||||||
next = data.urls[i + 1];
|
next = data.state.urls[i + 1];
|
||||||
|
|
||||||
console.log('Index: ' + i);
|
console.log('Index: ' + i);
|
||||||
console.log(data.url, data.urls, next);
|
console.log(data.state.url, data.state.urls, next);
|
||||||
|
|
||||||
if (data.urls.length > i && data.urls.length != 0 && next) {
|
if (data.state.urls.length > i && data.state.urls.length != 0 && next) {
|
||||||
getSong(next.url);
|
getSong(next.url);
|
||||||
} else if (data.loop) {
|
} else if (player.state.loop) {
|
||||||
console.log(data.url, data.urls[0]);
|
console.log(data.state.url, data.state.urls[0]);
|
||||||
data.url = data.urls[0].url;
|
data.state.url = data.state.urls[0].url;
|
||||||
getSong(data.urls[0].url);
|
getSong(data.state.urls[0].url);
|
||||||
} else {
|
} else {
|
||||||
data.urls = [];
|
data.state.urls = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,9 +134,9 @@ async function getExplore() {
|
||||||
|
|
||||||
console.log(json);
|
console.log(json);
|
||||||
|
|
||||||
data.items = {};
|
results.items.value = {};
|
||||||
|
|
||||||
data.items = {
|
results.items.value = {
|
||||||
songs: json.trending,
|
songs: json.trending,
|
||||||
albums: json.albums_and_singles,
|
albums: json.albums_and_singles,
|
||||||
};
|
};
|
||||||
|
@ -183,14 +150,13 @@ async function getSong(e) {
|
||||||
|
|
||||||
console.log(json);
|
console.log(json);
|
||||||
|
|
||||||
data.artUrl = json.thumbnailUrl;
|
data.state.art = json.thumbnailUrl;
|
||||||
data.description = json.description;
|
data.state.description = json.description;
|
||||||
data.cover = `--art: url(${json.thumbnailUrl});`;
|
data.state.title = json.title;
|
||||||
data.nowtitle = json.title;
|
data.state.artist = json.uploader.replace(' - Topic', '');
|
||||||
data.nowartist = json.uploader.split(' - ')[0];
|
data.state.artistUrl = json.uploaderUrl;
|
||||||
data.artistUrl = json.uploaderUrl;
|
player.state.duration = json.duration;
|
||||||
data.duration = json.duration;
|
data.state.url = e;
|
||||||
data.url = e;
|
|
||||||
|
|
||||||
await getNext(hash);
|
await getNext(hash);
|
||||||
|
|
||||||
|
@ -208,16 +174,15 @@ async function getAlbum(e) {
|
||||||
|
|
||||||
console.log(json, json.relatedStreams);
|
console.log(json, json.relatedStreams);
|
||||||
|
|
||||||
data.songItems = {
|
results.resetItems();
|
||||||
|
results.setItem('songs', {
|
||||||
items: json.relatedStreams,
|
items: json.relatedStreams,
|
||||||
title: json.name,
|
title: json.name,
|
||||||
};
|
});
|
||||||
|
|
||||||
history.pushState({}, '', e);
|
useRoute(e);
|
||||||
|
|
||||||
for (let i in artist) {
|
artist.reset();
|
||||||
artist[i] = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getArtist(e) {
|
async function getArtist(e) {
|
||||||
|
@ -229,36 +194,40 @@ async function getArtist(e) {
|
||||||
|
|
||||||
console.log(json);
|
console.log(json);
|
||||||
|
|
||||||
data.items = json.items;
|
for (let i in json.items) {
|
||||||
data.items.notes = json.playlistId;
|
results.setItem(i, { items: json.items[i] });
|
||||||
json.items = null;
|
|
||||||
|
|
||||||
for (let i in json) {
|
|
||||||
artist[i] = json[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
history.pushState({}, '', '/channel/' + e);
|
console.log(results.items);
|
||||||
|
|
||||||
|
json.items = undefined;
|
||||||
|
|
||||||
|
artist.reset();
|
||||||
|
artist.set(json);
|
||||||
|
|
||||||
|
useRoute('/channel/' + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getNext(hash) {
|
async function getNext(hash) {
|
||||||
if (
|
if (
|
||||||
useStore().getItem('next') !== 'false' &&
|
store.getItem('next') !== 'false' &&
|
||||||
(!data.urls ||
|
(!data.state.urls ||
|
||||||
!data.urls.filter(s => s.url == data.url)[0] ||
|
!data.state.urls.filter(s => s.url == data.state.url)[0] ||
|
||||||
data.urls.length == 1)
|
data.state.urls.length == 1)
|
||||||
) {
|
) {
|
||||||
const json = await getJson(
|
const json = await getJson(
|
||||||
'https://hyperpipeapi.onrender.com/next/' + hash,
|
'https://hyperpipeapi.onrender.com/next/' + hash,
|
||||||
);
|
);
|
||||||
|
|
||||||
data.lyrics = json.lyricsId;
|
data.state.lyrics = json.lyricsId;
|
||||||
|
|
||||||
data.url = json.songs[0]
|
data.state.url = json.songs[0]
|
||||||
? '/watch?v=' + json.songs[0].id
|
? '/watch?v=' + json.songs[0].id
|
||||||
: '/watch?v=' + hash;
|
: '/watch?v=' + hash;
|
||||||
|
|
||||||
console.log(json);
|
console.log(json);
|
||||||
|
|
||||||
data.urls =
|
data.state.urls =
|
||||||
json.songs.length > 0
|
json.songs.length > 0
|
||||||
? json.songs.map(i => ({
|
? json.songs.map(i => ({
|
||||||
...i,
|
...i,
|
||||||
|
@ -267,43 +236,29 @@ async function getNext(hash) {
|
||||||
id: undefined,
|
id: undefined,
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
: data.urls;
|
: data.state.urls;
|
||||||
|
|
||||||
setMetadata();
|
setMetadata();
|
||||||
|
|
||||||
console.log(data.urls);
|
console.log(data.state.urls);
|
||||||
} else {
|
} else {
|
||||||
setMetadata();
|
setMetadata();
|
||||||
|
|
||||||
if (data.urls.length == 0) {
|
if (data.state.urls.length == 0) {
|
||||||
data.urls = [
|
data.state.urls = [
|
||||||
{
|
{
|
||||||
title: nowtitle,
|
title: nowtitle,
|
||||||
url: data.url,
|
url: data.state.url,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setVolume(vol) {
|
|
||||||
audio.value.volume = vol;
|
|
||||||
}
|
|
||||||
|
|
||||||
function playPause() {
|
|
||||||
if (audio.value.paused) {
|
|
||||||
audio.value.play();
|
|
||||||
data.state = 'pause';
|
|
||||||
} else {
|
|
||||||
audio.value.pause();
|
|
||||||
data.state = 'play';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Stream(res) {
|
function Stream(res) {
|
||||||
console.log(res);
|
console.log(res);
|
||||||
|
|
||||||
if (Hls.isSupported() && useStore().hls !== 'false') {
|
if (Hls.isSupported() && store.hls !== 'false') {
|
||||||
window.hls = new Hls();
|
window.hls = new Hls();
|
||||||
|
|
||||||
window.hls.attachMedia(audio.value);
|
window.hls.attachMedia(audio.value);
|
||||||
|
@ -312,7 +267,7 @@ function Stream(res) {
|
||||||
window.hls.loadSource(res.hls);
|
window.hls.loadSource(res.hls);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
data.audioSrc = res.stream;
|
data.state.src = res.stream;
|
||||||
audio.value.load();
|
audio.value.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,24 +275,23 @@ function Stream(res) {
|
||||||
function audioCanPlay() {
|
function audioCanPlay() {
|
||||||
useLazyLoad();
|
useLazyLoad();
|
||||||
|
|
||||||
audio.value.play().catch(err => {
|
if (audio.value.paused) {
|
||||||
alert(err);
|
player.toggle('play');
|
||||||
});
|
|
||||||
data.state = 'pause';
|
|
||||||
|
|
||||||
if (location.pathname != '/playlist') {
|
|
||||||
history.pushState({}, '', data.url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.title = `Playing: ${data.nowtitle} by ${data.nowartist}`;
|
if (location.pathname != '/playlist') {
|
||||||
|
useRoute(data.state.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.title = `Playing: ${data.state.title} by ${data.state.artist}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SaveTrack(e) {
|
function SaveTrack(e) {
|
||||||
useUpdatePlaylist(
|
useUpdatePlaylist(
|
||||||
e,
|
e,
|
||||||
{
|
{
|
||||||
url: data.url,
|
url: data.state.url,
|
||||||
title: data.nowtitle,
|
title: data.state.title,
|
||||||
},
|
},
|
||||||
e => {
|
e => {
|
||||||
if (e === true) {
|
if (e === true) {
|
||||||
|
@ -349,7 +303,7 @@ function SaveTrack(e) {
|
||||||
|
|
||||||
function setMetadata() {
|
function setMetadata() {
|
||||||
if ('mediaSession' in navigator) {
|
if ('mediaSession' in navigator) {
|
||||||
const now = data.urls.filter(u => u.url === data.url)[0];
|
const now = data.state.urls.filter(u => u.url === data.state.url)[0];
|
||||||
|
|
||||||
let artwork = [],
|
let artwork = [],
|
||||||
album = undefined;
|
album = undefined;
|
||||||
|
@ -366,24 +320,44 @@ function setMetadata() {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
artwork = [{ src: data.artUrl, type: 'image/webp' }];
|
artwork = [{ src: data.state.art, type: 'image/webp' }];
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(album, artwork);
|
console.log(album, artwork);
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.mediaSession.metadata = new MediaMetadata({
|
navigator.mediaSession.metadata = new MediaMetadata({
|
||||||
title: data.nowtitle,
|
title: data.state.title,
|
||||||
artist: data.nowartist,
|
artist: data.state.artist,
|
||||||
album: album,
|
album: album,
|
||||||
artwork: artwork,
|
artwork: artwork,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => player.state.play,
|
||||||
|
() => {
|
||||||
|
if (audio.value.paused) {
|
||||||
|
audio.value
|
||||||
|
.play()
|
||||||
|
.then(() => {
|
||||||
|
player.state.state = 'pause';
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
alert(err);
|
||||||
|
player.state.state = 'play';
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
player.state.state = 'play';
|
||||||
|
audio.value.pause();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
if (useStore().theme) {
|
if (store.theme) {
|
||||||
document.body.setAttribute('data-theme', useStore().theme);
|
document.body.setAttribute('data-theme', store.theme);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -404,25 +378,31 @@ onMounted(() => {
|
||||||
|
|
||||||
/* Alert User on close if url is present */
|
/* Alert User on close if url is present */
|
||||||
window.onbeforeunload = () => {
|
window.onbeforeunload = () => {
|
||||||
if (data.url) {
|
if (data.state.url) {
|
||||||
return 'Are you Sure?';
|
return 'Are you Sure?';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Media Session Controls */
|
/* Media Session Controls */
|
||||||
if ('mediaSession' in navigator) {
|
if ('mediaSession' in navigator) {
|
||||||
navigator.mediaSession.setActionHandler('play', playPause);
|
navigator.mediaSession.setActionHandler('play', () => {
|
||||||
navigator.mediaSession.setActionHandler('pause', playPause);
|
player.state.state = 'play';
|
||||||
|
});
|
||||||
|
|
||||||
|
navigator.mediaSession.setActionHandler('pause', () => {
|
||||||
|
player.state.state = 'pause';
|
||||||
|
});
|
||||||
|
|
||||||
navigator.mediaSession.setActionHandler('previoustrack', () => {
|
navigator.mediaSession.setActionHandler('previoustrack', () => {
|
||||||
if (data.urls.length > 2) {
|
if (data.state.urls.length > 2) {
|
||||||
const i = data.urls.map(s => s.url).indexOf(data.url);
|
const i = data.state.urls.map(s => s.url).indexOf(data.state.url);
|
||||||
getSong(data.urls[i - 1].url);
|
getSong(data.state.urls[i - 1].url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
navigator.mediaSession.setActionHandler('nexttrack', () => {
|
navigator.mediaSession.setActionHandler('nexttrack', () => {
|
||||||
if (data.urls.length > 2) {
|
if (data.state.urls.length > 2) {
|
||||||
const i = data.urls.map(s => s.url).indexOf(data.url);
|
const i = data.state.urls.map(s => s.url).indexOf(data.state.url);
|
||||||
getSong(data.urls[i + 1].url);
|
getSong(data.state.urls[i + 1].url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -437,108 +417,66 @@ onMounted(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NavBar
|
<NavBar />
|
||||||
@update-search="
|
|
||||||
e => {
|
|
||||||
search = e;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
@update-page="
|
|
||||||
e => {
|
|
||||||
page = e;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
:search="search" />
|
|
||||||
|
|
||||||
<template v-if="artist && page == 'home'">
|
<template v-if="artist.state.title && nav.state.page == 'home'">
|
||||||
<Artist
|
<Artist @playall="getAlbum" />
|
||||||
@playall="getAlbum"
|
|
||||||
:title="artist.title"
|
|
||||||
:desc="artist.description"
|
|
||||||
:subs="artist.subscriberCount"
|
|
||||||
:thumbs="artist.thumbnails"
|
|
||||||
:play="artist.playlistId" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<header v-if="!artist.title">
|
<header v-if="!artist.state.title">
|
||||||
<div v-if="data.cover" class="art bg-img" :style="data.cover"></div>
|
<div v-show="data.state.art" class="art bg-img"></div>
|
||||||
|
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<NowPlaying
|
<NowPlaying @get-artist="getArtist" />
|
||||||
@get-artist="getArtist"
|
|
||||||
:title="data.nowtitle"
|
|
||||||
:artist="data.nowartist"
|
|
||||||
:artistUrl="data.artistUrl" />
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="placeholder">
|
<main class="placeholder">
|
||||||
<KeepAlive>
|
<KeepAlive>
|
||||||
<Search
|
<Search
|
||||||
v-if="page == 'home'"
|
v-if="nav.state.page == 'home'"
|
||||||
@get-album="getAlbum"
|
@get-album="getAlbum"
|
||||||
@get-artist="getArtist"
|
@get-artist="getArtist"
|
||||||
@play-urls="playList"
|
@play-urls="playList"
|
||||||
@add-song="addSong"
|
@add-song="addSong" />
|
||||||
:items="data.items"
|
|
||||||
:songItems="data.songItems"
|
|
||||||
:search="search" />
|
|
||||||
</KeepAlive>
|
</KeepAlive>
|
||||||
|
|
||||||
<KeepAlive>
|
<KeepAlive>
|
||||||
<Genres
|
<Genres
|
||||||
v-if="page == 'genres'"
|
v-if="nav.state.page == 'genres'"
|
||||||
:id="genreid"
|
:id="genreid"
|
||||||
@get-album="
|
@get-album="
|
||||||
e => {
|
e => {
|
||||||
getAlbum(e);
|
getAlbum(e);
|
||||||
page = 'home';
|
nav.state.page = 'home';
|
||||||
}
|
}
|
||||||
" />
|
" />
|
||||||
</KeepAlive>
|
</KeepAlive>
|
||||||
|
|
||||||
<NewPlaylist v-if="page == 'playlist'" @play-urls="playList" />
|
<NewPlaylist v-if="nav.state.page == 'playlist'" @play-urls="playList" />
|
||||||
|
|
||||||
<Prefs v-if="page == 'prefs'" />
|
<Prefs v-if="nav.state.page == 'prefs'" />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<Playlists
|
<Playlists v-if="player.state.playlist" @playthis="playThis" />
|
||||||
@playthis="playThis"
|
|
||||||
:url="data.url"
|
|
||||||
:urls="data.urls"
|
|
||||||
:show="data.showplaylist" />
|
|
||||||
|
|
||||||
<Lyrics
|
<Lyrics v-if="player.state.lyrics" />
|
||||||
v-if="data.showlyrics"
|
|
||||||
:id="data.lyrics"
|
|
||||||
:curl="data.url"
|
|
||||||
:iniurl="data.urls[0]?.url" />
|
|
||||||
|
|
||||||
<Info v-if="data.showinfo" :text="data.description" />
|
<Info v-if="player.state.info" :text="data.state.description" />
|
||||||
|
|
||||||
<StatusBar
|
<StatusBar @save="SaveTrack" @change-time="setTime" />
|
||||||
@play="playPause"
|
|
||||||
@vol="setVolume"
|
|
||||||
@toggle="Toggle"
|
|
||||||
@save="SaveTrack"
|
|
||||||
@change-time="setTime"
|
|
||||||
:state="data.state"
|
|
||||||
:time="data.time"
|
|
||||||
:show="data.showplaylist"
|
|
||||||
:lyrics="data.showlyrics"
|
|
||||||
:loop="data.loop" />
|
|
||||||
|
|
||||||
<audio
|
<audio
|
||||||
id="audio"
|
id="audio"
|
||||||
ref="audio"
|
ref="audio"
|
||||||
:volume="useStore().vol ? useStore().vol / 100 : 1"
|
:volume="player.state.vol"
|
||||||
@canplay="audioCanPlay"
|
@canplay="audioCanPlay"
|
||||||
@timeupdate="timeUpdate($event.target.currentTime)"
|
@timeupdate="player.setTime($event.target.currentTime)"
|
||||||
@ended="playNext"
|
@ended="playNext"
|
||||||
autoplay>
|
autoplay>
|
||||||
<source
|
<source
|
||||||
v-if="useStore().getItem('hls') != 'false'"
|
v-if="store.getItem('hls') != 'false'"
|
||||||
v-for="src in data.audioSrc"
|
v-for="src in data.state.audioSrc"
|
||||||
:key="src.url"
|
:key="src.url"
|
||||||
:src="src.url"
|
:src="src.url"
|
||||||
:type="src.mimeType" />
|
:type="src.mimeType" />
|
||||||
|
@ -573,7 +511,9 @@ header {
|
||||||
height: 175px;
|
height: 175px;
|
||||||
width: 175px;
|
width: 175px;
|
||||||
}
|
}
|
||||||
|
.bg-img {
|
||||||
|
--art: v-bind(`url(${data.state.art}) `);
|
||||||
|
}
|
||||||
img,
|
img,
|
||||||
.card,
|
.card,
|
||||||
.card-bg {
|
.card-bg {
|
||||||
|
|
|
@ -70,6 +70,22 @@ body[data-theme='nord'] {
|
||||||
--color-text: #d8dee9;
|
--color-text: #d8dee9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body[data-theme='dracula'] {
|
||||||
|
--color-foreground: #bd93f9;
|
||||||
|
--color-background: #282a36;
|
||||||
|
--color-background-soft: #44475a;
|
||||||
|
--color-background-mute: #44475a;
|
||||||
|
|
||||||
|
--color-border: #44475a;
|
||||||
|
--color-border-hover: #44475a;
|
||||||
|
|
||||||
|
--color-shadow: #6272a4;
|
||||||
|
--color-scrollbar: #bd93f9;
|
||||||
|
|
||||||
|
--color-heading: var(--vt-c-text-dark-1);
|
||||||
|
--color-text: var(--vt-c-text-dark-2);
|
||||||
|
}
|
||||||
|
|
||||||
*,
|
*,
|
||||||
*::before,
|
*::before,
|
||||||
*::after {
|
*::after {
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
import { ref, onUpdated } from 'vue';
|
import { ref, onUpdated } from 'vue';
|
||||||
import PlayBtn from './PlayBtn.vue';
|
import PlayBtn from './PlayBtn.vue';
|
||||||
|
|
||||||
defineProps(['title', 'desc', 'subs', 'thumbs', 'play']);
|
import { useArtist } from '@/stores/results.js';
|
||||||
|
|
||||||
|
const artist = useArtist();
|
||||||
|
|
||||||
defineEmits(['playall']);
|
defineEmits(['playall']);
|
||||||
|
|
||||||
const show = ref(-1);
|
const show = ref(-1);
|
||||||
|
@ -14,14 +17,21 @@ onUpdated(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="show == 0 && title" class="us-wrap">
|
<div v-if="show == 0 && artist.state.title" class="us-wrap">
|
||||||
<div class="bg-imgfill" :style="'--art: url(' + thumbs[1].url + ');'"></div>
|
<div
|
||||||
|
class="bg-imgfill"
|
||||||
|
:style="'--art: url(' + artist.state.thumbnails[1].url + ');'"></div>
|
||||||
<div class="us-main">
|
<div class="us-main">
|
||||||
<h2>{{ title }}</h2>
|
<h2>{{ artist.state.title }}</h2>
|
||||||
<p @click="$event.target.classList.toggle('more')">{{ desc }}</p>
|
<p @click="$event.target.classList.toggle('more')">
|
||||||
|
{{ artist.state.description }}
|
||||||
|
</p>
|
||||||
<div class="us-playwrap">
|
<div class="us-playwrap">
|
||||||
<PlayBtn @click="$emit('playall', '/playlist?list=' + play)" />
|
<PlayBtn
|
||||||
<span class="us-box subs">{{ subs || 0 }}</span>
|
@click="
|
||||||
|
$emit('playall', '/playlist?list=' + artist.state.playlistId)
|
||||||
|
" />
|
||||||
|
<span class="us-box subs">{{ artist.state.subscriberCount || 0 }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -132,9 +132,9 @@ onMounted(get);
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
.btn-grid {
|
.btn-grid {
|
||||||
grid-template-columns: calc(100% / 5) calc(100% / 5) calc(100% / 5) calc(
|
grid-template-columns:
|
||||||
100% / 5
|
calc(100% / 5) calc(100% / 5) calc(100% / 5) calc(100% / 5)
|
||||||
) calc(100% / 5);
|
calc(100% / 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -10,7 +10,18 @@ const parse = d =>
|
||||||
<template>
|
<template>
|
||||||
<TextModal>
|
<TextModal>
|
||||||
<template #content>
|
<template #content>
|
||||||
<pre>{{ parse(text.replaceAll('<br>', '\n')) }}</pre>
|
<pre class="placeholder">{{
|
||||||
|
text ? parse(text.replaceAll('<br>', '\n')) : ''
|
||||||
|
}}</pre>
|
||||||
</template>
|
</template>
|
||||||
</TextModal>
|
</TextModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.placeholder:empty::before {
|
||||||
|
--ico: '\F3B9';
|
||||||
|
}
|
||||||
|
.placeholder:empty::after {
|
||||||
|
--text: 'No Information Available...';
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,38 +1,31 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
import { getJson } from '../scripts/fetch.js';
|
import { getJson } from '@/scripts/fetch.js';
|
||||||
|
import { useData } from '@/stores/player.js';
|
||||||
|
|
||||||
import TextModal from './TextModal.vue';
|
import TextModal from './TextModal.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const data = useData(),
|
||||||
id: String,
|
|
||||||
curl: String,
|
|
||||||
iniurl: String,
|
|
||||||
}),
|
|
||||||
text = ref(''),
|
text = ref(''),
|
||||||
source = ref(''),
|
source = ref(''),
|
||||||
status = ref(false);
|
status = ref(false);
|
||||||
|
|
||||||
console.log(props);
|
|
||||||
|
|
||||||
function get() {
|
function get() {
|
||||||
status.value = false;
|
status.value = false;
|
||||||
|
|
||||||
if (props.id && props.curl === props.iniurl) {
|
if (data.state.lyrics && data.state.urls === data.state.urls[0]?.url) {
|
||||||
console.log(props.id);
|
getJson(
|
||||||
|
'https://hyperpipeapi.onrender.com/browse/' + data.state.lyrics,
|
||||||
getJson('https://hyperpipeapi.onrender.com/browse/' + props.id).then(
|
).then(res => {
|
||||||
res => {
|
text.value = res.text;
|
||||||
text.value = res.text;
|
source.value = res.source;
|
||||||
source.value = res.source;
|
status.value = true;
|
||||||
status.value = true;
|
});
|
||||||
},
|
} else if (data.state.urls[0]?.url) {
|
||||||
);
|
|
||||||
} else if (props.curl) {
|
|
||||||
getJson(
|
getJson(
|
||||||
'https://hyperpipeapi.onrender.com/next/' +
|
'https://hyperpipeapi.onrender.com/next/' +
|
||||||
props.curl.replace('/watch?v=', ''),
|
data.state.urls[0]?.url.replace('/watch?v=', ''),
|
||||||
).then(next => {
|
).then(next => {
|
||||||
if (next.lyricsId) {
|
if (next.lyricsId) {
|
||||||
getJson(
|
getJson(
|
||||||
|
@ -50,7 +43,7 @@ function get() {
|
||||||
get();
|
get();
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.curl,
|
() => data.state.urls[0]?.url,
|
||||||
() => {
|
() => {
|
||||||
get();
|
get();
|
||||||
},
|
},
|
||||||
|
@ -60,22 +53,24 @@ watch(
|
||||||
<template>
|
<template>
|
||||||
<TextModal>
|
<TextModal>
|
||||||
<template #content>
|
<template #content>
|
||||||
<pre class="placeholder" :data-loaded="curl ? status : true">{{
|
<pre
|
||||||
text
|
class="placeholder"
|
||||||
}}</pre>
|
:data-loaded="data.state.urls[0]?.url ? status : true"
|
||||||
|
>{{ text }}</pre
|
||||||
|
>
|
||||||
<div>{{ source }}</div>
|
<div>{{ source }}</div>
|
||||||
</template>
|
</template>
|
||||||
</TextModal>
|
</TextModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
pre:empty::before {
|
.placeholder:empty::before {
|
||||||
--ico: '\f3a5';
|
--ico: '\f3a5';
|
||||||
}
|
}
|
||||||
pre[data-loaded='false']:empty::after {
|
.placeholder[data-loaded='false']:empty::after {
|
||||||
--text: 'Fetching Lyrics...';
|
--text: 'Fetching Lyrics...';
|
||||||
}
|
}
|
||||||
pre[data-loaded='true']:empty::after {
|
.placeholder[data-loaded='true']:empty::after {
|
||||||
--text: 'No Lyrics...';
|
--text: 'No Lyrics...';
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -2,30 +2,9 @@
|
||||||
import { reactive } from 'vue';
|
import { reactive } from 'vue';
|
||||||
import SearchBar from '../components/SearchBar.vue';
|
import SearchBar from '../components/SearchBar.vue';
|
||||||
|
|
||||||
defineProps({
|
import { useNav } from '@/stores/misc.js';
|
||||||
search: String,
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits(['update-page', 'update-search']),
|
const nav = useNav();
|
||||||
page = reactive({
|
|
||||||
home: true,
|
|
||||||
playlist: false,
|
|
||||||
prefs: false,
|
|
||||||
genres: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const Toggle = p => {
|
|
||||||
for (let pg in page) {
|
|
||||||
page[pg] = false;
|
|
||||||
}
|
|
||||||
page[p] = true;
|
|
||||||
emit('update-page', p);
|
|
||||||
|
|
||||||
console.log(page[p], p);
|
|
||||||
},
|
|
||||||
home = () => {
|
|
||||||
history.pushState('', {}, '/');
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -35,30 +14,24 @@ const Toggle = p => {
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<span
|
<span
|
||||||
class="nav-ico bi bi-house"
|
class="nav-ico bi bi-house"
|
||||||
:data-active="page.home"
|
:data-active="nav.state.page == 'home'"
|
||||||
@click="Toggle('home')"></span>
|
@click="nav.state.page = 'home'"></span>
|
||||||
<span
|
<span
|
||||||
class="nav-ico bi bi-compass"
|
class="nav-ico bi bi-compass"
|
||||||
:data-active="page.genres"
|
:data-active="nav.state.page == 'genres'"
|
||||||
@click="Toggle('genres')"></span>
|
@click="nav.state.page = 'genres'"></span>
|
||||||
<span
|
<span
|
||||||
class="nav-ico bi bi-collection"
|
class="nav-ico bi bi-collection"
|
||||||
:data-active="page.playlist"
|
:data-active="nav.state.page == 'playlist'"
|
||||||
@click="Toggle('playlist')"></span>
|
@click="nav.state.page = 'playlist'"></span>
|
||||||
<span
|
<span
|
||||||
class="nav-ico bi bi-gear"
|
class="nav-ico bi bi-gear"
|
||||||
:data-active="page.prefs"
|
:data-active="nav.state.page == 'prefs'"
|
||||||
@click="Toggle('prefs')"></span>
|
@click="nav.state.page = 'prefs'"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<SearchBar
|
<SearchBar />
|
||||||
:search="search"
|
|
||||||
@update-search="
|
|
||||||
e => {
|
|
||||||
$emit('update-search', e);
|
|
||||||
}
|
|
||||||
" />
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,28 +1,20 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
defineProps({
|
import { useData } from '@/stores/player.js';
|
||||||
title: {
|
|
||||||
type: String,
|
const data = useData();
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
artist: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
artistUrl: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
defineEmits(['get-artist']);
|
defineEmits(['get-artist']);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="title && artist" class="wrap">
|
<div v-if="data.state.title && data.state.artist" class="wrap">
|
||||||
<h1>{{ title }}</h1>
|
<h1>{{ data.state.title }}</h1>
|
||||||
<h3>
|
<h3>
|
||||||
<a :href="artistUrl" @click.prevent="$emit('get-artist', artistUrl)">{{
|
<a
|
||||||
artist
|
:href="data.state.artistUrl"
|
||||||
}}</a>
|
@click.prevent="$emit('get-artist', data.state.artistUrl)"
|
||||||
|
>{{ data.state.artist }}</a
|
||||||
|
>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
|
defineProps(['ico']);
|
||||||
defineEmits(['click']);
|
defineEmits(['click']);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button class="bi bi-play" @click="$emit('click')"></button>
|
<button
|
||||||
|
:class="'bi bi-' + (ico ? ico : 'play')"
|
||||||
|
@click="$emit('click')"></button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
button {
|
.bi {
|
||||||
height: 4rem;
|
height: 4rem;
|
||||||
width: 4rem;
|
width: 4rem;
|
||||||
font-size: 4rem;
|
font-size: 4rem;
|
||||||
|
@ -21,10 +24,14 @@ button {
|
||||||
transition: background 0.4s ease;
|
transition: background 0.4s ease;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
button:before {
|
.bi-play:before {
|
||||||
padding-left: 0.2rem;
|
padding-left: 0.2rem;
|
||||||
}
|
}
|
||||||
button:hover {
|
.bi:hover,
|
||||||
|
.bi:not(.bi-play) {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
.bi:not(.bi-play) {
|
||||||
|
font-size: 3rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
defineProps({
|
import { useData, usePlayer } from '@/stores/player.js';
|
||||||
url: String,
|
|
||||||
urls: Array,
|
const player = usePlayer(),
|
||||||
show: Boolean,
|
data = useData();
|
||||||
});
|
|
||||||
|
|
||||||
defineEmits(['playthis']);
|
defineEmits(['playthis']);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Transition name="fade">
|
<Transition name="fade">
|
||||||
<div v-if="show" class="pl-modal placeholder">
|
<div class="pl-modal placeholder">
|
||||||
<template v-for="plurl in urls">
|
<template v-for="plurl in data.state.urls">
|
||||||
<div class="pl-item" @click="$emit('playthis', plurl)">
|
<div class="pl-item" @click="$emit('playthis', plurl)">
|
||||||
<span v-if="url == plurl.url" class="bars-wrap">
|
<span v-if="data.state.url == plurl.url" class="bars-wrap">
|
||||||
<div class="bars"></div>
|
<div class="bars"></div>
|
||||||
<div class="bars"></div>
|
<div class="bars"></div>
|
||||||
<div class="bars"></div>
|
<div class="bars"></div>
|
||||||
|
@ -22,7 +21,8 @@ defineEmits(['playthis']);
|
||||||
<img
|
<img
|
||||||
:src="plurl.thumbnails[0].url"
|
:src="plurl.thumbnails[0].url"
|
||||||
:height="plurl.thumbnails[0].height"
|
:height="plurl.thumbnails[0].height"
|
||||||
:width="plurl.thumbnails[0].width" />
|
:width="plurl.thumbnails[0].width"
|
||||||
|
loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<span class="pl-main caps">{{ plurl.title }}</span>
|
<span class="pl-main caps">{{ plurl.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -63,6 +63,7 @@ onMounted(() => {
|
||||||
@change="setTheme($event.target.value)">
|
@change="setTheme($event.target.value)">
|
||||||
<option value="dark">Dark (Default)</option>
|
<option value="dark">Dark (Default)</option>
|
||||||
<option value="light">Light</option>
|
<option value="light">Light</option>
|
||||||
|
<option value="dracula">Dracula</option>
|
||||||
<option value="nord">Nord</option>
|
<option value="nord">Nord</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|
|
@ -1,40 +1,45 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, watch } from 'vue';
|
import { ref, reactive, watch, onUpdated } from 'vue';
|
||||||
|
|
||||||
import PlayBtn from './PlayBtn.vue';
|
import PlayBtn from './PlayBtn.vue';
|
||||||
import SongItem from './SongItem.vue';
|
import SongItem from './SongItem.vue';
|
||||||
import AlbumItem from './AlbumItem.vue';
|
import AlbumItem from './AlbumItem.vue';
|
||||||
|
|
||||||
import { getJsonPiped, getPipedQuery } from '../scripts/fetch.js';
|
import { getJsonPiped, getPipedQuery } from '../scripts/fetch.js';
|
||||||
import { useLazyLoad } from '../scripts/util.js';
|
import { useLazyLoad, useRoute } from '../scripts/util.js';
|
||||||
|
import { useCreatePlaylist } from '../scripts/db.js';
|
||||||
|
|
||||||
const props = defineProps(['search', 'songItems', 'items']),
|
import { useResults, useArtist } from '@/stores/results.js';
|
||||||
emit = defineEmits(['get-album', 'get-artist', 'play-urls', 'add-song']),
|
import { useNav } from '@/stores/misc.js';
|
||||||
|
|
||||||
|
const results = useResults(),
|
||||||
|
nav = useNav(),
|
||||||
|
artist = useArtist();
|
||||||
|
|
||||||
|
const emit = defineEmits(['get-album', 'get-artist', 'play-urls', 'add-song']),
|
||||||
filters = ['music_songs', 'music_albums', 'music_artists'],
|
filters = ['music_songs', 'music_albums', 'music_artists'],
|
||||||
filter = ref('music_songs'),
|
filter = ref('music_songs'),
|
||||||
isSearch = ref(/search/.test(location.pathname)),
|
isSearch = ref(/search/.test(location.pathname));
|
||||||
data = reactive({
|
|
||||||
notes: null,
|
|
||||||
albums: null,
|
|
||||||
albumTitle: null,
|
|
||||||
songs: null,
|
|
||||||
artists: null,
|
|
||||||
recommendedArtists: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const Reset = () => {
|
const playAlbum = () => {
|
||||||
isSearch.value = /search/.test(location.pathname);
|
const urls = results.items?.songs?.items?.map(item => {
|
||||||
|
|
||||||
for (let i in data) {
|
|
||||||
data[i] = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
playAlbum = () => {
|
|
||||||
const urls = data.songs.items.map(item => {
|
|
||||||
return { url: item.url, title: item.title };
|
return { url: item.url, title: item.title };
|
||||||
});
|
});
|
||||||
|
|
||||||
emit('play-urls', urls);
|
emit('play-urls', urls);
|
||||||
},
|
},
|
||||||
|
saveAlbum = () => {
|
||||||
|
const urls = results.items?.songs?.items?.map(item => {
|
||||||
|
return { url: item.url, title: item.title };
|
||||||
|
}),
|
||||||
|
title = results.items?.songs?.title;
|
||||||
|
|
||||||
|
if (title) {
|
||||||
|
useCreatePlaylist(title, urls, () => {
|
||||||
|
alert('Saved!');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
getSearch = q => {
|
getSearch = q => {
|
||||||
if (q) {
|
if (q) {
|
||||||
const pq = q.split(' ').join('+');
|
const pq = q.split(' ').join('+');
|
||||||
|
@ -47,74 +52,60 @@ const Reset = () => {
|
||||||
getResults(pq);
|
getResults(pq);
|
||||||
useLazyLoad();
|
useLazyLoad();
|
||||||
} else {
|
} else {
|
||||||
Reset();
|
results.resetItems();
|
||||||
|
|
||||||
history.pushState({}, '', '/');
|
useRoute('/');
|
||||||
document.title = 'Hyperpipe';
|
document.title = 'Hyperpipe';
|
||||||
|
|
||||||
console.log('No Search');
|
console.log('No Search');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getResults = async q => {
|
getResults = async q => {
|
||||||
const f = filter.value || 'music_songs',
|
results.resetItems();
|
||||||
json = await getJsonPiped(`/search?q=${q}&filter=${f}`);
|
|
||||||
|
|
||||||
data[f.split('_')[1]] = json;
|
const f = filter.value || 'music_songs',
|
||||||
console.log(json, data);
|
json = await getJsonPiped(`/search?q=${q}&filter=${f}`),
|
||||||
|
key = f.split('_')[1];
|
||||||
|
|
||||||
|
results.setItem(key, json);
|
||||||
|
|
||||||
|
console.log(json, key);
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.search,
|
() => nav.state.search,
|
||||||
n => {
|
n => {
|
||||||
if (n) {
|
if (n) {
|
||||||
Reset();
|
|
||||||
|
|
||||||
n = n.replace(location.search || '', '');
|
n = n.replace(location.search || '', '');
|
||||||
|
|
||||||
console.log(n);
|
console.log(n);
|
||||||
|
|
||||||
|
artist.reset();
|
||||||
getSearch(n);
|
getSearch(n);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
onUpdated(() => {
|
||||||
() => props.songItems,
|
isSearch.value = /search/.test(location.pathname);
|
||||||
i => {
|
});
|
||||||
console.log(i);
|
|
||||||
|
|
||||||
Reset();
|
|
||||||
|
|
||||||
data.songs = i;
|
|
||||||
data.albumTitle = i.title;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.items,
|
|
||||||
itms => {
|
|
||||||
Reset();
|
|
||||||
|
|
||||||
console.log(itms);
|
|
||||||
|
|
||||||
for (let i in itms) {
|
|
||||||
data[i] = {};
|
|
||||||
data[i].items = itms[i];
|
|
||||||
|
|
||||||
console.log(i, data[i]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="data.songs && data.songs.corrected" class="text-full">
|
<div
|
||||||
Did you mean, "<span class="caps">{{ data.songs.suggestion }}</span
|
v-if="results.items.songs && results.items.songs.corrected"
|
||||||
|
class="text-full">
|
||||||
|
Did you mean, "<span class="caps">{{
|
||||||
|
results.items?.songs?.suggestion
|
||||||
|
}}</span
|
||||||
>"!!
|
>"!!
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="data.albumTitle" class="text-full flex">
|
<div v-if="results.items?.songs?.title" class="text-full flex">
|
||||||
<PlayBtn @click="playAlbum" />
|
<PlayBtn @click="playAlbum" />
|
||||||
<span>{{ data.albumTitle }}</span>
|
<PlayBtn ico="plus" @click="saveAlbum" />
|
||||||
|
|
||||||
|
<span>{{ results.items?.songs?.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="isSearch" class="filters">
|
<div v-if="isSearch" class="filters">
|
||||||
|
@ -123,18 +114,19 @@ watch(
|
||||||
class="filter caps"
|
class="filter caps"
|
||||||
@click="
|
@click="
|
||||||
filter = f;
|
filter = f;
|
||||||
Reset();
|
getSearch(nav.state.search);
|
||||||
getSearch(search);
|
|
||||||
"
|
"
|
||||||
:data-active="f == filter">
|
:data-active="f == filter">
|
||||||
{{ f.split('_')[1] }}
|
{{ f.split('_')[1] }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="data.songs && data.songs.items[0]" class="search-songs">
|
<div
|
||||||
|
v-if="results.items.songs && results.items.songs.items[0]"
|
||||||
|
class="search-songs">
|
||||||
<h2>Songs</h2>
|
<h2>Songs</h2>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<template v-for="song in data.songs.items">
|
<template v-for="song in results.items.songs.items">
|
||||||
<SongItem
|
<SongItem
|
||||||
:author="song.uploaderName || song.subtitle"
|
:author="song.uploaderName || song.subtitle"
|
||||||
:title="song.title || song.name"
|
:title="song.title || song.name"
|
||||||
|
@ -163,18 +155,22 @@ watch(
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
v-if="data.notes"
|
v-if="results.items.notes"
|
||||||
@click.prevent="$emit('get-album', '/playlist?list=' + data.notes.items)"
|
@click.prevent="
|
||||||
|
$emit('get-album', '/playlist?list=' + results.items.notes.items)
|
||||||
|
"
|
||||||
class="more"
|
class="more"
|
||||||
:href="'/playlist?list=' + data.notes.items"
|
:href="'/playlist?list=' + results.items.notes.items"
|
||||||
>See All</a
|
>See All</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="data.albums && data.albums.items[0]" class="search-albums">
|
<div
|
||||||
|
v-if="results.items.albums && results.items.albums.items[0]"
|
||||||
|
class="search-albums">
|
||||||
<h2>Albums</h2>
|
<h2>Albums</h2>
|
||||||
<div class="grid-3">
|
<div class="grid-3">
|
||||||
<template v-for="album in data.albums.items">
|
<template v-for="album in results.items.albums.items">
|
||||||
<AlbumItem
|
<AlbumItem
|
||||||
:author="album.uploaderName || album.subtitle"
|
:author="album.uploaderName || album.subtitle"
|
||||||
:name="album.name || album.title"
|
:name="album.name || album.title"
|
||||||
|
@ -186,10 +182,12 @@ watch(
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="data.singles && data.singles.items[0]" class="search-albums">
|
<div
|
||||||
|
v-if="results.items.singles && results.items.singles.items[0]"
|
||||||
|
class="search-albums">
|
||||||
<h2>Singles</h2>
|
<h2>Singles</h2>
|
||||||
<div class="grid-3">
|
<div class="grid-3">
|
||||||
<template v-for="single in data.singles.items">
|
<template v-for="single in results.items.singles.items">
|
||||||
<AlbumItem
|
<AlbumItem
|
||||||
:author="single.subtitle"
|
:author="single.subtitle"
|
||||||
:name="single.title"
|
:name="single.title"
|
||||||
|
@ -201,16 +199,17 @@ watch(
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
(data.recommendedArtists && data.recommendedArtists.items[0]) ||
|
(results.items.recommendedArtists &&
|
||||||
(data.artists && data.artists.items[0])
|
results.items.recommendedArtists.items[0]) ||
|
||||||
|
(results.items.artists && results.items.artists.items[0])
|
||||||
"
|
"
|
||||||
class="search-artists">
|
class="search-artists">
|
||||||
<h2>{{ data.artists ? 'Artists' : 'Similar Artists' }}</h2>
|
<h2>{{ results.items.artists ? 'Artists' : 'Similar Artists' }}</h2>
|
||||||
<div class="grid-3 circle">
|
<div class="grid-3 circle">
|
||||||
<template
|
<template
|
||||||
v-for="artist in data.artists
|
v-for="artist in results.items.artists
|
||||||
? data.artists.items
|
? results.items.artists.items
|
||||||
: data.recommendedArtists.items">
|
: results.items.recommendedArtists.items">
|
||||||
<AlbumItem
|
<AlbumItem
|
||||||
:author="artist.subtitle"
|
:author="artist.subtitle"
|
||||||
:name="artist.name || artist.title"
|
:name="artist.name || artist.title"
|
||||||
|
@ -241,6 +240,9 @@ watch(
|
||||||
.search-artists {
|
.search-artists {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
:deep(.bi-play) {
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
}
|
||||||
.filters {
|
.filters {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import { useNav } from '@/stores/misc.js';
|
||||||
|
|
||||||
defineProps({
|
const show = ref(false),
|
||||||
search: String,
|
nav = useNav();
|
||||||
});
|
|
||||||
|
|
||||||
defineEmits(['update-search']);
|
|
||||||
|
|
||||||
const show = ref(false);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -22,8 +18,8 @@ const show = ref(false);
|
||||||
type="text"
|
type="text"
|
||||||
aria-label="Search Input"
|
aria-label="Search Input"
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
@change="$emit('update-search', $event.target.value)"
|
@change="nav.state.search = $event.target.value"
|
||||||
:value="search" />
|
:value="nav.state.search" />
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -6,16 +6,12 @@ import Modal from './Modal.vue';
|
||||||
import { useStore } from '../scripts/util.js';
|
import { useStore } from '../scripts/util.js';
|
||||||
import { useListPlaylists } from '../scripts/db.js';
|
import { useListPlaylists } from '../scripts/db.js';
|
||||||
|
|
||||||
defineProps({
|
import { usePlayer } from '../stores/player.js';
|
||||||
state: String,
|
|
||||||
time: Number,
|
|
||||||
show: Boolean,
|
|
||||||
loop: Boolean,
|
|
||||||
lyrics: Boolean,
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits(['vol', 'play', 'toggle', 'save', 'change-time']),
|
const player = usePlayer(),
|
||||||
vol = ref(useStore().vol / 100 || 1),
|
store = useStore();
|
||||||
|
|
||||||
|
const emit = defineEmits(['vol', 'save', 'change-time']),
|
||||||
showme = reactive({
|
showme = reactive({
|
||||||
menu: false,
|
menu: false,
|
||||||
pl: false,
|
pl: false,
|
||||||
|
@ -76,13 +72,13 @@ function Save() {
|
||||||
<button
|
<button
|
||||||
id="btn-play-pause"
|
id="btn-play-pause"
|
||||||
aria-label="Play or Pause"
|
aria-label="Play or Pause"
|
||||||
:class="'bi bi-' + state"
|
:class="'bi bi-' + player.state.status"
|
||||||
@click="$emit('play')"></button>
|
@click="player.toggle('play')"></button>
|
||||||
|
|
||||||
<div id="statusbar-progress" class="range-wrap">
|
<div id="statusbar-progress" class="range-wrap">
|
||||||
<input
|
<input
|
||||||
aria-label="Change Time"
|
aria-label="Change Time"
|
||||||
:value="time"
|
:value="player.state.time"
|
||||||
type="range"
|
type="range"
|
||||||
name="statusbar-progress"
|
name="statusbar-progress"
|
||||||
max="100"
|
max="100"
|
||||||
|
@ -102,13 +98,10 @@ function Save() {
|
||||||
id="vol-input"
|
id="vol-input"
|
||||||
aria-label="Volume Input"
|
aria-label="Volume Input"
|
||||||
type="range"
|
type="range"
|
||||||
:value="vol"
|
:value="player.state.vol"
|
||||||
max="1"
|
max="1"
|
||||||
step=".01"
|
step=".01"
|
||||||
@input="
|
@input="player.state.vol = $event.target.value" />
|
||||||
$emit('vol', $event.target.value);
|
|
||||||
vol = $event.target.value;
|
|
||||||
" />
|
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
</button>
|
</button>
|
||||||
|
@ -117,15 +110,15 @@ function Save() {
|
||||||
aria-label="More Controls"
|
aria-label="More Controls"
|
||||||
@click="
|
@click="
|
||||||
showme.menu = !showme.menu;
|
showme.menu = !showme.menu;
|
||||||
show ? $emit('toggle', 'showplaylist') : '';
|
player.state.playlist ? player.toggle('playlist') : '';
|
||||||
lyrics ? $emit('toggle', 'showlyrics') : '';
|
player.state.lyrics ? player.toggle('lyrics') : '';
|
||||||
"></button>
|
"></button>
|
||||||
<div id="menu" v-if="showme.menu" class="popup">
|
<div id="menu" v-if="showme.menu" class="popup">
|
||||||
<button
|
<button
|
||||||
id="info-btn"
|
id="info-btn"
|
||||||
class="bi bi-info-circle"
|
class="bi bi-info-circle"
|
||||||
aria-label="Show Information About Song"
|
aria-label="Show Information About Song"
|
||||||
@click="$emit('toggle', 'showinfo')"></button>
|
@click="player.toggle('info')"></button>
|
||||||
<button
|
<button
|
||||||
id="addToPlaylist"
|
id="addToPlaylist"
|
||||||
title="Add Current Song to a Playlist"
|
title="Add Current Song to a Playlist"
|
||||||
|
@ -136,18 +129,21 @@ function Save() {
|
||||||
id="list-btn"
|
id="list-btn"
|
||||||
title="Current Playlist"
|
title="Current Playlist"
|
||||||
aria-label="Current Playlist"
|
aria-label="Current Playlist"
|
||||||
:class="'bi bi-music-note-list ' + show"
|
class="bi bi-music-note-list"
|
||||||
@click="$emit('toggle', 'showplaylist')"></button>
|
:data-active="player.state.playlist"
|
||||||
|
@click="player.toggle('playlist')"></button>
|
||||||
<button
|
<button
|
||||||
id="btn-lyrics"
|
id="btn-lyrics"
|
||||||
:class="'bi bi-file-music ' + lyrics"
|
class="bi bi-file-music"
|
||||||
@click="$emit('toggle', 'showlyrics')"></button>
|
:data-active="player.state.lyrics"
|
||||||
|
@click="player.toggle('lyrics')"></button>
|
||||||
<button
|
<button
|
||||||
id="loop-btn"
|
id="loop-btn"
|
||||||
title="Loop"
|
title="Loop"
|
||||||
aria-label="Loop"
|
aria-label="Loop"
|
||||||
:class="'bi bi-infinity ' + loop"
|
class="bi bi-infinity"
|
||||||
@click="$emit('toggle', 'loop')"></button>
|
:data-active="player.state.loop"
|
||||||
|
@click="player.toggle('loop')"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -302,7 +298,7 @@ input[type='range']::-moz-range-track {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
#statusbar-progress {
|
#statusbar-progress {
|
||||||
--fw: v-bind('time + "%"');
|
--fw: v-bind('player.state.time + "%"');
|
||||||
width: 50vw;
|
width: 50vw;
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
|
import { createPinia } from 'pinia';
|
||||||
|
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
|
|
||||||
const app = createApp(App);
|
const pinia = createPinia(),
|
||||||
|
app = createApp(App);
|
||||||
|
|
||||||
|
app.use(pinia);
|
||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
|
|
11
src/stores/misc.js
Normal file
11
src/stores/misc.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export const useNav = defineStore('nav', () => {
|
||||||
|
const state = reactive({
|
||||||
|
search: '',
|
||||||
|
page: 'home',
|
||||||
|
});
|
||||||
|
|
||||||
|
return { state };
|
||||||
|
});
|
50
src/stores/player.js
Normal file
50
src/stores/player.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
import { useStore } from '../scripts/util.js';
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
|
||||||
|
export const useData = defineStore('data', () => {
|
||||||
|
const state = reactive({
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
artist: '',
|
||||||
|
art: '',
|
||||||
|
url: '',
|
||||||
|
artistUrl: '',
|
||||||
|
lyrics: '',
|
||||||
|
src: [],
|
||||||
|
urls: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
return { state };
|
||||||
|
});
|
||||||
|
|
||||||
|
export const usePlayer = defineStore('player', () => {
|
||||||
|
const state = reactive({
|
||||||
|
loop: false,
|
||||||
|
play: false,
|
||||||
|
status: 'play',
|
||||||
|
duration: 0,
|
||||||
|
time: 0,
|
||||||
|
playlist: false,
|
||||||
|
lyrics: false,
|
||||||
|
info: false,
|
||||||
|
vol: store.vol ? store.vol / 100 : 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggle(i) {
|
||||||
|
console.log(i, state[i]);
|
||||||
|
if (typeof state[i] == 'boolean') {
|
||||||
|
state[i] = !state[i];
|
||||||
|
}
|
||||||
|
console.log(i, state[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTime(t) {
|
||||||
|
state.time = Math.floor((t / state.duration) * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { state, toggle, setTime };
|
||||||
|
});
|
44
src/stores/results.js
Normal file
44
src/stores/results.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { ref, reactive } from 'vue';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export const useResults = defineStore('results', () => {
|
||||||
|
const items = ref({}),
|
||||||
|
search = ref('');
|
||||||
|
|
||||||
|
function setItem(key, val) {
|
||||||
|
items.value[key] = val;
|
||||||
|
console.log(items.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetItems() {
|
||||||
|
for (let i in items.value) {
|
||||||
|
items.value[i] = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { items, search, setItem, resetItems };
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useArtist = defineStore('artist', () => {
|
||||||
|
const state = reactive({
|
||||||
|
playlistId: null,
|
||||||
|
title: null,
|
||||||
|
description: null,
|
||||||
|
subscriberCount: 0,
|
||||||
|
thumbnails: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
for (let i in state) {
|
||||||
|
state[i] = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function set(obj) {
|
||||||
|
for (let i in obj) {
|
||||||
|
state[i] = obj[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { state, set, reset };
|
||||||
|
});
|
Loading…
Add table
Add a link
Reference in a new issue