search feature

This commit is contained in:
ju09279
2024-08-18 02:20:46 +02:00
parent b4ac10c006
commit 97521f19e5
13 changed files with 783 additions and 88 deletions

View File

@@ -0,0 +1,42 @@
<script setup lang="ts">
import { updateLanguageServiceSourceFile } from 'typescript';
import type { Song, CollectionPreview } from '../script/types'
import { useAudioStore } from '@/stores/audioStore';
import { ref } from 'vue';
import { RouterLink } from 'vue-router';
const audioStore = useAudioStore()
const props = defineProps<{
songs :Song[];
artist: string[];
}>();
function update(hash: string){
audioStore.setSong(props.songs.at(props.songs.findIndex(s => s.hash==hash)))
}
</script>
<template>
<div class="h-full overflow-scroll border border-pink-500 rounded-lg bg-gray-800">
<h3>Artists</h3>
<ul>
<li v-for="(artist, index) in props.artist" :key="index">
<RouterLink class="flex" :to="'/search?a=' + artist">{{ artist }}</RouterLink>
</li>
</ul>
<h3>Songs</h3>
<ul>
<li v-for="(song, index) in props.songs" :key="index">
<button @click="update(song.hash)" class="flex">
<img class="h-12 w-12" :src="song.previewimage">
<div class="flex flex-col">
{{ song.name }} - {{ song.artist }}
</div>
</button>
</li>
</ul>
</div>
</template>

View File

