Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion lib/reencodarr/media.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2353,7 +2353,6 @@ defmodule Reencodarr.Media do

case Flop.validate_and_run(base_query, flop_params, for: Video) do
{:ok, {videos, meta}} ->
videos = Repo.preload(videos, :chosen_vmaf)
{videos, meta}

{:error, _meta} ->
Expand Down
15 changes: 12 additions & 3 deletions lib/reencodarr/videos/state.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ defmodule Reencodarr.Videos.State do

alias Reencodarr.Media

@spec load(map()) :: map()
def load(assigns) do
@spec load(map(), keyword()) :: map()
def load(assigns, opts \\ []) do
include_state_counts? = Keyword.get(opts, :include_state_counts, true)

{videos, meta} =
Media.list_videos_paginated(
page: assigns.page,
Expand All @@ -17,13 +19,20 @@ defmodule Reencodarr.Videos.State do
sort_dir: assigns.sort_dir
)

state_counts =
if include_state_counts? do
Media.count_videos_by_state()
else
Map.get(assigns, :state_counts, %{})
end

%{
videos: videos,
meta: meta,
total: meta.total_count || 0,
page: meta.current_page || assigns.page,
per_page: meta.page_size || assigns.per_page,
state_counts: Media.count_videos_by_state()
state_counts: state_counts
}
end
end
26 changes: 24 additions & 2 deletions lib/reencodarr_web/live/dashboard_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -620,13 +620,24 @@ defmodule ReencodarrWeb.DashboardLive do
<% end %>
</div>
<% else %>
<!-- Idle: Show compact status -->
<!-- Idle/Paused: Show compact status and allow resume when paused -->
<div class="text-sm text-gray-400">
<span>Queue: {@queue_count}</span>
<%= if @status == :idle do %>
<span class="ml-2">• Idle</span>
<% end %>
</div>

<%= if @status == :paused do %>
<div class="mt-2">
<.active_job_controls
status={@status}
suspend_event="suspend_crf_search"
resume_event="resume_crf_search"
fail_event="fail_crf_search"
/>
</div>
<% end %>
<% end %>

<!-- Always show next-up videos -->
Expand Down Expand Up @@ -730,13 +741,24 @@ defmodule ReencodarrWeb.DashboardLive do
/>
</div>
<% else %>
<!-- Idle: Show compact status -->
<!-- Idle/Paused: Show compact status and allow resume when paused -->
<div class="text-sm text-gray-400">
<span>Queue: {@queue_count}</span>
<%= if @status == :idle do %>
<span class="ml-2">• Idle</span>
<% end %>
</div>

<%= if @status == :paused do %>
<div class="mt-2">
<.active_job_controls
status={@status}
suspend_event="suspend_encode"
resume_event="resume_encode"
fail_event="fail_encode"
/>
</div>
<% end %>
<% end %>

<!-- Always show next-up videos -->
Expand Down
121 changes: 52 additions & 69 deletions lib/reencodarr_web/live/videos_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ defmodule ReencodarrWeb.VideosLive do
|> assign(filters)
|> then(fn s ->
if connected?(s) and s.assigns.loaded_once and filters_changed?,
do: load_data(s),
do: load_data(s, include_state_counts: false),
else: s
end)

Expand Down Expand Up @@ -114,33 +114,20 @@ defmodule ReencodarrWeb.VideosLive do
# ---------------------------------------------------------------------------

@impl true
def handle_event("search", %{"search" => q}, socket) do
{:noreply, push_patch(socket, to: patch_path(socket.assigns, search: q, page: 1))}
end

@impl true
def handle_event("filter_state", %{"state" => state}, socket) do
{:noreply,
push_patch(socket,
to: patch_path(socket.assigns, state: nilify_empty(state), page: 1)
)}
end
def handle_event("set_filters", params, socket) do
search = Map.get(params, "search", socket.assigns.search)
state = params |> Map.get("state") |> nilify_empty()
service = params |> Map.get("service") |> nilify_empty()
hdr = params |> Map.get("hdr") |> nilify_empty() |> parse_hdr_param() |> hdr_to_param()

