Spaces:
Paused
Paused
File size: 3,456 Bytes
9ed8df3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | #!/usr/bin/env python3
"""
Patch @met4citizen/talkinghead/dist/talkinghead.mjs to fix:
"Cannot read properties of undefined (reading 'value')"
Root cause: this.mtRandomized contains morph keys not in this.mtAvatar for
the loaded GLB model. Line 2619: j = this.mtAvatar[i]; if (!j.needsUpdate) crashes.
"""
import os, sys, re
PKG_DIR = os.path.join(os.getcwd(), 'node_modules', '@met4citizen', 'talkinghead')
dist_files = ['dist/talkinghead.mjs', 'dist/talkinghead.js']
target_path = None
for name in dist_files:
p = os.path.join(PKG_DIR, name)
if os.path.exists(p):
target_path = p
break
if not target_path:
print('ERROR: talkinghead file not found in ' + PKG_DIR)
sys.exit(1)
print('Patching: ' + target_path)
with open(target_path, 'r') as f:
code = f.read()
if '__VT_PATCHED__' in code:
print('Already patched, skipping.')
sys.exit(0)
# Fix 1: Null check for mtRandomized loop (line ~2619)
old1 = "j = this.mtAvatar[i];\n if ( !j.needsUpdate ) {"
new1 = "j = this.mtAvatar[i];\n if ( !j ) continue;\n __VT_PATCHED__=1;\n if ( !j.needsUpdate ) {"
if old1 in code:
code = code.replace(old1, new1)
print('OK Fix1: added null check for mtRandomized loop')
else:
code = re.sub(
r'(j = this\.mtAvatar\[i\];)\s*\n\s*if\s*\(\s*!j\.needsUpdate',
r'\1\n if ( !j ) continue;\n __VT_PATCHED__=1;\n if ( !j.needsUpdate',
code
)
# Fix 2: Guard direct .value accesses on specific keys
for key in ['bodyRotateY', 'eyeLookInLeft', 'eyeLookOutLeft', 'eyesLookDown',
'browDownLeft', 'browDownRight', 'eyeBlinkLeft', 'eyeBlinkRight',
'eyesLookUp', 'bodyRotateX', 'bodyRotateZ', 'headRotateX',
'headRotateY', 'headRotateZ']:
full_access = 'this.mtAvatar[' + repr(key) + '].value'
safe_access = "(this.mtAvatar[" + repr(key) + "] || {}).value || 0"
code = code.replace('this.mtAvatar[' + repr(key) + '].value', safe_access)
if '(this.mtAvatar[' + repr('bodyRotateY') + '] || {}).value' in code:
print('OK Fix2: guarded direct .value accesses on known keys')
# Fix 3: Wrap animate method in try/catch as safety net
match = re.search(
r'prototype\.animate\s*=\s*function\s*\(\s*t\s*\)\s*\{',
code
)
if match:
brace_open = code.index('{', match.end() - 1)
depth = 1
pos = brace_open + 1
while depth > 0 and pos < len(code):
ch = code[pos]
if ch == '{': depth += 1
elif ch == '}': depth -= 1
pos += 1
brace_close = pos - 1
indent = ' '
body = code[brace_open + 1:brace_close]
if '__VT_CATCH__' not in body:
new_body = (
indent + '__VT_CATCH__=1;\n'
+ indent + 'try {\n'
+ body + '\n'
+ indent + '} catch (e) {\n'
+ indent + indent + 'if (!(e && e.message && e.message.includes("Cannot read properties of undefined"))) {\n'
+ indent + indent + indent + 'console.warn("[patch_talkinghead] animate:", e?.message || e);\n'
+ indent + indent + indent + 'throw e;\n'
+ indent + indent + '}\n'
+ indent + '}\n'
)
code = code[:brace_open + 1] + new_body + code[brace_close + 1:]
print('OK Fix3: wrapped animate() in try/catch')
else:
print('WARN Fix3: could not wrap animate()')
with open(target_path, 'w') as f:
f.write(code)
print('Done. Wrote ' + str(len(code)) + ' bytes')
|