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