From c5408d76b6f7a483cb318c3f7b3cae71973e80a7 Mon Sep 17 00:00:00 2001 From: Eclypsed Date: Fri, 5 Apr 2024 02:00:17 -0400 Subject: [PATCH] ConnectionInfo and the db ConnectionRow types are now completely seperate things. Started on audio fetching yay! --- src/app.d.ts | 17 +++++ src/lib/components/media/mediaCard.svelte | 17 +++-- src/lib/components/media/mediaPlayer.svelte | 19 +++++ .../media/scrollableCardMenu.svelte | 2 +- src/lib/server/connections.ts | 14 ++-- src/lib/server/db.ts | 66 ++++++++-------- src/lib/server/jellyfin.ts | 45 ++++------- src/lib/server/youtube-music.ts | 72 ++++++++---------- src/lib/stores.ts | 2 +- src/routes/(app)/+layout.svelte | 75 +++++++------------ src/routes/(app)/+page.server.ts | 8 +- src/routes/(app)/+page.svelte | 1 - src/routes/(app)/user/+page.server.ts | 3 +- src/routes/(app)/user/+page.svelte | 11 +-- .../(app)/user/connectionProfile.svelte | 20 +++-- src/routes/api/audio/+server.ts | 39 ++++++++++ src/routes/api/connections/+server.ts | 2 +- .../api/users/[userId]/connections/+server.ts | 2 +- src/routes/settings/+layout.svelte | 57 -------------- src/routes/settings/+page.svelte | 1 - 20 files changed, 220 insertions(+), 253 deletions(-) create mode 100644 src/lib/components/media/mediaPlayer.svelte create mode 100644 src/routes/api/audio/+server.ts delete mode 100644 src/routes/settings/+layout.svelte delete mode 100644 src/routes/settings/+page.svelte diff --git a/src/app.d.ts b/src/app.d.ts index 6d684f0..1cc1fde 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -24,11 +24,28 @@ declare global { passwordHash: string } + type ConnectionInfo = { + id: string + userId: string + } & ({ + type: 'jellyfin' + serverUrl: string + serverName: string + jellyfinUserId: string + username: string + } | { + type: 'youtube-music' + youtubeUserId: string + username: string + profilePicture: string + }) + interface Connection { public id: string getRecommendations: () => Promise<(Song | Album | Artist | Playlist)[]> getConnectionInfo: () => Promise search: (searchTerm: string, filter?: 'song' | 'album' | 'artist' | 'playlist') => Promise<(Song | Album | Artist | Playlist)[]> + getSongAudio: (id: string) => Promise> } // These Schemas should only contain general info data that is necessary for data fetching purposes. // They are NOT meant to be stores for large amounts of data, i.e. Don't include the data for every single song the Playlist type. diff --git a/src/lib/components/media/mediaCard.svelte b/src/lib/components/media/mediaCard.svelte index 0ca8a72..c2ed557 100644 --- a/src/lib/components/media/mediaCard.svelte +++ b/src/lib/components/media/mediaCard.svelte @@ -1,10 +1,17 @@
@@ -17,7 +24,7 @@
{/if} - + @@ -39,12 +46,6 @@ diff --git a/src/lib/components/media/scrollableCardMenu.svelte b/src/lib/components/media/scrollableCardMenu.svelte index fd258b0..a2b241c 100644 --- a/src/lib/components/media/scrollableCardMenu.svelte +++ b/src/lib/components/media/scrollableCardMenu.svelte @@ -1,6 +1,6 @@ -{#if $pageWidth >= 768} -
-
-
- {#each data.navTabs as nav} - - {/each} -
-
- {#each data.playlistTabs as playlist} - setTooltip(event.detail.x, event.detail.y, event.detail.content)} on:mouseleave={() => (playlistTooltip.style.display = 'none')} /> - {/each} -
- +
+
+
+ {#each data.navTabs as nav} + + {/each} +
+
+ {#each data.playlistTabs as playlist} + setTooltip(event.detail.x, event.detail.y, event.detail.content)} on:mouseleave={() => (playlistTooltip.style.display = 'none')} /> + {/each} +
+ -
-
- -
- -
-
- -
-{:else} -
-
- -
-
- - -
-
-{/if} +
+
+ +
+ +
+
+ {#if $currentlyPlaying} + + {/if} +
+
diff --git a/src/routes/(app)/+page.server.ts b/src/routes/(app)/+page.server.ts index 20eebe8..19b2bf3 100644 --- a/src/routes/(app)/+page.server.ts +++ b/src/routes/(app)/+page.server.ts @@ -2,9 +2,11 @@ import { SECRET_INTERNAL_API_KEY } from '$env/static/private' import type { PageServerLoad } from './$types' export const load: PageServerLoad = async ({ locals, fetch }) => { - const recommendationResponse = await fetch(`/api/users/${locals.user.id}/recommendations`, { headers: { apikey: SECRET_INTERNAL_API_KEY } }) - const recommendationData = await recommendationResponse.json() - const { recommendations } = recommendationData + const recommendationResponse = await fetch(`/api/users/${locals.user.id}/recommendations`, { + headers: { apikey: SECRET_INTERNAL_API_KEY }, + }).then((response) => response.json()) + + const recommendations: (Song | Album | Artist | Playlist)[] = recommendationResponse.recommendations return { recommendations } } diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index 0f95936..e5eda48 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -1,6 +1,5 @@
{serviceData.displayName} icon - {#if profilePicture} - + {#if 'profilePicture' in connectionInfo} + {/if}
-
{serviceData.displayName}
+
{serviceData.displayName} - {connectionInfo.id}
{subHeaderItems.join(' - ')}
@@ -40,7 +46,7 @@ Delete Connection - + {/if}
diff --git a/src/routes/api/audio/+server.ts b/src/routes/api/audio/+server.ts new file mode 100644 index 0000000..78694e5 --- /dev/null +++ b/src/routes/api/audio/+server.ts @@ -0,0 +1,39 @@ +import type { RequestHandler } from '@sveltejs/kit' +import { Connections } from '$lib/server/connections' +import ytdl from 'ytdl-core' + +export const GET: RequestHandler = async ({ url, request }) => { + const connectionId = url.searchParams.get('connectionId') + const id = url.searchParams.get('id') + if (!(connectionId && id)) return new Response('Missing query parameter', { status: 400 }) + const range = request.headers.get('range') + if (!range) return new Response('Missing Range Header') + + const videourl = `http://www.youtube.com/watch?v=${id}` + + const videoInfo = await ytdl.getInfo(videourl) + const format = ytdl.chooseFormat(videoInfo.formats, { quality: 'highestaudio', filter: 'audioonly' }) + + const audioSize = format.contentLength + const CHUNK_SIZE = 5 * 10 ** 6 + const start = Number(range.replace(/\D/g, '')) + const end = Math.min(start + CHUNK_SIZE, Number(audioSize) - 1) + const contentLength = end - start + 1 + + const headers = new Headers({ + 'Content-Range': `bytes ${start}-${end}/${audioSize}`, + 'Accept-Ranges': 'bytes', + 'Content-Length': contentLength.toString(), + 'Content-Type': 'audio/webm', + }) + + const partialStream = ytdl(videourl, { format, range: { start, end } }) + + // @ts-ignore IDK enough about streaming to understand what the problem is here + // but it appears that ytdl has a custom version of a readable stream type they use internally + // and is what gets returned by ytdl(). Svelte will only allow you to send back the type ReadableStream + // so it ts gets mad if you try to send back their internal type. + // IDK to me a custom readable type seems incredibly stupid but what do I know? + // Currently haven't found a way to convert their readable to ReadableStream type, casting doesn't seem to work either. + return new Response(partialStream, { status: 206, headers }) +} diff --git a/src/routes/api/connections/+server.ts b/src/routes/api/connections/+server.ts index f95a662..4693496 100644 --- a/src/routes/api/connections/+server.ts +++ b/src/routes/api/connections/+server.ts @@ -1,5 +1,5 @@ import type { RequestHandler } from '@sveltejs/kit' -import { Connections, type ConnectionInfo } from '$lib/server/connections' +import { Connections } from '$lib/server/connections' export const GET: RequestHandler = async ({ url }) => { const ids = url.searchParams.get('ids')?.replace(/\s/g, '').split(',') diff --git a/src/routes/api/users/[userId]/connections/+server.ts b/src/routes/api/users/[userId]/connections/+server.ts index 3df1b33..6ec20ca 100644 --- a/src/routes/api/users/[userId]/connections/+server.ts +++ b/src/routes/api/users/[userId]/connections/+server.ts @@ -1,5 +1,5 @@ import type { RequestHandler } from '@sveltejs/kit' -import { Connections, type ConnectionInfo } from '$lib/server/connections' +import { Connections } from '$lib/server/connections' export const GET: RequestHandler = async ({ params }) => { const userId = params.userId! diff --git a/src/routes/settings/+layout.svelte b/src/routes/settings/+layout.svelte deleted file mode 100644 index f1a1c6d..0000000 --- a/src/routes/settings/+layout.svelte +++ /dev/null @@ -1,57 +0,0 @@ - - -
-

- - goto('/user')}> - - - - Settings -

-
- - -
-
diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte deleted file mode 100644 index 4c288a3..0000000 --- a/src/routes/settings/+page.svelte +++ /dev/null @@ -1 +0,0 @@ -

Main Settings Page