| |
| """Rebuild ALL .lab files from source manifests. ASCII-safe regex (\\u escapes). |
| |
| Priority: noselleel: soniox > text_raw > manifest text |
| eqkawkab: text_raw > manifest text |
| sadek/mosaif: transcripts.jsonl text |
| All output stripped of tashkeel (harakat, Quranic marks, superscript alef, |
| tatweel) using explicit codepoint escapes - no literal Arabic in this file. |
| """ |
|
|
| import json |
| import re |
| import unicodedata |
| from pathlib import Path |
|
|
| RAW = Path("/opt/work/data_raw") |
| TASHKEEL_RE = re.compile( |
| "[ؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۨ-ۭـ]" |
| ) |
| WS_RE = re.compile(r"\s+") |
|
|
|
|
| def strip(t: str) -> str: |
| t = unicodedata.normalize("NFC", str(t)) |
| t = "".join(c for c in t if unicodedata.category(c)[0] != "C") |
| return WS_RE.sub(" ", TASHKEEL_RE.sub("", t)).strip() |
|
|
|
|
| def load_map(path: Path, key="text"): |
| m = {} |
| if path.exists(): |
| with open(path, encoding="utf-8") as f: |
| for line in f: |
| line = line.strip() |
| if line: |
| r = json.loads(line) |
| v = r.get(key) |
| if v and str(v).strip(): |
| s = strip(v) |
| if s: |
| m[r["id"]] = s |
| return m |
|
|
|
|
| |
| diac_in = "بَلْ دِي" |
| plain_out = "بل دي" |
| assert strip(diac_in) == plain_out, "strip() broken - aborting" |
| plain = "بل دي إن قصة" |
| assert strip(plain) == plain, "strip() destroys plain arabic - aborting" |
| print("strip() self-test OK") |
|
|
| nos = {} |
| for fn in ("train.jsonl", "dev.jsonl"): |
| nos.update(load_map(RAW / "noselleel-egyptian-tts" / fn)) |
| nos.update(load_map(RAW / "noselleel-egyptian-tts/transcripts_diac.jsonl", "text_raw")) |
| nos.update(load_map(RAW / "noselleel-egyptian-tts/transcripts_soniox/train.jsonl")) |
|
|
| eqk = {} |
| for fn in ("train.jsonl", "dev.jsonl"): |
| eqk.update(load_map(RAW / "eqkawkab-egyptian-tts" / fn)) |
| eqk.update(load_map(RAW / "eqkawkab-egyptian-tts/transcripts_diac.jsonl", "text_raw")) |
|
|
| sad = load_map(RAW / "moustafa-sadek-egyptian-tts/transcripts.jsonl") |
| mos = load_map(RAW / "mosaifside-egyptian-tts/transcripts.jsonl") |
| print(f"maps: nos={len(nos)} eqk={len(eqk)} sad={len(sad)} mos={len(mos)}") |
| assert len(nos) > 8700 and len(eqk) > 2000, "maps too small - aborting" |
|
|
| BY_PREFIX = {"nos": nos, "eqk": eqk, "sad": sad, "mos": mos} |
| ok = 0 |
| missing = [] |
| for root in (Path("/opt/work/data-fs"), Path("/opt/work/data-fs-val")): |
| if not root.exists(): |
| continue |
| for wav in root.rglob("*.wav"): |
| src = BY_PREFIX.get(wav.parent.name[:3]) |
| text = (src or {}).get(wav.stem, "") |
| if text: |
| wav.with_suffix(".lab").write_text(text, encoding="utf-8") |
| ok += 1 |
| else: |
| missing.append(str(wav)) |
| print(f"labs written: {ok}; wavs without text: {len(missing)}") |
| for p in missing[:5]: |
| print(" NO-TEXT:", p) |
|
|
| |
| bad_empty = bad_tash = 0 |
| for root in (Path("/opt/work/data-fs"), Path("/opt/work/data-fs-val")): |
| for lab in root.rglob("*.lab"): |
| t = lab.read_text(encoding="utf-8") |
| if not t.strip(): |
| bad_empty += 1 |
| elif TASHKEEL_RE.search(t): |
| bad_tash += 1 |
| print(f"verify: empty={bad_empty} with_tashkeel={bad_tash}") |
|
|
| ref = eqk.get("1elysrns6eE_0096", "") |
| Path("/opt/work/eval/ref_voice.txt").write_text(ref, encoding="utf-8") |
| print("ref len:", len(ref)) |
|
|
| for hj in Path("/opt/work/eval").glob("dev_holdout*.jsonl"): |
| rows = [] |
| for line in hj.read_text(encoding="utf-8").splitlines(): |
| if line.strip(): |
| r = json.loads(line) |
| pref = "nos" if r["ds"].startswith("nos") else "eqk" |
| t = BY_PREFIX[pref].get(r["id"], "") |
| if t: |
| r["text"] = t |
| rows.append(r) |
| hj.write_text("\n".join(json.dumps(r, ensure_ascii=False) for r in rows) + "\n", |
| encoding="utf-8") |
| print(f"{hj.name}: {len(rows)} rows") |
| print("RELAB3_DONE") |
|
|