AstraOS commited on
Commit
d7b0da7
Β·
verified Β·
1 Parent(s): 241b621

Update dvd_converter.py

Browse files
Files changed (1) hide show
  1. dvd_converter.py +63 -41
dvd_converter.py CHANGED
@@ -1250,26 +1250,23 @@ def build_pre_scale_chain(info: dict, cfg: argparse.Namespace) -> str:
1250
  else:
1251
  log.info(" ⏭️ denoise skipped (--no-denoise)")
1252
 
1253
- # 4. Scale β€” handled in build_filter_complex via letterbox (force_original_aspect_ratio + pad)
1254
- log.info(" βœ… scale/letterbox: %dΓ—%d (fit-to-frame, black bars) %s",
1255
- cfg.width, cfg.height, cfg.scale_flags)
1256
 
1257
  return ",".join(filters) if filters else "null"
1258
 
1259
 
1260
  def build_filter_complex(pre_scale: str, cfg: argparse.Namespace) -> str:
1261
- """Build the full filter_complex string with letterbox fill + deflicker + eq.
1262
-
1263
- Letterboxing (fit-to-frame):
1264
- β€’ Video is scaled so it fits entirely within the target WΓ—H frame,
1265
- preserving the original aspect ratio β€” no cropping, no stretching.
1266
- β€’ If the source is wider than the target (e.g. 16:9 into 4:3), black
1267
- bars are added to the top and bottom (letterbox).
1268
- β€’ If the source is taller than the target, black bars are added to the
1269
- left and right (pillarbox).
1270
- β€’ The frame always touches at least two edges; the player scales the
1271
- whole frame to fill its screen, so the video fills the horizontal
1272
- extent and the player shrinks it to keep the height in bounds.
1273
  """
1274
 
