diff --git a/chromecast/src/api.ts b/chromecast/src/api.ts index 04afde59..5aca81ea 100644 --- a/chromecast/src/api.ts +++ b/chromecast/src/api.ts @@ -81,6 +81,14 @@ export type Item = { direct: string; transmux: string; }; + nextEpisode: { + id: number, + slug: string, + }, + previousEpisode: { + id: number, + slug: string, + } }; export const getItem = async (slug: string, apiUrl: string) => { @@ -125,3 +133,13 @@ export const itemToMovie = (item: Item) => { return metadata; } +export const itemToMedia = (item: Item, apiUrl: string) => { + const media = new cast.framework.messages.MediaInformation(); + media.contentUrl = item.link.direct; + media.metadata = item.isMovie + ? itemToMovie(item) + : itemToTvMetadata(item); + media.customData = item; + media.customData.serverUrl = apiUrl; + return media; +} diff --git a/chromecast/src/index.ts b/chromecast/src/index.ts index 5a3ff202..52532960 100644 --- a/chromecast/src/index.ts +++ b/chromecast/src/index.ts @@ -18,7 +18,8 @@ * along with Kyoo. If not, see . */ -import { getItem, itemToMovie, itemToTvMetadata } from "./api"; +import { getItem, itemToMedia } from "./api"; +import { Queue } from "./queue"; const Command = cast.framework.messages.Command; const context = cast.framework.CastReceiverContext.getInstance(); @@ -35,27 +36,22 @@ playerManager.setSupportedMediaCommands( Command.STREAM_TRANSFER, ); - - playerManager.setMessageInterceptor( cast.framework.messages.MessageType.LOAD, async (loadRequestData) => { if (loadRequestData.media.contentUrl && loadRequestData.media.metadata) return loadRequestData; + const apiUrl = loadRequestData.media.customData.serverUrl; const item = await getItem( loadRequestData.media.contentId, - loadRequestData.media.customData.serverUrl, + apiUrl, ); if (!item) { return new cast.framework.messages.ErrorData(cast.framework.messages.ErrorType.LOAD_FAILED); } - loadRequestData.media.contentUrl = item.link.direct; - loadRequestData.media.metadata = item.isMovie - ? itemToMovie(item) - : itemToTvMetadata(item); - loadRequestData.media.customData = item; + loadRequestData.media = itemToMedia(item, apiUrl); return loadRequestData; }, ); -context.start(); +context.start({ queue: new Queue() }); diff --git a/chromecast/src/queue.ts b/chromecast/src/queue.ts new file mode 100644 index 00000000..927a97b9 --- /dev/null +++ b/chromecast/src/queue.ts @@ -0,0 +1,73 @@ +/* + * Kyoo - A portable and vast media library solution. + * Copyright (c) Kyoo. + * + * See AUTHORS.md and LICENSE file in the project root for full license information. + * + * Kyoo is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * Kyoo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Kyoo. If not, see . + */ + +import { QueueManager } from "chromecast-caf-receiver/cast.framework"; +import { LoadRequestData, QueueData, QueueItem } from "chromecast-caf-receiver/cast.framework.messages"; +import { getItem, Item, itemToMedia } from "./api"; + +export class Queue extends cast.framework.QueueBase { + queue: QueueManager | null; + + constructor() { + super(); + this.queue = cast.framework.CastReceiverContext.getInstance().getPlayerManager().getQueueManager(); + } + + initialize(requestData: LoadRequestData): QueueData { + if (requestData.queueData) return requestData.queueData; + + const queueData = new cast.framework.messages.QueueData(); + queueData.name = "queue"; + queueData.items = [requestData.media]; + return queueData; + } + + async nextItems(itemId?: number): Promise { + const current = this.queue?.getItems().find(x => x.itemId == itemId); + if (!current || !current.media?.contentId || !current.media.customData?.serverUrl) return []; + + const metadata = current?.media?.customData as Item; + const apiUrl = current.media?.customData.serverUrl; + if (!metadata.nextEpisode) return []; + + const item = await getItem(metadata.nextEpisode.slug, apiUrl); + if (!item) return []; + + const data = new cast.framework.messages.QueueItem(); + data.media = itemToMedia(item, apiUrl); + return [data]; + } + + async prevItems(itemId?: number): Promise { + const current = this.queue?.getItems().find(x => x.itemId == itemId); + if (!current || !current.media?.contentId || !current.media.customData?.serverUrl) return []; + + const metadata = current?.media?.customData as Item; + const apiUrl = current.media?.customData.serverUrl; + if (!metadata.previousEpisode) return []; + + const item = await getItem(metadata.previousEpisode.slug, apiUrl); + if (!item) return []; + + const data = new cast.framework.messages.QueueItem(); + data.media = itemToMedia(item, apiUrl); + return [data]; + } +} diff --git a/front/src/player/cast/state.tsx b/front/src/player/cast/state.tsx index cb7e9e5c..1fc1c3ba 100644 --- a/front/src/player/cast/state.tsx +++ b/front/src/player/cast/state.tsx @@ -91,7 +91,7 @@ export const useCastController = () => { [cast.framework.RemotePlayerEventType.DURATION_CHANGED, (event) => setDuration(event.value)], [ cast.framework.RemotePlayerEventType.MEDIA_INFO_CHANGED, - () => setMedia(player.mediaInfo?.customData), + () => setMedia(player.mediaInfo?.customData ?? null), ], ];