Coverage for pyguymer3/media/return_subtitle_extent.py: 3%
34 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
1#!/usr/bin/env python3
3# Define function ...
4def return_subtitle_extent(
5 fname,
6 /,
7 *,
8 cwd = None,
9 debug = __debug__,
10 ensureNFC = True,
11 ffmpegPath = None,
12 ffprobePath = None,
13 playlist = -1,
14 subtitle = 0,
15 timeout = 60.0,
16):
17 # Import standard modules ...
18 import re
19 import shutil
20 import subprocess
22 # Import sub-functions ...
23 from .return_media_duration import return_media_duration
24 from .return_video_frame_rate import return_video_frame_rate
25 from .return_video_height import return_video_height
26 from .return_video_width import return_video_width
28 # **************************************************************************
30 # Try to find the paths if the user did not provide them ...
31 if ffmpegPath is None:
32 ffmpegPath = shutil.which("ffmpeg")
33 if ffprobePath is None:
34 ffprobePath = shutil.which("ffprobe")
35 assert ffmpegPath is not None, "\"ffmpeg\" is not installed"
36 assert ffprobePath is not None, "\"ffprobe\" is not installed"
38 # Check input ...
39 if fname.startswith("bluray:") and playlist < 0:
40 raise Exception("a Blu-ray was specified but no playlist was supplied") from None
42 # Find out information about video ...
43 duration = return_media_duration(
44 fname,
45 cwd = cwd,
46 debug = debug,
47 ensureNFC = ensureNFC,
48 ffprobePath = ffprobePath,
49 playlist = playlist,
50 timeout = timeout,
51 ) # [s]
52 fps = return_video_frame_rate(
53 fname,
54 cwd = cwd,
55 debug = debug,
56 ensureNFC = ensureNFC,
57 ffprobePath = ffprobePath,
58 playlist = playlist,
59 timeout = timeout,
60 ) # [Hz]
61 width = return_video_width(
62 fname,
63 cwd = cwd,
64 debug = debug,
65 ensureNFC = ensureNFC,
66 ffprobePath = ffprobePath,
67 playlist = playlist,
68 timeout = timeout,
69 ) # [px]
70 height = return_video_height(
71 fname,
72 cwd = cwd,
73 debug = debug,
74 ensureNFC = ensureNFC,
75 ffprobePath = ffprobePath,
76 playlist = playlist,
77 timeout = timeout,
78 ) # [px]
80 # Check if it is a Blu-ray ...
81 if fname.startswith("bluray:"):
82 # Find stream info ...
83 resp = subprocess.run(
84 [
85 ffmpegPath,
86 "-hide_banner",
87 "-probesize", "1G",
88 "-analyzeduration", "1800M",
89 "-f", "lavfi",
90 "-i", f"color=color=black:size={width:d}x{height:d}:rate={fps:f}:duration={duration:f},format=yuv420p",
91 "-playlist", f"{playlist:d}",
92 "-i", fname,
93 "-filter_complex", f"[0:v:0][1:s:{subtitle:d}]overlay,cropdetect",
94 "-an",
95 "-sn",
96 "-vn",
97 "-y",
98 "-f", "null",
99 "/dev/null",
100 ],
101 check = True,
102 cwd = cwd,
103 encoding = "utf-8",
104 stderr = subprocess.STDOUT,
105 stdout = subprocess.PIPE,
106 timeout = None,
107 )
108 else:
109 # Attempt to survey the file ...
110 try:
111 # Find stream info ...
112 resp = subprocess.run(
113 [
114 ffmpegPath,
115 "-hide_banner",
116 "-probesize", "1G",
117 "-analyzeduration", "1800M",
118 "-f", "lavfi",
119 "-i", f"color=color=black:size={width:d}x{height:d}:rate={fps:f}:duration={duration:f},format=yuv420p",
120 "-i", fname,
121 "-filter_complex", f"[0:v:0][1:s:{subtitle:d}]overlay,cropdetect",
122 "-an",
123 "-sn",
124 "-vn",
125 "-y",
126 "-f", "null",
127 "/dev/null",
128 ],
129 check = True,
130 cwd = cwd,
131 encoding = "utf-8",
132 stderr = subprocess.STDOUT,
133 stdout = subprocess.PIPE,
134 timeout = None,
135 )
136 except subprocess.CalledProcessError:
137 # Fallback and attempt to find stream info as a raw M-JPEG stream ...
138 resp = subprocess.run(
139 [
140 ffmpegPath,
141 "-hide_banner",
142 "-probesize", "1G",
143 "-analyzeduration", "1800M",
144 "-f", "lavfi",
145 "-i", f"color=color=black:size={width:d}x{height:d}:rate={fps:f}:duration={duration:f},format=yuv420p",
146 "-f", "mjpeg",
147 "-i", fname,
148 "-filter_complex", f"[0:v:0][1:s:{subtitle:d}]overlay,cropdetect",
149 "-an",
150 "-sn",
151 "-vn",
152 "-y",
153 "-f", "null",
154 "/dev/null",
155 ],
156 check = True,
157 cwd = cwd,
158 encoding = "utf-8",
159 stderr = subprocess.STDOUT,
160 stdout = subprocess.PIPE,
161 timeout = None,
162 )
164 # Initialize values ...
165 y1 = height # [px]
166 y2 = 0 # [px]
168 # Loop over matches ...
169 for match in re.findall(r"crop=[0-9]+:[0-9]+:[0-9]+:[0-9]+", resp.stdout):
170 # Extract information ...
171 h = int(match.split("=")[1].split(":")[1]) # [px]
172 y = int(match.split("=")[1].split(":")[3]) # [px]
174 # Update values ...
175 y1 = min(y1, y) # [px]
176 y2 = max(y2, y + h) # [px]
178 # Return answer ...
179 return y1, y2