Detect embedded subtitle tracks in analyzers

Add a subtitle branch to the ffprobe/avprobe stream loops that collects
each embedded subtitle track (index, codec, language, title, default/
forced flags) and classifies it as text or image by codec. This is the
basis for letting the user pick a track to burn into the picture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Luna 2026-06-02 00:39:13 -07:00
parent 75aa10e4c8
commit a158119d41
3 changed files with 48 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
__pycache__/
*.pyc
*.pyo
build/
dist/
*.egg-info/
src/devedeng/data/locale/

View file

@ -90,6 +90,8 @@ class avprobe(devedeng.avbase.avbase):
self.original_fps = 0 self.original_fps = 0
self.original_aspect_ratio = None self.original_aspect_ratio = None
self.prerotation = 0 self.prerotation = 0
# embedded subtitle tracks (see ffprobe.process_json for the schema)
self.subtitle_tracks = []
self.config.append_static_log( self.config.append_static_log(
"Getting data for {:s} with avprobe".format(file_name)) "Getting data for {:s} with avprobe".format(file_name))
@ -136,6 +138,23 @@ class avprobe(devedeng.avbase.avbase):
self.original_audiorate_uncompressed = int( self.original_audiorate_uncompressed = int(
float(element["sample_rate"])) float(element["sample_rate"]))
elif (element["codec_type"] == "subtitle"):
codec = element.get("codec_name", "")
image_codecs = ("hdmv_pgs_subtitle", "pgssub", "dvd_subtitle",
"dvdsub", "xsub")
kind = "image" if codec in image_codecs else "text"
tags = element.get("tags", {})
disposition = element.get("disposition", {})
self.subtitle_tracks.append({
"index": element["index"],
"codec": codec,
"language": tags.get("language", ""),
"title": tags.get("title", ""),
"default": bool(disposition.get("default", 0)),
"forced": bool(disposition.get("forced", 0)),
"kind": kind,
})
self.original_size = str(self.original_width) + "x" + str(self.original_height) self.original_size = str(self.original_width) + "x" + str(self.original_height)
if (self.original_aspect_ratio is None): if (self.original_aspect_ratio is None):
if (self.original_height != 0): if (self.original_height != 0):

View file

@ -80,6 +80,9 @@ class ffprobe(devedeng.executor.executor):
self.audio_streams = 0 self.audio_streams = 0
self.video_list = [] self.video_list = []
self.video_streams = 0 self.video_streams = 0
# embedded subtitle tracks: list of dicts with index, codec, language,
# title, default/forced flags, and "kind" ("text" or "image")
self.subtitle_tracks = []
self.original_width = 0 self.original_width = 0
self.original_height = 0 self.original_height = 0
self.original_length = -1 self.original_length = -1
@ -135,6 +138,25 @@ class ffprobe(devedeng.executor.executor):
self.original_audiorate_uncompressed = int( self.original_audiorate_uncompressed = int(
float(element["sample_rate"])) float(element["sample_rate"]))
elif (element["codec_type"] == "subtitle"):
codec = element.get("codec_name", "")
# bitmap (image) subtitles can't be burned with the text
# `subtitles` filter; they must be overlaid instead
image_codecs = ("hdmv_pgs_subtitle", "pgssub", "dvd_subtitle",
"dvdsub", "xsub")
kind = "image" if codec in image_codecs else "text"
tags = element.get("tags", {})
disposition = element.get("disposition", {})
self.subtitle_tracks.append({
"index": element["index"],
"codec": codec,
"language": tags.get("language", ""),
"title": tags.get("title", ""),
"default": bool(disposition.get("default", 0)),
"forced": bool(disposition.get("forced", 0)),
"kind": kind,
})
self.original_size = str(self.original_width) + "x" + str(self.original_height) self.original_size = str(self.original_width) + "x" + str(self.original_height)
if (self.original_aspect_ratio is None): if (self.original_aspect_ratio is None):
if (self.original_height != 0): if (self.original_height != 0):