1275
  deflicker_part = (
@@ -1297,35 +1294,60 @@ def build_filter_complex(pre_scale: str, cfg: argparse.Namespace) -> str:
1297
 
1298
  post_filter = (f"{cfg.post_output_filter}," if cfg.post_output_filter else "")
1299
 
1300
- # ── Letterbox scale + pad ────────────────────────────────────────────────
1301
- # Step 1: scale down (never up beyond target) preserving aspect ratio.
1302
- # force_original_aspect_ratio=decrease ensures the result fits
1303
- # inside WΓ—H without cropping.
1304
- # Step 2: pad to exact target dimensions, centring the scaled picture,
1305
- # filling the remainder with black.
1306
- letterbox = (
1307
- f"scale={cfg.width}:{cfg.height}"
1308
- f":force_original_aspect_ratio=decrease"
1309
- f":flags={cfg.scale_flags},"
1310
- f"pad={cfg.width}:{cfg.height}"
1311
- f":(ow-iw)/2:(oh-ih)/2"
1312
- f":black"
1313
- )
1314
 
1315
- log.info(
1316
- " βœ… letterbox: scaleβ†’%dΓ—%d (fit) + pad (black bars) [%s]",
1317
- cfg.width, cfg.height, cfg.scale_flags,
1318
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1319
 
1320
  return (
1321
- f"[0:v]{pre_scale}[prescaled];"
1322
- f"[prescaled]{letterbox}[filled];"
1323
- "[filled]"
1324
- f"{deflicker_part}"
1325
- f"{sharpen_eq_part}"
1326
- f"setsar={cfg.sar},"
1327
- f"{post_filter}"
1328
- "null[out]"
1329
  ).replace(",null[out]", "[out]").replace("null[out]", "[out]")
1330
 
1331
 
 
1250
  else:
1251
  log.info(" ⏭️ denoise skipped (--no-denoise)")
1252
 
1253
+ # 4. Final scale/fill is handled in build_filter_complex (letterbox or blur mode)
1254
+ fill_mode = getattr(cfg, "bg_fill_mode", "letterbox")
1255
+ log.info(" βœ… fill mode: %s β€” final scale/pad applied in filter_complex", fill_mode)
1256
 
1257
  return ",".join(filters) if filters else "null"
1258
 
1259
 
1260
  def build_filter_complex(pre_scale: str, cfg: argparse.Namespace) -> str:
1261
+ """Build the full filter_complex string with selectable fill mode + deflicker + eq.
1262
+
1263
+ bg_fill_mode (cfg attribute, default "letterbox"):
1264
+ "letterbox" β€” scale to fit inside WΓ—H preserving aspect ratio, pad remainder
1265
+ with solid black bars. No cropping, no blur, no stretching.
1266
+ This is standard Letterboxing / Pillarboxing behaviour.
1267
+ "blur" β€” scale to fill WΓ—H, then composite the sharp foreground (scaled
1268
+ to fit) over a blurred, darkened copy of the same frame.
1269
+ Produces an aesthetically filled background instead of bars.
 
 
 
1270
  """
1271
 
1272
  deflicker_part = (
 
1294
 
1295
  post_filter = (f"{cfg.post_output_filter}," if cfg.post_output_filter else "")
1296
 
1297
+ # Resolve bg_fill_mode β€” attribute may be missing on hand-built cfg objects
1298
+ fill_mode = getattr(cfg, "bg_fill_mode", "letterbox")
 
 
 
 
 
 
 
 
 
 
 
 
1299
 
1300
+ # ── MODE: letterbox (black bars) ─────────────────────────────────────────
1301
+ # scale down to fit entirely inside WΓ—H (never upscale beyond target),
1302
+ # then pad to exact dimensions centring the picture with black.
1303
+ if fill_mode != "blur":
1304
+ log.info(
1305
+ " βœ… fill=letterbox: scaleβ†’%dΓ—%d (fit) + pad black [%s]",
1306
+ cfg.width, cfg.height, cfg.scale_flags,
1307
+ )
1308
+ fill_chain = (
1309
+ f"[0:v]{pre_scale}[prescaled];"
1310
+ f"[prescaled]"
1311
+ f"scale={cfg.width}:{cfg.height}"
1312
+ f":force_original_aspect_ratio=decrease"
1313
+ f":flags={cfg.scale_flags},"
1314
+ f"pad={cfg.width}:{cfg.height}"
1315
+ f":(ow-iw)/2:(oh-ih)/2"
1316
+ f":black"
1317
+ f"[filled];"
1318
+ )
1319
+
1320
+ # ── MODE: blur background ────────────────────────────────────────────────
1321
+ # Split the pre-scaled stream into foreground (fit-scaled) and background
1322
+ # (fill-scaled β†’ blurred β†’ darkened), then overlay the foreground centred.
1323
+ else:
1324
+ ov_x = cfg.overlay_x if hasattr(cfg, "overlay_x") else cfg.overlay_x_expr
1325
+ ov_y = cfg.overlay_y if hasattr(cfg, "overlay_y") else cfg.overlay_y_expr
1326
+ log.info(
1327
+ " βœ… fill=blur: avgblur r=%d bg_bright=%.2f bg_sat=%.2f [%s]",
1328
+ cfg.blur_radius, cfg.bg_brightness, cfg.bg_saturation, cfg.bg_scale_flags,
1329
+ )
1330
+ fill_chain = (
1331
+ f"[0:v]{pre_scale}[prescaled];"
1332
+ "[prescaled]split=2[fg][bg_src];"
1333
+ f"[fg]scale={cfg.width}:-2:flags={cfg.scale_flags}[fg_scaled];"
1334
+ f"[bg_src]scale={cfg.width}:{cfg.height}:flags={cfg.bg_scale_flags},"
1335
+ f"avgblur={cfg.blur_radius},"
1336
+ f"eq=brightness={cfg.bg_brightness}"
1337
+ f":saturation={cfg.bg_saturation}"
1338
+ f":contrast={cfg.bg_contrast}"
1339
+ f":gamma={cfg.bg_gamma}[bg];"
1340
+ f"[bg][fg_scaled]overlay={ov_x}:{ov_y}[filled];"
1341
+ )
1342
 
1343
  return (
1344
+ fill_chain
1345
+ + "[filled]"
1346
+ + deflicker_part
1347
+ + sharpen_eq_part
1348
+ + f"setsar={cfg.sar},"
1349
+ + post_filter
1350
+ + "null[out]"
 
1351
  ).replace(",null[out]", "[out]").replace("null[out]", "[out]")
1352
 
1353