Fixed audio api endpoint, media player mostly functional
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user