defmodule ExshomePlayer.Services.PlayerState do @moduledoc """ A module for storing a playback state for the MPV client. """ alias __MODULE__ alias Exshome.Event alias ExshomePlayer.Events.{MpvEvent, PlayerFileEnd, PlayerStateEvent} alias ExshomePlayer.Services.MpvSocket use Exshome.Dependency.GenServerDependency, name: "mpv_client", dependencies: [{MpvSocket, :socket}], events: [MpvEvent] @keys [ :path, :pause, :volume, :duration, :time_pos, :metadata ] defstruct @keys @type t() :: %__MODULE__{ path: String.t() | nil, pause: boolean() | nil, volume: float() | nil, duration: float() | nil, time_pos: float() | nil, metadata: map() | nil } @impl GenServerDependency def handle_dependency_change(%DependencyState{} = state) do if state.deps.socket == :connected do subscribe_to_player_state() update_value(state, %PlayerState{}) else update_value(state, Dependency.NotReady) end end @impl GenServerDependency def handle_event( %MpvEvent{type: "property-change", data: %{"name" => name} = event}, %DependencyState{value: %PlayerState{} = value} = state ) do new_value = Map.put( value, property_mapping()[name], event["data"] ) update_value(state, new_value) end def handle_event( %MpvEvent{type: "end-file", data: %{"reason" => reason}}, %DependencyState{} = state ) do Event.broadcast(%PlayerFileEnd{reason: reason}) state end def handle_event(%MpvEvent{} = event, %DependencyState{} = state) do Event.broadcast(%PlayerStateEvent{data: event.data, type: event.type}) state end @spec subscribe_to_player_state() :: term() defp subscribe_to_player_state do property_mapping() |> Map.keys() |> Enum.each(&observe_property/1) end defp observe_property(property) do %{} = MpvSocket.send_command(["observe_property", 1, property]) end defp property_mapping do for key <- @keys, into: %{} do property_key = key |> Atom.to_string() |> String.replace(~r/_/, "-") {property_key, key} end end end