Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ coverage
*.local
.yarn/*
.yarn
**/wwwroot/client-build
wwwroot/**

/cypress/videos/
/cypress/screenshots/
Expand Down
10 changes: 10 additions & 0 deletions .idea/.idea.LowPressureZone/.idea/dataSources.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/.idea.LowPressureZone/.idea/db-forest-config.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 1 addition & 31 deletions LowPressureZone.sln
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1. Main Application", "1. M
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2. Auxiliary", "2. Auxiliary", "{21C27C36-5628-47D2-A6B4-21A4748CEE6A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3. Core", "3. Core", "{C8CE0E2B-370A-43AB-888A-65BF960BA18B}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3. Infrastructure", "3. Infrastructure", "{C8CE0E2B-370A-43AB-888A-65BF960BA18B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LowPressureZone.Core", "src\server\LowPressureZone.Core\LowPressureZone.Core.csproj", "{6F15A668-3BE3-469D-9983-87CD9936BF61}"
EndProject
Expand All @@ -52,10 +52,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LowPressureZone.Aspire", "s
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0. Development Runtime", "0. Development Runtime", "{BD16DBED-7A26-43B3-A844-1ADF1321285C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LowPressureZone.Aspire.Migrations", "src\server\LowPressureZone.Aspire.Migrations\LowPressureZone.Aspire.Migrations.csproj", "{559DF07D-BE6D-4C9B-9C90-1C81146C40F7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LowPressureZone.Aspire.ServiceDefaults", "src\server\LowPressureZone.Aspire.ServiceDefaults\LowPressureZone.Aspire.ServiceDefaults.csproj", "{41881D9F-8A28-427D-84B2-48FDA7630CD1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -150,30 +146,6 @@ Global
{66F83131-6ACD-4067-BDE4-8B788DBFDCED}.Release|x64.Build.0 = Release|Any CPU
{66F83131-6ACD-4067-BDE4-8B788DBFDCED}.Release|x86.ActiveCfg = Release|Any CPU
{66F83131-6ACD-4067-BDE4-8B788DBFDCED}.Release|x86.Build.0 = Release|Any CPU
{559DF07D-BE6D-4C9B-9C90-1C81146C40F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{559DF07D-BE6D-4C9B-9C90-1C81146C40F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{559DF07D-BE6D-4C9B-9C90-1C81146C40F7}.Debug|x64.ActiveCfg = Debug|Any CPU
{559DF07D-BE6D-4C9B-9C90-1C81146C40F7}.Debug|x64.Build.0 = Debug|Any CPU
{559DF07D-BE6D-4C9B-9C90-1C81146C40F7}.Debug|x86.ActiveCfg = Debug|Any CPU
{559DF07D-BE6D-4C9B-9C90-1C81146C40F7}.Debug|x86.Build.0 = Debug|Any CPU
{559DF07D-BE6D-4C9B-9C90-1C81146C40F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{559DF07D-BE6D-4C9B-9C90-1C81146C40F7}.Release|Any CPU.Build.0 = Release|Any CPU
{559DF07D-BE6D-4C9B-9C90-1C81146C40F7}.Release|x64.ActiveCfg = Release|Any CPU
{559DF07D-BE6D-4C9B-9C90-1C81146C40F7}.Release|x64.Build.0 = Release|Any CPU
{559DF07D-BE6D-4C9B-9C90-1C81146C40F7}.Release|x86.ActiveCfg = Release|Any CPU
{559DF07D-BE6D-4C9B-9C90-1C81146C40F7}.Release|x86.Build.0 = Release|Any CPU
{41881D9F-8A28-427D-84B2-48FDA7630CD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{41881D9F-8A28-427D-84B2-48FDA7630CD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{41881D9F-8A28-427D-84B2-48FDA7630CD1}.Debug|x64.ActiveCfg = Debug|Any CPU
{41881D9F-8A28-427D-84B2-48FDA7630CD1}.Debug|x64.Build.0 = Debug|Any CPU
{41881D9F-8A28-427D-84B2-48FDA7630CD1}.Debug|x86.ActiveCfg = Debug|Any CPU
{41881D9F-8A28-427D-84B2-48FDA7630CD1}.Debug|x86.Build.0 = Debug|Any CPU
{41881D9F-8A28-427D-84B2-48FDA7630CD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41881D9F-8A28-427D-84B2-48FDA7630CD1}.Release|Any CPU.Build.0 = Release|Any CPU
{41881D9F-8A28-427D-84B2-48FDA7630CD1}.Release|x64.ActiveCfg = Release|Any CPU
{41881D9F-8A28-427D-84B2-48FDA7630CD1}.Release|x64.Build.0 = Release|Any CPU
{41881D9F-8A28-427D-84B2-48FDA7630CD1}.Release|x86.ActiveCfg = Release|Any CPU
{41881D9F-8A28-427D-84B2-48FDA7630CD1}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -186,8 +158,6 @@ Global
{6F15A668-3BE3-469D-9983-87CD9936BF61} = {C8CE0E2B-370A-43AB-888A-65BF960BA18B}
{5DEF3DA9-DCB9-4C29-921B-52AEA5F9D053} = {2E2C54E2-972F-4583-BB87-A5B5606D397D}
{66F83131-6ACD-4067-BDE4-8B788DBFDCED} = {BD16DBED-7A26-43B3-A844-1ADF1321285C}
{559DF07D-BE6D-4C9B-9C90-1C81146C40F7} = {BD16DBED-7A26-43B3-A844-1ADF1321285C}
{41881D9F-8A28-427D-84B2-48FDA7630CD1} = {BD16DBED-7A26-43B3-A844-1ADF1321285C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E01F24AA-56EC-4C41-A7A6-82A3D36C4C70}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0.0",
"description": "Handles development dependencies and deployment.",
"scripts": {
"deploy-prod": "./tools/deploy-client.sh && ./tools/deploy-server.sh",
"deploy-prod": "git reset --hard && chmod +x ./tools/deploy-client.sh && chmod +x ./tools/deploy-server.sh && ./tools/deploy-client.sh && ./tools/deploy-server.sh",
"initialize-development": "node -e \"const p=process.platform; const cmd=p==='win32'?'npm run initialize-development:windows':(p==='darwin'?'npm run initialize-development:macos':'npm run initialize-development:linux'); require('child_process').execSync(cmd,{stdio:'inherit'})\"",
"initialize-development:windows": "powershell -File .\\tools\\initialize-development.windows.ps1",
"initialize-development:linux": "bash ./tools/initialize-development.linux.sh",
Expand Down
11 changes: 8 additions & 3 deletions src/client/src/api/resources/streamApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ export interface StreamStatusResponse {
isStatusStale: boolean
name: string | null
type: string | null
listenUrl: string | null
listenerCount: number
startedAt: string | null
durationSeconds: number | null
mounts: Mount[]
}

export interface Mount {
name: string
url: string
}

export const defaultStreamStatus: StreamStatusResponse = {
Expand All @@ -26,10 +31,10 @@ export const defaultStreamStatus: StreamStatusResponse = {
isStatusStale: false,
name: null,
type: null,
listenUrl: null,
listenerCount: 0,
startedAt: null,
durationSeconds: null
durationSeconds: null,
mounts: [],
}

export interface ConnectionInformationResponse {
Expand Down
103 changes: 96 additions & 7 deletions src/client/src/components/controls/PlayButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,33 @@
rounded
size="large"
@click="toggleVolumeSlider">
<span v-if="volumeSliderAmount === 100" class="pi pi-volume-up" />
<span v-else-if="volumeSliderAmount === 0" class="pi pi-volume-off" />
<span v-else class="pi pi-volume-down" />
<span
v-if="volumeSliderAmount === 100"
class="pi pi-volume-up" />
<span
v-else-if="volumeSliderAmount === 0"
class="pi pi-volume-off" />
<span
v-else
class="pi pi-volume-down" />
</Button>
</div>
<div class="play-button__volume">
<Slider
v-model.number="volumeSliderAmount"
class="play-button__volume__slider" />
</div>
<div class="play-button__bitrates">
<SelectButton
v-model="selectedMount"
:options="streamStore.mounts"
option-label="name" />
</div>
</div>
</template>

<script lang="ts" setup>
import { Button, Slider, useToast } from 'primevue'
import { Button, Slider, useToast, SelectButton } from 'primevue'
import { computed, onMounted, onUnmounted, type Ref, ref, watch } from 'vue'
import clamp from '@/utils/clamp.ts'
import { useDebounceFn, useLocalStorage, useResizeObserver } from '@vueuse/core'
Expand All @@ -56,6 +68,15 @@ import { formatElapsedTime } from '@/utils/dateUtils.ts'
const toast = useToast()
const streamStore = useStreamStore()

const selectedMount = computed({
get() {
return streamStore.mounts.find((mount) => mount.name === streamStore.selectedMountName)
},
set(value) {
streamStore.selectMount(value)
}
})

enum PlayState {
Playing,
Paused
Expand Down Expand Up @@ -97,9 +118,9 @@ const stopAudio = () => {

const startAudio = () => {
audioAbortController = new AbortController()
audio = new Audio(streamStore.status.listenUrl ?? '')
audio = new Audio(streamStore.selectedMountUrl ?? '')
audio.volume = volume.value
audio.preload = 'metadata'
audio.preload = 'none'
addAudioEventListeners()
audio.play()
}
Expand Down Expand Up @@ -128,6 +149,13 @@ watch(playState, (newPlayState) => {
}
})

watch(selectedMount, (newValue, oldValue) => {
if (playState.value === PlayState.Playing && newValue?.name !== oldValue?.name) {
stopAudio()
startAudio()
}
})

const addAudioEventListeners = () => {
const listenerOptions = { signal: audioAbortController.signal }
audio?.addEventListener('playing', handlePlaying, listenerOptions)
Expand Down Expand Up @@ -244,6 +272,7 @@ const volumeSliderAreaMarginTopValue = computed(() => (showVolumeSliderArea.valu
const volumeSliderAreaBorder = computed(() =>
showVolumeSliderArea.value ? '1px solid var(--p-panel-border-color)' : 'none'
)
const bitrateAreaHeight = computed(() => showVolumeSliderArea.value ? '38px' : '0px')

watch(volume, () => {
if (audio !== undefined) {
Expand Down Expand Up @@ -284,6 +313,9 @@ $text-translate-amount: v-bind(nameTranslateWidthPx);
calc(100dvw - 2 * #{variables.$space-l}),
calc(30px + 28px + 10px + 46px + 10px + #{$text-width})
);
display: flex;
flex-direction: column;
align-items: center;

transition:
width 0.1s ease,
Expand Down Expand Up @@ -389,13 +421,70 @@ $text-translate-amount: v-bind(nameTranslateWidthPx);
transition:
padding 0.1s ease-in-out,
margin-top 0.1s ease-in-out;
//border 0.1s ease-in-out;
border-radius: calc(2 * #{variables.$space-l});
border: v-bind(volumeSliderAreaBorder);

div.p-slider {
display: v-bind(volumeSliderDisplayValue);
}
}

&__bitrates {
width: min-content;
height: v-bind(bitrateAreaHeight);
background-color: var(--p-panel-background);
border-radius: variables.$space-l;
overflow: hidden;
margin-top: calc(v-bind(volumeSliderAreaMarginTopValue));
transition:
height 0.1s ease-in-out,
margin-top 0.1s ease-in-out;
display: flex;
flex-direction: row;
justify-content: space-evenly;
align-items: center;

div.p-selectbutton {
border-left-radius: variables.$space-l;
margin-left: auto;
border: none;

button.p-togglebutton {
border: none;
background: var(--p-panel-background);

span.p-togglebutton-content {
border-radius: 0;
}
}

button.p-togglebutton-checked {
span.p-togglebutton-content {
background: var(--p-button-primary-background);
color: var(--p-button-primary-color);
}
}

button.p-togglebutton:first-child {
border-top-left-radius: variables.$space-l;
border-bottom-left-radius: variables.$space-l;

span.p-togglebutton-content {
border-top-left-radius: variables.$space-l;
border-bottom-left-radius: variables.$space-l;
}
}

button.p-togglebutton:last-child {
border-top-right-radius: variables.$space-l;
border-bottom-right-radius: variables.$space-l;

span.p-togglebutton-content {
border-top-right-radius: variables.$space-l;
border-bottom-right-radius: variables.$space-l;
}
}
}
}
}
</style>
20 changes: 19 additions & 1 deletion src/client/src/stores/streamStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@
import { defineStore } from 'pinia'
import streamApi, {
defaultStreamStatus,
type Mount,
type StreamStatusResponse
} from '@/api/resources/streamApi.ts'
import { useRefresh } from '@/composables/useRefresh.ts'
import areObjectPropertiesEqual from "@/utils/areObjectPropertiesEqual.ts";
import areObjectPropertiesEqual from '@/utils/areObjectPropertiesEqual.ts'
import { useLocalStorage } from '@vueuse/core'

export const useStreamStore = defineStore('streamStore', () => {
const status = ref<StreamStatusResponse>(defaultStreamStatus)
const getStatus = computed(() => status.value)
const mounts = computed(() =>
status.value ? status.value.mounts.sort((a, b) => a.name.localeCompare(b.name)) : []
)
const selectedMountName = useLocalStorage<string | undefined>('selectedMount', undefined)
const selectedMountUrl = computed(() => mounts.value.find(mount => selectedMountName.value === mount.name)?.url)

const { isAutoRefreshing } = useRefresh(streamApi.getStatus, (data) => updateStatus(data), {
autoRefreshInterval: 5000
Expand All @@ -18,6 +25,9 @@ export const useStreamStore = defineStore('streamStore', () => {
const updateStatus = (newStatus: StreamStatusResponse) => {
if (areObjectPropertiesEqual(status.value, newStatus)) return
status.value = newStatus
if (selectedMountName.value === undefined) {
selectedMountName.value = newStatus.mounts.find((mount) => mount.name === '320')?.name
}
}

let isTitleUpdatingStarted = false
Expand Down Expand Up @@ -45,8 +55,16 @@ export const useStreamStore = defineStore('streamStore', () => {
document.title = 'Low Pressure Zone'
}

const selectMount = (mount: Mount | undefined) => {
if (mount) selectedMountName.value = mount.name
}

return {
status: getStatus,
mounts,
selectedMountName,
selectedMountUrl,
selectMount,
isAutoRefreshing,
startTitleUpdating,
stopTitleUpdating
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ public StreamStatusResponse FromEntity(StreamStatus status)
IsStatusStale = status.IsStatusStale,
Name = status.Name,
Type = status.Type,
ListenUrl = status.ListenUrl,
ListenerCount = status.ListenerCount,
StartedAt = status.StartedAt,
DurationSeconds = status.Duration?.TotalSeconds
DurationSeconds = status.Duration?.TotalSeconds,
Mounts = status.Mounts
};
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace LowPressureZone.Api.Endpoints.Stream.Status;
using LowPressureZone.Api.Models.Stream;

namespace LowPressureZone.Api.Endpoints.Stream.Status;

public sealed class StreamStatusResponse
{
Expand All @@ -7,8 +9,8 @@ public sealed class StreamStatusResponse
public required bool IsStatusStale { get; set; }
public required string? Name { get; set; }
public required string? Type { get; set; }
public required string? ListenUrl { get; set; }
public required int ListenerCount { get; set; }
public required DateTimeOffset? StartedAt { get; set; }
public required double? DurationSeconds { get; set; }
public required IEnumerable<Mount> Mounts { get; set; }
}
Loading
Loading