Skip to content

Commit 73605d5

Browse files
committed
Fix MKV attachment cleanup detection tests
1 parent 4debef0 commit 73605d5

2 files changed

Lines changed: 118 additions & 25 deletions

File tree

lib/reencodarr/ab_av1/helper.ex

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,33 @@ defmodule Reencodarr.AbAv1.Helper do
107107

108108
@spec parse_attached_pictures(String.t()) :: {:ok, list(map())}
109109
defp parse_attached_pictures(output) do
110-
case Jason.decode(output) do
111-
{:ok, %{"streams" => streams}} ->
112-
pics = Enum.filter(streams, &attached_picture?/1)
113-
{:ok, pics}
110+
case extract_ffprobe_json(output) do
111+
{:ok, json} ->
112+
case Jason.decode(json) do
113+
{:ok, %{"streams" => streams}} ->
114+
pics = Enum.filter(streams, &attached_picture?/1)
115+
{:ok, pics}
116+
117+
_ ->
118+
{:ok, []}
119+
end
114120

115-
_ ->
121+
:error ->
116122
{:ok, []}
117123
end
118124
end
119125

126+
defp extract_ffprobe_json(output) do
127+
case :binary.match(output, "{") do
128+
{idx, _len} ->
129+
json = binary_part(output, idx, byte_size(output) - idx) |> String.trim()
130+
{:ok, json}
131+
132+
:nomatch ->
133+
:error
134+
end
135+
end
136+
120137
@spec attached_picture?(map()) :: boolean()
121138
defp attached_picture?(stream) do
122139
get_in(stream, ["disposition", "attached_pic"]) == 1 or
@@ -135,23 +152,42 @@ defmodule Reencodarr.AbAv1.Helper do
135152

136153
@spec clean_mkv_in_place(String.t(), list(map())) :: {:ok, String.t()}
137154
defp clean_mkv_in_place(file_path, attached_pics) do
138-
# Step 1: Delete image attachments via mkvpropedit (in-place, fast)
139-
delete_image_attachments(file_path)
140-
141-
# Step 2: Check if any are actual MJPEG video tracks (not just attachments)
142155
has_mjpeg_tracks =
143156
Enum.any?(attached_pics, fn s ->
144157
s["codec_type"] == "video" and s["codec_name"] == "mjpeg" and
145158
get_in(s, ["disposition", "attached_pic"]) != 1
146159
end)
147160

148-
if has_mjpeg_tracks do
149-
# mkvpropedit can't delete tracks — fall back to ffmpeg remux
150-
Logger.info("Found MJPEG video tracks in #{file_path}, using ffmpeg remux")
151-
clean_via_ffmpeg_remux(file_path)
152-
else
153-
Logger.info("Successfully cleaned image attachments from #{file_path} (in-place)")
154-
{:ok, file_path}
161+
# Step 1: Delete image attachments via mkvpropedit (in-place, fast)
162+
delete_image_attachments(file_path)
163+
164+
case detect_attached_pictures(file_path) do
165+
{:ok, []} when has_mjpeg_tracks ->
166+
# mkvpropedit only handles attachments, not real tracks.
167+
Logger.info("Found MJPEG video tracks in #{file_path}, using ffmpeg remux")
168+
clean_via_ffmpeg_remux(file_path)
169+
170+
{:ok, []} ->
171+
Logger.info("Successfully cleaned image attachments from #{file_path} (in-place)")
172+
{:ok, file_path}
173+
174+
{:ok, remaining} ->
175+
Logger.warning(
176+
"mkvpropedit left #{length(remaining)} image stream(s) in #{file_path}; falling back to ffmpeg remux"
177+
)
178+
179+
clean_via_ffmpeg_remux(file_path)
180+
181+
{:error, reason} ->
182+
Logger.warning(
183+
"Failed to verify MKV attachment cleanup for #{file_path}: #{inspect(reason)}"
184+
)
185+
186+
if has_mjpeg_tracks do
187+
clean_via_ffmpeg_remux(file_path)
188+
else
189+
{:ok, file_path}
190+
end
155191
end
156192
end
157193

test/reencodarr/ab_av1/helper_test.exs

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ defmodule Reencodarr.AbAv1.HelperTest do
6969
test "detects streams with attached_pic disposition" do
7070
file_path = "/media/movie_with_cover.mkv"
7171

