caption-qa

Par github · awesome-copilot

Vérifiez les sous-titres et légendes SRT ou WebVTT par rapport aux standards publiés (Netflix English, BBC subtitle guidelines, DCMP Captioning Key) pour la vitesse de lecture, la longueur des lignes, le nombre de lignes, la durée d'affichage, les écarts et les chevauchements, puis corrigez les non-conformités en retemporant avant de reformuler. À utiliser lorsqu'on vous demande de faire un QA, un lint ou une validation de légendes, de vérifier si des sous-titres défilent trop vite, de préparer des légendes pour une diffusion Netflix, broadcast ou éducative, ou d'ajouter une vérification de légendes à un pipeline CI.

npx skills add https://github.com/github/awesome-copilot --skill caption-qa

Vérification des sous-titres

Les sous-titres échouent la vérification de livraison et d'accessibilité sur les nombres : caractères par seconde, caractères par ligne, durée d'affichage et l'intervalle entre sous-titres. Chaque standard publie ses propres limites, et elles diffèrent, donc « ces sous-titres sont-ils acceptables ? » n'a pas de réponse tant que vous ne savez pas où ils vont. Cette skill choisit le bon ensemble de limites, mesure chaque cue de la même façon que les limites publiées sont écrites, et corrige les défaillances sans couper les paroles du locuteur.

Quand utiliser cette skill

  • L'utilisateur a un fichier .srt ou .vtt et demande s'il est prêt à être livré, ou pourquoi une plateforme l'a rejeté.
  • L'utilisateur demande si les sous-titres ou les intertitres se lisent trop vite, ou ont des lignes trop longues.
  • Les sous-titres sont destinés à Netflix, un diffuseur, ou des médias éducatifs américains, et doivent respecter cette spécification.
  • L'utilisateur veut des vérifications de sous-titres dans CI pour qu'un mauvais fichier provoque l'échec de la compilation.

Choisir le standard

Si l'utilisateur ne nomme pas de cible, utilisez Netflix English (adulte) et dites-le.

Standard Vitesse de lecture Longueur de ligne Lignes Durée d'affichage Intervalle entre sous-titres
Netflix English, adulte 20 caractères par seconde 42 caractères 2 5/6 s à 7 s Au moins 2 images ; combler les intervalles sous 0,5 s à 2 images
Netflix English, enfants 17 caractères par seconde 42 caractères 2 5/6 s à 7 s Identique à l'adulte
BBC subtitle guidelines 160-180 mots par minute ; viser environ 0,3 s par mot 37 caractères (diffusion) ; en ligne, 68 % de la largeur d'une image 16:9 2 - Si vous laissez un intervalle, au moins 1 s, de préférence 1,5 s
DCMP Captioning Key (médias éducatifs américains) 130 / 140 / 160 mots par minute pour les niveaux inférieur / moyen / supérieur - 2 (préféré) 40 images (1 s 10 images) à 6 s -

Sources : Netflix English (USA) Timed Text Style Guide, Netflix subtitle timing guidelines, BBC subtitle guidelines, DCMP Captioning Key: text et presentation rate. La durée par mot et l'intervalle minimal de la BBC sont des recommandations, donc reportez-les comme des suggestions, non comme des défaillances.

Mesurer chaque cue de la même façon

  • Caractères : lettres, chiffres, espaces et ponctuation dans le texte affiché. Ne comptez pas les sauts de ligne, les balises de formatage (<i>, <b>, {\an8}) ni le numéro de cue.
  • Vitesse de lecture : caractères divisés par la durée (heure de fin moins heure de début), en secondes.
  • Mots par minute : mots divisés par la durée, multiplié par 60. Un mot est une suite de caractères qui contient une lettre ou un chiffre, donc un tiret seul n'en est pas un.
  • Intervalle : court de l'heure de fin d'un cue à l'heure de début du suivant. Les limites en images dépendent de la fréquence d'images : 2 images = 0,083 s à 24 fps mais 0,067 s à 30 fps. Demandez la fréquence d'images, ou indiquez que vous avez supposé 24 fps.
  • Chevauchement : un cue qui se termine après le début du suivant est toujours une défaillance, selon tous les standards.
  • Lisez les deux formats d'horodatage : 00:00:01,000 (SRT) et 00:00:01.000 ou 01.000 (WebVTT). Ignorez les blocs WebVTT NOTE, STYLE et REGION, et traitez les paramètres de cue après les horodatages (align:start) comme faisant partie de la ligne d'horodatage.

