mirror of
https://github.com/JuLi0n21/pwa-player.git
synced 2026-04-19 23:40:05 +00:00
resizing images ondemand
This commit is contained in:
@@ -2,6 +2,9 @@ using Microsoft.AspNetCore.Builder;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@@ -103,7 +106,7 @@ app.MapGet("/api/v1/audio/{*fileName}", async (string fileName, HttpContext cont
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
app.MapGet("/api/v1/images/{*filename}", async (string filename) =>
|
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);
|
||||||
@@ -125,11 +128,71 @@ app.MapGet("/api/v1/images/{*filename}", async (string filename) =>
|
|||||||
_ => "application/octet-stream",
|
_ => "application/octet-stream",
|
||||||
};
|
};
|
||||||
|
|
||||||
var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, useAsync: true);
|
if (w == null || h == null)
|
||||||
|
{
|
||||||
|
var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, useAsync: true);
|
||||||
|
return Results.Stream(fileStream, contentType, filename);
|
||||||
|
}
|
||||||
|
using var originalImage = new Bitmap(filePath);
|
||||||
|
|
||||||
|
|
||||||
|
// If resizing is requested, resize the image
|
||||||
|
Bitmap resizedImage;
|
||||||
|
if (w.HasValue || h.HasValue)
|
||||||
|
{
|
||||||
|
resizedImage = ResizeImage(originalImage, w, h);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resizedImage = new Bitmap(originalImage); // Keep original size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the resized image to a memory stream
|
||||||
|
var memoryStream = new MemoryStream();
|
||||||
|
resizedImage.Save(memoryStream, GetImageFormat(fileExtension));
|
||||||
|
memoryStream.Position = 0; // Reset stream position
|
||||||
|
|
||||||
|
return Results.File(memoryStream, contentType);
|
||||||
|
|
||||||
return Results.Stream(fileStream, contentType, filename);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static Bitmap ResizeImage(Image originalImage, int? width, int? height)
|
||||||
|
{
|
||||||
|
int newWidth = width ?? originalImage.Width;
|
||||||
|
int newHeight = height ?? originalImage.Height;
|
||||||
|
|
||||||
|
if (width == null)
|
||||||
|
{
|
||||||
|
newWidth = originalImage.Width * newHeight / originalImage.Height;
|
||||||
|
}
|
||||||
|
else if (height == null)
|
||||||
|
{
|
||||||
|
newHeight = originalImage.Height * newWidth / originalImage.Width;
|
||||||
|
}
|
||||||
|
|
||||||
|
var resizedImage = new Bitmap(newWidth, newHeight);
|
||||||
|
using (var graphics = Graphics.FromImage(resizedImage))
|
||||||
|
{
|
||||||
|
graphics.CompositingQuality = CompositingQuality.HighQuality;
|
||||||
|
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||||
|
graphics.SmoothingMode = SmoothingMode.HighQuality;
|
||||||
|
graphics.DrawImage(originalImage, 0, 0, newWidth, newHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resizedImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ImageFormat GetImageFormat(string extension)
|
||||||
|
{
|
||||||
|
return extension switch
|
||||||
|
{
|
||||||
|
".jpg" or ".jpeg" => ImageFormat.Jpeg,
|
||||||
|
".png" => ImageFormat.Png,
|
||||||
|
".gif" => ImageFormat.Gif,
|
||||||
|
".bmp" => ImageFormat.Bmp,
|
||||||
|
".webp" => ImageFormat.Webp,
|
||||||
|
_ => ImageFormat.Png,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
@@ -12,14 +12,7 @@
|
|||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"applicationUrl": "https://localhost:7254;http://localhost:5153",
|
"applicationUrl": "http://localhost:5153",
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"IIS Express": {
|
|
||||||
"commandName": "IISExpress",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OsuParsers" Version="1.7.1" />
|
<PackageReference Include="OsuParsers" Version="1.7.1" />
|
||||||
|
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -47,10 +47,6 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<div class="flex-1 flex-col h-full overflow-scroll">
|
<div class="flex-1 flex-col h-full overflow-scroll">
|
||||||
|
|
||||||
<SongItem
|
<SongItem v-for="(song, index) in songs" :key="index" :song="song" />
|
||||||
v-for="(song, index) in songs"
|
|
||||||
:key="index"
|
|
||||||
:song="song"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ const audioStore = useAudioStore();
|
|||||||
<hr>
|
<hr>
|
||||||
<div class="relative wrapper p-1 grow text-yellow-500">
|
<div class="relative wrapper p-1 grow text-yellow-500">
|
||||||
|
|
||||||
<img :src="encodeURI(audioStore.bgimg)" class="absolute top-0 left-0 w-full 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 justify-around my-2 z-10">
|
||||||
|
|
||||||
@@ -37,7 +37,8 @@ const audioStore = useAudioStore();
|
|||||||
<i class="fa-solid fa-arrow-up"></i>
|
<i class="fa-solid fa-arrow-up"></i>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|
||||||
<marquee class="relative mx-16 text-2xl font-bold text-pink-500" behavior="scroll">{{ audioStore.artist }} - {{ audioStore.title
|
<marquee class="relative mx-16 text-2xl font-bold text-pink-500" behavior="scroll">{{ audioStore.artist }} - {{
|
||||||
|
audioStore.title
|
||||||
}}</marquee>
|
}}</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>
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ 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 }>();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const audioStore = useAudioStore();
|
const audioStore = useAudioStore();
|
||||||
|
|
||||||
function updateSong(){
|
function updateSong() {
|
||||||
|
|
||||||
let updated = props.song;
|
let updated = props.song;
|
||||||
audioStore.setSong(updated)
|
audioStore.setSong(updated)
|
||||||
@@ -19,7 +19,7 @@ function updateSong(){
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div @click="updateSong" class="m-2 border border-pink-500 rounded-lg flex">
|
<div @click="updateSong" class="m-2 border border-pink-500 rounded-lg flex">
|
||||||
<img class="h-16 w-16 m-1 rounded-lg" :src="encodeURI(props.song.previewimage)" loading="lazy" />
|
<img class="h-16 w-16 m-1 rounded-lg" :src="encodeURI(props.song.previewimage + '?h=64&w=64')" loading="lazy" />
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<h3 class="text-nowrap overflow-scroll">
|
<h3 class="text-nowrap overflow-scroll">
|
||||||
<slot name="songName">{{ props.song.name }}</slot>
|
<slot name="songName">{{ props.song.name }}</slot>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { Song, CollectionPreview } from '@/script/types';
|
|||||||
|
|
||||||
export const useUserStore = defineStore('userStore', () => {
|
export const useUserStore = defineStore('userStore', () => {
|
||||||
const userId = ref(null)
|
const userId = ref(null)
|
||||||
const baseUrl = ref('https://localhost:7254/')
|
const baseUrl = ref('https://service.illegalesachen.download/')
|
||||||
|
|
||||||
async function fetchSong(hash: string): Promise<Song> {
|
async function fetchSong(hash: string): Promise<Song> {
|
||||||
try {
|
try {
|
||||||
@@ -20,12 +20,12 @@ export const useUserStore = defineStore('userStore', () => {
|
|||||||
console.error('Failed to fetch songs:', error);
|
console.error('Failed to fetch songs:', error);
|
||||||
return {
|
return {
|
||||||
hash: "-1",
|
hash: "-1",
|
||||||
name: "song name",
|
name: "song name",
|
||||||
artist: "artist name",
|
artist: "artist name",
|
||||||
length: 0,
|
length: 0,
|
||||||
url: "song.mp3",
|
url: "song.mp3",
|
||||||
previewimage: "404.im5",
|
previewimage: "404.im5",
|
||||||
mapper: "map",
|
mapper: "map",
|
||||||
} as Song;
|
} as Song;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const collections = ref<CollectionPreview[]>([]);
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const data = await userStore.fetchCollections();
|
const data = await userStore.fetchCollections();
|
||||||
data.forEach(song => {
|
data.forEach(song => {
|
||||||
song.previewimage = `${userStore.baseUrl}api/v1/images/${song.previewimage}`;
|
song.previewimage = `${userStore.baseUrl}api/v1/images/${song.previewimage}?h=80&w=80`;
|
||||||
})
|
})
|
||||||
collections.value = data;
|
collections.value = data;
|
||||||
|
|
||||||
@@ -21,12 +21,7 @@ onMounted(async () => {
|
|||||||
<template>
|
<template>
|
||||||
<main class="flex-1 text-center flex flex-col h-full overflow-scroll">
|
<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">
|
||||||
<CollectionListItem
|
<CollectionListItem v-for="(collection, index) in collections" :key="index" :collection="collection" />
|
||||||
v-for="(collection, index) in collections"
|
|
||||||
:key="index"
|
|
||||||
:collection="collection"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ const audioStore = useAudioStore();
|
|||||||
<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" :src="encodeURI(audioStore.bgimg)" :key="audioStore.bgimg" />
|
<img class="h-72 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" />
|
||||||
</i>
|
</i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -46,11 +47,11 @@ const audioStore = useAudioStore();
|
|||||||
<p>{{ audioStore.artist }}</p>
|
<p>{{ audioStore.artist }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col justify-between mb-4">
|
<div class="flex flex-col justify-between mb-4">
|
||||||
<i @click="audioStore.toggleRepeat" :class="[audioStore.repeat ? 'text-pink-500' : '']"
|
<i @click="audioStore.toggleRepeat" :class="[audioStore.repeat ? 'text-pink-500' : '']"
|
||||||
class="fa-solid fa-repeat"></i>
|
class="fa-solid fa-repeat"></i>
|
||||||
<i @click="this.$router.go(-1);" class="fa-solid fa-arrow-down"></i>
|
<i @click="this.$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"
|
||||||
|
|||||||
Reference in New Issue
Block a user