@impl true
def handle_event("filter_service", %{"service" => svc}, socket) do
{:noreply,
push_patch(socket,
to: patch_path(socket.assigns, service: nilify_empty(svc), page: 1)
)}
end

@impl true
def handle_event("filter_hdr", %{"hdr" => hdr}, socket) do
{:noreply,
push_patch(socket,
to:
patch_path(socket.assigns,
hdr: hdr_to_param(parse_hdr_param(nilify_empty(hdr))),
search: search,
state: state,
service: service,
hdr: hdr,
page: 1
)
)}
Expand Down Expand Up @@ -281,8 +268,8 @@ defmodule ReencodarrWeb.VideosLive do
def handle_event("reset_selected", _params, socket) do
ids = MapSet.to_list(socket.assigns.selected)
Enum.each(ids, &reset_video_by_id/1)

socket = socket |> assign(selected: MapSet.new()) |> load_data()

{:noreply, put_flash(socket, :info, "Reset #{length(ids)} video(s) to needs_analysis")}
end

Expand All @@ -299,7 +286,7 @@ defmodule ReencodarrWeb.VideosLive do
put_flash(socket, :error, "No selected videos were eligible for queue prioritization")}

{:ok, count} ->
socket = socket |> assign(selected: MapSet.new()) |> load_data()
socket = socket |> assign(selected: MapSet.new())
{:noreply, put_flash(socket, :info, "Prioritized #{count} video(s)")}

{:error, _reason} ->
Expand Down Expand Up @@ -345,7 +332,7 @@ defmodule ReencodarrWeb.VideosLive do
with {:ok, id} <- Parsers.parse_integer_exact(id_str),
{:ok, count} <- Media.prioritize_video(id),
true <- count > 0 do
{:noreply, socket |> put_flash(:info, "Prioritized video") |> load_data()}
{:noreply, socket |> put_flash(:info, "Prioritized video")}
else
{:ok, 0} -> {:noreply, put_flash(socket, :error, "Video is not currently queueable")}
false -> {:noreply, put_flash(socket, :error, "Video is not currently queueable")}
Expand All @@ -356,20 +343,21 @@ defmodule ReencodarrWeb.VideosLive do

@impl true
def handle_event("fail_video", %{"id" => id_str}, socket) do
result =
with {:ok, id} <- Parsers.parse_integer_exact(id_str) do
fail_video_by_id(id)
end
case Parsers.parse_integer_exact(id_str) do
{:ok, id} ->
case fail_video_by_id(id) do
:ok ->
{:noreply, socket |> put_flash(:info, "Job stopped") |> load_data()}

case result do
:ok ->
{:noreply, socket |> put_flash(:info, "Job stopped") |> load_data()}
{:error, :active_mismatch} ->
{:noreply, socket |> put_flash(:error, "That video is not the active job")}

{:error, :active_mismatch} ->
{:noreply, socket |> put_flash(:error, "That video is not the active job") |> load_data()}
_ ->
{:noreply, socket |> put_flash(:error, "Unable to stop job")}
end

_ ->
{:noreply, socket |> put_flash(:error, "Unable to stop job") |> load_data()}
{:noreply, socket |> put_flash(:error, "Unable to stop job")}
end
end

Expand Down Expand Up @@ -398,8 +386,7 @@ defmodule ReencodarrWeb.VideosLive do
|> put_flash(
:info,
"Prioritized #{count} #{Path.basename(season_dir)} video(s)"
)
|> load_data()}
)}

{:error, _reason} ->
{:noreply, put_flash(socket, :error, "Failed to prioritize season videos")}
Expand Down Expand Up @@ -442,8 +429,7 @@ defmodule ReencodarrWeb.VideosLive do
{:noreply,
socket
|> assign(:expanded_bad_forms, List.delete(socket.assigns.expanded_bad_forms, id))
|> put_flash(:info, "Marked as bad")
|> load_data()}
|> put_flash(:info, "Marked as bad")}
else
:not_found -> {:noreply, put_flash(socket, :error, "Video not found")}
{:error, _} -> {:noreply, put_flash(socket, :error, "Mark bad failed")}
Expand All @@ -455,18 +441,22 @@ defmodule ReencodarrWeb.VideosLive do
# Private helpers
# ---------------------------------------------------------------------------