Flux de travail

  1. Trouver la cible (plateforme, diffuseur ou niveau d'audience) et choisir le standard dans le tableau.
  2. Mesurer chaque cue, ou exécuter la vérification en ligne de commande ci-dessous et lire sa sortie.
  3. Rapporter d'abord une ligne de décompte (« 212 sur 240 sous-titres respectent Netflix English, adulte »). Puis regrouper les défaillances par règle, avec les numéros de cue et les horodatages. Montrer les premiers cues de chaque type au lieu de centaines de lignes.
  4. Corriger, en gardant les paroles, dans cet ordre :
    1. Étendre l'heure de fin dans l'intervalle suivant, en gardant l'intervalle minimal.
    2. Commencer le cue plus tôt si le cue précédent se termine bien avant.
    3. Diviser un long cue en deux à une limite de phrase, chacun respectant la durée d'affichage minimale.
    4. Fusionner un cue éclair (sous la durée minimale) avec son voisin.
    5. Ré-couper les lignes : casser après la ponctuation ou avant une conjonction ou préposition, et garder un article avec son nom et un prénom avec un nom de famille.
    6. Éditer le libellé seulement si l'utilisateur demande des sous-titres édités (non verbatim).
  5. Mesurer à nouveau après correction. Un rééchelonnement peut créer un nouveau chevauchement ou un intervalle sous le minimum.

Script de vérification de référence

Quand l'utilisateur veut une vérification reproductible (un dossier de fichiers, ou CI), écrivez ce script, ou adaptez-le au standard choisi. Il applique les limites Netflix adulte du tableau, affiche chaque sous-titre défaillant et quitte avec le statut 1 si un sous-titre échoue, pour qu'il puisse bloquer une compilation. Il ne nécessite que Python 3.

import re
import sys

# Netflix English, adult programs. Change these for another standard.
LIMITS = {"cps": 20, "cpl": 42, "lines": 2, "min_s": 5 / 6, "max_s": 7, "gap_frames": 2}
FPS = 24
T = r"(?:(\d+):)?(\d{1,2}):(\d{1,2})[,.](\d{1,3})"
TIMING = re.compile(T + r"\s*-->\s*" + T)
TAGS = re.compile(r"<[^>]+>|\{\\[^}]*\}")


def seconds(h, m, s, ms):
    return int(h or 0) * 3600 + int(m) * 60 + int(s) + int((ms + "00")[:3]) / 1000


def read_cues(path):
    text = open(path, encoding="utf-8-sig").read().replace("\r\n", "\n")
    for block in re.split(r"\n[ \t]*\n", text):
        lines = block.strip().split("\n")
        for i, line in enumerate(lines[:3]):
            m = TIMING.search(line)
            if m:
                body = [TAGS.sub("", t).rstrip() for t in lines[i + 1:]]
                g = m.groups()
                yield seconds(*g[:4]), seconds(*g[4:]), [t for t in body if t.strip()]
                break


def check(path, lim=LIMITS, fps=FPS):
    cues, failed = list(read_cues(path)), 0
    for n, (start, end, text) in enumerate(cues, 1):
        dur, chars, issues = end - start, sum(len(t) for t in text), []
        if dur > 0 and chars / dur > lim["cps"]:
            issues.append(f"{chars / dur:.1f} characters a second")
        issues += [f"line {j} is {len(t)} characters"
                   for j, t in enumerate(text, 1) if len(t) > lim["cpl"]]
        if len(text) > lim["lines"]:
            issues.append(f"{len(text)} lines")
        if dur < lim["min_s"] or dur > lim["max_s"]:
            issues.append(f"on screen {dur:.2f} s")
        if n < len(cues):
            gap = cues[n][0] - end
            if gap < 0:
                issues.append(f"overlaps the next caption by {-gap:.2f} s")
            elif gap * fps < lim["gap_frames"] - 0.01:
                issues.append(f"gap of {gap * fps:.0f} frames at {fps} fps")
        if issues:
            failed += 1
            print(f"{path} #{n} {start:.3f} -> {end:.3f}: " + "; ".join(issues))
    print(f"{path}: {len(cues) - failed} of {len(cues)} captions pass")
    return failed == 0


if __name__ == "__main__":
    results = [check(p) for p in sys.argv[1:]]
    sys.exit(0 if results and all(results) else 1)

Exécutez-le avec python3 check_captions.py captions/*.srt. Pour un autre standard, changez LIMITS et ajoutez une vérification de mots par minute (mots divisés par la durée, multiplié par 60) pour la BBC et DCMP. Le script ne vérifie pas les règles au niveau des recommandations (chaînage d'intervalles courts, la durée par mot de la BBC) ; rapportez-les séparément.

Dans GitHub Actions :

- run: python3 check_captions.py captions/*.srt

Pièges

  • Respecter ces nombres ne certifie pas un fichier. Netflix et les diffuseurs vérifient aussi le style : italiques, étiquettes de locuteur, descriptions sonores et position.
  • Les limites ont été lues à partir des pages de chaque standard en octobre 2026. Vérifiez la source avant de citer une limite comme actuelle.
  • Seuls SRT et WebVTT sont couverts. Convertissez d'abord ASS avec ffmpeg -i subs.ass subs.srt, ce qui préserve l'horodatage et enveloppe le texte dans des balises de police que le décompte de caractères ignore.

Skills similaires