mirror of
https://github.com/JuLi0n21/pwa-player.git
synced 2026-04-19 23:40:05 +00:00
localstorage upgrage, custome colors, fixed broken files (encoding issues)
This commit is contained in:
@@ -90,9 +90,8 @@ app.MapGet("/api/v1/audio/{*fileName}", async (string fileName, HttpContext cont
|
|||||||
var decodedFileName = Uri.UnescapeDataString(fileName);
|
var decodedFileName = Uri.UnescapeDataString(fileName);
|
||||||
var filePath = Path.Combine(Osudb.osufolder, "Songs", decodedFileName);
|
var filePath = Path.Combine(Osudb.osufolder, "Songs", decodedFileName);
|
||||||
|
|
||||||
if (!System.IO.File.Exists(filePath))
|
if (!File.Exists(filePath))
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Not Found: {filePath}");
|
|
||||||
return Results.NotFound();
|
return Results.NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,11 +124,15 @@ app.MapGet("/api/v1/search/artist", async (string? q, int? limit, int? offset) =
|
|||||||
|
|
||||||
app.MapGet("/api/v1/search/songs", async (string? q, int? limit, int? offset) =>
|
app.MapGet("/api/v1/search/songs", async (string? q, int? limit, int? offset) =>
|
||||||
{
|
{
|
||||||
|
var limitv = limit ?? 100;
|
||||||
|
var offsetv = offset ?? 0;
|
||||||
return Results.Ok();
|
return Results.Ok();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.MapGet("/api/v1/search/collections", async (string? q, int? limit, int? offset) =>
|
app.MapGet("/api/v1/search/collections", async (string? q, int? limit, int? offset) =>
|
||||||
{
|
{
|
||||||
|
var limitv = limit ?? 100;
|
||||||
|
var offsetv = offset ?? 0;
|
||||||
return Results.Ok();
|
return Results.Ok();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -138,10 +141,9 @@ app.MapGet("/api/v1/images/{*filename}", async (string filename, int? h, int? w)
|
|||||||
var decodedFileName = Uri.UnescapeDataString(filename);
|
var decodedFileName = Uri.UnescapeDataString(filename);
|
||||||
var filePath = Path.Combine(Osudb.osufolder, "Songs", decodedFileName);
|
var filePath = Path.Combine(Osudb.osufolder, "Songs", decodedFileName);
|
||||||
|
|
||||||
if (!System.IO.File.Exists(filePath))
|
if (!File.Exists(filePath))
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Not Found: {filePath}");
|
filePath = "default-bg.png";
|
||||||
return Results.NotFound();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileExtension = Path.GetExtension(filePath).ToLowerInvariant();
|
var fileExtension = Path.GetExtension(filePath).ToLowerInvariant();
|
||||||
|
|||||||
@@ -184,22 +184,7 @@ namespace shitweb
|
|||||||
{
|
{
|
||||||
while (reader.Read())
|
while (reader.Read())
|
||||||
{
|
{
|
||||||
string folder = reader.GetString(reader.GetOrdinal("FolderName"));
|
songs.Add(new Song(reader));
|
||||||
string file = reader.GetString(reader.GetOrdinal("FileName"));
|
|
||||||
string audio = reader.GetString(reader.GetOrdinal("AudioFileName"));
|
|
||||||
|
|
||||||
var img = Osudb.getBG(folder, file);
|
|
||||||
Song song = new Song(
|
|
||||||
hash: reader.GetString(reader.GetOrdinal("MD5Hash")),
|
|
||||||
name: reader.GetString(reader.GetOrdinal("Title")),
|
|
||||||
artist: reader.GetString(reader.GetOrdinal("Artist")),
|
|
||||||
length: reader.GetInt32(reader.GetOrdinal("TotalTime")),
|
|
||||||
url: $"{folder}/{audio}",
|
|
||||||
previewimage: img,
|
|
||||||
mapper: reader.GetString(reader.GetOrdinal("Creator"))
|
|
||||||
);
|
|
||||||
|
|
||||||
songs.Add(song);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -281,23 +266,7 @@ namespace shitweb
|
|||||||
{
|
{
|
||||||
while (reader.Read())
|
while (reader.Read())
|
||||||
{
|
{
|
||||||
|
return new Song(reader);
|
||||||
string folder = reader.GetString(reader.GetOrdinal("FolderName"));
|
|
||||||
string file = reader.GetString(reader.GetOrdinal("FileName"));
|
|
||||||
string audio = reader.GetString(reader.GetOrdinal("AudioFileName"));
|
|
||||||
|
|
||||||
var img = Osudb.getBG(folder, file);
|
|
||||||
Song song = new Song(
|
|
||||||
hash: reader.GetString(reader.GetOrdinal("MD5Hash")),
|
|
||||||
name: reader.GetString(reader.GetOrdinal("Title")),
|
|
||||||
artist: reader.GetString(reader.GetOrdinal("Artist")),
|
|
||||||
length: reader.GetInt32(reader.GetOrdinal("TotalTime")),
|
|
||||||
url: $"{folder}/{audio}",
|
|
||||||
previewimage: img,
|
|
||||||
mapper: reader.GetString(reader.GetOrdinal("Creator"))
|
|
||||||
);
|
|
||||||
|
|
||||||
return song;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,22 +305,7 @@ namespace shitweb
|
|||||||
|
|
||||||
while (reader.Read()) {
|
while (reader.Read()) {
|
||||||
|
|
||||||
string folder = reader.GetString(reader.GetOrdinal("FolderName"));
|
search.Songs.Add(new Song(reader));
|
||||||
string file = reader.GetString(reader.GetOrdinal("FileName"));
|
|
||||||
string audio = reader.GetString(reader.GetOrdinal("AudioFileName"));
|
|
||||||
|
|
||||||
var img = Osudb.getBG(folder, file);
|
|
||||||
Song song = new Song(
|
|
||||||
hash: reader.GetString(reader.GetOrdinal("MD5Hash")),
|
|
||||||
name: reader.GetString(reader.GetOrdinal("Title")),
|
|
||||||
artist: reader.GetString(reader.GetOrdinal("Artist")),
|
|
||||||
length: reader.GetInt32(reader.GetOrdinal("TotalTime")),
|
|
||||||
url: $"{folder}/{audio}",
|
|
||||||
previewimage: img,
|
|
||||||
mapper: reader.GetString(reader.GetOrdinal("Creator"))
|
|
||||||
);
|
|
||||||
|
|
||||||
search.Songs.Add(song);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,6 +314,7 @@ namespace shitweb
|
|||||||
Artist
|
Artist
|
||||||
FROM Beatmap
|
FROM Beatmap
|
||||||
WHERE Artist LIKE @query
|
WHERE Artist LIKE @query
|
||||||
|
OR Title LIKE @query
|
||||||
Group By Artist
|
Group By Artist
|
||||||
LIMIT 5";
|
LIMIT 5";
|
||||||
|
|
||||||
@@ -417,22 +372,7 @@ namespace shitweb
|
|||||||
|
|
||||||
while (reader.Read())
|
while (reader.Read())
|
||||||
{
|
{
|
||||||
string folder = reader.GetString(reader.GetOrdinal("FolderName"));
|
songs.Add(new Song(reader));
|
||||||
string file = reader.GetString(reader.GetOrdinal("FileName"));
|
|
||||||
string audio = reader.GetString(reader.GetOrdinal("AudioFileName"));
|
|
||||||
|
|
||||||
var img = Osudb.getBG(folder, file);
|
|
||||||
Song song = new Song(
|
|
||||||
hash: reader.GetString(reader.GetOrdinal("MD5Hash")),
|
|
||||||
name: reader.GetString(reader.GetOrdinal("Title")),
|
|
||||||
artist: reader.GetString(reader.GetOrdinal("Artist")),
|
|
||||||
length: reader.GetInt32(reader.GetOrdinal("TotalTime")),
|
|
||||||
url: $"{folder}/{audio}",
|
|
||||||
previewimage: img,
|
|
||||||
mapper: reader.GetString(reader.GetOrdinal("Creator"))
|
|
||||||
);
|
|
||||||
|
|
||||||
songs.Add(song);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
backend/default-bg.png
Normal file
BIN
backend/default-bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
@@ -144,9 +144,9 @@ public class Osudb
|
|||||||
|
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
string background = match.Groups["image_filename"].Value;
|
string background = Uri.EscapeDataString(match.Groups["image_filename"].Value);
|
||||||
|
|
||||||
return Path.Combine(songfolder, background);
|
return Path.Combine(Uri.EscapeDataString(songfolder), background);
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern = @"\d+,\d+,""(?<image_filename>[^""]+\.[a-zA-Z]+)""";
|
pattern = @"\d+,\d+,""(?<image_filename>[^""]+\.[a-zA-Z]+)""";
|
||||||
@@ -155,10 +155,10 @@ public class Osudb
|
|||||||
|
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
string background = match.Groups["image_filename"].Value;
|
string background = Uri.EscapeDataString(match.Groups["image_filename"].Value);
|
||||||
return Path.Combine(songfolder, background);
|
return Path.Combine(Uri.EscapeDataString(songfolder), background);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return "default-bg.png";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,4 +12,10 @@
|
|||||||
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
|
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="default-bg.png">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using OsuParsers.Database.Objects;
|
using OsuParsers.Database.Objects;
|
||||||
using OsuParsers.Enums.Database;
|
using OsuParsers.Enums.Database;
|
||||||
using OsuParsers.Enums;
|
using OsuParsers.Enums;
|
||||||
|
using System.Data.SQLite;
|
||||||
|
using System.CodeDom;
|
||||||
|
|
||||||
public class Song{
|
public class Song{
|
||||||
public string hash {get; set;}
|
public string hash {get; set;}
|
||||||
@@ -15,13 +17,27 @@ public class Song{
|
|||||||
this.hash = hash; this.name = name; this.artist = artist; this.length = length; this.url = url; this.previewimage = previewimage; this.mapper = mapper;
|
this.hash = hash; this.name = name; this.artist = artist; this.length = length; this.url = url; this.previewimage = previewimage; this.mapper = mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Song(SQLiteDataReader reader) {
|
||||||
|
string folder = reader.GetString(reader.GetOrdinal("FolderName"));
|
||||||
|
string file = reader.GetString(reader.GetOrdinal("FileName"));
|
||||||
|
string audio = reader.GetString(reader.GetOrdinal("AudioFileName"));
|
||||||
|
|
||||||
|
this.hash = reader.GetString(reader.GetOrdinal("MD5Hash"));
|
||||||
|
this.name = reader.GetString(reader.GetOrdinal("Title"));
|
||||||
|
this.artist = reader.GetString(reader.GetOrdinal("Artist"));
|
||||||
|
this.length = reader.GetInt32(reader.GetOrdinal("TotalTime"));
|
||||||
|
this.url = Uri.EscapeDataString($"{folder}/{audio}");
|
||||||
|
this.previewimage = Osudb.getBG(folder, file);
|
||||||
|
this.mapper = reader.GetString(reader.GetOrdinal("Creator"));
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CollectionPreview{
|
public class CollectionPreview{
|
||||||
public int index { get; set;}
|
public int index { get; set;}
|
||||||
public string name {get; set;}
|
public string? name {get; set;}
|
||||||
public int length {get; set;}
|
public int length {get; set;}
|
||||||
public string previewimage {get; set;}
|
public string? previewimage {get; set;}
|
||||||
|
|
||||||
private CollectionPreview() { }
|
private CollectionPreview() { }
|
||||||
|
|
||||||
@@ -31,9 +47,9 @@ public class CollectionPreview{
|
|||||||
|
|
||||||
}
|
}
|
||||||
public class Collection{
|
public class Collection{
|
||||||
public string name {get; set;}
|
public string? name {get; set;}
|
||||||
public int length {get; set;}
|
public int length {get; set;}
|
||||||
public List<Song> songs { get; set;}
|
public List<Song> songs { get; set;} = new List<Song>();
|
||||||
|
|
||||||
private Collection() { }
|
private Collection() { }
|
||||||
|
|
||||||
@@ -50,31 +66,31 @@ public class ActiveSearch{
|
|||||||
|
|
||||||
public class Beatmap
|
public class Beatmap
|
||||||
{
|
{
|
||||||
public string Artist { get; set; }
|
public string? Artist { get; set; }
|
||||||
public string ArtistUnicode { get; set; }
|
public string? ArtistUnicode { get; set; }
|
||||||
public string Title { get; set; }
|
public string? Title { get; set; }
|
||||||
public string TitleUnicode { get; set; }
|
public string? TitleUnicode { get; set; }
|
||||||
public string Creator { get; set; }
|
public string? Creator { get; set; }
|
||||||
public string Difficulty { get; set; }
|
public string? Difficulty { get; set; }
|
||||||
public string AudioFileName { get; set; }
|
public string? AudioFileName { get; set; }
|
||||||
public string MD5Hash { get; set; }
|
public string? MD5Hash { get; set; }
|
||||||
public string FileName { get; set; }
|
public string? FileName { get; set; }
|
||||||
public RankedStatus RankedStatus { get; set; }
|
public RankedStatus RankedStatus { get; set; }
|
||||||
public DateTime LastModifiedTime { get; set; }
|
public DateTime LastModifiedTime { get; set; }
|
||||||
public int TotalTime { get; set; }
|
public int TotalTime { get; set; }
|
||||||
public int AudioPreviewTime { get; set; }
|
public int AudioPreviewTime { get; set; }
|
||||||
public int BeatmapId { get; set; }
|
public int BeatmapId { get; set; }
|
||||||
public int BeatmapSetId { get; set; }
|
public int BeatmapSetId { get; set; }
|
||||||
public string Source { get; set; }
|
public string? Source { get; set; }
|
||||||
public string Tags { get; set; }
|
public string? Tags { get; set; }
|
||||||
public DateTime LastPlayed { get; set; }
|
public DateTime LastPlayed { get; set; }
|
||||||
public string FolderName { get; set; }
|
public string? FolderName { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BeatmapSet {
|
public class BeatmapSet {
|
||||||
public int BeatmapSetId { get; set; }
|
public int BeatmapSetId { get; set; }
|
||||||
public string FolderName { get; set; }
|
public string? FolderName { get; set; }
|
||||||
public string Creator { get; set; }
|
public string? Creator { get; set; }
|
||||||
public DateTime LastModifiedTime { get; set; }
|
public DateTime LastModifiedTime { get; set; }
|
||||||
public List<Beatmap> Beatmaps { get; private set; } = new List<Beatmap>();
|
public List<Beatmap> Beatmaps { get; private set; } = new List<Beatmap>();
|
||||||
|
|
||||||
|
|||||||
BIN
frontend/public/default-bg.png
Normal file
BIN
frontend/public/default-bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
@@ -32,11 +32,24 @@ watch(route, async (to) => {
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function loadColors() {
|
||||||
|
|
||||||
|
document.documentElement.style.setProperty('--background-color', localStorage.getItem('bgColor') || '#1c1719');
|
||||||
|
document.documentElement.style.setProperty('--action-color', localStorage.getItem('actionColor') || '#eab308');
|
||||||
|
|
||||||
|
document.documentElement.style.setProperty('--information-color', localStorage.getItem('infoColor') || '#ec4899');
|
||||||
|
document.documentElement.style.setProperty('--border-color', localStorage.getItem('borderColor') || '#ec4899');
|
||||||
|
|
||||||
|
console.log(localStorage.getItem('bgColor'));
|
||||||
|
}
|
||||||
|
|
||||||
|
loadColors();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<contentw class="flex flex-col h-screen max-h-screen wrapper text-pink-500 text-xl">
|
<contentw class="flex flex-col h-screen max-h-screen wrapper info text-xl">
|
||||||
|
|
||||||
<RouterView />
|
<RouterView />
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
body {
|
body {
|
||||||
background-color: rgba(28, 23, 25, 0);
|
background-color: var(--background-color);
|
||||||
margin:0;
|
margin:0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
@@ -28,5 +28,40 @@ min-height: 0px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.router-link-active {
|
.router-link-active {
|
||||||
color: #ec4899;
|
color: var(--action-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--background-color: rgba(28, 23, 25, 1);
|
||||||
|
--information-color: #ec4899;
|
||||||
|
--border-color: #ec4899;
|
||||||
|
--action-color: #eab308;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bordercolor {
|
||||||
|
border-color: var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg {
|
||||||
|
background-color: var(--background-color)
|
||||||
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
color: var(--action-color)
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
color: var(--information-color)
|
||||||
|
}
|
||||||
|
|
||||||
|
.info:hover {
|
||||||
|
color: var(--information-color)
|
||||||
|
}
|
||||||
|
|
||||||
|
searcheven {
|
||||||
|
background-color: lighten(var(--background-color),10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
searchodd {
|
||||||
|
background-color: lighten(var(--background-color),15%);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
max-width: 1280px;
|
max-width: 1280px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
background-color: rgba(28, 23, 25, 1);
|
background-color: rgba(--background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
a,
|
a,
|
||||||
.green {
|
.green {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: hsla(160, 100%, 37%, 1);
|
color: --information-color;
|
||||||
color: rgba(221, 159, 8, 1);
|
|
||||||
transition: 0.4s;
|
transition: 0.4s;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,35 +8,49 @@ import { RouterLink } from 'vue-router';
|
|||||||
const audioStore = useAudioStore()
|
const audioStore = useAudioStore()
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
songs :Song[];
|
songs: Song[];
|
||||||
artist: string[];
|
artist: string[];
|
||||||
|
search: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function update(hash: string){
|
function update(hash: string) {
|
||||||
|
|
||||||
audioStore.setSong(props.songs.at(props.songs.findIndex(s => s.hash==hash)))
|
audioStore.setSong(props.songs.at(props.songs.findIndex(s => s.hash == hash)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function highlightText(text: string, searchterm: string) {
|
||||||
|
if (!searchterm) return text;
|
||||||
|
const regex = new RegExp(`(${searchterm})`, 'gi');
|
||||||
|
return text.replace(regex, '<span style="color: yellow;">$1</span>');
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-full overflow-scroll border border-pink-500 rounded-lg bg-gray-800">
|
<div class="h-full w-full overflow-scroll overflow-x-hidden border bordercolor rounded-lg text-xs bg">
|
||||||
<h3>Artists</h3>
|
<div v-if="props.artist && props.artist.length > 0" class="border bordercolor">
|
||||||
<ul>
|
<h2 class="text-2xl action">Artists</h2>
|
||||||
<li v-for="(artist, index) in props.artist" :key="index">
|
<ul>
|
||||||
<RouterLink class="flex" :to="'/search?a=' + artist">{{ artist }}</RouterLink>
|
<li v-for="(artist, index) in props.artist" :key="index" class="rounded-lg">
|
||||||
</li>
|
<RouterLink class="flex" :to="'/search?a=' + artist" v-html="highlightText(artist, props.search)">
|
||||||
</ul>
|
</RouterLink>
|
||||||
<h3>Songs</h3>
|
</li>
|
||||||
<ul>
|
</ul>
|
||||||
<li v-for="(song, index) in props.songs" :key="index">
|
</div>
|
||||||
<button @click="update(song.hash)" class="flex">
|
<div v-if="props.songs && props.songs.length > 0" class="border bordercolor">
|
||||||
<img class="h-12 w-12" :src="song.previewimage">
|
<h2 class="text-2xl action">Songs</h2>
|
||||||
<div class="flex flex-col">
|
<ul>
|
||||||
{{ song.name }} - {{ song.artist }}
|
<li v-for="(song, index) in props.songs" :key="index" class="rounded-lg">
|
||||||
</div>
|
<button @click="update(song.hash)" class="flex">
|
||||||
</button>
|
<img :src="encodeURI(song.previewimage + '?h=120&w=120')" class="h-12 w-12"></img>
|
||||||
</li>
|
<p class="text-nowrap text-ellipsis overflow-hidden ml-2">
|
||||||
</ul>
|
<span v-html="highlightText(song.name, search)"></span> - <span
|
||||||
|
v-html="highlightText(song.artist, props.search)"></span>
|
||||||
|
</p>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -5,10 +5,10 @@ import { useUserStore } from '@/stores/userStore';
|
|||||||
import { useAudioStore } from '@/stores/audioStore';
|
import { useAudioStore } from '@/stores/audioStore';
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const audioStore = useAudioStore();
|
const audioStore = useAudioStore();
|
||||||
const props = defineProps<{ collection: CollectionPreview}>();
|
const props = defineProps<{ collection: CollectionPreview }>();
|
||||||
|
|
||||||
|
|
||||||
function hi(){
|
function hi() {
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@@ -16,11 +16,11 @@ function hi(){
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<RouterLink @click.navtive="hi" :to="'/collection/' + props.collection.index">
|
<RouterLink @click.navtive="hi" :to="'/collection/' + props.collection.index">
|
||||||
<div class=" border border-pink-500 rounded-lg flex">
|
<div class=" border bordercolor rounded-lg flex">
|
||||||
<img class="h-20 w-20 m-2 rounded-lg" :src="props.collection.previewimage" loading="lazy" />
|
<img class="h-20 w-20 m-2 rounded-lg" :src="props.collection.previewimage" loading="lazy" />
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<h3 class="self-start text-yellow-500">{{ props.collection.name }}</h3>
|
<h3 class="self-start info">{{ props.collection.name }}</h3>
|
||||||
<h5 class="self-start text-yellow-500 text-sm">{{ props.collection.length }} Songs </h5>
|
<h5 class="self-start info text-sm">{{ props.collection.length }} Songs </h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<hr>
|
<hr>
|
||||||
<nav class="flex justify-around my-4 text-xl">
|
<nav class="flex justify-around my-2 text-sm">
|
||||||
<RouterLink class="p-1 rounded-full backdrop--light shadow-xl hover:text-pink-500" to="/me">
|
<RouterLink class="p-1 rounded-full backdrop--light shadow-xl hover:text-pink-500" to="/me">
|
||||||
<img src="https://a.ppy.sh/14100399" class="h-12 rounded-full">
|
<img src="https://a.ppy.sh/14100399" class="h-6 rounded-full">
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|
||||||
<RouterLink
|
<RouterLink class="flex flex-col justify-center p-2 rounded-full backdrop--light shadow-xl info" to="/menu"><i
|
||||||
class="flex flex-col justify-center text-2xl p-4 rounded-full backdrop--light shadow-xl hover:text-pink-500"
|
class="fa-solid fa-house"></i>
|
||||||
to="/menu"><i class="fa-solid fa-house"></i>
|
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|
||||||
<RouterLink
|
<RouterLink class="flex flex-col justify-center p-2 rounded-full backdrop--light shadow-xl info" to="/search">
|
||||||
class="flex flex-col justify-center text-2xl p-4 rounded-full backdrop--light shadow-xl hover:text-pink-500"
|
|
||||||
to="/search">
|
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
<i class="fa-solid fa-magnifying-glass"></i>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|
||||||
|
|||||||
@@ -9,37 +9,35 @@ const audioStore = useAudioStore();
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="relative wrapper p-1 grow text-yellow-500">
|
<div class="relative wrapper p-1 grow action">
|
||||||
|
<img :src="encodeURI(audioStore.bgimg + '?h=150&w=400')" class="w-full absolute top-0 left-0 right-0 h-full"
|
||||||
<img :src="encodeURI(audioStore.bgimg + '?h=150&w=400')" class="absolute top-0 left-0 w-full h-full"
|
|
||||||
:style="{ 'filter': 'blur(2px)', 'opacity': '0.5' }" alt="Background Image" />
|
:style="{ 'filter': 'blur(2px)', 'opacity': '0.5' }" alt="Background Image" />
|
||||||
|
|
||||||
<nav class="relative flex justify-around my-2 z-10">
|
<nav class="relative flex-col z-10">
|
||||||
|
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<RouterLink to="/nowplaying" class="grow overflow-hidden">
|
||||||
|
<p class="relative text-sm text-left font-bold info overflow-hidden text-ellipsis text-nowrap">
|
||||||
|
{{ audioStore.title }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="relative text-sm text-left font-bold info text-nowrap">
|
||||||
|
{{ audioStore.artist }}
|
||||||
|
</p>
|
||||||
|
</RouterLink>
|
||||||
|
<div class="flex flex-col text-center justify-center px-2" @click="audioStore.togglePlay">
|
||||||
|
<i :class="[audioStore.isPlaying ? ' fa-circle-play' : 'fa-circle-pause']" class="text-3xl fa-regular"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class=" grow flex flex-col justify-around text-3xl text-center" to="/menu">
|
|
||||||
<i @click="audioStore.toggleShuffle" :class="[audioStore.shuffle ? 'text-pink-500' : '']"
|
|
||||||
class="fa-solid fa-shuffle"></i>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="w-full bg-gray-200 rounded-full h-0.5 dark:bg-gray-700">
|
||||||
<div class="flex-col grow text-center justify-center hover:text-pink-500" @click="audioStore.togglePlay">
|
<div class="bg-blue-600 h-0.5 rounded-full dark:bg-yellow-500"
|
||||||
<i :class="[audioStore.isPlaying ? ' fa-circle-play' : 'fa-circle-pause']" class="text-7xl fa-regular"></i>
|
:style="{ 'width': audioStore.percentDone + '%' }">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grow flex flex-col justify-around text-3xl text-center hover:text-pink-500" to="/menu">
|
|
||||||
<i @click="audioStore.toggleRepeat" :class="[audioStore.repeat ? 'text-pink-500' : '']"
|
|
||||||
class="fa-solid fa-repeat "></i>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
<RouterLink class="absolute right-2 bottom-2" to="/nowplaying">
|
|
||||||
<i class="fa-solid fa-arrow-up"></i>
|
|
||||||
</RouterLink>
|
|
||||||
|
|
||||||
<marquee class="relative mx-16 text-2xl font-bold text-pink-500" behavior="scroll">{{ audioStore.artist }} - {{
|
|
||||||
audioStore.title
|
|
||||||
}}</marquee>
|
|
||||||
<audio controls class="hidden" id="audio-player" :src="audioStore.songSrc"
|
<audio controls class="hidden" id="audio-player" :src="audioStore.songSrc"
|
||||||
@timeupdate="audioStore.update"></audio>
|
@timeupdate="audioStore.update"></audio>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ import { useAudioStore } from '@/stores/audioStore';
|
|||||||
import { useUserStore } from '@/stores/userStore';
|
import { useUserStore } from '@/stores/userStore';
|
||||||
import type { Song } from '@/script/types';
|
import type { Song } from '@/script/types';
|
||||||
|
|
||||||
const props = defineProps<{ song: Song }>();
|
const props = defineProps<{
|
||||||
|
song: Song,
|
||||||
|
action?: string,
|
||||||
|
info?: string,
|
||||||
|
border?: string,
|
||||||
|
}>();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const audioStore = useAudioStore();
|
const audioStore = useAudioStore();
|
||||||
|
|
||||||
@@ -18,17 +23,21 @@ function updateSong() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div @click="updateSong" class="m-2 border border-pink-500 rounded-lg flex">
|
<div @click="updateSong" :style="{ borderColor: border }" class="m-1 border bordercolor rounded-lg flex">
|
||||||
<img class="h-16 w-16 m-1 rounded-lg" :src="encodeURI(props.song.previewimage + '?h=64&w=64')" loading="lazy" />
|
<img class="h-14 w-14 m-1 rounded-lg"
|
||||||
<div class="flex flex-col">
|
:src="encodeURI(props.song?.previewimage ? props.song?.previewimage + '?h=56&w=56' : '/default-bg.png')"
|
||||||
<h3 class="text-nowrap overflow-scroll">
|
loading="lazy" />
|
||||||
<slot name="songName">{{ props.song.name }}</slot>
|
<div class="flex flex-col overflow-hidden">
|
||||||
</h3>
|
<p :style="{ color: info }" class="text-nowrap text-ellipsis overflow-hidden text-base info">
|
||||||
<h5 class="text-yellow-500 text-sm">
|
<slot name="songName">{{ props.song?.name ?? 'Title' }}</slot>
|
||||||
<slot name="artist">{{ props.song.artist }}</slot>
|
</p>
|
||||||
|
<h5 :style="{ color: action }" class="action text-sm text-nowrap text-ellipsis overflow-hidden text-base">
|
||||||
|
<slot name="artist">{{ props.song?.artist ?? 'Artist' }}</slot>
|
||||||
</h5>
|
</h5>
|
||||||
<h5 class="text-yellow-500 text-sm">
|
<h5 :style="{ color: action }" class="action text-sm">
|
||||||
<slot name="length">{{ props.song.length }}</slot>
|
<slot name="length">{{ Math.floor(props.song?.length ?? 0 / 60000) }}:{{ Math.floor((props.song?.length ?? 0 /
|
||||||
|
1000)
|
||||||
|
% 60).toString().padStart(2, '0') }}</slot>
|
||||||
</h5>
|
</h5>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
0
frontend/src/public
Normal file
0
frontend/src/public
Normal file
@@ -20,6 +20,25 @@ export const useAudioStore = defineStore('audioStore', () => {
|
|||||||
const repeat = ref(false);
|
const repeat = ref(false);
|
||||||
|
|
||||||
const activeCollection = ref<Song[]>([]);
|
const activeCollection = ref<Song[]>([]);
|
||||||
|
const currentSong = ref<Song>(null);
|
||||||
|
|
||||||
|
function saveSongToLocalStorage(song: Song) {
|
||||||
|
localStorage.setItem('lastPlayedSong', JSON.stringify(song));
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSongFromLocalStorage(): Song | null {
|
||||||
|
const song = localStorage.getItem('lastPlayedSong');
|
||||||
|
return song ? JSON.parse(song) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveCollectionToLocalStorage(collection: Song[]) {
|
||||||
|
localStorage.setItem('lastActiveCollection', JSON.stringify(collection));
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCollectionFromLocalStorage(): Song[] | null {
|
||||||
|
const collection = localStorage.getItem('lastActiveCollection');
|
||||||
|
return collection ? JSON.parse(collection) : null;
|
||||||
|
}
|
||||||
|
|
||||||
function togglePlay() {
|
function togglePlay() {
|
||||||
var audio = document.getElementById("audio-player") as HTMLAudioElement;
|
var audio = document.getElementById("audio-player") as HTMLAudioElement;
|
||||||
@@ -37,15 +56,15 @@ export const useAudioStore = defineStore('audioStore', () => {
|
|||||||
|
|
||||||
isPlaying.value = audio.paused;
|
isPlaying.value = audio.paused;
|
||||||
|
|
||||||
let current_min = Math.round(audio.currentTime / 60);
|
let current_min = Math.floor(audio.currentTime / 60);
|
||||||
let current_sec = (Math.round(audio.currentTime) % 60);
|
let current_sec = Math.round(audio.currentTime % 60);
|
||||||
|
|
||||||
if (!isNaN(current_sec) && !isNaN(current_min)) {
|
if (!isNaN(current_sec) && !isNaN(current_min)) {
|
||||||
currentTime.value = current_min + ':' + current_sec.toString().padStart(2, '0');
|
currentTime.value = current_min + ':' + current_sec.toString().padStart(2, '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
let duration_min = Math.round(audio.duration / 60);
|
let duration_min = Math.floor(audio.duration / 60);
|
||||||
let duration_sec = (Math.round(audio.duration) % 60);
|
let duration_sec = Math.round(audio.duration % 60);
|
||||||
|
|
||||||
if (!isNaN(duration_sec) && !isNaN(duration_min)) {
|
if (!isNaN(duration_sec) && !isNaN(duration_min)) {
|
||||||
duration.value = duration_min + ':' + duration_sec.toString().padStart(2, '0');
|
duration.value = duration_min + ':' + duration_sec.toString().padStart(2, '0');
|
||||||
@@ -85,7 +104,6 @@ export const useAudioStore = defineStore('audioStore', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateTime() {
|
function updateTime() {
|
||||||
console.log('currenttime')
|
|
||||||
|
|
||||||
var audioslider = document.getElementById("audio-slider") as HTMLInputElement;
|
var audioslider = document.getElementById("audio-slider") as HTMLInputElement;
|
||||||
|
|
||||||
@@ -96,9 +114,10 @@ export const useAudioStore = defineStore('audioStore', () => {
|
|||||||
|
|
||||||
function togglePrev() {
|
function togglePrev() {
|
||||||
let index = activeCollection.value.findIndex(s => s.hash == hash.value);
|
let index = activeCollection.value.findIndex(s => s.hash == hash.value);
|
||||||
setSong(activeCollection.value[(index - 1) % activeCollection.value.length])
|
setSong(activeCollection.value[Math.abs((index - 1 + activeCollection.value.length) % activeCollection.value.length)])
|
||||||
|
|
||||||
|
console.log(Math.abs((index - 1 + activeCollection.value.length) % activeCollection.value.length))
|
||||||
|
|
||||||
console.log('prev')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleNext() {
|
function toggleNext() {
|
||||||
@@ -123,24 +142,32 @@ export const useAudioStore = defineStore('audioStore', () => {
|
|||||||
repeat.value = !repeat.value;
|
repeat.value = !repeat.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSong(song: Song) {
|
function setSong(song: Song | null) {
|
||||||
|
|
||||||
|
if (song === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.log('setSong', song)
|
console.log('setSong', song)
|
||||||
var audio = document.getElementById("audio-player") as HTMLAudioElement;
|
var audio = document.getElementById("audio-player") as HTMLAudioElement;
|
||||||
|
|
||||||
|
songSrc.value = song.url;
|
||||||
|
artist.value = song.artist;
|
||||||
|
title.value = song.name;
|
||||||
|
bgimg.value = song.previewimage;
|
||||||
|
hash.value = song.hash;
|
||||||
|
|
||||||
|
currentSong.value = song;
|
||||||
|
saveSongToLocalStorage(song);
|
||||||
|
|
||||||
|
if (audio === null) { return; }
|
||||||
|
|
||||||
if (!audio.paused) {
|
if (!audio.paused) {
|
||||||
audio.pause
|
audio.pause
|
||||||
}
|
}
|
||||||
|
|
||||||
audio.src = song.url;
|
audio.src = song.url;
|
||||||
|
|
||||||
songSrc.value = song.url
|
|
||||||
|
|
||||||
artist.value = song.artist;
|
|
||||||
title.value = song.name;
|
|
||||||
bgimg.value = song.previewimage;
|
|
||||||
hash.value = song.hash;
|
|
||||||
|
|
||||||
console.log("bg", bgimg.value)
|
|
||||||
|
|
||||||
audio.addEventListener('canplaythrough', () => {
|
audio.addEventListener('canplaythrough', () => {
|
||||||
audio.play().catch(error => {
|
audio.play().catch(error => {
|
||||||
@@ -149,10 +176,14 @@ export const useAudioStore = defineStore('audioStore', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCollection(songs: Song[]) {
|
function setCollection(songs: Song[] | null) {
|
||||||
|
if (songs === null) { return; }
|
||||||
activeCollection.value = songs;
|
activeCollection.value = songs;
|
||||||
|
saveCollectionToLocalStorage(songs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setSong(loadSongFromLocalStorage());
|
||||||
|
setCollection(loadCollectionFromLocalStorage());
|
||||||
|
|
||||||
return { setCollection, songSrc, updateTime, artist, title, bgimg, shuffle, repeat, setSong, togglePlay, togglePrev, toggleNext, toggleRepeat, toggleShuffle, isPlaying, currentTime, duration, update, percentDone }
|
return { setCollection, songSrc, updateTime, artist, title, bgimg, shuffle, repeat, setSong, togglePlay, togglePrev, toggleNext, toggleRepeat, toggleShuffle, isPlaying, currentTime, duration, update, percentDone, currentSong }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,15 +1,57 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useUserStore } from '@/stores/userStore';
|
import { useUserStore } from '@/stores/userStore';
|
||||||
|
import SongItem from '../components/SongItem.vue'
|
||||||
|
import { useAudioStore } from '@/stores/audioStore';
|
||||||
|
|
||||||
|
const audioStore = useAudioStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const bgColor = ref('');
|
||||||
|
const actionColor = ref('');
|
||||||
|
const infoColor = ref('');
|
||||||
|
const borderColor = ref('');
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
var input = document.getElementById("url-input") as HTMLAudioElement;
|
var input = document.getElementById("url-input") as HTMLAudioElement;
|
||||||
console.log(input.value)
|
console.log(input.value)
|
||||||
userStore.baseUrl = input.value;
|
userStore.baseUrl = input.value;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function save(bg: string | null, main: string | null, info: string | null, border: string | null) {
|
||||||
|
|
||||||
|
document.documentElement.style.setProperty('--background-color', bg ?? bgColor.value);
|
||||||
|
document.documentElement.style.setProperty('--action-color', main ?? actionColor.value);
|
||||||
|
document.documentElement.style.setProperty('--information-color', info ?? infoColor.value);
|
||||||
|
document.documentElement.style.setProperty('--border-color', border ?? borderColor.value);
|
||||||
|
|
||||||
|
localStorage.setItem('bgColor', bg ?? bgColor.value);
|
||||||
|
localStorage.setItem('actionColor', main ?? actionColor.value);
|
||||||
|
localStorage.setItem('infoColor', info ?? infoColor.value);
|
||||||
|
localStorage.setItem('borderColor', border ?? borderColor.value);
|
||||||
|
|
||||||
|
console.log("bg", bgColor.value, "action:", actionColor.value, "info", infoColor.value, "border", borderColor.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
reset();
|
||||||
|
})
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
|
||||||
|
bgColor.value = localStorage.getItem('bgColor') || '#1c1719';
|
||||||
|
actionColor.value = localStorage.getItem('actionColor') || '#eab308';
|
||||||
|
infoColor.value = localStorage.getItem('infoColor') || '#ec4899';
|
||||||
|
borderColor.value = localStorage.getItem('borderColor') || '#ec4899';
|
||||||
|
|
||||||
|
document.documentElement.style.setProperty('--background-color', bgColor.value);
|
||||||
|
document.documentElement.style.setProperty('--action-color', actionColor.value);
|
||||||
|
document.documentElement.style.setProperty('--information-color', infoColor.value);
|
||||||
|
document.documentElement.style.setProperty('--border-color', borderColor.value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -23,8 +65,93 @@ function update() {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="flex-1">
|
<main class="flex-1 flex flex-col overflow-scroll">
|
||||||
<h1> Meeeeee </h1>
|
<h1> Meeeeee </h1>
|
||||||
<input @change="update" type="text" id="url-input" :value="userStore.baseUrl" />
|
<input @change="update" type="text" id="url-input" :value="userStore.baseUrl" />
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="flex flex-col w-full justify-around p-10">
|
||||||
|
<div class="flex flex-1 justify-between">
|
||||||
|
<p>Background:</p>
|
||||||
|
<input type="color" id="bgPicker" v-model="bgColor" @input="save()"
|
||||||
|
class="appearance-none w-8 h-8 border border-2 p-0 overflow-hidden cursor-pointer">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-1 justify-between">
|
||||||
|
<p>Main:</p>
|
||||||
|
<input type="color" id="actionPicker" v-model="actionColor" @input="save()"
|
||||||
|
class="appearance-none w-8 h-8 border border-2 p-0 overflow-hidden cursor-pointer">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-1 justify-between">
|
||||||
|
<p>Secondary:</p>
|
||||||
|
<input type="color" id="infoPicker" v-model="infoColor" @input="save()"
|
||||||
|
class="appearance-none w-8 h-8 border border-2 p-0 overflow-hidden cursor-pointer">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-1 justify-between">
|
||||||
|
<p>Border:</p>
|
||||||
|
<input type="color" id="borderPicker" v-model="borderColor" @input="save()"
|
||||||
|
class="appearance-none w-8 h-8 border border-2 p-0 overflow-hidden cursor-pointer">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full p-2">
|
||||||
|
<p>Current</p>
|
||||||
|
<SongItem :song="audioStore.currentSong" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full p-2 bg-black">
|
||||||
|
<p class="flex-1 flex justify-between" style=" color : #57db5d">StaryNight <button style="border-color : #b3002d"
|
||||||
|
class="border rounded-lg p-0.5" @click="save('#000000', '#5e2d8f', '#57db5d', '#b3002d')">Choose
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
<SongItem :song="audioStore.currentSong" :border="'#b3002d'" :action="'#5e2d8f'" :info="'#57db5d'" />
|
||||||
|
</div>
|
||||||
|
<div class="w-full p-2" style="background-color: #1c1719">
|
||||||
|
<p class="flex-1 flex justify-between" style=" color : #ec4889">Default<button style="border-color : #ec4889"
|
||||||
|
class="border rounded-lg p-0.5" @click="save('#1c1719', '#eab308', '#ec4889', '#ec4889')">Choose
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
<SongItem :song="audioStore.currentSong" :border="'#ec4889'" :info="'#ec4889'" :action="'#eab308'" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full p-2" style="background-color: #ff4c4c">
|
||||||
|
<p class="flex-1 flex justify-between" style="color: #ffffff">
|
||||||
|
Bright Sunset
|
||||||
|
<button style="border-color: #ffffff" class="border rounded-lg p-0.5"
|
||||||
|
@click="save('#ff4c4c', '#ffcc00', '#ffffff', '#ffffff')">Choose</button>
|
||||||
|
</p>
|
||||||
|
<SongItem :song="audioStore.currentSong" :border="'#ffffff'" :info="'#ffffff'" :action="'#ffcc00'" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full p-2" style="background-color: #003d00">
|
||||||
|
<p class="flex-1 flex justify-between" style="color: #e0f8d8">
|
||||||
|
Forest Night
|
||||||
|
<button style="border-color: #e0f8d8" class="border rounded-lg p-0.5"
|
||||||
|
@click="save('#003d00', '#a8d5a2', '#e0f8d8', '#e0f8d8')">Choose</button>
|
||||||
|
</p>
|
||||||
|
<SongItem :song="audioStore.currentSong" :border="'#e0f8d8'" :info="'#e0f8d8'" :action="'#a8d5a2'" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full p-2" style="background-color: #00274d">
|
||||||
|
<p class="flex-1 flex justify-between" style="color: #00ffff">
|
||||||
|
Electric Blue
|
||||||
|
<button style="border-color: #00ffff" class="border rounded-lg p-0.5"
|
||||||
|
@click="save('#00274d', '#0099ff', '#00ffff', '#00ffff')">Choose</button>
|
||||||
|
</p>
|
||||||
|
<SongItem :song="audioStore.currentSong" :border="'#00ffff'" :info="'#00ffff'" :action="'#0099ff'" />
|
||||||
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
input[type='color']::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='color']::-webkit-color-swatch {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -19,17 +19,18 @@ const audioStore = useAudioStore();
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="flex-1 flex justify-center text-center text-yellow-600">
|
<main class="flex-1 flex justify-center text-center action">
|
||||||
<div class="flex flex-col justify-around">
|
<div class="flex flex-col justify-around">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<i class="relative p-36 fa-solid fa-play">
|
<i class="relative p-36 fa-solid fa-play">
|
||||||
|
|
||||||
<img class="h-72 absolute top-4 left-0 bottom-0 right-0 bg-center bg-cover rounded-lg"
|
<img class="absolute top-4 left-0 bottom-0 right-0 bg-center bg-cover rounded-lg"
|
||||||
:src="encodeURI(audioStore.bgimg + '?h=500&w=500')" :key="audioStore.bgimg" />
|
:src="encodeURI(audioStore.bgimg + '?h=320&w=320')" :key="audioStore.bgimg" />
|
||||||
</i>
|
</i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="h-1/3 flex flex-col justify-center">
|
||||||
|
<div class="flex-1"></div>
|
||||||
<div>
|
<div>
|
||||||
<div class="flex w-screen justify-around">
|
<div class="flex w-screen justify-around">
|
||||||
<i class="fa-solid fa-backward-step text-5xl self-center" @click="audioStore.togglePrev"></i>
|
<i class="fa-solid fa-backward-step text-5xl self-center" @click="audioStore.togglePrev"></i>
|
||||||
@@ -38,23 +39,27 @@ const audioStore = useAudioStore();
|
|||||||
<i class="fa-solid fa-forward-step text-5xl self-center" @click="audioStore.toggleNext"></i>
|
<i class="fa-solid fa-forward-step text-5xl self-center" @click="audioStore.toggleNext"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-1 justify-around">
|
<div class="flex flex-1 justify-around ml-4">
|
||||||
<i @click="audioStore.toggleShuffle" :class="[audioStore.shuffle ? 'text-pink-500' : '']"
|
<i @click="audioStore.toggleShuffle" :class="[audioStore.shuffle ? 'info' : '']"
|
||||||
class="fa-solid fa-shuffle"></i>
|
class="fa-solid fa-shuffle"></i>
|
||||||
|
|
||||||
<div class="m-4 text-pink-500 max-w-1/2 overflow-idden">
|
<div class="m-4 info flex-1 overflow-idden">
|
||||||
<p>{{ audioStore.title }}</p>
|
<p>{{ audioStore.title }}</p>
|
||||||
<p>{{ audioStore.artist }}</p>
|
<RouterLink :to="'search?a=' + audioStore.artist">
|
||||||
|
|
||||||
|
{{ audioStore.artist }}
|
||||||
|
|
||||||
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col justify-between mb-4">
|
<div class="flex flex-col justify-between mb-4 mr-4">
|
||||||
<i @click="audioStore.toggleRepeat" :class="[audioStore.repeat ? 'text-pink-500' : '']"
|
<i @click="audioStore.toggleRepeat" :class="[audioStore.repeat ? 'info' : '']"
|
||||||
class="fa-solid fa-repeat"></i>
|
class="fa-solid fa-repeat"></i>
|
||||||
<i @click="$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>
|
</div>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<input
|
<input
|
||||||
class="appearance-none mx-4 flex-1 bg-yellow-200 bg-opacity-20 accent-yellow-600 rounded-lg outline-none slider"
|
class="appearance-none mx-4 flex-1 bg-yellow-200 bg-opacity-20 accent-yellow-600 rounded-lg outline-none slider "
|
||||||
type="range" id="audio-slider" @change="audioStore.updateTime" max="100" :value="audioStore.percentDone">
|
type="range" id="audio-slider" @change="audioStore.updateTime" max="100" :value="audioStore.percentDone">
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between mx-4">
|
<div class="flex justify-between mx-4">
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const fetchRecent = async () => {
|
|||||||
songs.value = [...songs.value, ...data];
|
songs.value = [...songs.value, ...data];
|
||||||
|
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
audioStore.setCollection(null);
|
audioStore.setCollection(songs.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,12 +15,11 @@ const activesongs = ref<Song[]>([]);
|
|||||||
const songs = ref<Song[]>([]);
|
const songs = ref<Song[]>([]);
|
||||||
const artists = ref<string[]>([]);
|
const artists = ref<string[]>([]);
|
||||||
const showSearch = ref(false);
|
const showSearch = ref(false);
|
||||||
|
const searchTerm = ref('');
|
||||||
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|
||||||
await loadartistifexist();
|
await loadartistifexist();
|
||||||
|
|
||||||
const container = document.querySelector('.search') as HTMLInputElement;
|
const container = document.querySelector('.search') as HTMLInputElement;
|
||||||
|
|
||||||
@@ -29,31 +28,43 @@ await loadartistifexist();
|
|||||||
showSearch.value = true;
|
showSearch.value = true;
|
||||||
|
|
||||||
const target = event.target as HTMLInputElement;
|
const target = event.target as HTMLInputElement;
|
||||||
if(target.value != undefined && target.value != ""){
|
if (target.value != undefined && target.value != "") {
|
||||||
const data = await userStore.fetchActiveSearch(target.value)
|
const data = await userStore.fetchActiveSearch(target.value)
|
||||||
router.push({ query: {s: target.value } });
|
router.push({ query: { s: target.value } });
|
||||||
|
searchTerm.value = target.value
|
||||||
|
|
||||||
data.songs.forEach(song => {
|
data.songs.forEach(song => {
|
||||||
song.previewimage = `${userStore.baseUrl}api/v1/images/${song.previewimage}`;
|
song.previewimage = `${userStore.baseUrl}api/v1/images/${song.previewimage}`;
|
||||||
song.url = `${userStore.baseUrl}api/v1/audio/${song.url}`;
|
song.url = `${userStore.baseUrl}api/v1/audio/${song.url}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
activesongs.value = data.songs;
|
activesongs.value = data.songs;
|
||||||
audioStore.setCollection(data.songs)
|
audioStore.setCollection(data.songs)
|
||||||
artists.value = data.artist;
|
artists.value = data.artist;
|
||||||
} else {
|
} else {
|
||||||
activesongs.value = [];
|
activesongs.value = [];
|
||||||
artists.value = [];
|
artists.value = [];
|
||||||
showSearch.value = false;
|
showSearch.value = false;
|
||||||
|
router.push({ query: { s: target.value } });
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
)}
|
container.addEventListener('enter', async (event: Event) => {
|
||||||
const s = route.query.s as string;
|
|
||||||
if(s){container.value = s; container.dispatchEvent(new Event('input'))}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function loadartistifexist(){
|
const target = event.target as HTMLInputElement;
|
||||||
|
if (target.value != undefined && target.value != "") {
|
||||||
|
showSearch.value = false
|
||||||
|
const data = await userStore.fetchActiveSearch(target.value)
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
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;
|
const query = route.query.a as string;
|
||||||
if (query) {
|
if (query) {
|
||||||
showSearch.value = false;
|
showSearch.value = false;
|
||||||
@@ -62,14 +73,23 @@ async function loadartistifexist(){
|
|||||||
console.log(data);
|
console.log(data);
|
||||||
|
|
||||||
data.forEach(song => {
|
data.forEach(song => {
|
||||||
song.previewimage = `${userStore.baseUrl}api/v1/images/${song.previewimage}`;
|
song.previewimage = `${userStore.baseUrl}api/v1/images/${song.previewimage}`;
|
||||||
song.url = `${userStore.baseUrl}api/v1/audio/${song.url}`;
|
song.url = `${userStore.baseUrl}api/v1/audio/${song.url}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
songs.value = data;
|
songs.value = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function emptySearch() {
|
||||||
|
const container = document.querySelector('.search') as HTMLInputElement;
|
||||||
|
|
||||||
|
container.value = "";
|
||||||
|
container.dispatchEvent(new Event('input'))
|
||||||
|
songs.value = [];
|
||||||
|
artists.value = [];
|
||||||
|
|
||||||
|
}
|
||||||
watch(() => route.query.a, async (newQuery) => {
|
watch(() => route.query.a, async (newQuery) => {
|
||||||
|
|
||||||
await loadartistifexist();
|
await loadartistifexist();
|
||||||
@@ -90,12 +110,18 @@ watch(() => route.query.a, async (newQuery) => {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main class="flex flex-col flex-1 flex-col w-full h-full overflow-scroll">
|
<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" />
|
<input placeholder="Type to Search..."
|
||||||
|
class="flex-1 max-h-12 search border bordercolor accent-pink-800 bg-yellow-300 bg-opacity-20 rounded-lg m-2 p-2" />
|
||||||
|
|
||||||
|
<div class="absolute h-16 right-4 flex flex-col justify-center">
|
||||||
|
<i @click="emptySearch" class="far fa-times-circle opacity-50"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="relative flex flex-col w-full h-full overflow-scroll">
|
<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">
|
<div v-if="showSearch" class="absolute w-full text-center search-recommendations z-20">
|
||||||
<ActiveSearchList :songs="activesongs" :artist="artists"/>
|
<ActiveSearchList :songs="activesongs" :artist="artists" :search="searchTerm" />
|
||||||
</div>
|
</div>
|
||||||
<SongItem v-for="(song, index) in songs" :key="index" :song="song" />
|
<SongItem v-for="(song, index) in songs" :key="index" :song="song" />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user