From c0b77bec3a722788e5fc047fd6761ef4d62680de Mon Sep 17 00:00:00 2001 From: wheheohu Date: Mon, 6 Jul 2026 13:23:26 +0800 Subject: [PATCH 1/4] Fix colon-format timecode regexes rejecting >2-digit hours The hours capture group in all colon-separated timecode patterns was `(\d\d{1})` (exactly two digits), so non-strict timecodes exceeding 99 hours (e.g. `100:00:00:00`) failed to match at the regex layer and raised DFTTTimecodeTypeError before any strict/non-strict handling ran. Change the hours group to `(\d{2,})` (2-or-more digits, unbounded) in SMPTE_NDF/DF/union, SRT, FFMPEG, and DLP patterns. Keeping the `{2,}` lower bound preserves the zero-pad requirement, so unpadded single-digit hours (`1:00:00:00`) are still rejected. The output side already handled large hours (`{:02d}` is a minimum width), so no core changes were needed. Add tests covering >2- and 4-digit hours across all colon formats plus a regression guard for single-digit rejection. Co-Authored-By: Claude Opus 4.8 (1M context) --- dftt_timecode/pattern.py | 16 ++++++++++------ test/test_dftt_timecode.py | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/dftt_timecode/pattern.py b/dftt_timecode/pattern.py index ed5c984..7e841e4 100644 --- a/dftt_timecode/pattern.py +++ b/dftt_timecode/pattern.py @@ -4,11 +4,15 @@ This module defines regex patterns for matching various professional timecode formats used in film, television, and video production. All patterns support negative timecodes with an optional leading minus sign. + +For the colon-separated formats (SMPTE, SRT, FFMPEG, DLP), the hours field must be +zero-padded to at least 2 digits but is otherwise unbounded, so timecodes may exceed +99 hours (e.g. ``100:00:00:00``) as produced in non-strict mode without 24h wraparound. """ import re -SMPTE_NDF_REGEX = re.compile(r'^(?:-)?(?:(?:(?:(\d\d{1}):){1}([0-5]?\d):){1}([0-5]?\d):){1}(\d?\d\d{1}){1}$') +SMPTE_NDF_REGEX = re.compile(r'^(?:-)?(?:(?:(?:(\d{2,}):){1}([0-5]?\d):){1}([0-5]?\d):){1}(\d?\d\d{1}){1}$') """Regex pattern for SMPTE Non-Drop-Frame (NDF) timecode format. Matches: ``HH:MM:SS:FF`` where frames use colon separator. @@ -21,7 +25,7 @@ The colon separator before frames distinguishes NDF from drop-frame format. """ -SMPTE_DF_REGEX = re.compile(r'^(?:-)?(?:(?:(?:(\d\d{1}):){1}([0-5]?\d):){1}([0-5]?\d);){1}(\d?\d\d{1}){1}$') +SMPTE_DF_REGEX = re.compile(r'^(?:-)?(?:(?:(?:(\d{2,}):){1}([0-5]?\d):){1}([0-5]?\d);){1}(\d?\d\d{1}){1}$') """Regex pattern for SMPTE Drop-Frame (DF) timecode format. Matches: ``HH:MM:SS;FF`` where frames use semicolon separator. @@ -35,7 +39,7 @@ Used primarily with 29.97 fps and its multiples (59.94, 119.88). """ -SMPTE_REGEX = re.compile(r'^(?:-)?(?:(?:(?:(\d\d{1}):){1}([0-5]?\d):){1}([0-5]?\d);?:?){1}(\d?\d\d{1}){1}$') +SMPTE_REGEX = re.compile(r'^(?:-)?(?:(?:(?:(\d{2,}):){1}([0-5]?\d):){1}([0-5]?\d);?:?){1}(\d?\d\d{1}){1}$') """Regex pattern for any SMPTE timecode format (both NDF and DF). Matches: ``HH:MM:SS:FF`` or ``HH:MM:SS;FF`` @@ -44,7 +48,7 @@ accepting either colon or semicolon before the frame number. """ -SRT_REGEX = re.compile(r'^(?:-)?(?:(?:(?:(\d\d{1}):){1}([0-5]?\d):){1}([0-5]?\d),){1}(\d\d\d){1}$') +SRT_REGEX = re.compile(r'^(?:-)?(?:(?:(?:(\d{2,}):){1}([0-5]?\d):){1}([0-5]?\d),){1}(\d\d\d){1}$') """Regex pattern for SubRip (SRT) subtitle timecode format. Matches: ``HH:MM:SS,mmm`` where mmm is milliseconds. @@ -56,7 +60,7 @@ Uses comma separator before milliseconds. This format is frame rate independent. """ -FFMPEG_REGEX = re.compile(r'^(?:-)?(?:(?:(?:(\d\d{1}):){1}([0-5]?\d):){1}([0-5]?\d)\.){1}(\d?\d+){1}$') +FFMPEG_REGEX = re.compile(r'^(?:-)?(?:(?:(?:(\d{2,}):){1}([0-5]?\d):){1}([0-5]?\d)\.){1}(\d?\d+){1}$') """Regex pattern for FFmpeg timecode format. Matches: ``HH:MM:SS.ss`` where ss is sub-seconds (centiseconds). @@ -68,7 +72,7 @@ Uses period separator before sub-seconds. Commonly used in video processing tools. """ -DLP_REGEX = re.compile(r'^(?:-)?(?:(?:(?:(\d\d{1}):){1}([0-5]?\d):){1}([0-5]?\d):){1}([01][0-9][0-9]|2[0-4][0-9]|25[0]){1}$') +DLP_REGEX = re.compile(r'^(?:-)?(?:(?:(?:(\d{2,}):){1}([0-5]?\d):){1}([0-5]?\d):){1}([01][0-9][0-9]|2[0-4][0-9]|25[0]){1}$') """Regex pattern for DLP Cinema timecode format. Matches: ``HH:MM:SS:sss`` where sss is sub-frames (0-249). diff --git a/test/test_dftt_timecode.py b/test/test_dftt_timecode.py index 858f58d..9532f29 100644 --- a/test/test_dftt_timecode.py +++ b/test/test_dftt_timecode.py @@ -66,6 +66,45 @@ def test_invalid_timecode(timecode_value, timecode_type, fps, drop_frame, strict TC(timecode_value, timecode_type, fps, drop_frame, strict) +@pytest.mark.parametrize( + "timecode_value, timecode_type, fps, drop_frame, strict, output_type, expected_output", + [ + ("100:00:00:00", "auto", 24, False, False, "smpte", "100:00:00:00"), + ("123:45:59:12", "auto", 24, False, False, "smpte", "123:45:59:12"), + ("1000:00:00:00", "auto", 24, False, False, "smpte", "1000:00:00:00"), + ("100:00:00;00", "auto", 29.97, True, False, "smpte", "100:00:00;00"), + ("100:00:00,000", "srt", 24, False, False, "srt", "100:00:00,000"), + ("1000:00:00,000", "srt", 24, False, False, "srt", "1000:00:00,000"), + ("100:00:00.67", "ffmpeg", 24, False, False, "ffmpeg", "100:00:00.67"), + ("100:00:00:102", "dlp", 24, False, False, "dlp", "100:00:00:102"), + ], + ids=["smpte_ndf", "smpte_ndf_full", "smpte_ndf_4digit", "smpte_df", "srt", "srt_4digit", "ffmpeg", "dlp"], +) +def test_hours_over_two_digits( + timecode_value, timecode_type, fps, drop_frame, strict, output_type, expected_output +): + # Non-strict timecodes may exceed 99h; the hours field must parse and round-trip. + tc = TC(timecode_value, timecode_type, fps, drop_frame, strict) + assert tc.timecode_output(output_type) == expected_output + + +def test_hours_over_two_digits_framecount(): + assert TC("100:00:00:00", "auto", 24, False, False).framecount == 100 * 3600 * 24 + assert TC("1000:00:00:00", "auto", 24, False, False).framecount == 1000 * 3600 * 24 + assert ( + TC("123:45:59:12", "auto", 24, False, False).framecount + == 123 * 3600 * 24 + 45 * 60 * 24 + 59 * 24 + 12 + ) + + +def test_unpadded_single_digit_hours_rejected(): + # Hours must still be zero-padded to at least 2 digits; single-digit is invalid. + from dftt_timecode.error import DFTTTimecodeTypeError + + with pytest.raises(DFTTTimecodeTypeError): + TC("1:00:00:00", "auto", 24, False, False) + + @pytest.mark.parametrize( "timecode_value, timecode_type, fps, drop_frame, strict, result_fps", [ From 648988b157612ff112db48de5ec01b54eccc3234 Mon Sep 17 00:00:00 2001 From: wheheohu Date: Mon, 6 Jul 2026 20:34:08 +0800 Subject: [PATCH 2/4] Update docs for unbounded timecode hours range Commit c0b77be lifted the exactly-2-digit (max 99h) limit on the hours field of colon-separated timecodes, so the documented "-99 to 99 hours" range is no longer accurate. - index.rst: reword the "Extended Time Range" feature bullet to describe unbounded, zero-padded hours (e.g. 100:00:00:00) - zh_CN index.po/.mo: update the msgid to mirror the new source and retranslate the msgstr - CHANGELOG: add a bilingual [Unreleased] Fixed entry for the regex fix Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 6 ++++++ docs/index.rst | 2 +- docs/locale/zh_CN/LC_MESSAGES/index.mo | Bin 2726 -> 2886 bytes docs/locale/zh_CN/LC_MESSAGES/index.po | 9 ++++++--- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa0a75c..c3d278c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Colon-separated timecode formats (SMPTE, SRT, FFMPEG, DLP) no longer reject timecodes whose hours field has more than 2 digits. The hours group was limited to exactly 2 digits (max 99 hours), so non-strict timecodes such as `100:00:00:00` raised `DFTTTimecodeTypeError` at the regex layer before any strict/non-strict handling. The hours field now accepts any number of digits (still zero-padded to at least 2), lifting the previous -99–99 hour range limit; single-digit unpadded hours (`1:00:00:00`) remain invalid + + 冒号分隔的时码格式(SMPTE、SRT、FFMPEG、DLP)不再拒绝小时字段超过两位的时码。原先小时捕获组被限制为恰好两位(最大 99 小时),导致 `100:00:00:00` 等非严格模式时码在进入严格/非严格处理前就在正则层抛出 `DFTTTimecodeTypeError`。现在小时字段可接受任意位数(仍需补零至至少两位),此前 -99–99 小时的范围限制已被取消;未补零的单位数小时(`1:00:00:00`)仍视为无效 + ## [1.0.0] - 2026-05-12 ### Summary diff --git a/docs/index.rst b/docs/index.rst index ab847ca..410515e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -22,7 +22,7 @@ Features - **Multiple Timecode Format Support**: SMPTE (DF/NDF), SRT, DLP (Cine Canvas), FFMPEG, FCPX, frame count, timestamp - **High Frame Rate Support**: Supports frame rates from 0.01 to 999.99 fps - **Drop-Frame/Non-Drop-Frame**: Strictly supports SMPTE DF/NDF formats -- **Extended Time Range**: Currently supports time range from -99 to 99 hours +- **Extended Time Range**: Hours are unbounded (zero-padded to at least 2 digits), so non-strict timecodes may exceed 99 hours (e.g. ``100:00:00:00``) - **Strict Mode**: 24-hour cycling mode that automatically converts timecodes outside the 0-24 hour range - **High Precision**: Internal storage using high-precision Fraction timestamps for accurate conversions - **Rich Operators**: Comprehensive support for arithmetic and comparison operations between timecodes and numbers diff --git a/docs/locale/zh_CN/LC_MESSAGES/index.mo b/docs/locale/zh_CN/LC_MESSAGES/index.mo index ebcc54d15cdd2d07291ff8915a5370c3c247b1a9..ab361bad0ce7a23f4d55e40fc879f607282fdfa8 100644 GIT binary patch delta 674 zcmZ9|Pe>F|90%~<9wI_-DtNF%%((NKHgZR1X2H5lMW$u4 zii(t4E4UW@p@pSok*SEFL&qiP)@f$kIqji?*S@cvF2OLL_j|ng{(f%`&wlG%>$5hA z=y!-{94?26uEPq{VWNR(9=?St>^eo%1{VXj;2G5Uz#?o!{RK|K@2~})i`4gHumklV zY=xI2WKk50No?RO%>)Op!JDX6C;}?58UBKzp$}hT=o5Gm^$UdT!JlvhW-!GSSb!Ql zfm2XzBASLrP+X+F*{UxX(nW9_ibkV&ordG^0-Olyhk>h51Z)QN0YoOff#M<`p!gF< z@GATY#f{J7Ho{&g`VEI#HeQEjtuZa58%)+Y%cN(uOj_Xzi{0V6);}*RqA)cko9q^s z4U-KqC8efJqq~n88cS>Ge#6vLNt2nWIiA!MZm>D|HscFPjxQ4lcH?h)j1Q;-Op*rU z@sWRzBz03y&sgzNxGfSI-k6F`TW%riZI`^Qg14P>%8&i0Yc+e>-@RMi&-**2>QWJp zTUf^w&d1f7{itR?ad&dwy90mkxl_)2Ioo@+@7h_XymI{P0iup`_uTCJYH6|hc}YxD YZ-7we(~5g||G$YMl7!IWk7y7X7zxo- z)L??rpHO=*bCWL(K~P&u-;;MapYuDM`#k5lkNwx4{7`yY2=Q$aVhYF2LWD7k8h&8{ zS387=;4Ajvn5FFuc9WM3qi82@;w)y+g)heWJNA-W*oD6qkrp;KcB>Ga2{+0D9*p7u z$^uz*;u*?@S9rst%jhT1Q)Cj4aTMQ-?53DXUcfng!XSDb?Ew;wbbGVIfh?Ry+2|5G z@fJt0V&sP5C(1wZV`K{-u@x?qgLqI5;>ATAL^<)c;UUU?=O$<2&a^T-kG})HTerAThP}mmFwA~#_4?{ im-)X}Kd8uy^~y=3_|UxAWBn-BTDjeNsaDnP&*nd37+5F( diff --git a/docs/locale/zh_CN/LC_MESSAGES/index.po b/docs/locale/zh_CN/LC_MESSAGES/index.po index 2dbfabd..f017062 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/index.po +++ b/docs/locale/zh_CN/LC_MESSAGES/index.po @@ -79,9 +79,12 @@ msgstr "**跳帧/非跳帧**:严格支持 SMPTE DF/NDF 格式" #: ../../index.rst:25 msgid "" -"**Extended Time Range**: Currently supports time range from -99 to 99 " -"hours" -msgstr "**扩展时间范围**:目前支持 -99 到 99 小时的时间范围" +"**Extended Time Range**: Hours are unbounded (zero-padded to at least 2 " +"digits), so non-strict timecodes may exceed 99 hours (e.g. " +"``100:00:00:00``)" +msgstr "" +"**扩展时间范围**:小时数无上限(需补零至至少 2 位),因此非严格模式下的时码可超过 99 " +"小时(例如 ``100:00:00:00``)" #: ../../index.rst:26 msgid "" From 2dd587e5e4e4f00120dc45a0513443fce0667c64 Mon Sep 17 00:00:00 2001 From: wheheohu Date: Mon, 6 Jul 2026 20:34:35 +0800 Subject: [PATCH 3/4] Stop tracking compiled .mo gettext catalogs The .mo files are binary artifacts compiled from the .po sources. Sphinx auto-compiles .po -> .mo at build time (gettext_auto_build defaults to True), and CI's `make html-all` builds the translated site that way, so the committed .mo files are redundant and only add binary churn to diffs. Remove them from version control (files remain on disk) and ignore locale/**/*.mo. The .po files remain the tracked source of truth. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/.gitignore | 5 +++++ docs/locale/zh_CN/LC_MESSAGES/README.mo | Bin 3289 -> 0 bytes .../zh_CN/LC_MESSAGES/api/dftt_timecode.mo | Bin 519 -> 0 bytes .../zh_CN/LC_MESSAGES/api/dftt_timerange.mo | Bin 521 -> 0 bytes docs/locale/zh_CN/LC_MESSAGES/api/error.mo | Bin 959 -> 0 bytes docs/locale/zh_CN/LC_MESSAGES/changelog.mo | Bin 990 -> 0 bytes docs/locale/zh_CN/LC_MESSAGES/contributing.mo | Bin 15051 -> 0 bytes docs/locale/zh_CN/LC_MESSAGES/index.mo | Bin 2886 -> 0 bytes docs/locale/zh_CN/LC_MESSAGES/installation.mo | Bin 2613 -> 0 bytes docs/locale/zh_CN/LC_MESSAGES/quickstart.mo | Bin 1791 -> 0 bytes docs/locale/zh_CN/LC_MESSAGES/user_guide.mo | Bin 6349 -> 0 bytes 11 files changed, 5 insertions(+) delete mode 100644 docs/locale/zh_CN/LC_MESSAGES/README.mo delete mode 100644 docs/locale/zh_CN/LC_MESSAGES/api/dftt_timecode.mo delete mode 100644 docs/locale/zh_CN/LC_MESSAGES/api/dftt_timerange.mo delete mode 100644 docs/locale/zh_CN/LC_MESSAGES/api/error.mo delete mode 100644 docs/locale/zh_CN/LC_MESSAGES/changelog.mo delete mode 100644 docs/locale/zh_CN/LC_MESSAGES/contributing.mo delete mode 100644 docs/locale/zh_CN/LC_MESSAGES/index.mo delete mode 100644 docs/locale/zh_CN/LC_MESSAGES/installation.mo delete mode 100644 docs/locale/zh_CN/LC_MESSAGES/quickstart.mo delete mode 100644 docs/locale/zh_CN/LC_MESSAGES/user_guide.mo diff --git a/docs/.gitignore b/docs/.gitignore index 598c405..cabb8b1 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -7,6 +7,11 @@ __pycache__/ .DS_Store Thumbs.db +# Compiled gettext catalogs — regenerated from .po at build time +# (sphinx-build auto-compiles with gettext_auto_build). Keep .po in git, +# not the binary .mo artifacts. +locale/**/*.mo + # Note: _static/ and _templates/ are NOT ignored # because they contain source files for the documentation # (custom templates and static assets like switcher.json) diff --git a/docs/locale/zh_CN/LC_MESSAGES/README.mo b/docs/locale/zh_CN/LC_MESSAGES/README.mo deleted file mode 100644 index de59310b69e1bc12467f8dbb58afb16906e970af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3289 zcmaKt-)|h%6~}K&+ETY51hkZjmJ0%vADR7O8WJ`QB5@KDHNhrc0`$c@yK{H<*0VFq z{D{3ptsICQZ{pZiVw@(i2_^|xfQ<=p;;?I5@YI+73ze$u%y^&tP@z8brQbU<8@#cF zCHv!h=bUrTuY2yl?%(tt!4JjnBjWc5__OBckslJW59{B64}w|nSK!;=bKn^GTks#? zJ)j3Z1bzz^za#w^gy6Uns%F4w9_)|jAa|_j{PuJ z-|ygbIBZejm*Pi(=q;VT%&mQPns&%=gZAW^*6@kSaJ6Wg`9RLfGg^?jM$?9@W%7~r zr}Y>J3i^;S*ln+rB4Q-?Z}Pg6t>cgS8{RrMY!EV5p1`iU^DlFEGNP>CR(z#Can zL|!zcu_$~b`!&--JifZY-m{XjfWPFq*dtY80PYZ$$i?68@KT+n_xxjbNe0j0kCmw+0PrzSj@KBpn9x9Wc2C zzmfnv+aY!9`N_T*NRfaqwYgnBvMK zauG8mbS)bxmVBpz>n(P8&c+~spf7+SkhVog4J&QPRAZ0kY_2esYbbUqXqfP7be8inPnl znNm_u(3Xz&q||0G-&tul%95b1vDPh8JSMfqX*`~IZtJtLZLt^xr9&8RBFP)bwI84HHGyjRbvDx9OjF$(A+xhdl3=4Xzk+PS;|s@#kO4VaBKtOvo2o4q!Ce zNNR%qCEwNd%I&q3WO5uPFMHYum+SG)!jgu&u1L?K?RU8`@IZqv58Yc@lJ6np-U|Hy#;j zS1Cbv$!Vt2)<}FSj_{u*cl@|Jb*Z+v?2Sz~ZlQRca1&{*G8}w%X3lz_eNJ{}}%cS8eB@G57ZLY&1jfjM`0y54ux8Jw#vRGh&9ckFZb?Df^^ zP@q(+o)6p$kGXk0@DuV)kIk*VcSxvFxw7uXSgj7?$ylwH-4Cixb;PTVI|Jv4d!yo< z_`n^Xt540>CqKlo^`rC7(kI^VoI6qW&L68^8WswthZ;W5mRBzIyXC(6L?1ramL^x` z$5*T8MTXYA5wx~&qOlu36-`@s3EcS+D&s*O zf;b53fXcX&Q?<(VuyXJ#$b&OCAl%yEZGbZ~qM%C`gxMgU-q;u4JN1PQOgD1gcr=_F zS89tB?)*{b@-c68z^}s0W$(hQv-qAf)bEUb+C2EFuo1oyft$X{#Vg*#$zV-_;)IK) z>0h5b=k^`N7etGSV*KAi<6Kzs%E!H#GOD|7{EOC^Jzu+7cBiM@$>rwh?~J^&()VuT zpmihPD-ViR9v*N;u5L8%>;0#^#o3Jq{mQi|Z)nyXI_{i0d&hJ~m!sV_9_^0bUn`$q obN@cP(KL6KKdtv&bo#H<&rUatuOH`LrRt0hVhp&0H$tiZ1HXCGr2qf` diff --git a/docs/locale/zh_CN/LC_MESSAGES/api/dftt_timecode.mo b/docs/locale/zh_CN/LC_MESSAGES/api/dftt_timecode.mo deleted file mode 100644 index f7243b5d6988ccf292945b7e97f67d553a39a08f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 519 zcmaJ;T}#6-6s_V@pTuV$@~){#$K1$P(S6Ban`IP1U*xXMx@en}G!^In@HhCc{24x* zxv>d~7Y^JLPI7L}{Wv;!b8PKf99x{O$d78<{?H<@c;9oJ$%cR0-s#PaiA2t0m2h+w z4xLb|M;;qIOzFUp7t0DH zYA?hSXpv}0l_Wkrzf5xwnHQ`4!X1H3D@g4CuZxH7WXo~HA>SPS|3a8m8ZtakIxiWL z#WyVj+Cu9#AT4)19QSuwQIEJ?B@LH$=4D~M4Sz9bMFv8)P`m(LauZZ$j+;B(>Th$d kabMa!2$?d}oC)JzbIG+0(5Aa&r~@;eA>X47Yw>$=Uo1A4ivR!s diff --git a/docs/locale/zh_CN/LC_MESSAGES/api/dftt_timerange.mo b/docs/locale/zh_CN/LC_MESSAGES/api/dftt_timerange.mo deleted file mode 100644 index 85e41a877ca18230eaad8d13d710f12f9f9563be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 521 zcmaJ;!Ab)$5M9O7-t_Dtcg>otR7+|_ZL1VoyOc%Hi;UapHoBXXY*wuQ;WzkK{)Shl zR@8$y@L*ne$(wn3+uC>usMZPFgxw|iRY%ls6552Fdo~CRm%j^W1h(shC5Qk%&Tj%+(%Jqbw)p^BJt5}$fQr>Y0 zp6>E4cvLJFaf3_2W67jc`-eNCDFg}m7$5c0;}2b|_Qgs!fXegKfvF1c)+u&vYj&d- zH5hg@(+}YE#6fP2h~>daUV<*7%$CRDfVQe3rvaX4x1Gyx&QpZ)>+t^H<1B0%M+~19vSu<;O6FW0OD?(~K zssiTY{wPq`6%NXbkGVK6DMmWgc(S(bCU=cJi4O)$h0bcUrl{$Xc3 zc6ZxugeZ+&Nw^A$7SchNMT90-E;__up#sM*A&(w$NHx zG&(Ov{BbpTu+sdrnXGRmYj?mmS%2Hwx*69#fRNnXjF%dz``^nQH&&bVMzelDUbzP@ e?byk!+wtO?_~l0P-EyC*@m1iwl065XOe$`R!9d@&?U5*JZcqYTl2Xi9d+G2ou!_yi4c5n_oTKn_R1xGyG zX}Qj1dZ2AZ?16wg{*CN~JzI4M4tFpA9-%KN3LL1N(7DJ)tgvH7t!6W-9b1j&OmBB@ z$9}D#L76OvSzw@fv;es!xh?fVfGKNECrLy^wie}(+O*G0|29N~+QFd0&@3A@<*3Yb z5k>?Q5UM+Bqc)r)0kSl+5mG40^2_S;^{u&;tta=B(pWNIs!lxIem$Pdy-OaiSMPm_ zS08Mztj3>a;`v1&C*_IC$F<6bXdQ-jeBscyjIE#xT|R6LgAUNdN!< diff --git a/docs/locale/zh_CN/LC_MESSAGES/contributing.mo b/docs/locale/zh_CN/LC_MESSAGES/contributing.mo deleted file mode 100644 index 79f8b4b86d95104c31bc1d5d4bc5104014955fd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15051 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 S$I)f2#BcpS`t z{QMoT2K*i5{wm-UV!j8yf%Rn=xdi?OMnMNg;@~`Jg7?67F!Pioy#p?RT*vb;%1J&ZDu+ORZlt=t3c{px6XZI60J)AO@F@5T$o0MgF`Tb~++QOX zS63Ia%)A`8lpNi5&@|+&WnEpA9ATEGGQB`-C!aShW|JdriDV0j#kU=d#Yx(N5Y|SgooAC)D(h`be_u#EF~TsV#HH|K6aiiQ#o1-%= zuhSrlTrr6U8Ga!0pa8X^0!Y=7_GCSY9Y~PcX3e0aSuwg4p2B#%EzzcJzp)pe*h4e0>h z8a9;@g+wbpSQZK2zVT%v9YohUEyXGXg-YKS76d!EM}>OwA*vTNXtf)H2CQD}&4O-K zK3$D3Bpr4%^#o=CgiG(()FS&FPtT2b=8>5!vZ!BGtIW>!0a=1t-5nnt)G z^7>nuoTBL=H1(dE1Lz?KmpV#5${uSzxVe^-RLNBz{m!MbxZf*_7>eSXyq}+Q+q_ZRHjN zR}JkJC5@dLtJy~lexWI{TZodqN+;DxLnzV+7r24`j|*;bYFp*f*Wz1U-QAUorQ$Vj zV#OPo^9D~nIOOrzT^YtX@9ekkccb3eVrA^MKiK1r-wsZ?^TT-Njm~>x6Q$xn<;zd* zuJl*F>h>qERmKB^-m!nFvdR6LKE)qRI=)e{`Rg+?8&3_*1ET z_9w4+XN$bk=1%=>ww8)Z@Bou2a;bRHpIt)%phyhg zYKty?jy#Ao?~k`{wqI%anmat;_AZu|FKkxG9k}fGtOjwd->h_B=Fv@^lh9#a|Bv2a LpF1@6w{iLx(Kc2Y diff --git a/docs/locale/zh_CN/LC_MESSAGES/installation.mo b/docs/locale/zh_CN/LC_MESSAGES/installation.mo deleted file mode 100644 index 8cf69d405bedbe684b89e25f88a70b3c247f38e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2613 zcma)++iw(A9LGn+TjTw~OF%q{mq6{TyG5jKDG?}`CZG*$FlZt@-8s8EFgvr(Wy=^I z7D{iSlp?m!8*M?fUT86h(srRfN_^D^UrY=;v%86=Fa803&&;;FC4{)?^fPD9@Ao^u z-}jvJ`}3Jq0|I_8;#q@d)qQ+a{{4-yh~YorT5$FKg769$244UVfa^gUd>*_Aehg;8 zdT{jvzWoQ_V;CO+p9OnBZa)D&0nV1@e*t%5{5!ZAT(e3LV&HzT3j7&-4*V6Y0dIqk zgHJu^$6X7)jqyhCQBW(<1|P<_9fXT;0z`^JKll}DI|IIkaXpN9U9BM3;|R#}9Rgnl zuYkOc-@tv~UtkEVdjz$D2f>HHPVh-cnjPD{tJEtZa~sJ?^F24 z{fEGpz^_Z=TOjxU1LXcLh(Fr$=Wpi5T^Yockz!imFd*fbYZJv zGF?T~G*j#vVi`O}N=}7^?K-0-Bb20N-L$Aen2I+|T}sMYSYMrP5l})m9oA#$Xzw29hBZb4g9-rix5k6^0n4 zpf*WTmBt~8YKDa-)i9}|5dKswQ#MBqWL?VU8r2YAj2gnsXkh)aFO(!H8a;%dyeKBg zrnh8Llh~4xr}>HjRMwKD*=}yS%jdnCrAL|2Y?}#e)s_abArJ})d)OzItb3Uo!d??a zQe7g7+^SREUYW9{DmGcCYJ?s3HiuVNRu!^%k)-uP1j#dMpiGmrQJa_=YVb9YVy1b( zl#@_fLS;sIX9<+H(qYdzrE3RS)Wjr|s@RF;^(qAqkgcB36{%3{lEh-N$|V0tr0i12 zfT<;wmKMGY7H^Xk1+N-uwPZyiQsrUxzAvX;J}oPYZj;S~pSDJH3pW5Y8)0D|)8&}W z&ur;1_#`(_e}7tSA}3Rt?%kM*BM=sFeWLt|8A8lbqo$@QhM;Ki zIF=BS)JzCEcGg!-Y?4H*Z@@ZC8h1t_B(kJ;Fc1ucHqzRvJ&dm+?l$7GByO|fh8WSp zq^kM-i0Iuf_==6VW?@nrtlcPvf?{omghJsBn_dsr1%ogY_pr3g*SM?Qn?c`h7p{&N z(Wz=E{91ll`yWGi(x$E=A3+Gn0QTN)3!pwj>+3BADab*Yfee$1=yWRcH(3IeI z4;C^Vi`m}XjfMQ@Q@m*|W`_l5bgJAMN$&b(r+dVipDPRvyC=r;eKXF5cEQPJoKvTB zH^&Os1_ftkHaFMje?m03`*d#pJMJ+(U5Z?~n7R2&g}$Ghn`hmTADo46i9sgso{f09=oSW_OH}K9z#&?p(E-VZ=S3H5; z^T)AE{;A+IrBtkRNt~6UbI*Qc;GZuq%WrxMGfTp`$EKh?cA%IUE-Z98Gg+tO5`=fh OE)^^6xZnTjQ2zn2S;e{l diff --git a/docs/locale/zh_CN/LC_MESSAGES/quickstart.mo b/docs/locale/zh_CN/LC_MESSAGES/quickstart.mo deleted file mode 100644 index 4b02db6f85e0b952f04f21ea61c9f2d56e28d877..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1791 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 From e37fed627270b02d01f8da9f3e8fa9b6088b9bdc Mon Sep 17 00:00:00 2001 From: wheheohu Date: Mon, 6 Jul 2026 20:52:35 +0800 Subject: [PATCH 4/4] Reclassify unbounded-hours changelog entry as Added The lifted 99-hour cap on colon-separated timecodes reads as a new capability rather than a defect fix, so move the [Unreleased] entry from Fixed to Added and reword it in feature terms (both en and zh). Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3d278c..18ac897 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Fixed +### Added -- Colon-separated timecode formats (SMPTE, SRT, FFMPEG, DLP) no longer reject timecodes whose hours field has more than 2 digits. The hours group was limited to exactly 2 digits (max 99 hours), so non-strict timecodes such as `100:00:00:00` raised `DFTTTimecodeTypeError` at the regex layer before any strict/non-strict handling. The hours field now accepts any number of digits (still zero-padded to at least 2), lifting the previous -99–99 hour range limit; single-digit unpadded hours (`1:00:00:00`) remain invalid +- Support for timecodes whose hours field exceeds 99 hours. Colon-separated formats (SMPTE, SRT, FFMPEG, DLP) now accept an unbounded hours field (still zero-padded to at least 2 digits), so non-strict timecodes such as `100:00:00:00` are parsed correctly. This lifts the previous -99–99 hour range limit; single-digit unpadded hours (`1:00:00:00`) remain invalid - 冒号分隔的时码格式(SMPTE、SRT、FFMPEG、DLP)不再拒绝小时字段超过两位的时码。原先小时捕获组被限制为恰好两位(最大 99 小时),导致 `100:00:00:00` 等非严格模式时码在进入严格/非严格处理前就在正则层抛出 `DFTTTimecodeTypeError`。现在小时字段可接受任意位数(仍需补零至至少两位),此前 -99–99 小时的范围限制已被取消;未补零的单位数小时(`1:00:00:00`)仍视为无效 + 支持小时字段超过 99 小时的时码。冒号分隔格式(SMPTE、SRT、FFMPEG、DLP)现在接受无上限的小时字段(仍需补零至至少两位),因此 `100:00:00:00` 等非严格模式时码可被正确解析。此前 -99–99 小时的范围限制已被取消;未补零的单位数小时(`1:00:00:00`)仍视为无效 ## [1.0.0] - 2026-05-12