Simplification de la web UI en reposant davantage sur le serveur.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRenderersStore } from '@/stores/renderers'
|
||||
import { ref } from 'vue'
|
||||
import { useRenderers } from '@/composables/useRenderers'
|
||||
import { Play, ListPlus, ChevronRight } from 'lucide-vue-next'
|
||||
|
||||
defineProps<{
|
||||
@@ -14,12 +14,10 @@ const emit = defineEmits<{
|
||||
addToQueue: [rendererId: string]
|
||||
}>()
|
||||
|
||||
const renderersStore = useRenderersStore()
|
||||
const { onlineRenderers } = useRenderers()
|
||||
const showMenu = ref(false)
|
||||
const showRendererSubmenu = ref<'play' | 'queue' | null>(null)
|
||||
|
||||
const onlineRenderers = computed(() => renderersStore.onlineRenderers)
|
||||
|
||||
function handleAction(action: 'play' | 'queue', rendererId: string) {
|
||||
showMenu.value = false
|
||||
showRendererSubmenu.value = null
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRenderersStore } from '@/stores/renderers'
|
||||
import { computed, toRef } from 'vue'
|
||||
import { useRenderer } from '@/composables/useRenderers'
|
||||
import { Music } from 'lucide-vue-next'
|
||||
|
||||
const props = defineProps<{
|
||||
rendererId: string
|
||||
}>()
|
||||
|
||||
const renderersStore = useRenderersStore()
|
||||
|
||||
const state = computed(() => renderersStore.getStateById(props.rendererId))
|
||||
const { state } = useRenderer(toRef(props, 'rendererId'))
|
||||
const metadata = computed(() => state.value?.current_track)
|
||||
|
||||
// Calcul du pourcentage de progression
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useMediaServersStore } from '@/stores/mediaServers'
|
||||
import { useRenderersStore } from '@/stores/renderers'
|
||||
import { useMediaServers } from '@/composables/useMediaServers'
|
||||
import { useRenderers } from '@/composables/useRenderers'
|
||||
import { useUIStore } from '@/stores/ui'
|
||||
import type { BreadcrumbItem } from './Breadcrumb.vue'
|
||||
import Breadcrumb from './Breadcrumb.vue'
|
||||
import ContainerItem from './ContainerItem.vue'
|
||||
import MediaItem from './MediaItem.vue'
|
||||
@@ -14,8 +13,21 @@ const props = defineProps<{
|
||||
containerId: string
|
||||
}>()
|
||||
|
||||
const mediaServersStore = useMediaServersStore()
|
||||
const renderersStore = useRenderersStore()
|
||||
const {
|
||||
getBrowseCached,
|
||||
browseContainer,
|
||||
currentPath: breadcrumbPath,
|
||||
loading,
|
||||
error
|
||||
} = useMediaServers()
|
||||
|
||||
const {
|
||||
playContent,
|
||||
addToQueue,
|
||||
attachAndPlayPlaylist,
|
||||
attachPlaylist,
|
||||
fetchQueue
|
||||
} = useRenderers()
|
||||
const uiStore = useUIStore()
|
||||
|
||||
// Flags pour gérer le rechargement automatique avec debounce et cooldown
|
||||
@@ -25,7 +37,7 @@ const lastRefreshTime = ref<number>(0)
|
||||
const REFRESH_COOLDOWN_MS = 5000 // Ne pas recharger plus d'une fois toutes les 5 secondes
|
||||
|
||||
const browseData = computed(() =>
|
||||
mediaServersStore.getBrowseCached(props.serverId, props.containerId)
|
||||
getBrowseCached(props.serverId, props.containerId)
|
||||
)
|
||||
|
||||
const containers = computed(() =>
|
||||
@@ -36,17 +48,12 @@ const items = computed(() =>
|
||||
browseData.value?.entries.filter((e) => !e.is_container) || []
|
||||
)
|
||||
|
||||
const loading = computed(() => mediaServersStore.loading)
|
||||
const error = computed(() => mediaServersStore.error)
|
||||
|
||||
const breadcrumbPath = computed<BreadcrumbItem[]>(() => mediaServersStore.currentPath)
|
||||
|
||||
// Charger le container au montage et quand containerId change
|
||||
watch(
|
||||
() => props.containerId,
|
||||
async (newContainerId) => {
|
||||
if (newContainerId) {
|
||||
await mediaServersStore.browseContainer(props.serverId, newContainerId)
|
||||
await browseContainer(props.serverId, newContainerId)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
@@ -85,7 +92,7 @@ watch(
|
||||
`[MediaBrowser] Cache invalidé pour ${props.serverId}/${props.containerId}, rechargement après debounce...`
|
||||
)
|
||||
isRefreshing.value = true
|
||||
await mediaServersStore.browseContainer(props.serverId, props.containerId)
|
||||
await browseContainer(props.serverId, props.containerId, false)
|
||||
lastRefreshTime.value = Date.now() // Enregistrer le moment du rechargement
|
||||
isRefreshing.value = false
|
||||
refreshTimeoutId.value = null
|
||||
@@ -110,7 +117,7 @@ function handleBrowseContainer(containerId: string) {
|
||||
// Actions handlers pour les containers (playlists/albums)
|
||||
async function handlePlayContainer(containerId: string, rendererId: string) {
|
||||
try {
|
||||
await renderersStore.attachAndPlayPlaylist(rendererId, props.serverId, containerId)
|
||||
await attachAndPlayPlaylist(rendererId, props.serverId, containerId)
|
||||
uiStore.notifySuccess('Lecture de la playlist démarrée !')
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Erreur inconnue'
|
||||
@@ -121,8 +128,8 @@ async function handlePlayContainer(containerId: string, rendererId: string) {
|
||||
async function handleQueueContainer(containerId: string, rendererId: string) {
|
||||
try {
|
||||
// Attacher la playlist (sans démarrer la lecture)
|
||||
await renderersStore.attachPlaylist(rendererId, props.serverId, containerId)
|
||||
await renderersStore.fetchQueue(rendererId)
|
||||
await attachPlaylist(rendererId, props.serverId, containerId)
|
||||
await fetchQueue(rendererId, true)
|
||||
uiStore.notifySuccess('Playlist attachée à la queue !')
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Erreur inconnue'
|
||||
@@ -133,7 +140,7 @@ async function handleQueueContainer(containerId: string, rendererId: string) {
|
||||
// Actions handlers pour les items (tracks)
|
||||
async function handlePlayItem(itemId: string, rendererId: string) {
|
||||
try {
|
||||
await renderersStore.playContent(rendererId, props.serverId, itemId)
|
||||
await playContent(rendererId, props.serverId, itemId)
|
||||
uiStore.notifySuccess('Lecture démarrée !')
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Erreur inconnue'
|
||||
@@ -143,7 +150,7 @@ async function handlePlayItem(itemId: string, rendererId: string) {
|
||||
|
||||
async function handleQueueItem(itemId: string, rendererId: string) {
|
||||
try {
|
||||
await renderersStore.addToQueue(rendererId, props.serverId, itemId)
|
||||
await addToQueue(rendererId, props.serverId, itemId)
|
||||
uiStore.notifySuccess('Ajouté à la queue !')
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Erreur inconnue'
|
||||
@@ -170,7 +177,7 @@ async function handleQueueItem(itemId: string, rendererId: string) {
|
||||
<!-- Error state -->
|
||||
<div v-else-if="error" class="browser-error">
|
||||
<p class="error-message">{{ error }}</p>
|
||||
<button class="btn btn-secondary" @click="mediaServersStore.browseContainer(serverId, containerId)">
|
||||
<button class="btn btn-secondary" @click="browseContainer(serverId, containerId, false)">
|
||||
Réessayer
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRenderersStore } from '@/stores/renderers'
|
||||
import { computed, toRef } from 'vue'
|
||||
import { useRenderer, useRenderers } from '@/composables/useRenderers'
|
||||
import { useUIStore } from '@/stores/ui'
|
||||
import { Link, Unlink } from 'lucide-vue-next'
|
||||
|
||||
@@ -8,15 +8,15 @@ const props = defineProps<{
|
||||
rendererId: string
|
||||
}>()
|
||||
|
||||
const renderersStore = useRenderersStore()
|
||||
const { binding } = useRenderer(toRef(props, 'rendererId'))
|
||||
const { detachPlaylist } = useRenderers()
|
||||
const uiStore = useUIStore()
|
||||
|
||||
const binding = computed(() => renderersStore.getBindingById(props.rendererId))
|
||||
const isAttached = computed(() => !!binding.value)
|
||||
|
||||
async function handleDetach() {
|
||||
try {
|
||||
await renderersStore.detachPlaylist(props.rendererId)
|
||||
await detachPlaylist(props.rendererId)
|
||||
uiStore.notifySuccess('Playlist détachée avec succès')
|
||||
} catch (error) {
|
||||
uiStore.notifyError(`Impossible de détacher la playlist: ${error instanceof Error ? error.message : 'Erreur inconnue'}`)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch, nextTick } from 'vue'
|
||||
import { useRenderersStore } from '@/stores/renderers'
|
||||
import { computed, ref, watch, nextTick, toRef } from 'vue'
|
||||
import { useRenderer } from '@/composables/useRenderers'
|
||||
import QueueItem from './QueueItem.vue'
|
||||
import { Link } from 'lucide-vue-next'
|
||||
|
||||
@@ -8,10 +8,7 @@ const props = defineProps<{
|
||||
rendererId: string
|
||||
}>()
|
||||
|
||||
const renderersStore = useRenderersStore()
|
||||
|
||||
const queue = computed(() => renderersStore.getQueueById(props.rendererId))
|
||||
const binding = computed(() => renderersStore.getBindingById(props.rendererId))
|
||||
const { queue, binding } = useRenderer(toRef(props, 'rendererId'))
|
||||
|
||||
const isAttached = computed(() => !!binding.value)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRenderersStore } from '@/stores/renderers'
|
||||
import { computed, toRef } from 'vue'
|
||||
import { useRenderer, useRenderers } from '@/composables/useRenderers'
|
||||
import { useUIStore } from '@/stores/ui'
|
||||
import { Play, Pause, Square, SkipForward } from 'lucide-vue-next'
|
||||
|
||||
@@ -8,20 +8,17 @@ const props = defineProps<{
|
||||
rendererId: string
|
||||
}>()
|
||||
|
||||
const renderersStore = useRenderersStore()
|
||||
const { state } = useRenderer(toRef(props, 'rendererId'))
|
||||
const { resumeOrPlayFromQueue, pause, stop, next } = useRenderers()
|
||||
const uiStore = useUIStore()
|
||||
|
||||
const state = computed(() => renderersStore.getStateById(props.rendererId))
|
||||
const isPlaying = computed(() => state.value?.transport_state === 'PLAYING')
|
||||
const isPaused = computed(() => state.value?.transport_state === 'PAUSED')
|
||||
const isStopped = computed(() => state.value?.transport_state === 'STOPPED' || state.value?.transport_state === 'NO_MEDIA')
|
||||
|
||||
async function handlePlay() {
|
||||
try {
|
||||
// Utilise resumeOrPlayFromQueue qui choisit intelligemment entre:
|
||||
// - Resume (si PAUSED)
|
||||
// - Play from queue (si STOPPED/NO_MEDIA avec queue non vide)
|
||||
await renderersStore.resumeOrPlayFromQueue(props.rendererId)
|
||||
await resumeOrPlayFromQueue(props.rendererId)
|
||||
} catch (error) {
|
||||
uiStore.notifyError(`Impossible de démarrer la lecture: ${error instanceof Error ? error.message : 'Erreur inconnue'}`)
|
||||
}
|
||||
@@ -29,7 +26,7 @@ async function handlePlay() {
|
||||
|
||||
async function handlePause() {
|
||||
try {
|
||||
await renderersStore.pause(props.rendererId)
|
||||
await pause(props.rendererId)
|
||||
} catch (error) {
|
||||
uiStore.notifyError(`Impossible de mettre en pause: ${error instanceof Error ? error.message : 'Erreur inconnue'}`)
|
||||
}
|
||||
@@ -37,7 +34,7 @@ async function handlePause() {
|
||||
|
||||
async function handleStop() {
|
||||
try {
|
||||
await renderersStore.stop(props.rendererId)
|
||||
await stop(props.rendererId)
|
||||
} catch (error) {
|
||||
uiStore.notifyError(`Impossible d'arrêter la lecture: ${error instanceof Error ? error.message : 'Erreur inconnue'}`)
|
||||
}
|
||||
@@ -45,7 +42,7 @@ async function handleStop() {
|
||||
|
||||
async function handleNext() {
|
||||
try {
|
||||
await renderersStore.next(props.rendererId)
|
||||
await next(props.rendererId)
|
||||
} catch (error) {
|
||||
uiStore.notifyError(`Impossible de passer au morceau suivant: ${error instanceof Error ? error.message : 'Erreur inconnue'}`)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useRenderersStore } from '@/stores/renderers'
|
||||
import { ref, watch, toRef } from 'vue'
|
||||
import { useRenderer, useRenderers } from '@/composables/useRenderers'
|
||||
import { useUIStore } from '@/stores/ui'
|
||||
import { Volume2, VolumeX } from 'lucide-vue-next'
|
||||
|
||||
@@ -8,10 +8,10 @@ const props = defineProps<{
|
||||
rendererId: string
|
||||
}>()
|
||||
|
||||
const renderersStore = useRenderersStore()
|
||||
const { state } = useRenderer(toRef(props, 'rendererId'))
|
||||
const { setVolume, toggleMute } = useRenderers()
|
||||
const uiStore = useUIStore()
|
||||
|
||||
const state = computed(() => renderersStore.getStateById(props.rendererId))
|
||||
const localVolume = ref(state.value?.volume ?? 50)
|
||||
|
||||
// Synchroniser localVolume avec le state
|
||||
@@ -34,7 +34,7 @@ function handleVolumeChange(event: Event) {
|
||||
|
||||
debounceTimer = window.setTimeout(async () => {
|
||||
try {
|
||||
await renderersStore.setVolume(props.rendererId, localVolume.value)
|
||||
await setVolume(props.rendererId, localVolume.value)
|
||||
} catch (error) {
|
||||
uiStore.notifyError(`Impossible de régler le volume: ${error instanceof Error ? error.message : 'Erreur inconnue'}`)
|
||||
}
|
||||
@@ -44,7 +44,7 @@ function handleVolumeChange(event: Event) {
|
||||
|
||||
async function handleToggleMute() {
|
||||
try {
|
||||
await renderersStore.toggleMute(props.rendererId)
|
||||
await toggleMute(props.rendererId)
|
||||
} catch (error) {
|
||||
uiStore.notifyError(`Impossible de basculer le mode muet: ${error instanceof Error ? error.message : 'Erreur inconnue'}`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user