update thingys

This commit is contained in:
2025-05-25 23:27:42 +02:00
parent 6058a96258
commit 5d8aa468e8
13 changed files with 891 additions and 944 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,10 @@
"@types/node": "^22.9.3",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/tsconfig": "^0.7.0",
"autoprefixer": "^10.4.21",
"npm-run-all2": "^7.0.1",
"postcss": "^8.5.3",
"tailwindcss": "^4.1.7",
"typescript": "~5.6.3",
"vite": "^6.0.1",
"vite-plugin-vue-devtools": "^7.6.5",

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import SongItem from '../components/SongItem.vue'
import type { Song } from '../script/types'
import { ref, onMounted } from 'vue'
import { type Song, mapApiToCollection } from '../script/types'
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { useRoute } from 'vue-router'
import CollectionListItem from '../components/CollectionListItem.vue'
import { useAudio } from '@/composables/useAudio'
@@ -13,24 +13,86 @@ const { musicApi } = useApi()
const songs = ref<Song[]>([])
const name = ref('name')
const limit = 50
let offset = ref(0)
let loading = ref(false)
let hasMore = ref(true)
onMounted(async () => {
const fetchMoreSongs = async () => {
if (loading.value || !hasMore.value) return
loading.value = true
try {
const response = await musicApi().musicBackendCollections(Array.isArray(route.params.id) ? route.params.id[0] : route.params.id) // Adjust method name if needed
const response = await musicApi().musicBackendCollections(
"",
Array.isArray(route.params.id) ? route.params.id[0] : route.params.id,
limit,
offset.value
)
const base = import.meta.env.VITE_MUSIC_API_URL || 'http://localhost:8080'
const col = mapApiToCollection(response.data)
response.songs.forEach(song => {
song.previewimage = `${base}/api/v1/images/${song.previewimage}`
song.url = `${base}/api/v1/audio/${song.url}`
})
name.value = response.name
songs.value = response.songs
if (offset.value === 0) {
name.value = col.name
songs.value = col.songs
} else {
songs.value.push(...col.songs)
}
audioStore.setCollection(songs.value)
if (col.songs.length < limit) {
hasMore.value = false
}
offset.value += limit
} catch (error) {
console.error('Error fetching collection:', error)
console.error('Error fetching songs:', error)
} finally {
loading.value = false
}
}
const onScroll = (e: Event) => {
const target = e.target as HTMLElement
const scrollTop = target.scrollTop
const scrollHeight = target.scrollHeight
const clientHeight = target.clientHeight
if (scrollTop + clientHeight >= scrollHeight * 0.9) {
fetchMoreSongs()
}
}
onMounted(() => {
fetchMoreSongs()
const container = document.querySelector('.coll-container')
container?.addEventListener('scroll', onScroll)
})
onBeforeUnmount(() => {
const container = document.querySelector('.coll-container')
container?.removeEventListener('scroll', onScroll)
})
</script>
<template>
<main class="flex-1 text-center flex flex-col h-full overflow-y-hidden">
<header v-show="true">
<div class="wrapper">
<nav class="flex justify-start my-2 mx-1 space-x-1 overflow-y-scroll flex-nowrap text-nowrap">
<RouterLink class="p-1 rounded-full backdrop--light shadow-xl" to="/menu/collections"><i class="fa-solid fa-arrow-left"></i>
</RouterLink>
<p class="p-1 rounded-full backdrop--light shadow-xl">{{ name }}</p>
</nav>
<hr>
</div>
</header>
<div
class="flex-1 flex-col overflow-y-scroll coll-container"
>
<SongItem v-for="(song, index) in songs" :key="index" :song="song" />
</div>
</main>
</template>

View File

@@ -3,15 +3,11 @@ import type { CollectionPreview } from '@/script/types';
const props = defineProps<{ collection: CollectionPreview }>();
function hi() {
}
</script>
<template>
<RouterLink @click.navtive="hi" :to="'/collection/' + props.collection.index">
<RouterLink :to="'/collection/' + props.collection.index +1">
<div class=" border bordercolor rounded-lg flex">
<img class="h-20 w-20 m-2 rounded-lg" :src="props.collection.previewimage" loading="lazy" />
<div class="flex flex-col">

View File

@@ -6,7 +6,7 @@ let userInstance: ReturnType<typeof createUser> | null = null;
function createUser() {
const user = ref<Me | null>(null);
const baseUrl = ref(import.meta.env.VITE_BACKEND_URL || 'http://localhost:8080');
const proxyUrl = ref(import.meta.env.VITE_PROXY_URL || 'http://localhost:8081');
const proxyUrl = ref(import.meta.env.VITE_PROXY_URL || 'https://proxy.illegalesachen.download');
function saveUser(u: Me | null) {
localStorage.setItem('activeUser', JSON.stringify(u));

View File

@@ -1,4 +1,4 @@
import type { Apiv1Song, v1CollectionPreview } from '@/generated';
import type { Apiv1Song, v1CollectionPreview, v1Collection } from '@/generated';
export type Song = {
hash: string;
@@ -36,16 +36,36 @@ export type CollectionPreview = {
previewimage: string;
};
export type Collection = {
name: string;
items: number;
songs: Song[];
}
export function mapApiToCollection(coll : v1Collection): Collection {
return {
name: coll.name,
items: coll.items,
songs: mapApiToSongs(coll.songs)
}
}
export function mapToCollectionPreview(
apiCollection: v1CollectionPreview,
index: number
): CollectionPreview {
const image = apiCollection.image;
const imageIsMissing = !image || image === "404.png";
return {
index,
name: apiCollection.name,
length: apiCollection.items,
previewimage: `${basePath}/api/v1/image/${apiCollection.image}`,
previewimage: imageIsMissing
? "/404.gif"
: `${basePath}/api/v1/image/${btoa(image).replace(/=+$/, '')}`,
};
}

View File

@@ -18,8 +18,8 @@ const fetchCollections = async () => {
if (isLoading.value) return;
isLoading.value = true;
const response = await api.musicBackendCollections(offset.value, limit.value);
let songs = mapApiToCollectionPreview(response.data.songs)
const response = await api.musicBackendSearchCollections("", limit.value, offset.value);
let songs = mapApiToCollectionPreview(response.data.collections)
collections.value = [...collections.value, ...songs];
offset.value += limit.value;
@@ -46,8 +46,8 @@ onMounted(async () => {
</script>
<template>
<main class="flex-1 text-center flex flex-col h-full overflow-y-scroll">
<div class="flex flex-col overflow-scroll collection-container">
<main class="flex-1 text-center flex flex-col h-full overflow-y-hidden">
<div class="flex flex-col overflow-y-scroll collection-container">
<CollectionListItem v-for="(collection, index) in collections" :key="index" :collection="collection" />
</div>
</main>

View File

@@ -1,7 +1,7 @@
<template>
<main class="flex-1 flex-col overflow-hidden">
<div class="flex-1 flex-col h-full overflow-y-scroll song-container">
<main class="flex-1 flex-col">
<div class="flex-1 flex-col h-full overflow-y-hidden song-container">
<p class="p-1 rounded-full backdrop--light shadow-xl text-center">History</p>
<p v-if="songs.length === 0">No songs found</p>
<SongItem v-for="(song, index) in songs" :key="song.hash" :song="song" />

View File

@@ -91,7 +91,7 @@ function reset() {
<div>
<p>{{ userStore.user.value.name }}</p>
<p>{{ userStore.user.value.endpoint == "" ? 'Not Connected' : 'Connected' }}</p>
<p>Sharing: <button @click="share" class="border bordercolor rounded-lg p-0.5">{{ userStore.user.value.share
<p>Sharing: <button @click="userStore.user.value.share" class="border bordercolor rounded-lg p-0.5">{{ userStore.user.value.share
}}</button></p>
<button @click="getMe" class="border bordercolor rounded-lg p-0.5"> Refresh
</button>

View File

@@ -27,11 +27,6 @@ async function fetchActiveSearch(term: string) {
const songData = mapApiToSongs(response.data.songs)
songData.forEach((song: Song) => {
song.previewimage = `${userStore.baseUrl.value}/api/v1/images/${song.previewimage}`
song.url = `${userStore.baseUrl.value}/api/v1/audio/${song.url}`
})
activesongs.value = songData
if (response.data.artist) artists.value = [response.data.artist]

View File

@@ -340,16 +340,29 @@ func getRecent(db *sql.DB, limit, offset int) ([]Song, error) {
}
func getSearch(db *sql.DB, q string, limit, offset int) (ActiveSearch, error) {
rows, err := db.Query("SELECT BeatmapId, MD5Hash, Title, Artist, Creator, Folder, File, Audio, TotalTime FROM Beatmap WHERE MD5Hash FROM Songs WHERE Title LIKE ? OR Artist LIKE ? LIMIT ? OFFSET ?", "%"+q+"%", "%"+q+"%", limit, offset)
rows, err := db.Query(
`
SELECT
BeatmapId,
MD5Hash,
Title,
Artist,
Creator,
Folder,
File,
Audio,
TotalTime
FROM Beatmap WHERE Title LIKE ? OR Artist LIKE ? LIMIT ? OFFSET ?
`, "%"+q+"%", "%"+q+"%", limit, offset)
if err != nil {
return ActiveSearch{}, err
}
defer rows.Close()
_, err = scanSongs(rows)
s, err := scanSongs(rows)
if err != nil {
return ActiveSearch{}, err
}
return ActiveSearch{}, nil
return ActiveSearch{Songs: s}, nil
}
func getArtists(db *sql.DB, q string, limit, offset int) ([]Artist, error) {
@@ -471,13 +484,16 @@ func getCollectionByName(db *sql.DB, limit, offset int, name string) (Collection
func getCollections(db *sql.DB, q string, limit, offset int) ([]CollectionPreview, error) {
rows, err := db.Query(`
SELECT
c.Name, COUNT(b.MD5Hash), b.Folder, b.File
c.Name,
COUNT(b.MD5Hash) AS Count,
MIN(b.Folder) AS Folder,
MIN(b.File) AS File
FROM Collection c
Join Beatmap b ON c.MD5Hash = b.MD5Hash
JOIN Beatmap b ON c.MD5Hash = b.MD5Hash
WHERE c.Name LIKE ?
GROUP BY c.NAME
GROUP BY c.Name
LIMIT ?
OFFSET ?;`, "%"+q+"%", limit, offset)
OFFSET ?`, "%"+q+"%", limit, offset)
if err != nil {
return []CollectionPreview{}, err
}

View File

@@ -110,7 +110,6 @@ func (s *Server) Recent(ctx context.Context, req *v1.RecentRequest) (*v1.RecentR
func (s *Server) Favorite(ctx context.Context, req *v1.FavoriteRequest) (*v1.FavoriteResponse, error) {
limit := defaultLimit(int(req.Limit))
offset := int(req.Offset)
favorites, err := getFavorites(s.Db, req.Query, limit, offset)
@@ -125,10 +124,9 @@ func (s *Server) Favorite(ctx context.Context, req *v1.FavoriteRequest) (*v1.Fav
}
func (s *Server) Collections(ctx context.Context, req *v1.CollectionRequest) (*v1.CollectionResponse, error) {
limit := defaultLimit(int(req.Limit))
offset := int(req.Offset)
name := req.Name
if name != "" {
@@ -143,12 +141,13 @@ func (s *Server) Collections(ctx context.Context, req *v1.CollectionRequest) (*v
Name: c.Name,
}, nil
}
fmt.Println(limit, offset, req.Index)
c, err := getCollection(s.Db, limit, offset, int(req.Index))
if err != nil {
fmt.Println(err)
return nil, status.Errorf(codes.Internal, fmt.Sprintf("failed to fetch collection with index: %d", req.Index))
}
fmt.Println(c)
return &v1.CollectionResponse{
Songs: toProtoSongs(c.Songs),
Items: int32(c.Items),
@@ -163,10 +162,8 @@ func (s *Server) Search(ctx context.Context, req *v1.SearchSharedRequest) (*v1.S
return nil, status.Error(codes.InvalidArgument, "query cant be empty")
}
limit := defaultLimit(int(req.Limit))
offset := int(req.Offset)
search, err := getSearch(s.Db, q, limit, offset)
if err != nil {
@@ -182,15 +179,11 @@ func (s *Server) Search(ctx context.Context, req *v1.SearchSharedRequest) (*v1.S
func (s *Server) SearchCollections(ctx context.Context, req *v1.SearchCollectionRequest) (*v1.SearchCollectionResponse, error) {
q := req.Query
if q == "" {
return nil, status.Errorf(codes.InvalidArgument, "query is required")
}
limit := defaultLimit(int(req.Limit))
offset := int(req.Offset)
fmt.Println(req)
preview, err := getCollections(s.Db, q, limit, offset)
if err != nil {
fmt.Println(err)

View File

@@ -1,4 +1,4 @@
FROM golang:1.23 AS builder
FROM golang:1.24.3 AS builder
WORKDIR /app