Skip to content

Commit 226b25d

Browse files
mjcCopilot
andcommitted
Harden FailuresLive filter handling and expand tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0dfdab9 commit 226b25d

2 files changed

Lines changed: 211 additions & 35 deletions

File tree

lib/reencodarr_web/live/failures_live.ex

Lines changed: 76 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ defmodule ReencodarrWeb.FailuresLive do
2727
alias Reencodarr.Repo
2828

2929
@update_interval 30_000
30+
@stage_filter_values ["all", "analysis", "crf_search", "encoding", "post_process"]
31+
@category_filter_values ["all", "file_access", "process_failure", "timeout", "codec_issues"]
3032

3133
@impl true
3234
def mount(_params, _session, socket) do
@@ -47,7 +49,7 @@ defmodule ReencodarrWeb.FailuresLive do
4749
@impl true
4850
def handle_info(:update_failures_data, socket) do
4951
schedule_periodic_update()
50-
{:noreply, load_failures_data(socket)}
52+
{:noreply, async_load_failures(socket)}
5153
end
5254

5355
# Handle events that might affect failures (video state changes, encoding completion, etc.)
@@ -60,8 +62,7 @@ defmodule ReencodarrWeb.FailuresLive do
6062
:video_failed
6163
] do
6264
# Reload failures when pipeline events occur that might change failure state
63-
socket = load_failures_data(socket)
64-
{:noreply, socket}
65+
{:noreply, async_load_failures(socket)}
6566
end
6667

6768
# Catch-all for other Events we don't need to handle
@@ -85,8 +86,10 @@ defmodule ReencodarrWeb.FailuresLive do
8586
Media.resolve_video_failures(video.id)
8687

8788
# Reload the failures data
88-
socket = load_failures_data(socket)
89-
{:noreply, put_flash(socket, :info, "Video #{video.id} marked for retry")}
89+
{:noreply,
90+
socket
91+
|> put_flash(:info, "Video #{video.id} marked for retry")
92+
|> async_load_failures()}
9093
end
9194

9295
{:error, _} ->
@@ -100,22 +103,23 @@ defmodule ReencodarrWeb.FailuresLive do
100103
Media.reset_failed_videos()
101104

102105
# Reload the failures data
103-
socket = load_failures_data(socket)
104-
{:noreply, put_flash(socket, :info, "All failed videos have been reset")}
106+
{:noreply,
107+
socket
108+
|> put_flash(:info, "All failed videos have been reset")
109+
|> async_load_failures()}
105110
end
106111

107112
@impl true
108113
def handle_event("retry_failure_code", %{"code" => failure_code}, socket) do
109114
result = Media.retry_failed_videos_by_failure_code(failure_code)
110115

111-
socket = load_failures_data(socket)
112-
113116
{:noreply,
114-
put_flash(
115-
socket,
117+
socket
118+
|> put_flash(
116119
:info,
117120
"Queued retry for #{result.videos_retried} failed videos with #{failure_code}"
118-
)}
121+
)
122+
|> async_load_failures()}
119123
end
120124

121125
@impl true
@@ -173,7 +177,7 @@ defmodule ReencodarrWeb.FailuresLive do
173177
socket =
174178
socket
175179
|> assign(:selected_videos, MapSet.new())
176-
|> load_failures_data()
180+
|> async_load_failures()
177181

178182
count = Enum.count(selected_ids)
179183
{:noreply, put_flash(socket, :info, "Retrying #{count} selected videos")}
@@ -182,24 +186,28 @@ defmodule ReencodarrWeb.FailuresLive do
182186

183187
@impl true
184188
def handle_event("filter_failures", %{"filter" => filter}, socket) do
189+
normalized_filter = if filter in @stage_filter_values, do: filter, else: "all"
190+
185191
socket =
186192
socket
187-
|> assign(:failure_filter, filter)
193+
|> assign(:failure_filter, normalized_filter)
188194
# Reset to first page when filtering
189195
|> assign(:page, 1)
190-
|> load_failures_data()
196+
|> async_load_failures()
191197

192198
{:noreply, socket}
193199
end
194200

195201
@impl true
196202
def handle_event("filter_category", %{"category" => category}, socket) do
203+
normalized_category = if category in @category_filter_values, do: category, else: "all"
204+
197205
socket =
198206
socket
199-
|> assign(:category_filter, category)
207+
|> assign(:category_filter, normalized_category)
200208
# Reset to first page when filtering
201209
|> assign(:page, 1)
202-
|> load_failures_data()
210+
|> async_load_failures()
203211