@@ -70,6 +70,7 @@ export const useAudioStore = defineStore('audioStore', () => {
audio.pause()
audio.currentTime = 0;
audio.play()
return;
}
if (shuffle.value) {
@@ -77,6 +78,7 @@ export const useAudioStore = defineStore('audioStore', () => {
setSong(activeCollection.value[Math.floor(activeCollection.value.length * Math.random())])
audio.play()
return;
}
toggleNext()
@@ -89,7 +91,7 @@ export const useAudioStore = defineStore('audioStore', () => {
var audio = document.getElementById("audio-player") as HTMLAudioElement;
audio.currentTime = Math.round((audioslider.value / 100) * audio.duration)
audio.currentTime = Math.round((Number(audioslider.value) / 100) * audio.duration)
}
function togglePrev() {

View File

@@ -60,9 +60,9 @@ export const useUserStore = defineStore('userStore', () => {
}
async function fetchCollections(): Promise<CollectionPreview[]> {
const cacheKey = 'collections_cache';
const url = `${baseUrl.value}api/v1/collections/`;
async function fetchCollections(offset: number, limit: number): Promise<CollectionPreview[]> {
const cacheKey = `collections_cache_${offset}_${limit}`;
const url = `${baseUrl.value}api/v1/collections/?offset=${offset}&limit=${limit}`;
return fetchWithCache<CollectionPreview[]>(cacheKey, url);
}
@@ -84,7 +84,17 @@ export const useUserStore = defineStore('userStore', () => {
return fetchWithCache<Song[]>(cacheKey, url);
}
async function fetchActiveSearch(query: string): Promise<{}> {
const cacheKey = `collections_activeSearch_${query}`;
const url = `${baseUrl.value}api/v1/search/active?q=${query}`;
return fetchWithCache(cacheKey, url);
}
async function fetchSearchArtist(query: string): Promise<Song[]> {
const cacheKey = `collections_artist_${query}`;
const url = `${baseUrl.value}api/v1/search/artist?q=${query}`;
return fetchWithCache<Song[]>(cacheKey, url);
}
return { fetchSong, fetchCollections, fetchCollection, fetchRecent, fetchFavorites, userId, baseUrl }
return { fetchSong, fetchActiveSearch, fetchSearchArtist, fetchCollections, fetchCollection, fetchRecent, fetchFavorites, userId, baseUrl }
})

View File

@@ -7,20 +7,47 @@ import CollectionListItem from '../components/CollectionListItem.vue'
const userStore = useUserStore();
const collections = ref<CollectionPreview[]>([]);
const limit = ref(10);
const offset = ref(0);
const isLoading = ref(false);
onMounted(async () => {
const data = await userStore.fetchCollections();
const fetchCollections = async () => {
if (isLoading.value) return;
isLoading.value = true;
const data = await userStore.fetchCollections(offset.value, limit.value);
data.forEach(song => {
song.previewimage = `${userStore.baseUrl}api/v1/images/${song.previewimage}?h=80&w=80`;
})
collections.value = data;
});
collections.value = [...collections.value, ...data];
offset.value += limit.value;
isLoading.value = false;
};
onMounted(async () => {
await fetchCollections();
const container = document.querySelector('.collection-container');
if (container) {
container.addEventListener('scroll', async () => {
const scrollTop = container.scrollTop;
const scrollHeight = container.scrollHeight;
const clientHeight = container.clientHeight;
if (scrollTop + clientHeight >= scrollHeight * 0.9 && !isLoading.value) {
await fetchCollections();
}
});
}
});
</script>
<template>
<main class="flex-1 text-center flex flex-col h-full overflow-scroll">
<div class="flex flex-col overflow-scroll">
<div class="flex flex-col overflow-scroll collection-container">
<CollectionListItem v-for="(collection, index) in collections" :key="index" :collection="collection" />
</div>
</main>

View File

@@ -49,7 +49,7 @@ const audioStore = useAudioStore();
<div class="flex flex-col justify-between mb-4">
<i @click="audioStore.toggleRepeat" :class="[audioStore.repeat ? 'text-pink-500' : '']"
class="fa-solid fa-repeat"></i>
<i @click="this.$router.go(-1);" class="fa-solid fa-arrow-down"></i>
<i @click="$router.go(-1);" class="fa-solid fa-arrow-down"></i>
</div>
</div>
<div class="flex">

View File

@@ -1,10 +1,70 @@
<script setup lang="ts">
import SongItem from '../components/SongItem.vue'
import type { Song, CollectionPreview } from '../script/types'
import { ref, onMounted } from 'vue'
import { useUserStore } from '@/stores/userStore';
import { useRoute } from 'vue-router';
import CollectionListItem from '../components/CollectionListItem.vue'
import { useAudioStore } from '@/stores/audioStore';
const route = useRoute();
const userStore = useUserStore();
const audioStore = useAudioStore();
const songs = ref<Song[]>([]);
const name = ref('name');
const limit = ref(100);
const offset = ref(0);
const isLoading = ref(false);
const fetchRecent = async () => {
if (isLoading.value) return;
isLoading.value = true;
const data = await userStore.fetchRecent(limit.value, offset.value);
data.forEach(song => {
song.previewimage = `${userStore.baseUrl}api/v1/images/${song.previewimage}`;
song.url = `${userStore.baseUrl}api/v1/audio/${song.url}`;
});
offset.value += limit.value;
console.log(data)
songs.value = [...songs.value, ...data];
isLoading.value = false;
audioStore.setCollection(null);
}
onMounted(async () => {
await fetchRecent();
const container = document.querySelector('.song-container');
if (container) {
container.addEventListener('scroll', async () => {
const scrollTop = container.scrollTop;
const scrollHeight = container.scrollHeight;
const clientHeight = container.clientHeight;
if (scrollTop + clientHeight >= scrollHeight * 0.9 && !isLoading.value) {
await fetchRecent();
}
});
}
});
</script>
<template>
<main class="flex-1 flex-col overflow-scroll">
<div class="flex-1 flex-col h-full overflow-scroll song-container">
<SongItem v-for="(song, index) in songs" :key="index" :song="song" />
</div>
</main>
</template>

View File

@@ -1,4 +1,81 @@
<script setup lang="ts">
import type { Song, CollectionPreview } from '../script/types'
import { useUserStore } from '@/stores/userStore';
import { onMounted, ref, watch } from 'vue';
import ActiveSearchList from '../components/ActiveSearchList.vue'
import { useRoute, useRouter } from 'vue-router';
import SongItem from '../components/SongItem.vue'
import { useAudioStore } from '@/stores/audioStore';
const router = useRouter();
const route = useRoute();
const audioStore = useAudioStore();
const userStore = useUserStore();
const activesongs = ref<Song[]>([]);
const songs = ref<Song[]>([]);
const artists = ref<string[]>([]);
const showSearch = ref(false);
onMounted(async () => {
await loadartistifexist();
const container = document.querySelector('.search') as HTMLInputElement;
if (container) {
container.addEventListener('input', async (event: Event) => {
showSearch.value = true;
const target = event.target as HTMLInputElement;
if(target.value != undefined && target.value != ""){
const data = await userStore.fetchActiveSearch(target.value)
router.push({ query: {s: target.value } });
data.songs.forEach(song => {
song.previewimage = `${userStore.baseUrl}api/v1/images/${song.previewimage}`;
song.url = `${userStore.baseUrl}api/v1/audio/${song.url}`;
});
activesongs.value = data.songs;
audioStore.setCollection(data.songs)
artists.value = data.artist;
} else {
activesongs.value = [];
artists.value = [];
showSearch.value = false;
}
}
)}
const s = route.query.s as string;
if(s){container.value = s; container.dispatchEvent(new Event('input'))}
});
async function loadartistifexist(){
const query = route.query.a as string;
if (query) {
showSearch.value = false;
const data = await userStore.fetchSearchArtist(query)
console.log(data);
data.forEach(song => {
song.previewimage = `${userStore.baseUrl}api/v1/images/${song.previewimage}`;
song.url = `${userStore.baseUrl}api/v1/audio/${song.url}`;
});
songs.value = data;
}
}
watch(() => route.query.a, async (newQuery) => {
await loadartistifexist();
});
</script>
<template>
@@ -12,8 +89,13 @@
<hr>
</div>
</header>
<main class="flex-1 flex-col">
<h1> Search...</h1>
<input class="flex-1 border border-pink-500 accent-pink-800 bg-yellow-300 bg-opacity-20 rounded-lg m-2 p-2" />
<main class="flex flex-col flex-1 flex-col w-full h-full overflow-scroll">
<input placeholder="Type to Search..." class="flex-1 max-h-12 search border border-pink-500 accent-pink-800 bg-yellow-300 bg-opacity-20 rounded-lg m-2 p-2" />
<div class="relative flex flex-col w-full h-full overflow-scroll">
<div v-if="showSearch" class="absolute w-full text-center search-recommendations z -20">
<ActiveSearchList :songs="activesongs" :artist="artists"/>
</div>
<SongItem v-for="(song, index) in songs" :key="index" :song="song" />
</div>
</main>
</template>