{"record":{"id":"72f73b6f49be905f","repo":"affaan-m/ECC","slug":"bad-timecode-tc-r","errorCode":null,"errorMessage":"bad timecode {tc!r}","messagePattern":"bad timecode (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/export.py","lineNumber":246,"sourceCode":"\n# ---------------------------------------------------------------------------\n# EDL parsing (round-trip validation)\n# ---------------------------------------------------------------------------\n\n_TC_RE = re.compile(r\"^(\\d{2}):(\\d{2}):(\\d{2})[:;](\\d{2})$\")\n_EVENT_RE = re.compile(\n    r\"^(\\d+)\\s+(\\S+)\\s+V\\s+C\\s+\"\n    r\"(\\d{2}:\\d{2}:\\d{2}[:;]\\d{2})\\s+\"\n    r\"(\\d{2}:\\d{2}:\\d{2}[:;]\\d{2})\\s+\"\n    r\"(\\d{2}:\\d{2}:\\d{2}[:;]\\d{2})\\s+\"\n    r\"(\\d{2}:\\d{2}:\\d{2}[:;]\\d{2})\\s*$\"\n)\n\n\ndef _tc_to_frames(tc: str, fps: float | Fraction, drop: bool) -> int:\n    m = _TC_RE.match(tc)\n    if not m:\n        raise ValueError(f\"bad timecode {tc!r}\")\n    hh, mm, ss, ff = (int(g) for g in m.groups())\n    rate = int(round(float(fps_fraction(fps))))\n    displayed = (hh * 3600 + mm * 60 + ss) * rate + ff\n    if drop:\n        dropped = int(round(float(fps_fraction(fps)) * 0.066666))\n        total_minutes = hh * 60 + mm\n        # Skipped frame numbers before this TC: 2/min except every 10th min.\n        skipped = dropped * (total_minutes - total_minutes // 10)\n        return displayed - skipped\n    return displayed\n\n\ndef parse_edl(text: str, fps: float = 24.0) -> list[dict]:\n    \"\"\"Parse a CMX3600 EDL back into structured events (round-trip check).\"\"\"\n    drop = \"FCM: DROP FRAME\" in text\n    events: list[dict] = []\n    for line in text.splitlines():\n        m = _EVENT_RE.match(line)","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/export.py#L228-L264","documentation":"_tc_to_frames parses an SMPTE-style timecode string with _TC_RE and raises ValueError when the string does not match the expected HH:MM:SS:FF pattern. It is used by parse_edl to convert EDL timecodes into frame counts.","triggerScenarios":"parse_edl encounters a timecode field that is not in the expected format, e.g. '1:23:45:06' (single-digit hour if the regex requires two), '00:00:00;05' with a semicolon if the regex only accepts colons, or a truncated/garbage value like 'N/A'.","commonSituations":"Hand-edited EDL files, exported EDLs from NLEs using drop-frame semicolon notation or different field separators, and blank timecode cells in sparse EDLs.","solutions":["Open the EDL and fix the malformed timecode to match the expected format (e.g. 'HH:MM:SS:FF')","Check _TC_RE in export.py to confirm the exact accepted pattern including drop-frame ';' separators","Pre-clean source EDLs from non-standard exporters or re-export with standard timecode notation"],"exampleFix":"// before\nedl = parse_edl(\"clip.tc = '1:23:45:6'\")  # single-digit fields\n// after\nedl = parse_edl(\"clip.tc = '01:23:45:06'\")  # zero-padded HH:MM:SS:FF","handlingStrategy":"validation","validationCode":"import re\n_TC = re.compile(r\"^\\d{2}:\\d{2}:\\d{2}[:;]\\d{2}$\")\nassert _TC.match(tc), f\"malformed timecode: {tc!r}\"","typeGuard":"def is_valid_timecode(tc) -> bool:\n    return isinstance(tc, str) and bool(_TC_RE.match(tc))","tryCatchPattern":"try:\n    frames = _tc_to_frames(tc, fps, drop)\nexcept ValueError as e:\n    print(f\"EDL line has bad timecode: {e}; fix the source EDL\")\n    continue","preventionTips":["Validate every timecode field against the expected regex before parsing the EDL","Normalize drop-frame ';' notation and zero-pad single-digit fields during pre-processing","Keep a lint script that scans EDLs for non-conforming timecodes"],"tags":["timecode","parsing","edl"],"backgroundTag":"invalid-date-format","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}