diff --git a/dist/index.html b/dist/index.html index 69d121e..17caf80 100644 --- a/dist/index.html +++ b/dist/index.html @@ -5,10 +5,10 @@ Вечерний детектив - - - - + + + + diff --git a/proto/main.proto b/proto/main.proto index 31e76aa..9cf2a10 100644 --- a/proto/main.proto +++ b/proto/main.proto @@ -277,6 +277,62 @@ service EveningDetectiveServer { }; } + rpc GetPlaylists(GetPlaylistsReq) returns (GetPlaylistsRsp) { + option (google.api.http) = { get: "/api/playlists" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Музыка"; + summary: "Получить плейлисты"; + }; + } + + rpc CreatePlaylist(CreatePlaylistReq) returns (CreatePlaylistRsp) { + option (google.api.http) = { post: "/api/playlists" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Музыка"; + summary: "Создать плейлист"; + }; + } + + rpc UpdatePlaylist(UpdatePlaylistReq) returns (UpdatePlaylistRsp) { + option (google.api.http) = { put: "/api/playlists/{ID}" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Музыка"; + summary: "Изменить плейлист"; + }; + } + + rpc DeletePlaylist(DeletePlaylistReq) returns (DeletePlaylistRsp) { + option (google.api.http) = { delete: "/api/playlists/{ID}" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Музыка"; + summary: "Удалить плейлист"; + }; + } + + rpc UploadPlaylistTrack(UploadPlaylistTrackReq) returns (UploadPlaylistTrackRsp) { + option (google.api.http) = { post: "/api/playlists/{playlistID}/tracks" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Музыка"; + summary: "Загрузить MP3 в плейлист"; + }; + } + + rpc DeletePlaylistTrack(DeletePlaylistTrackReq) returns (DeletePlaylistTrackRsp) { + option (google.api.http) = { delete: "/api/playlists/{playlistID}/tracks/{trackID}" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Музыка"; + summary: "Удалить MP3 из плейлиста"; + }; + } + + rpc SetScenarioPlaylist(SetScenarioPlaylistReq) returns (SetScenarioPlaylistRsp) { + option (google.api.http) = { put: "/api/scenarios/{ID}/playlist" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Музыка"; + summary: "Назначить плейлист сценарию"; + }; + } + rpc AddScenario(AddScenarioReq) returns (AddScenarioRsp) { option (google.api.http) = { post: "/api/scenario" @@ -596,6 +652,31 @@ service EveningDetectiveServer { }; } + rpc GetGameMusic(GetGameMusicReq) returns (GetGameMusicRsp) { + option (google.api.http) = { get: "/api/games/{gameID}/music" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Музыка"; + summary: "Получить музыку игры"; + }; + } + + rpc SetGameMusic(SetGameMusicReq) returns (SetGameMusicRsp) { + option (google.api.http) = { post: "/api/games/{gameID}/music" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Музыка"; + summary: "Изменить музыку игры"; + }; + } + + rpc AdvanceScreenMusic(AdvanceScreenMusicReq) returns (AdvanceScreenMusicRsp) { + option (google.api.http) = { post: "/api/screens/music/advance" body: "*" }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + security: { security_requirement: {} } + tags: "Музыка"; + summary: "Перейти к следующему треку экрана"; + }; + } + // Привязать экран к игре. Требует авторизацию организатора, автора или администратора. rpc BindScreen(BindScreenReq) returns (BindScreenRsp) { option (google.api.http) = { @@ -688,18 +769,18 @@ service EveningDetectiveServer { rpc GetScreenPlan(GetScreenPlanReq) returns (GetScreenPlanRsp) { option (google.api.http) = { get: "/api/games/{gameID}/screen-plan" }; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { - tags: "Экраны"; + tags : "Экраны"; summary: "Получить план экранов"; }; } rpc SaveScreenPlan(SaveScreenPlanReq) returns (SaveScreenPlanRsp) { option (google.api.http) = { - put: "/api/games/{gameID}/screen-plan" + put : "/api/games/{gameID}/screen-plan" body: "*" }; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { - tags: "Экраны"; + tags : "Экраны"; summary: "Сохранить план экранов"; }; } @@ -710,7 +791,7 @@ service EveningDetectiveServer { body: "*" }; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { - tags: "Экраны"; + tags : "Экраны"; summary: "Перейти на шаг плана экранов"; }; } @@ -1164,6 +1245,73 @@ message DownloadFileReq { string filename = 1; } +message PlaylistTrack { + int32 ID = 1; + string name = 2; + string filename = 3; + int32 position = 4; +} + +message Playlist { + int32 ID = 1; + string name = 2; + repeated PlaylistTrack tracks = 3; +} + +message GetPlaylistsReq {} + +message GetPlaylistsRsp { + repeated Playlist playlists = 1; +} + +message CreatePlaylistReq { + string name = 1; +} + +message CreatePlaylistRsp { + Playlist playlist = 1; +} + +message UpdatePlaylistReq { + int32 ID = 1; + string name = 2; +} + +message UpdatePlaylistRsp { + Playlist playlist = 1; +} + +message DeletePlaylistReq { + int32 ID = 1; +} + +message DeletePlaylistRsp {} + +message UploadPlaylistTrackReq { + int32 playlistID = 1; + string name = 2; + string filename = 3; + bytes data = 4; +} + +message UploadPlaylistTrackRsp { + Playlist playlist = 1; +} + +message DeletePlaylistTrackReq { + int32 playlistID = 1; + int32 trackID = 2; +} + +message DeletePlaylistTrackRsp {} + +message SetScenarioPlaylistReq { + int32 ID = 1; + int32 playlistID = 2; +} + +message SetScenarioPlaylistRsp {} + message AddScenarioReq { string name = 1; } @@ -1206,6 +1354,7 @@ message Scenario { double average_rating = 11; int32 reviews_count = 12; string difficulty = 13; + optional int32 music_playlist_id = 14 [json_name = "music_playlist_id"]; } message Story { @@ -1487,6 +1636,7 @@ message ScreenState { int64 timer_remaining_seconds = 18; ScreenTimerState timer_state = 19; string text = 20; + MusicState music = 21; } message CreateScreenSessionReq {} @@ -1513,6 +1663,57 @@ message GetGameScreensRsp { repeated ScreenState screens = 1; } +enum MusicPlaybackState { + MUSIC_PLAYBACK_UNSPECIFIED = 0; + MUSIC_PLAYBACK_STOPPED = 1; + MUSIC_PLAYBACK_PLAYING = 2; + MUSIC_PLAYBACK_PAUSED = 3; +} + +message MusicState { + Playlist playlist = 1; + int32 track_index = 2; + int64 position_ms = 3; + int32 volume = 4; + bool loop = 5; + MusicPlaybackState playback = 6; + int64 state_version = 7; +} + +message GetGameMusicReq { + int32 gameID = 1; +} + +message GetGameMusicRsp { + repeated Playlist playlists = 1; + MusicState state = 2; + int32 scenario_playlist_id = 3; + int32 game_playlist_id = 4; +} + +message SetGameMusicReq { + int32 gameID = 1; + int32 playlistID = 2; + int32 track_index = 3; + int64 position_ms = 4; + int32 volume = 5; + bool loop = 6; + MusicPlaybackState playback = 7; +} + +message SetGameMusicRsp { + MusicState state = 1; +} + +message AdvanceScreenMusicReq { + string screenID = 1; + string accessToken = 2; +} + +message AdvanceScreenMusicRsp { + MusicState state = 1; +} + message BindScreenReq { string screenID = 1; string accessToken = 2; @@ -1603,8 +1804,8 @@ message ScreenPlanStep { } message ScreenPlan { - repeated ScreenPlanStep steps = 1; - int32 current_step = 2; + repeated ScreenPlanStep steps = 1; + int32 current_step = 2; } message GetScreenPlanReq { @@ -1616,9 +1817,9 @@ message GetScreenPlanRsp { } message SaveScreenPlanReq { - int32 gameID = 1; - repeated ScreenPlanStep steps = 2; - int32 current_step = 3; + int32 gameID = 1; + repeated ScreenPlanStep steps = 2; + int32 current_step = 3; } message SaveScreenPlanRsp { @@ -1626,7 +1827,7 @@ message SaveScreenPlanRsp { } message SetScreenPlanStepReq { - int32 gameID = 1; + int32 gameID = 1; int32 current_step = 2; } diff --git a/src/api/generated/crabs/evening_detective_server/index.ts b/src/api/generated/crabs/evening_detective_server/index.ts index 5406cb5..bb93276 100644 --- a/src/api/generated/crabs/evening_detective_server/index.ts +++ b/src/api/generated/crabs/evening_detective_server/index.ts @@ -31,6 +31,11 @@ export type ScreenCommand = | "SCREEN_COMMAND_TIMER_PAUSE" | "SCREEN_COMMAND_TIMER_RESUME" | "SCREEN_COMMAND_TIMER_RESET"; +export type MusicPlaybackState = + | "MUSIC_PLAYBACK_UNSPECIFIED" + | "MUSIC_PLAYBACK_STOPPED" + | "MUSIC_PLAYBACK_PLAYING" + | "MUSIC_PLAYBACK_PAUSED"; export type PingReq = { }; @@ -197,6 +202,77 @@ export type DownloadFileReq = { filename: string | undefined; }; +export type PlaylistTrack = { + ID: number | undefined; + name: string | undefined; + filename: string | undefined; + position: number | undefined; +}; + +export type Playlist = { + ID: number | undefined; + name: string | undefined; + tracks: PlaylistTrack[] | undefined; +}; + +export type GetPlaylistsReq = { +}; + +export type GetPlaylistsRsp = { + playlists: Playlist[] | undefined; +}; + +export type CreatePlaylistReq = { + name: string | undefined; +}; + +export type CreatePlaylistRsp = { + playlist: Playlist | undefined; +}; + +export type UpdatePlaylistReq = { + ID: number | undefined; + name: string | undefined; +}; + +export type UpdatePlaylistRsp = { + playlist: Playlist | undefined; +}; + +export type DeletePlaylistReq = { + ID: number | undefined; +}; + +export type DeletePlaylistRsp = { +}; + +export type UploadPlaylistTrackReq = { + playlistID: number | undefined; + name: string | undefined; + filename: string | undefined; + data: string | undefined; +}; + +export type UploadPlaylistTrackRsp = { + playlist: Playlist | undefined; +}; + +export type DeletePlaylistTrackReq = { + playlistID: number | undefined; + trackID: number | undefined; +}; + +export type DeletePlaylistTrackRsp = { +}; + +export type SetScenarioPlaylistReq = { + ID: number | undefined; + playlistID: number | undefined; +}; + +export type SetScenarioPlaylistRsp = { +}; + export type AddScenarioReq = { name: string | undefined; }; @@ -226,6 +302,7 @@ export type Scenario = { averageRating: number | undefined; reviewsCount: number | undefined; difficulty: string | undefined; + music_playlist_id?: number; }; export type Story = { @@ -504,6 +581,17 @@ export type ScreenState = { timerRemainingSeconds: number | undefined; timerState: ScreenTimerState | undefined; text: string | undefined; + music: MusicState | undefined; +}; + +export type MusicState = { + playlist: Playlist | undefined; + trackIndex: number | undefined; + positionMs: number | undefined; + volume: number | undefined; + loop: boolean | undefined; + playback: MusicPlaybackState | undefined; + stateVersion: number | undefined; }; export type CreateScreenSessionReq = { @@ -531,6 +619,40 @@ export type GetGameScreensRsp = { screens: ScreenState[] | undefined; }; +export type GetGameMusicReq = { + gameID: number | undefined; +}; + +export type GetGameMusicRsp = { + playlists: Playlist[] | undefined; + state: MusicState | undefined; + scenarioPlaylistId: number | undefined; + gamePlaylistId: number | undefined; +}; + +export type SetGameMusicReq = { + gameID: number | undefined; + playlistID: number | undefined; + trackIndex: number | undefined; + positionMs: number | undefined; + volume: number | undefined; + loop: boolean | undefined; + playback: MusicPlaybackState | undefined; +}; + +export type SetGameMusicRsp = { + state: MusicState | undefined; +}; + +export type AdvanceScreenMusicReq = { + screenID: string | undefined; + accessToken: string | undefined; +}; + +export type AdvanceScreenMusicRsp = { + state: MusicState | undefined; +}; + export type BindScreenReq = { screenID: string | undefined; accessToken: string | undefined; @@ -1013,6 +1135,13 @@ export interface EveningDetectiveServer { GetPermissions(request: GetPermissionsReq): Promise; UploadFile(request: UploadFileReq): Promise; DownloadFile(request: DownloadFileReq): Promise; + GetPlaylists(request: GetPlaylistsReq): Promise; + CreatePlaylist(request: CreatePlaylistReq): Promise; + UpdatePlaylist(request: UpdatePlaylistReq): Promise; + DeletePlaylist(request: DeletePlaylistReq): Promise; + UploadPlaylistTrack(request: UploadPlaylistTrackReq): Promise; + DeletePlaylistTrack(request: DeletePlaylistTrackReq): Promise; + SetScenarioPlaylist(request: SetScenarioPlaylistReq): Promise; AddScenario(request: AddScenarioReq): Promise; GetMyScenarios(request: GetMyScenariosReq): Promise; GetScenariosCatalog(request: GetScenariosCatalogReq): Promise; @@ -1046,6 +1175,9 @@ export interface EveningDetectiveServer { // Получить состояние экрана по его токену. Вызывается самим экраном. GetScreenState(request: GetScreenStateReq): Promise; GetGameScreens(request: GetGameScreensReq): Promise; + GetGameMusic(request: GetGameMusicReq): Promise; + SetGameMusic(request: SetGameMusicReq): Promise; + AdvanceScreenMusic(request: AdvanceScreenMusicReq): Promise; // Привязать экран к игре. Требует авторизацию организатора, автора или администратора. BindScreen(request: BindScreenReq): Promise; // Отвязать экран от игры. Требует авторизацию организатора, автора или администратора. @@ -1477,6 +1609,143 @@ export function createEveningDetectiveServerClient( method: "DownloadFile", }) as Promise; }, + GetPlaylists(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + const path = `api/playlists`; // eslint-disable-line quotes + const body = null; + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "GET", + body, + }, { + service: "EveningDetectiveServer", + method: "GetPlaylists", + }) as Promise; + }, + CreatePlaylist(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + const path = `api/playlists`; // eslint-disable-line quotes + const body = JSON.stringify(request); + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "POST", + body, + }, { + service: "EveningDetectiveServer", + method: "CreatePlaylist", + }) as Promise; + }, + UpdatePlaylist(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + if (!request.ID) { + throw new Error("missing required field request.ID"); + } + const path = `api/playlists/${request.ID}`; // eslint-disable-line quotes + const body = JSON.stringify(request); + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "PUT", + body, + }, { + service: "EveningDetectiveServer", + method: "UpdatePlaylist", + }) as Promise; + }, + DeletePlaylist(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + if (!request.ID) { + throw new Error("missing required field request.ID"); + } + const path = `api/playlists/${request.ID}`; // eslint-disable-line quotes + const body = null; + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "DELETE", + body, + }, { + service: "EveningDetectiveServer", + method: "DeletePlaylist", + }) as Promise; + }, + UploadPlaylistTrack(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + if (!request.playlistID) { + throw new Error("missing required field request.playlistID"); + } + const path = `api/playlists/${request.playlistID}/tracks`; // eslint-disable-line quotes + const body = JSON.stringify(request); + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "POST", + body, + }, { + service: "EveningDetectiveServer", + method: "UploadPlaylistTrack", + }) as Promise; + }, + DeletePlaylistTrack(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + if (!request.playlistID) { + throw new Error("missing required field request.playlistID"); + } + if (!request.trackID) { + throw new Error("missing required field request.trackID"); + } + const path = `api/playlists/${request.playlistID}/tracks/${request.trackID}`; // eslint-disable-line quotes + const body = null; + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "DELETE", + body, + }, { + service: "EveningDetectiveServer", + method: "DeletePlaylistTrack", + }) as Promise; + }, + SetScenarioPlaylist(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + if (!request.ID) { + throw new Error("missing required field request.ID"); + } + const path = `api/scenarios/${request.ID}/playlist`; // eslint-disable-line quotes + const body = JSON.stringify(request); + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "PUT", + body, + }, { + service: "EveningDetectiveServer", + method: "SetScenarioPlaylist", + }) as Promise; + }, AddScenario(request) { // eslint-disable-line @typescript-eslint/no-unused-vars const path = `api/scenario`; // eslint-disable-line quotes const body = JSON.stringify(request); @@ -2045,6 +2314,63 @@ export function createEveningDetectiveServerClient( method: "GetGameScreens", }) as Promise; }, + GetGameMusic(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + if (!request.gameID) { + throw new Error("missing required field request.gameID"); + } + const path = `api/games/${request.gameID}/music`; // eslint-disable-line quotes + const body = null; + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "GET", + body, + }, { + service: "EveningDetectiveServer", + method: "GetGameMusic", + }) as Promise; + }, + SetGameMusic(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + if (!request.gameID) { + throw new Error("missing required field request.gameID"); + } + const path = `api/games/${request.gameID}/music`; // eslint-disable-line quotes + const body = JSON.stringify(request); + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "POST", + body, + }, { + service: "EveningDetectiveServer", + method: "SetGameMusic", + }) as Promise; + }, + AdvanceScreenMusic(request) { // eslint-disable-line @typescript-eslint/no-unused-vars + const path = `api/screens/music/advance`; // eslint-disable-line quotes + const body = JSON.stringify(request); + const queryParams: string[] = []; + let uri = path; + if (queryParams.length > 0) { + uri += `?${queryParams.join("&")}` + } + return handler({ + path: uri, + method: "POST", + body, + }, { + service: "EveningDetectiveServer", + method: "AdvanceScreenMusic", + }) as Promise; + }, BindScreen(request) { // eslint-disable-line @typescript-eslint/no-unused-vars if (!request.screenID) { throw new Error("missing required field request.screenID"); diff --git a/src/components/GamePage.vue b/src/components/GamePage.vue index f8c9cef..f77f96b 100644 --- a/src/components/GamePage.vue +++ b/src/components/GamePage.vue @@ -27,6 +27,9 @@ import type { Game, GameReview, MapItem, + MusicPlaybackState, + MusicState, + Playlist, Question, ResultsQuestion, ScreenCommand, @@ -114,6 +117,8 @@ const activeTeamCount = computed( () => (game.value.teams || []).filter((team) => team.participationStatus !== 'cancelled').length, ) const screens = ref([]) +const playlists = ref([]) +const music = ref() const screenPlan = ref() const screenPlanDraft = ref([]) const screenPlanDirty = ref(false) @@ -185,6 +190,17 @@ async function getGameScreens() { } getGameScreens() +async function getGameMusic() { + try { + const res = await client.GetGameMusic({ gameID: Number(gameId) }) + playlists.value = res.playlists || [] + music.value = res.state + } catch (err) { + message.error(err instanceof Error ? err.message : 'Не удалось получить музыку игры') + } +} +getGameMusic() + function isScreenPlanEditing() { return screenPlanMode.value === 'edit' } @@ -360,6 +376,81 @@ async function setSoundScreen(screen: ScreenState) { await getGameScreens() } +async function setGameMusic(change: { + playlistID?: number + trackIndex?: number + positionMs?: number + playback?: MusicPlaybackState + loop?: boolean + volume?: number +}) { + const current = music.value + try { + const res = await client.SetGameMusic({ + gameID: Number(gameId), + playlistID: change.playlistID ?? current?.playlist?.ID ?? 0, + trackIndex: change.trackIndex ?? current?.trackIndex ?? 0, + positionMs: change.positionMs ?? current?.positionMs ?? 0, + volume: change.volume ?? current?.volume ?? 100, + loop: change.loop ?? current?.loop ?? false, + playback: change.playback ?? current?.playback ?? 'MUSIC_PLAYBACK_STOPPED', + }) + music.value = res.state + } catch (err) { + message.error(err instanceof Error ? err.message : 'Не удалось изменить музыку') + } +} + +async function createPlaylist(name: string) { + try { + await client.CreatePlaylist({ name }) + await getGameMusic() + } catch (err) { + message.error(err instanceof Error ? err.message : 'Не удалось создать плейлист') + } +} + +async function deletePlaylist(id: number) { + try { + await client.DeletePlaylist({ ID: id }) + await getGameMusic() + } catch (err) { + message.error(err instanceof Error ? err.message : 'Не удалось удалить плейлист') + } +} + +async function uploadTrack(file: File) { + const playlistID = music.value?.playlist?.ID + if (!playlistID) { + message.warning('Сначала выберите плейлист') + return + } + try { + const data = uint8ToBase64(new Uint8Array(await file.arrayBuffer())) + await client.UploadPlaylistTrack({ playlistID, name: file.name, filename: file.name, data }) + await getGameMusic() + } catch (err) { + message.error(err instanceof Error ? err.message : 'Не удалось загрузить MP3') + } +} + +async function deleteTrack(id: number) { + const playlistID = music.value?.playlist?.ID + if (!playlistID) return + try { + await client.DeletePlaylistTrack({ playlistID, trackID: id }) + await getGameMusic() + } catch (err) { + message.error(err instanceof Error ? err.message : 'Не удалось удалить трек') + } +} + +function uint8ToBase64(data: Uint8Array): string { + let binary = '' + for (let i = 0; i < data.length; i += 0x8000) binary += String.fromCharCode(...data.subarray(i, i + 0x8000)) + return btoa(binary) +} + async function unbindScreen(screen: ScreenState) { await client.UnbindScreen({ screenID: screen.ID }) await getGameScreens() @@ -539,6 +630,7 @@ async function issueTickets() { useSimplePolling(() => { getGame(Number(gameId)) getGameScreens() + getGameMusic() getScreenPlan() getGameMap() }, 2000) @@ -811,9 +903,16 @@ useSimplePolling(() => { diff --git a/src/components/GameScreensPanel.vue b/src/components/GameScreensPanel.vue index f0f2322..0687665 100644 --- a/src/components/GameScreensPanel.vue +++ b/src/components/GameScreensPanel.vue @@ -1,23 +1,35 @@