Skip to content

Commit 06780fc

Browse files
committed
Some basic adjustments for playersources providing playback control
1 parent 8e88a4e commit 06780fc

File tree

5 files changed

+97
-15
lines changed

5 files changed

+97
-15
lines changed

src/composables/activeSource.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { computed, ComputedRef, Ref } from "vue";
2+
import { Player, PlayerSource } from "@/plugins/api/interfaces";
3+
4+
/**
5+
* Composable to get the active source from a player
6+
*/
7+
export function useActiveSource(
8+
player:
9+
| ComputedRef<Player | undefined>
10+
| Ref<Player | undefined>
11+
| Player
12+
| undefined,
13+
) {
14+
const activeSource = computed((): PlayerSource | undefined => {
15+
const playerObj =
16+
typeof player === "object" && "value" in player ? player.value : player;
17+
if (!playerObj?.active_source || !playerObj?.source_list) return undefined;
18+
return playerObj.source_list.find(
19+
(source) => source.id === playerObj.active_source,
20+
);
21+
});
22+
23+
return { activeSource };
24+
}

src/layouts/default/PlayerOSD/PlayerControlBtn/NextBtn.vue

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<Icon
44
v-if="isVisible && player"
55
v-bind="icon"
6-
:disabled="!queueHasNext && !playerHasNext"
6+
:disabled="!canNext"
77
icon="mdi-skip-next-outline"
88
variant="button"
99
@click="api.playerCommandNext(player.player_id)"
@@ -14,7 +14,8 @@
1414
import Icon, { IconProps } from "@/components/Icon.vue";
1515
import api from "@/plugins/api";
1616
import { Player, PlayerFeature, PlayerQueue } from "@/plugins/api/interfaces";
17-
import { computed } from "vue";
17+
import { useActiveSource } from "@/composables/activeSource";
18+
import { computed, toRef } from "vue";
1819
1920
// properties
2021
export interface Props {
@@ -29,12 +30,15 @@ const compProps = withDefaults(defineProps<Props>(), {
2930
icon: undefined,
3031
});
3132
33+
const { activeSource } = useActiveSource(toRef(compProps, "player"));
34+
3235
const queueHasNext = computed(() => {
3336
if (!compProps.playerQueue?.active) return false;
3437
return (
3538
(compProps.playerQueue.current_index || 0) < compProps.playerQueue.items - 1
3639
);
3740
});
41+
3842
const playerHasNext = computed(() => {
3943
if (!compProps.player) return false;
4044
if (compProps.playerQueue?.active) return false;
@@ -43,4 +47,13 @@ const playerHasNext = computed(() => {
4347
PlayerFeature.NEXT_PREVIOUS,
4448
);
4549
});
50+
51+
const canNext = computed(() => {
52+
// Check if active source can next/previous
53+
if (activeSource.value) {
54+
return activeSource.value.can_next_previous;
55+
}
56+
// Fall back to queue or player capabilities
57+
return queueHasNext.value || playerHasNext.value;
58+
});
4659
</script>

src/layouts/default/PlayerOSD/PlayerControlBtn/PlayBtn.vue

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<Icon
44
v-if="isVisible && player"
55
v-bind="icon"
6-
:disabled="!queueCanPlay && !playerCanPlay"
6+
:disabled="!canPlayPause"
77
:icon="iconStyle ? `${baseIcon}-${iconStyle}` : baseIcon"
88
variant="button"
99
@click="api.playerCommandPlayPause(player.player_id)"
@@ -14,7 +14,8 @@
1414
import Icon, { IconProps } from "@/components/Icon.vue";
1515
import api from "@/plugins/api";
1616
import { PlaybackState, Player, PlayerQueue } from "@/plugins/api/interfaces";
17-
import { computed } from "vue";
17+
import { useActiveSource } from "@/composables/activeSource";
18+
import { computed, toRef } from "vue";
1819
1920
// properties
2021
export interface Props {
@@ -32,16 +33,30 @@ const compProps = withDefaults(defineProps<Props>(), {
3233
icon: undefined,
3334
iconStyle: "circle",
3435
});
36+
37+
const { activeSource } = useActiveSource(toRef(compProps, "player"));
38+
3539
const queueCanPlay = computed(() => {
3640
if (!compProps.playerQueue) return false;
3741
return compProps.playerQueue.items > 0;
3842
});
43+
3944
const playerCanPlay = computed(() => {
4045
if (!compProps.player) return false;
4146
if (compProps.playerQueue?.active) return false;
4247
if (!compProps.player.current_media) return false;
4348
return true;
4449
});
50+
51+
const canPlayPause = computed(() => {
52+
// Check if active source can play/pause
53+
if (activeSource.value) {
54+
return activeSource.value.can_play_pause;
55+
}
56+
// Fall back to queue or player capabilities
57+
return queueCanPlay.value || playerCanPlay.value;
58+
});
59+
4560
const baseIcon = computed(() => {
4661
if (compProps.player?.playback_state == PlaybackState.PLAYING) {
4762
return "mdi-pause";

src/layouts/default/PlayerOSD/PlayerControlBtn/PreviousBtn.vue

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<Icon
44
v-if="isVisible && player"
55
v-bind="icon"
6-
:disabled="!queueHasPrevious && !playerHasPrevious"
6+
:disabled="!canPrevious"
77
icon="mdi-skip-previous-outline"
88
variant="button"
99
@click="api.playerCommandPrevious(player.player_id)"
@@ -14,7 +14,8 @@
1414
import Icon, { IconProps } from "@/components/Icon.vue";
1515
import api from "@/plugins/api";
1616
import { Player, PlayerFeature, PlayerQueue } from "@/plugins/api/interfaces";
17-
import { computed } from "vue";
17+
import { useActiveSource } from "@/composables/activeSource";
18+
import { computed, toRef } from "vue";
1819
1920
// properties
2021
export interface Props {
@@ -29,12 +30,15 @@ const compProps = withDefaults(defineProps<Props>(), {
2930
icon: undefined,
3031
});
3132
33+
const { activeSource } = useActiveSource(toRef(compProps, "player"));
34+
3235
const queueHasPrevious = computed(() => {
3336
if (!compProps.playerQueue?.active) return false;
3437
if (!compProps.playerQueue?.items || !compProps.playerQueue.current_index)
3538
return false;
3639
return compProps.playerQueue.current_index > 0;
3740
});
41+
3842
const playerHasPrevious = computed(() => {
3943
if (!compProps.player) return false;
4044
if (compProps.playerQueue?.active) return false;
@@ -43,4 +47,13 @@ const playerHasPrevious = computed(() => {
4347
PlayerFeature.NEXT_PREVIOUS,
4448
);
4549
});
50+
51+
const canPrevious = computed(() => {
52+
// Check if active source can next/previous
53+
if (activeSource.value) {
54+
return activeSource.value.can_next_previous;
55+
}
56+
// Fall back to queue or player capabilities
57+
return queueHasPrevious.value || playerHasPrevious.value;
58+
});
4659
</script>

src/layouts/default/PlayerOSD/PlayerTimeline.vue

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
<template>
22
<div style="width: auto; height: 24px">
3-
<div v-if="store.activePlayerQueue" style="width: 100%">
3+
<div v-if="store.activePlayer" style="width: 100%">
44
<v-slider
55
v-model="curTimeValue"
6-
:disabled="
7-
!store.curQueueItem ||
8-
!store.curQueueItem.media_item ||
9-
!store.curQueueItem.duration ||
10-
store.curQueueItem.media_item.media_type == MediaType.RADIO ||
11-
store.activePlayer?.powered == false
12-
"
6+
:disabled="!canSeek"
137
style="width: 100%"
148
:min="0"
159
:max="store.curQueueItem?.duration"
@@ -67,8 +61,9 @@
6761
import api from "@/plugins/api";
6862
import { MediaType } from "@/plugins/api/interfaces";
6963
import { store } from "@/plugins/store";
64+
import { useActiveSource } from "@/composables/activeSource";
7065
import { formatDuration } from "@/helpers/utils";
71-
import { ref, computed, watch } from "vue";
66+
import { ref, computed, watch, toRef } from "vue";
7267
7368
// properties
7469
export interface Props {
@@ -81,6 +76,8 @@ withDefaults(defineProps<Props>(), {
8176
color: undefined,
8277
});
8378
79+
const { activeSource } = useActiveSource(toRef(store, "activePlayer"));
80+
8481
// local refs
8582
const showRemainingTime = ref(false);
8683
const isThumbHidden = ref(true);
@@ -89,6 +86,24 @@ const curTimeValue = ref(0);
8986
const tempTime = ref(0);
9087
9188
// computed properties
89+
const canSeek = computed(() => {
90+
// Check if active source allows seeking
91+
// commented out to fix issue first with actually retrieving the elapsed time of the source
92+
// if (activeSource.value) {
93+
// return activeSource.value.can_seek;
94+
// }
95+
96+
if (store.curQueueItem?.media_item?.media_type == MediaType.RADIO)
97+
return false;
98+
if (store.activePlayer?.powered == false) return false;
99+
if (!store.curQueueItem) return false;
100+
if (!store.curQueueItem.media_item) return false;
101+
if (!store.curQueueItem.duration) return false;
102+
103+
// Default to true if no active source (queue control)
104+
return true;
105+
});
106+
92107
const playerCurTimeStr = computed(() => {
93108
if (!store.curQueueItem) return "0:00";
94109
if (showRemainingTime.value) {
@@ -99,13 +114,15 @@ const playerCurTimeStr = computed(() => {
99114
return `${formatDuration(curQueueItemTime.value)}`;
100115
}
101116
});
117+
102118
const playerTotalTimeStr = computed(() => {
103119
if (!store.curQueueItem) return "";
104120
if (!store.curQueueItem.duration) return "";
105121
if (store.curQueueItem.media_item?.media_type == MediaType.RADIO) return "";
106122
const totalSecs = store.curQueueItem.duration;
107123
return formatDuration(totalSecs);
108124
});
125+
109126
const curQueueItemTime = computed(() => {
110127
if (isDragging.value) {
111128
// eslint-disable-next-line vue/no-side-effects-in-computed-properties

0 commit comments

Comments
 (0)