import mysql.connector
import requests
import json
import time
import sys
# --- CONFIGURACIÓN ---
DB_CONFIG = {
'user': 'ghghgh',
'password': 'Kq093~oo5',
'database': 'para_modificar',
'unix_socket': '/run/mysqld/mysqld.sock'
}
PREFIX = "9ENkutm_"
OLLAMA_URL = "http://localhost:11434/api"
MODELO_TEXTO = "mistral"
MODELO_VECTOR = "all-minilm"
# Mapeo de Idiomas
LANG_NAMES = {
"en": "English", "es": "Spanish", "fr": "French", "de": "German",
"it": "Italian", "ro": "Romanian", "pt-br": "Brazilian Portuguese",
"pt-pt": "European Portuguese", "pl": "Polish", "tr": "Turkish",
"ru": "Russian", "vi": "Vietnamese", "da": "Danish", "sv": "Swedish",
"no": "Norwegian", "ko": "Korean", "ja": "Japanese",
"zh-hans": "Simplified Chinese", "zh-hant": "Traditional Chinese",
"hi": "Hindi"
}
def log(msg):
timestamp = time.strftime("%H:%M:%S")
print(f"[{timestamp}] {msg}", flush=True)
def generar_embedding(texto):
"""Genera vector de forma segura. Si falla, devuelve None."""
try:
res = requests.post(f"{OLLAMA_URL}/embeddings", json={
"model": MODELO_VECTOR,
"prompt": texto[:1000],
"options": {"num_thread": 2}
}, timeout=15)
if res.status_code == 200:
return res.json().get('embedding')
except Exception:
return None # Silencioso si falla el vector
def obtener_lote_pendiente(cursor, tamano=50):
"""Buscamos artículos que NO tengan VECTOR (embedding)."""
sql = f"""
SELECT p.ID, p.post_title, p.post_content, t.language_code
FROM {PREFIX}posts p
JOIN {PREFIX}icl_translations t ON p.ID = t.element_id AND t.element_type = 'post_post'
LEFT JOIN {PREFIX}mcm_embeddings me ON p.ID = me.post_id
WHERE p.post_status = 'publish'
AND p.post_type = 'post'
AND me.post_id IS NULL
ORDER BY p.ID DESC
LIMIT %s
"""
cursor.execute(sql, (tamano,))
return cursor.fetchall()
def check_existing_subtitle(cursor, post_id):
sql = f"SELECT meta_value FROM {PREFIX}postmeta WHERE post_id = %s AND meta_key = '_mcm_subtitle'"
cursor.execute(sql, (post_id,))
res = cursor.fetchone()
return res['meta_value'] if res else None
def procesar_blindado_v22():
log("🚀 INICIANDO V22: BLINDADO (Anti-Crash JSON)")
conn = None
try:
conn = mysql.connector.connect(**DB_CONFIG)
while True:
# 1. CARGAR LOTE
try:
cursor_read = conn.cursor(dictionary=True)
lote = obtener_lote_pendiente(cursor_read, tamano=50)
cursor_read.close()
except Exception as e:
log(f"⚠️ Error leyendo DB (Reintentando en 5s): {e}")
time.sleep(5)
# Reconexión básica
try: conn.reconnect(attempts=3, delay=2)
except: pass
continue
if not lote:
log("🎉 ¡Misión cumplida! Todo procesado.")
break
total = len(lote)
log(f"📦 Lote de {total} artículos...")
cursor_write = conn.cursor()
for i, item in enumerate(lote):
p_id = item['ID']
titulo = item['post_title']
contenido = item['post_content']
code = item['language_code']
lang_name = LANG_NAMES.get(code, "English")
# --- PASO 0: CHEQUEO PREVIO ---
cursor_check = conn.cursor(dictionary=True)
subtitulo_existente = check_existing_subtitle(cursor_check, p_id)
cursor_check.close()
estado_sub = "✅ MANTENIDO" if subtitulo_existente else "✨ GENERANDO"
print(f"\r ⚙️ [{i+1}/{total}] ID {p_id} ({code}) | {estado_sub}...", end="", flush=True)
# --- PASO A: GENERAR TEXTOS (CON SEGURIDAD JSON) ---
bajada_final = subtitulo_existente
seo_desc = ""
keyword = ""
prompt = ""
if subtitulo_existente:
prompt = (f"Task: Analyze title '{titulo}'. Language: {lang_name}. "
f"Output JSON keys: 'seo_desc', 'keyword'.")
else:
prompt = (f"Task: Analyze title '{titulo}'. Language: {lang_name}. "
f"Output JSON keys: 'summary' (max 20 words), 'seo_desc', 'keyword'.")
try:
res = requests.post(f"{OLLAMA_URL}/generate", json={
"model": MODELO_TEXTO,
"prompt": prompt,
"format": "json",
"stream": False,
"options": {"num_thread": 2, "num_ctx": 1024}
}, timeout=50) # Timeout generoso
if res.status_code == 200:
# 🛡️ AQUÍ ESTABA EL ERROR: json.loads podía fallar
try:
respuesta_texto = res.json().get('response', '{}')
data = json.loads(respuesta_texto)
if not subtitulo_existente:
bajada_final = data.get('summary', titulo)
seo_desc = data.get('seo_desc', '')
keyword = data.get('keyword', '')
except json.JSONDecodeError:
# Si Ollama devuelve basura, no morimos.
# log(f"⚠️ JSON inválido recibido de Ollama. Usando fallback.")
pass
except Exception as e:
pass # Timeout o error de conexión, seguimos vivo
# FALLBACK DE EMERGENCIA
if not bajada_final: bajada_final = titulo
# --- PASO B: VECTOR ---
texto_vector = titulo + " " + contenido[:500]
vector = generar_embedding(texto_vector)
# --- PASO C: GUARDADO ---
try:
# 1. Subtítulo (si toca)
if not subtitulo_existente:
cursor_write.execute(f"INSERT INTO {PREFIX}postmeta (post_id, meta_key, meta_value) VALUES (%s, '_mcm_subtitle', %s)", (p_id, bajada_final))
# 2. Rank Math (Siempre)
if seo_desc:
sql_seo = f"INSERT INTO {PREFIX}postmeta (post_id, meta_key, meta_value) VALUES (%s, 'rank_math_description', %s) ON DUPLICATE KEY UPDATE meta_value=%s"
cursor_write.execute(sql_seo, (p_id, seo_desc, seo_desc))
if keyword:
sql_kw = f"INSERT INTO {PREFIX}postmeta (post_id, meta_key, meta_value) VALUES (%s, 'rank_math_focus_keyword', %s) ON DUPLICATE KEY UPDATE meta_value=%s"
cursor_write.execute(sql_kw, (p_id, keyword, keyword))
# 3. Vector (Vital para avanzar)
if vector:
sql_vec = f"REPLACE INTO {PREFIX}mcm_embeddings (post_id, embedding, updated_at) VALUES (%s, %s, NOW())"
cursor_write.execute(sql_vec, (p_id, json.dumps(vector)))
else:
# Si falló el vector, metemos un dummy vacio para que no se repita el post eternamente
sql_dummy = f"REPLACE INTO {PREFIX}mcm_embeddings (post_id, embedding, updated_at) VALUES (%s, '[]', NOW())"
cursor_write.execute(sql_dummy, (p_id,))
conn.commit()
except Exception as e:
log(f"\n❌ Error guardando ID {p_id}: {e}")
cursor_write.close()
log(f"\n✅ Lote completado. Siguiente...")
except Exception as e:
log(f"🔥 Error Fatal Global: {e}")
time.sleep(5)
# Reintento automático del script entero
procesar_blindado_v22()
finally:
if conn and conn.is_connected(): conn.close()
if __name__ == "__main__":
procesar_blindado_v22()
Pangolin London 推出群展《Reconfiguring the Figure》,以 Lynn Chadwick 的創作為切入,回看戰後以來具象雕塑的演變。展覽將二十世紀中期的英國重要雕塑家與當代實踐並置,呈現材料、方法與語境更替之際,人體形象如何不斷被質疑、被拆解、再度重組。策展並非求一錘定音的論斷,而是鋪陳數條通往「身體」的路徑——直接再現、象徵替身、鏡面反射與以數據驅動的成像——同時勾勒世代之間的延續與迴響。
Chadwick 的作品構成展覽的歷史軸心。他捨棄柔軟的塑造,轉向由結構支撐的稜角語彙,塑造出或獨立或成雙、披覆、展翼、行走、坐臥的人物形象。這些易辨的輪廓刻意維持情感距離;拋光的青銅「面部」將觀者的目光反射回去,使「觀看本身」成為作品的議題。正如他所言:“No expression is an expression”(沒有表情也是一種表情),這句話為他的人體觀奠下基調。
展場中心擺放同名系列中體量最大的《Stairs》。兩位女性在簡潔的階梯上擦身而過——是致意,抑或冷靜的錯身?作品捕捉的恰是運動與靜止之間的臨界點,同時清楚揭示藝術家的工作方法:從線性的金屬骨架(armature)起步,再「加築」成穩固的量體——一種將建築式清晰與潛在張力織合的雕塑語言,曾深刻影響戰後英國雕塑的面貌。
這套語言與二十世紀中期的多種探索相互呼應:當時的雕塑家把「身體」推向感性、象徵、骨架化甚至機械化的方向,打亂了經典的期待。Geoffrey Clarke 的《Horse and Rider》結合鍛鐵與拾自諾曼第海岸的漂流木;這種混合式構築濃縮了那個時代的實驗精神——焊接與組合(assemblage)為具象表現開闢了除鑄造與傳統鑿刻之外的路徑。
同時期其他作品則把生命經驗與歷史餘震——往往與戰爭相關——轉譯為物質語言。George Fullard 的《The Infant St George》在粗獷的木與金屬表面留下衝擊痕跡;Elisabeth Frink 的《Soldier’s Head II》則把量體壓縮成帶傷而挑釁的頭部,既是符碼亦是證詞。即使英國雕塑當時強力轉向抽象,Frink 與 Fullard 等人仍把人體視為創新的試煉場。事實證明,具象不只撐過了轉向,更以其彈性持續吸納每一代人的焦慮、理想與技術。
由此,《Reconfiguring the Figure》把目光推入二十一世紀,關注當代實踐如何擴延「figure(形象)」的內涵。今日的具象不再等同於對身體的字面描摹;它納入以鏡面反射、科學影像與動物替身來表述人的「在場」與情感的策略,同時連結類比與數位流程的交織。
Zachary Eastwood-Bloom 的《Human Error》由鏡面玻璃構成的胸像組成,形體源於人工智慧生成的數據。這些形象既貼近人臉的熟悉,也刻意帶出陌生感:鏡面把觀者的身影折返,延續 Chadwick 的反射邏輯,卻把這場「相遇」安置於數位身份的當代語境。作品同時是肖像與介面——「人」的觀念在演算法流程與觀看機制之間被不斷媒介。
Angela Palmer 則把再現推進到表面之外,直指認知的「建築」。與 Harvard Medical School 合作的《The Last Frontier》,將一千張腦部微掃描生成的三維影像,逐層刻寫在 28 片玻璃上;當這些層次被同時觀看時,凝成一個懸浮、發光的量體,把科學影像真正嵌入雕塑實踐。作品把「形象」延展至心智層面——一種由層與透明度累加而成的肖像,而非一體成形。
Laura Ford 透過動物身體折返到人的形象。《Days of Judgement – Cat 2》取意自馬薩喬(Masaccio)的壁畫《逐出伊甸園》(The Expulsion from the Garden of Eden ),一位高挑、近似貓科的身影步伐寧靜卻暗潮湧動。即使沒有明確的表情,緊張仍然充滿;此替身成為承載罪疚與自我凝視的器皿。透過這具寓言性的軀體,Ford 指出:不必直面描繪人,也能讓脆弱與韌性被看見。
這些路徑一方面擴張具象的詞彙,一方面與 Chadwick 的實驗遺產保持清晰連線。無論是雕鑿、鑄造、焊接、組合、鏡面處理或醫學影像,人體形象仍是追問「雕塑中的人之在場為何」的彈性工具。無論身體被正面呈現,或僅作提示,皆是以物質語言探究人類經驗的高度適應性媒介。
參展陣容亦體現這種廣度。二十世紀中期的藝術家包括:Kenneth Armitage、Michael Ayrton、John Bridgeman、Ralph Brown、Reg Butler、Lynn Chadwick、Geoffrey Clarke、Elisabeth Frink、George Fullard、John Hoskin、Bryan Kneale、F. E. McWilliam、Eduardo Paolozzi、Rosemary Young;當代部分則有:Anthony Abrahams、Victoria Atkinson、David Bailey、Glenys Barton、Jon Buck、Terence Coventry、Zachary Eastwood-Bloom、Abigail Fallis、Laura Ford、Sue Freeborough、Thomas Merrett、Breon O’Casey、Angela Palmer、William Tucker、Anastassia Zamaraeva。
展覽同時提供相關資料。目錄可按需索取;參考影像涵蓋 Chadwick《Stairs》(青銅)、Clarke《Horse and Rider》(鍛鐵與漂流木)、Frink《Soldier’s Head II》(青銅)、Palmer《The Last Frontier》(刻於 28 片玻璃)、Eastwood-Bloom《Father Sky / Uranus》(青銅)等。這些素材呼應策展對「方法」的重心:媒材與工藝如何界定今日「形象」的邊界。
從焊接骨架到多層玻璃雕刻,再到源自 AI 的鏡面胸像,Pangolin London 將具象定位為一個持續展開的研究場,而非凝固的分類。選件顯示:雕塑家如何在親近與距離、表面與深度、解剖與類比之間進行協商。觀眾將在行進中遇見被正面呈現的身體、由反射平面暗示的身體,或由科學數據重建的身體;在每一種情境下,人體形象既承擔表述的重量,也不斷試探其邊界,以維持「被讀作人」的能量。
**地點與檔期:**Pangolin London,Kings Place,90 York Way,London N1 9AG——《Reconfiguring the Figure》展期為 2025 年 11 月 19 日至 2026 年 1 月 24 日。