defp load_data(socket) do
defp load_data(socket, opts \\ []) do
page_state =
VideosState.load(%{
page: socket.assigns.page,
per_page: socket.assigns.per_page,
state_filter: socket.assigns.state_filter,
service_filter: socket.assigns.service_filter,
hdr_filter: socket.assigns.hdr_filter,
search: socket.assigns.search,
sort_by: socket.assigns.sort_by,
sort_dir: socket.assigns.sort_dir
})
VideosState.load(
%{
state_counts: socket.assigns.state_counts,
page: socket.assigns.page,
per_page: socket.assigns.per_page,
state_filter: socket.assigns.state_filter,
service_filter: socket.assigns.service_filter,
hdr_filter: socket.assigns.hdr_filter,
search: socket.assigns.search,
sort_by: socket.assigns.sort_by,
sort_dir: socket.assigns.sort_dir
},
opts
)

assign_changed(socket, Map.put(page_state, :loading, false))
end
Expand Down Expand Up @@ -750,18 +740,17 @@ defmodule ReencodarrWeb.VideosLive do
<!-- Toolbar -->
<div class="bg-gray-800 rounded-lg border border-gray-700 p-3">
<div class="flex flex-wrap gap-3 items-center">
<form phx-change="search" class="flex-1 min-w-[180px]">
<input
type="text"
name="search"
value={@search}
placeholder="Search by path..."
phx-debounce="300"
class="w-full bg-gray-700 border border-gray-600 text-white rounded-lg px-3 py-2 text-sm focus:ring-purple-500 focus:border-purple-500 placeholder-gray-400"
/>
</form>

<form phx-change="filter_state">
<form id="videos-filters" phx-change="set_filters" class="contents">
<div class="flex-1 min-w-[180px]">
<input
type="text"
name="search"
value={@search}
placeholder="Search by path..."
phx-debounce="300"
class="w-full bg-gray-700 border border-gray-600 text-white rounded-lg px-3 py-2 text-sm focus:ring-purple-500 focus:border-purple-500 placeholder-gray-400"
/>
</div>
<select
name="state"
value={@state_filter || ""}
Expand All @@ -772,9 +761,6 @@ defmodule ReencodarrWeb.VideosLive do
<option value={s}>{s}</option>
<% end %>
</select>
</form>

<form phx-change="filter_service">
<select
name="service"
value={@service_filter || ""}
Expand All @@ -784,9 +770,6 @@ defmodule ReencodarrWeb.VideosLive do
<option value="sonarr">Sonarr (TV)</option>
<option value="radarr">Radarr (Movies)</option>
</select>
</form>

<form phx-change="filter_hdr">
<select
name="hdr"
value={hdr_to_param(@hdr_filter) || ""}
Expand Down
33 changes: 32 additions & 1 deletion nix/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,38 @@ in
beamPackages.mixRelease rec {
pname = "reencodarr";
version = "0.1.0";
src = lib.cleanSource ../.;
src = lib.cleanSourceWith {
src = ../.;

filter = path: type:
let
root = toString ../.;
pathStr = toString path;
relPath =
if pathStr == root
then ""
else lib.removePrefix "${root}/" pathStr;

excludedDirs = [
"_build"
"deps"
"node_modules"
"logs"
".elixir_ls"
".direnv"
".git"
];

isExcludedDir = dir: lib.hasInfix "/${dir}/" "/${relPath}/";

excluded =
lib.any isExcludedDir excludedDirs
|| builtins.match "priv/reencodarr_(dev|prod|test).*\\.db(-shm|-wal|-journal)?"
relPath
!= null;
in
lib.cleanSourceFilter path type && !excluded;
};

mixFodDeps = beamPackages.fetchMixDeps {
pname = "${pname}-mix-deps";
Expand Down
Loading
Loading