204212
{:noreply, socket}
205213
end
@@ -212,19 +220,19 @@ defmodule ReencodarrWeb.FailuresLive do
212220
|> assign(:category_filter, "all")
213221
|> assign(:search_term, "")
214222
|> assign(:page, 1)
215-
|> load_failures_data()
223+
|> async_load_failures()
216224

217225
{:noreply, socket}
218226
end
219227

220228
@impl true
221229
def handle_event("change_page", %{"page" => page}, socket) do
222-
page = Parsers.parse_int(page, 1)
230+
page = page |> Parsers.parse_int(1) |> max(1)
223231

224232
socket =
225233
socket
226234
|> assign(:page, page)
227-
|> load_failures_data()
235+
|> async_load_failures()
228236

229237
{:noreply, socket}
230238
end
@@ -233,14 +241,24 @@ defmodule ReencodarrWeb.FailuresLive do
233241
def handle_event("search", %{"search" => search_term}, socket) do
234242
socket =
235243
socket
236-
|> assign(:search_term, search_term)
244+
|> assign(:search_term, normalize_search_term(search_term))
237245
# Reset to first page when searching
238246
|> assign(:page, 1)
239-
|> load_failures_data()
247+
|> async_load_failures()
240248

241249
{:noreply, socket}
242250
end
243251

252+
@impl true
253+
def handle_async(:load_failures, {:ok, payload}, socket) do
254+
{:noreply, assign_failure_payload(socket, payload)}
255+
end
256+
257+
@impl true
258+
def handle_async(:load_failures, {:exit, _reason}, socket) do
259+
{:noreply, socket |> assign(:loading, false) |> put_flash(:error, "Failed to load failures")}
260+
end
261+
244262
# Private helper functions
245263

246264
defp retry_video(video_id) do
@@ -784,12 +802,32 @@ defmodule ReencodarrWeb.FailuresLive do
784802
end
785803

786804
defp load_failures_data(socket) do
805+
assign_failure_payload(socket, fetch_failure_payload(socket.assigns))
806+
end
807+
808+
defp async_load_failures(socket) do
809+
load_assigns = %{
810+
page: socket.assigns.page,
811+
per_page: socket.assigns.per_page,
812+
failure_filter: socket.assigns.failure_filter,
813+
category_filter: socket.assigns.category_filter,
814+
search_term: socket.assigns.search_term
815+
}
816+
817+
show_loading? = socket.assigns.failed_videos == []
818+
819+
socket
820+
|> assign(:loading, show_loading?)
821+
|> start_async(:load_failures, fn -> fetch_failure_payload(load_assigns) end)
822+
end
823+
824+
defp fetch_failure_payload(assigns) do
787825
# Get pagination info
788-
page = socket.assigns.page
789-
per_page = socket.assigns.per_page
790-
filter = socket.assigns.failure_filter
791-
category_filter = socket.assigns.category_filter
792-
search_term = socket.assigns.search_term
826+
page = assigns.page
827+
per_page = assigns.per_page
828+
filter = assigns.failure_filter
829+
category_filter = assigns.category_filter
830+
search_term = assigns.search_term
793831

794832
# Get failed videos with pagination and filtering
795833
{failed_videos, total_count} =
@@ -806,7 +844,7 @@ defmodule ReencodarrWeb.FailuresLive do
806844
# Calculate pagination info
807845
total_pages = ceil(total_count / per_page)
808846

809-
assign_changed(socket, %{
847+
%{
810848
loading: false,
811849
failed_videos: failed_videos,
812850
video_failures: video_failures,
@@ -815,7 +853,11 @@ defmodule ReencodarrWeb.FailuresLive do
815853
failure_code_actions: failure_code_actions,
816854
total_count: total_count,
817855
total_pages: total_pages
818-
})
856+
}
857+
end
858+
859+
defp assign_failure_payload(socket, payload) do
860+
assign_changed(socket, payload)
819861
end
820862

821863
defp get_failed_videos_paginated(page, per_page, stage_filter, category_filter, search_term) do
@@ -895,6 +937,11 @@ defmodule ReencodarrWeb.FailuresLive do
895937
from v in query, where: ^case_insensitive_like_condition
896938
end
897939

940+
defp normalize_search_term(search_term) when is_binary(search_term),
941+
do: String.trim(search_term)
942+
943+
defp normalize_search_term(_search_term), do: ""
944+
898945
defp apply_ordering(query) do
899946
from v in query, order_by: [desc: v.inserted_at]
900947
end

0 commit comments

Comments
 (0)