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

1#!/usr/bin/env python3 

2 

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 

21 

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 

27 

28 # ************************************************************************** 

29 

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" 

37 

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 

41 

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] 

79 

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 ) 

163 

164 # Initialize values ... 

165 y1 = height # [px] 

166 y2 = 0 # [px] 

167 

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] 

173 

174 # Update values ... 

175 y1 = min(y1, y) # [px] 

176 y2 = max(y2, y + h) # [px] 

177 

178 # Return answer ... 

179 return y1, y2