lib/exshome_player/services/mpv_server.ex

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
    {:ok, pid, _ospid} =
      :exec.run_link(
        mpv_server_command(),
        [
          {:group, 0},
          :kill_group,
          :stdout,
          :stderr
        ]
      )

    state
    |> update_data(fn _ -> %{server_pid: pid} end)
    |> update_value(:started)
  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

  defp mpv_server_command do
    [
      System.find_executable("mpv") |> String.to_charlist(),
      '--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: 0)
    defdelegate mpv_server_command(), to: @hook_module
  end
end