72-
ffprobe_response =
72+
ffprobe_with_images =
7373
Jason.encode!(%{
7474
"streams" => [
7575
%{
@@ -86,11 +86,30 @@ defmodule Reencodarr.AbAv1.HelperTest do
8686
]
8787
})
8888

89+
ffprobe_clean =
90+
Jason.encode!(%{
91+
"streams" => [
92+
%{
93+
"codec_name" => "hevc",
94+
"codec_type" => "video",
95+
"disposition" => %{"attached_pic" => 0}
96+
}
97+
]
98+
})
99+
100+
counter = :counters.new(1, [:atomics])
89101
:meck.new(System, [:passthrough])
90102

91103
:meck.expect(System, :cmd, fn
92-
"ffprobe", _, _ -> {ffprobe_response, 0}
93-
"mkvpropedit", _, _ -> {"Done.\n", 0}
104+
"ffprobe", _, _ ->
105+
:counters.add(counter, 1, 1)
106+
107+
if :counters.get(counter, 1) == 1,
108+
do: {ffprobe_with_images, 0},
109+
else: {ffprobe_clean, 0}
110+
111+
"mkvpropedit", _, _ ->
112+
{"Done.\n", 0}
94113
end)
95114

96115
assert {:ok, ^file_path} = Helper.clean_attachments(file_path)
@@ -135,7 +154,7 @@ defmodule Reencodarr.AbAv1.HelperTest do
135154
test "calls mkvpropedit in-place for MKV with image attachments" do
136155
file_path = "/media/movie.mkv"
137156

138-
ffprobe_response =
157+
ffprobe_with_images =
139158
Jason.encode!(%{
140159
"streams" => [
141160
%{
@@ -152,11 +171,30 @@ defmodule Reencodarr.AbAv1.HelperTest do
152171
]
153172
})
154173

174+
ffprobe_clean =
175+
Jason.encode!(%{
176+
"streams" => [
177+
%{
178+
"codec_name" => "hevc",
179+
"codec_type" => "video",
180+
"disposition" => %{"attached_pic" => 0}
181+
}
182+
]
183+
})
184+
185+
counter = :counters.new(1, [:atomics])
155186
:meck.new(System, [:passthrough])
156187

157188
:meck.expect(System, :cmd, fn
158-
"ffprobe", _, _ -> {ffprobe_response, 0}
159-
"mkvpropedit", [^file_path | _], _ -> {"Done.\n", 0}
189+
"ffprobe", _, _ ->
190+
:counters.add(counter, 1, 1)
191+
192+
if :counters.get(counter, 1) == 1,
193+
do: {ffprobe_with_images, 0},
194+
else: {ffprobe_clean, 0}
195+
196+
"mkvpropedit", [^file_path | _], _ ->
197+
{"Done.\n", 0}
160198
end)
161199

162200
assert {:ok, ^file_path} = Helper.clean_attachments(file_path)
@@ -171,7 +209,7 @@ defmodule Reencodarr.AbAv1.HelperTest do
171209
test "does not create temp files for MKV attachment-only case" do
172210
file_path = "/media/movie.MKV"
173211

174-
ffprobe_response =
212+
ffprobe_with_images =
175213
Jason.encode!(%{
176214
"streams" => [
177215
%{"codec_name" => "hevc", "codec_type" => "video"},
@@ -184,11 +222,30 @@ defmodule Reencodarr.AbAv1.HelperTest do
184222
]
185223
})
186224

225+
ffprobe_clean =
226+
Jason.encode!(%{
227+
"streams" => [
228+
%{
229+
"codec_name" => "hevc",
230+
"codec_type" => "video",
231+
"disposition" => %{"attached_pic" => 0}
232+
}
233+
]
234+
})
235+
236+
counter = :counters.new(1, [:atomics])
187237
:meck.new(System, [:passthrough])
188238

189239
:meck.expect(System, :cmd, fn
190-
"ffprobe", _, _ -> {ffprobe_response, 0}
191-
"mkvpropedit", _, _ -> {"Done.\n", 0}
240+
"ffprobe", _, _ ->
241+
:counters.add(counter, 1, 1)
242+
243+
if :counters.get(counter, 1) == 1,
244+
do: {ffprobe_with_images, 0},
245+
else: {ffprobe_clean, 0}
246+
247+
"mkvpropedit", _, _ ->
248+
{"Done.\n", 0}
192249
end)
193250

194251
{:ok, result_path} = Helper.clean_attachments(file_path)

0 commit comments

Comments
 (0)