Fixed audio api endpoint, media player mostly functional

This commit is contained in:
Eclypsed
2024-04-13 00:45:35 -04:00
parent faf3794c8f
commit 2ea07ba9fe
13 changed files with 214 additions and 251 deletions

View File

@@ -1,7 +1,6 @@
<script lang="ts">
import SearchBar from '$lib/components/util/searchBar.svelte'
import type { LayoutData } from './$types'
import { currentlyPlaying } from '$lib/stores'
import NavTab from '$lib/components/navbar/navTab.svelte'
import PlaylistTab from '$lib/components/navbar/playlistTab.svelte'
import MediaPlayer from '$lib/components/media/mediaPlayer.svelte'
@@ -46,8 +45,6 @@
<slot />
</section>
<section class="absolute bottom-0 z-40 grid max-h-full w-full place-items-center">
{#if $currentlyPlaying}
<MediaPlayer song={$currentlyPlaying} />
{/if}
<MediaPlayer />
</section>
</div>

View File

@@ -1,4 +1,5 @@
<script lang="ts">
import { currentlyPlaying } from '$lib/stores'
import type { PageServerData } from './$types'
export let data: PageServerData
@@ -7,7 +8,12 @@
{#if data.searchResults}
{#await data.searchResults then searchResults}
{#each searchResults as searchResult}
<div>{searchResult.name} - {searchResult.type}</div>
<button
on:click={() => {
if (searchResult.type === 'song') $currentlyPlaying = searchResult
}}
class="block bg-neutral-925">{searchResult.name} - {searchResult.type}</button
>
{/each}
{/await}
{/if}

View File

@@ -25,7 +25,7 @@
<div class="relative aspect-square h-full p-1">
<img src={serviceData.icon} alt="{serviceData.displayName} icon" />
{#if 'profilePicture' in connectionInfo}
<img src={connectionInfo.profilePicture} alt="" class="absolute bottom-0 right-0 aspect-square h-5 rounded-full" />
<img src="/api/remoteImage?url={connectionInfo.profilePicture}" alt="" class="absolute bottom-0 right-0 aspect-square h-5 rounded-full" />
{/if}
</div>
<div>

View File

@@ -1,25 +1,30 @@
import type { RequestHandler } from '@sveltejs/kit'
import { Connections } from '$lib/server/connections'
export const GET: RequestHandler = async ({ url }) => {
export const GET: RequestHandler = async ({ url, request }) => {
const connectionId = url.searchParams.get('connection')
const id = url.searchParams.get('id')
if (!(connectionId && id)) return new Response('Missing query parameter', { status: 400 })
const range = request.headers.get('range')
const connection = Connections.getConnections([connectionId])[0]
const stream = await connection.getAudioStream(id)
if (!stream.body) throw new Error(`Audio fetch did not return valid ReadableStream (Connection: ${connection.id})`)
const fetchStream = async (): Promise<Response> => {
const MAX_TRIES = 5
let tries = 0
while (tries < MAX_TRIES) {
++tries
const stream = await connection.getAudioStream(id, range).catch((reason) => {
console.error(`Audio stream fetch failed: ${reason}`)
return null
})
if (!stream || !stream.body) continue
const contentLength = stream.headers.get('Content-Length')
if (!contentLength || isNaN(Number(contentLength))) throw new Error(`Audio fetch did not return valid Content-Length header (Connection: ${connection.id})`)
return stream
}
const headers = new Headers({
'Content-Range': `bytes 0-${Number(contentLength) - 1}/${contentLength}`,
'Accept-Ranges': 'bytes',
'Content-Length': contentLength.toString(),
'Content-Type': 'audio/webm',
})
throw new Error(`Audio stream fetch to connection: ${connection.id} of id ${id} failed`)
}
return new Response(stream.body, { status: 206, headers })
return await fetchStream()
}

View File

@@ -6,18 +6,21 @@ export const GET: RequestHandler = async ({ url }) => {
const imageUrl = url.searchParams.get('url')
if (!imageUrl || !URL.canParse(imageUrl)) return new Response('Missing or invalid url parameter', { status: 400 })
const MAX_TRIES = 3
const fetchImage = async (): Promise<ArrayBuffer> => {
let tryCount = 0
while (tryCount < MAX_TRIES) {
++tryCount
try {
return await fetch(imageUrl).then((response) => response.arrayBuffer())
} catch (error) {
console.error(error)
continue
}
const MAX_TRIES = 3
let tries = 0
while (tries < MAX_TRIES) {
++tries
const response = await fetch(imageUrl).catch((reason) => {
console.error(`Image fetch to ${imageUrl} failed: ${reason}`)
return null
})
if (!response || !response.ok) continue
const contentType = response.headers.get('content-type')
if (!contentType || !contentType.startsWith('image')) throw new Error(`Url ${imageUrl} does not link to an image`)
return await response.arrayBuffer()
}
throw new Error('Exceed Max Retires')