feat(date-guess): recognise YY-MM-DD HH-MM-SS Synology export filenames
The 0525.mov-style export from Synology Photos uses 2-digit years, which
the existing patterns ignored (all required \d{4}). Result: filename
gave no signal, suggestion fell through to the YYYY/MM folder layout and
snapped to day 15. The explicit HH-MM-SS half rules out random digit
triples, so we trust YY → 2000+YY for this specific shape and surface
the actual capture time, not noon.
This commit is contained in:
@@ -64,6 +64,13 @@ def _segments(filepath: str) -> list[str]:
|
|||||||
return [s for s in re.split(r"[\\/]+", filepath) if s]
|
return [s for s in re.split(r"[\\/]+", filepath) if s]
|
||||||
|
|
||||||
|
|
||||||
|
# Synology Photos export: `YY-MM-DD HH-MM-SS NNNN.ext`. The explicit
|
||||||
|
# HH-MM-SS half is what makes the 2-digit year safe to trust — a random
|
||||||
|
# digit triple won't satisfy the hour/minute/second range checks below.
|
||||||
|
# YY is mapped to 2000+YY (this format is a recent export convention).
|
||||||
|
_SYNOLOGY_RE = re.compile(
|
||||||
|
r"(?<!\d)(\d{2})-(\d{2})-(\d{2})[\s_](\d{2})-(\d{2})-(\d{2})(?!\d)"
|
||||||
|
)
|
||||||
_COMPACT_RE = re.compile(r"(?<!\d)(\d{4})(\d{2})(\d{2})(?!\d)")
|
_COMPACT_RE = re.compile(r"(?<!\d)(\d{4})(\d{2})(\d{2})(?!\d)")
|
||||||
_DASHED_RE = re.compile(r"(?<!\d)(\d{4})[-_.](\d{1,2})[-_.](\d{1,2})(?!\d)")
|
_DASHED_RE = re.compile(r"(?<!\d)(\d{4})[-_.](\d{1,2})[-_.](\d{1,2})(?!\d)")
|
||||||
_MONTH_RE = re.compile(r"(?<!\d)(\d{4})[-_.](\d{1,2})(?!\d)")
|
_MONTH_RE = re.compile(r"(?<!\d)(\d{4})[-_.](\d{1,2})(?!\d)")
|
||||||
@@ -80,6 +87,24 @@ def _guess_from_string(
|
|||||||
if not input:
|
if not input:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
m = _SYNOLOGY_RE.search(input)
|
||||||
|
if m:
|
||||||
|
yy, mm, dd = int(m.group(1)), int(m.group(2)), int(m.group(3))
|
||||||
|
hh, mi, ss = int(m.group(4)), int(m.group(5)), int(m.group(6))
|
||||||
|
if hh < 24 and mi < 60 and ss < 60:
|
||||||
|
d = _make_date(2000 + yy, mm, dd)
|
||||||
|
if d:
|
||||||
|
d = d.replace(hour=hh, minute=mi, second=ss)
|
||||||
|
return DateGuess(
|
||||||
|
date=d,
|
||||||
|
confidence="high",
|
||||||
|
matched=(
|
||||||
|
f"{m.group(1)}-{m.group(2)}-{m.group(3)} "
|
||||||
|
f"{m.group(4)}:{m.group(5)}:{m.group(6)}"
|
||||||
|
),
|
||||||
|
source=source,
|
||||||
|
)
|
||||||
|
|
||||||
m = _COMPACT_RE.search(input)
|
m = _COMPACT_RE.search(input)
|
||||||
if m:
|
if m:
|
||||||
d = _make_date(int(m.group(1)), int(m.group(2)), int(m.group(3)))
|
d = _make_date(int(m.group(1)), int(m.group(2)), int(m.group(3)))
|
||||||
|
|||||||
@@ -78,6 +78,32 @@ function guessFromString(
|
|||||||
): DateGuess | null {
|
): DateGuess | null {
|
||||||
if (!input) return null
|
if (!input) return null
|
||||||
|
|
||||||
|
// Day+time: YY-MM-DD HH-MM-SS — Synology Photos export. The explicit
|
||||||
|
// HH-MM-SS half is what makes the 2-digit year safe to trust; a random
|
||||||
|
// digit triple won't satisfy the hour/minute/second range checks below.
|
||||||
|
// YY → 2000+YY (this format is a recent export convention).
|
||||||
|
const synology = input.match(
|
||||||
|
/(?<!\d)(\d{2})-(\d{2})-(\d{2})[\s_](\d{2})-(\d{2})-(\d{2})(?!\d)/,
|
||||||
|
)
|
||||||
|
if (synology) {
|
||||||
|
const yy = +synology[1], mm = +synology[2], dd = +synology[3]
|
||||||
|
const hh = +synology[4], mi = +synology[5], se = +synology[6]
|
||||||
|
if (hh < 24 && mi < 60 && se < 60) {
|
||||||
|
const date = makeDate(2000 + yy, mm, dd)
|
||||||
|
if (date) {
|
||||||
|
date.setHours(hh, mi, se, 0)
|
||||||
|
return {
|
||||||
|
date,
|
||||||
|
confidence: 'high',
|
||||||
|
matched:
|
||||||
|
`${synology[1]}-${synology[2]}-${synology[3]} ` +
|
||||||
|
`${synology[4]}:${synology[5]}:${synology[6]}`,
|
||||||
|
source,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Day-level: YYYYMMDD run bounded by non-digits ─ `IMG_20190712_153045`.
|
// Day-level: YYYYMMDD run bounded by non-digits ─ `IMG_20190712_153045`.
|
||||||
const compact = input.match(/(?<!\d)(\d{4})(\d{2})(\d{2})(?!\d)/)
|
const compact = input.match(/(?<!\d)(\d{4})(\d{2})(\d{2})(?!\d)/)
|
||||||
if (compact) {
|
if (compact) {
|
||||||
|
|||||||
Reference in New Issue
Block a user