From 8e60d11e62d4a253b1a8fc1ab25af2ad98fd68b3 Mon Sep 17 00:00:00 2001 From: sleepyfran Date: Thu, 22 Aug 2024 15:04:12 +0200 Subject: [PATCH] Initial implementation of media player --- packages/components/add-provider/package.json | 3 +- .../add-provider/src/AddProvider.tsx | 55 ++++++++++- packages/components/library/package.json | 3 +- packages/components/library/src/Library.tsx | 30 +++--- .../components/provider-status/package.json | 3 +- .../provider-status/src/ProviderStatus.tsx | 9 +- packages/core/types/src/model/file-system.ts | 18 +++- packages/core/types/src/model/track.ts | 3 +- .../services/active-media-provider-cache.ts | 49 +++++++++ packages/core/types/src/services/index.ts | 4 +- .../core/types/src/services/media-player.ts | 52 ++++++++++ .../{mediaProvider.ts => media-provider.ts} | 49 +++++++++ packages/core/types/src/services/player.ts | 28 +++++- .../html-audio-media-player/index.ts | 52 ++++++++++ .../html-audio-media-player/package.json | 15 +++ .../html-audio-media-player/tsconfig.json | 7 ++ .../infrastructure/onedrive-provider/index.ts | 6 +- .../src/apis/file-url-by-id.graph-api.ts | 28 ++++++ .../src/apis/list-folder.graph-api.ts | 6 +- .../src/apis/list-root.graph-api.ts | 8 +- .../src/onedrive-provider.ts | 6 +- .../active-media-provider-cache/index.ts | 65 ++++++++++++ .../active-media-provider-cache/package.json | 15 +++ .../active-media-provider-cache/tsconfig.json | 7 ++ packages/services/bootstrap-services/index.ts | 1 + .../services/bootstrap-services/package.json | 18 ++++ .../bootstrap-services/src/app-live.layer.ts | 14 +++ .../bootstrap-services/src/vite-env.d.ts | 1 + .../services/bootstrap-services/tsconfig.json | 7 ++ packages/services/bootstrap/package.json | 1 + packages/services/bootstrap/src/layers.ts | 2 + .../services/bootstrap/src/loaders/index.ts | 1 + .../services/bootstrap/src/loaders/player.ts | 70 +++++++++++++ .../bootstrap/src/loaders/provider.ts | 30 +++--- packages/services/player/src/player.ts | 99 +++++++++++++------ .../src/sync/file-based-sync.ts | 2 +- 36 files changed, 665 insertions(+), 102 deletions(-) create mode 100644 packages/core/types/src/services/active-media-provider-cache.ts create mode 100644 packages/core/types/src/services/media-player.ts rename packages/core/types/src/services/{mediaProvider.ts => media-provider.ts} (56%) create mode 100644 packages/infrastructure/html-audio-media-player/index.ts create mode 100644 packages/infrastructure/html-audio-media-player/package.json create mode 100644 packages/infrastructure/html-audio-media-player/tsconfig.json create mode 100644 packages/infrastructure/onedrive-provider/src/apis/file-url-by-id.graph-api.ts create mode 100644 packages/services/active-media-provider-cache/index.ts create mode 100644 packages/services/active-media-provider-cache/package.json create mode 100644 packages/services/active-media-provider-cache/tsconfig.json create mode 100644 packages/services/bootstrap-services/index.ts create mode 100644 packages/services/bootstrap-services/package.json create mode 100644 packages/services/bootstrap-services/src/app-live.layer.ts create mode 100644 packages/services/bootstrap-services/src/vite-env.d.ts create mode 100644 packages/services/bootstrap-services/tsconfig.json create mode 100644 packages/services/bootstrap/src/loaders/player.ts diff --git a/packages/components/add-provider/package.json b/packages/components/add-provider/package.json index a40b79b..7abc669 100644 --- a/packages/components/add-provider/package.json +++ b/packages/components/add-provider/package.json @@ -9,6 +9,7 @@ "dependencies": { "@echo/core-types": "^1.0.0", "@echo/services-bootstrap": "^1.0.0", + "@echo/services-bootstrap-services": "^1.0.0", "@effect-rx/rx": "^0.33.8", "@effect-rx/rx-react": "^0.30.11", "effect": "^3.2.8" @@ -17,4 +18,4 @@ "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22" } -} +} \ No newline at end of file diff --git a/packages/components/add-provider/src/AddProvider.tsx b/packages/components/add-provider/src/AddProvider.tsx index b36e103..9f8106a 100644 --- a/packages/components/add-provider/src/AddProvider.tsx +++ b/packages/components/add-provider/src/AddProvider.tsx @@ -1,21 +1,36 @@ import { + ActiveMediaProviderCache, AvailableProviders, MediaProviderMainThreadBroadcastChannel, type Authentication, type AuthenticationInfo, type FolderMetadata, + type MediaPlayer, type MediaProvider, type ProviderMetadata, } from "@echo/core-types"; -import { LazyLoadedProvider, MainLive } from "@echo/services-bootstrap"; +import { LazyLoadedProvider } from "@echo/services-bootstrap"; +import { AppLive } from "@echo/services-bootstrap-services"; +import { LazyLoadedMediaPlayer } from "@echo/services-bootstrap/src/loaders"; import { Rx } from "@effect-rx/rx"; import { useRx } from "@effect-rx/rx-react"; import { Effect, Match } from "effect"; import { useCallback, useEffect, useMemo } from "react"; -const runtime = Rx.runtime(MainLive); -const loadProviderFn = runtime.fn((metadata: ProviderMetadata) => - LazyLoadedProvider.load(metadata), +const runtime = Rx.runtime(AppLive); +const loadProviderFn = runtime.fn(LazyLoadedProvider.load); +const loadMediaPlayerFn = runtime.fn( + ({ + metadata, + authInfo, + }: { + metadata: ProviderMetadata; + authInfo: AuthenticationInfo; + }) => + Effect.gen(function* () { + const { createMediaPlayer } = yield* LazyLoadedMediaPlayer.load(metadata); + return yield* createMediaPlayer(authInfo); + }), ); export const AddProvider = () => { @@ -105,6 +120,18 @@ const listRootFn = runtime.fn( (mediaProvider: MediaProvider) => mediaProvider.listRoot, ); +const addMediaProviderToCacheFn = runtime.fn( + ({ + metadata, + provider, + player, + }: { + metadata: ProviderMetadata; + provider: MediaProvider; + player: MediaPlayer; + }) => ActiveMediaProviderCache.add(metadata, provider, player), +); + const SelectRoot = ({ authInfo, metadata, @@ -114,16 +141,34 @@ const SelectRoot = ({ metadata: ProviderMetadata; createMediaProvider: (authInfo: AuthenticationInfo) => MediaProvider; }) => { + const [mediaPlayerStatus, createMediaPlayer] = useRx(loadMediaPlayerFn); + const mediaProvider = useMemo( () => createMediaProvider(authInfo), [createMediaProvider, authInfo], ); const [listStatus, listRoot] = useRx(listRootFn); + const [, addMediaProviderToCache] = useRx(addMediaProviderToCacheFn); useEffect(() => { listRoot(mediaProvider); - }); + createMediaPlayer({ metadata, authInfo }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // TODO: Refactor this mess somehow? + useEffect(() => { + Match.value(mediaPlayerStatus).pipe( + Match.tag("Success", ({ value: player }) => { + addMediaProviderToCache({ + metadata, + provider: mediaProvider, + player, + }); + }), + ); + }, [mediaPlayerStatus, mediaProvider, metadata, addMediaProviderToCache]); return (
diff --git a/packages/components/library/package.json b/packages/components/library/package.json index ddc8cfd..fb1c6c5 100644 --- a/packages/components/library/package.json +++ b/packages/components/library/package.json @@ -8,8 +8,9 @@ }, "dependencies": { "@echo/core-types": "^1.0.0", - "@echo/services-bootstrap": "^1.0.0", + "@echo/services-bootstrap-services": "^1.0.0", "@echo/services-library": "^1.0.0", + "@echo/services-player": "^1.0.0", "@effect-rx/rx": "^0.33.8", "@effect-rx/rx-react": "^0.30.11", "effect": "^3.2.8" diff --git a/packages/components/library/src/Library.tsx b/packages/components/library/src/Library.tsx index 0419cf2..a26cf70 100644 --- a/packages/components/library/src/Library.tsx +++ b/packages/components/library/src/Library.tsx @@ -1,38 +1,30 @@ -import { Library, type Track } from "@echo/core-types"; -import { MainLive } from "@echo/services-bootstrap"; +import { Library, Player } from "@echo/core-types"; +import { AppLive } from "@echo/services-bootstrap-services"; import { Rx } from "@effect-rx/rx"; import { Layer, Stream } from "effect"; -import { Suspense, useState } from "react"; +import { Suspense } from "react"; import { LibraryLive } from "@echo/services-library"; -import { useRxSuspenseSuccess } from "@effect-rx/rx-react"; +import { PlayerLive } from "@echo/services-player"; +import { useRx, useRxSuspenseSuccess } from "@effect-rx/rx-react"; -const runtime = Rx.runtime(LibraryLive.pipe(Layer.provide(MainLive))); +const runtime = Rx.runtime( + Layer.mergeAll(LibraryLive, PlayerLive).pipe(Layer.provide(AppLive)), +); const observeLibrary = runtime.rx(Stream.unwrap(Library.observeAlbums())); +const playAlbumFn = runtime.fn(Player.playAlbum); const UserLibrary = () => { - const [src, setSrc] = useState(undefined); const albums = useRxSuspenseSuccess(observeLibrary).value; - - const playFirstTrack = (tracks: Track[]) => { - const track = tracks[0]; - switch (track.resource.type) { - case "file": - setSrc(track.resource.uri); - break; - case "api": - break; - } - }; + const [, playAlbum] = useRx(playAlbumFn); return (

-