defmodule ExshomePlayer.Services.MpvServer do
@moduledoc """
Starts MPV server.
"""
use Exshome.Dependency.GenServerDependency, name: "mpv_server"
@player_folder "player"
@music_folder Path.join(@player_folder, "music")
def restart do
call(:restart)
end
@impl GenServerDependency
def on_init(%DependencyState{} = state) do
start_mpv_server(state)
end
@impl GenServerDependency
def handle_call(:restart, _from, %DependencyState{} = state) do
{:reply, :exec.kill(state.data.server_pid, 9), state}
end
@impl GenServerDependency
def handle_info(
{:EXIT, server_pid, _reason},
%DependencyState{data: %{server_pid: server_pid}} = state
) do
state =
state
|> update_value(Dependency.NotReady)
|> start_mpv_server()
{:noreply, state}
end
@spec start_mpv_server(DependencyState.t()) :: DependencyState.t()
defp start_mpv_server(%DependencyState{} = state) do
case find_mpv_executable() do
{:ok, program} ->
{:ok, pid, _ospid} =
program
|> mpv_server_command()
|> :exec.run_link([
{:group, 0},
:kill_group,
:stdout,
:stderr
])
state
|> update_data(fn _ -> %{server_pid: pid} end)
|> update_value(:started)
{:error, :not_found} ->
update_value(state, Dependency.NotReady)
end
end
def socket_path do
Exshome.FileUtils.get_of_create_folder!(@player_folder)
|> Path.join("mpv_socket")
end
def music_folder do
Exshome.FileUtils.get_of_create_folder!(@music_folder)
end
@spec find_mpv_executable() :: {:ok, charlist()} | {:error, :not_found}
defp find_mpv_executable do
case System.find_executable("mpv") do
nil -> {:error, :not_found}
path -> {:ok, String.to_charlist(path)}
end
end
defp mpv_server_command(program) when is_list(program) do
[
program,
'--no-video',
'--idle',
'--no-cache',
'--no-terminal',
'--input-ipc-server=#{socket_path()}'
]
end
@hook_module Application.compile_env(:exshome, :mpv_server_hook_module)
if @hook_module do
defoverridable(mpv_server_command: 1)
defdelegate mpv_server_command(program), to: @hook_module
defoverridable(find_mpv_executable: 0)
defdelegate find_mpv_executable(), to: @hook_module
end
end