3.1.0: se instala solo en su sitio, manual dentro y el puerto para una sola copia
- instalar.py: instalar un paquete lo copia a la carpeta de programas del usuario (%LOCALAPPDATA%\Programs\OrganiLAB) con sus datos y su ffmpeg, lo da de alta desde ahí y lo vuelve a abrir desde su sitio. Del origen no se borra nada: queda instalado-en.txt, que además hace que abrir esa copia abra la instalada. Un destino ocupado no se pisa, y si ya hay un OrganiLAB instalado lo dice en vez de poner otro encima. - static/index.html: vista «Manual», con cada función del programa, qué hace y cómo lo hace (41 entradas, un apartado por pestaña). - organizador.py: ServidorLocal pide el puerto en exclusiva. Con «reutilizar dirección», en Windows dos copias escuchaban a la vez en el mismo puerto y se contestaban la una por la otra sin que nada fallara a la vista. - Pruebas: probar.py 19, prueba_funcional.py 26 de 26, prueba_ventana.py 18 desde el código y 17 con el paquete, incluida la instalación entera en un equipo de ensayo (ORGANILAB_ENSAYO) que no toca el Windows de verdad. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Este commit está contenido en:
@@ -5,7 +5,7 @@ sin abrirlo; esto abre OrganiLAB sobre una copia temporal (programa y datos apar
|
||||
comprueba desde otro hilo lo que solo se puede comprobar con la ventana delante:
|
||||
|
||||
py -3.12 prueba_ventana.py el programa desde el código
|
||||
py -3.12 prueba_ventana.py --paquete dist\OrganizadorLabstream el paquete, arrancado por su .exe
|
||||
py -3.12 prueba_ventana.py --paquete dist\OrganizadorLabstream el paquete, arrancado por su .exe, y su instalación
|
||||
… --captura ruta.png deja una imagen de la ventana entera
|
||||
|
||||
Durante medio minuto se ve una ventana que se abre, se mueve sola y se cierra. No hay que tocar nada.
|
||||
@@ -138,6 +138,21 @@ def marca_de(datos):
|
||||
return (zlib.crc32(os.path.normcase(os.path.abspath(datos)).encode("utf-8")) & 0x7FFFFFFF) or 1
|
||||
|
||||
|
||||
def cerrar_las_de(V, *marcas):
|
||||
"""Cierra las copias de ensayo que sigan abiertas (con sus cuadros de diálogo), pase lo que pase en la
|
||||
prueba: una copia olvidada se queda en la pantalla y, peor, atendiendo."""
|
||||
for _ in range(3):
|
||||
abiertas = [h for m in marcas for h in V._ventanas(m)]
|
||||
if not abiertas:
|
||||
return
|
||||
for h in abiertas:
|
||||
d = dialogo_de(h)
|
||||
if d:
|
||||
U.PostMessageW(d, WM_CLOSE, 0, 0)
|
||||
U.PostMessageW(h, WM_CLOSE, 0, 0)
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
def captura_pedida():
|
||||
return sys.argv[sys.argv.index("--captura") + 1] if "--captura" in sys.argv else ""
|
||||
|
||||
@@ -206,6 +221,10 @@ def desde_el_codigo(tmp):
|
||||
return r
|
||||
aj = v.en_ui(ajustes)
|
||||
anota("lo que es de navegador está apagado", not any(aj.values()), ", ".join(k for k, x in aj.items() if x) or "todo apagado")
|
||||
v.evaluar("irA('manual')")
|
||||
entradas = v.evaluar("document.querySelector('#t-manual').classList.contains('on') ? document.querySelectorAll('#t-manual dt').length : 0")
|
||||
anota("el manual está dentro del programa", (entradas or 0) > 35, f"{entradas} entradas")
|
||||
v.evaluar("irA('carpeta')")
|
||||
anota("la interfaz sabe que está en la ventana", v.evaluar("document.documentElement.classList.contains('nativo')") is True)
|
||||
|
||||
# el avance en la barra de tareas
|
||||
@@ -348,10 +367,89 @@ def el_paquete(tmp, origen):
|
||||
proc.kill()
|
||||
anota("al cerrar la ventana se cierra el programa", proc.returncode == 0, f"código {proc.returncode}")
|
||||
anota("y deja de atender", estado() is None)
|
||||
cerrar_las_de(V, marca)
|
||||
p = subprocess.run([exe, "ayuda"], capture_output=True, timeout=60, stdin=subprocess.DEVNULL, env=entorno)
|
||||
anota("por consola responde a sus comandos", p.returncode == 0 and b"asistente" in p.stdout)
|
||||
|
||||
|
||||
def instalar_el_paquete(tmp, origen):
|
||||
"""La instalación de punta a punta, sin tocar el Windows de verdad: con ORGANILAB_ENSAYO el «equipo» es una
|
||||
carpeta temporal. El paquete se abre desde unas «Descargas», ofrece instalarse, se copia a su sitio, se da
|
||||
de alta, se cierra y vuelve a abrirse desde allí; y abrir después la copia de Descargas abre la instalada."""
|
||||
import winreg
|
||||
equipo = os.path.join(tmp, "equipo")
|
||||
os.environ["ORGANILAB_ENSAYO"] = equipo
|
||||
os.environ.pop("ORGANIZADOR_PUERTO", None)
|
||||
sys.path.insert(0, AQUI)
|
||||
import ventana as V
|
||||
import instalar
|
||||
descarga = os.path.join(tmp, "Descargas", "OrganizadorLabstream")
|
||||
shutil.copytree(origen, descarga, ignore=shutil.ignore_patterns("ffmpeg", "actualizacion", "__pycache__"))
|
||||
os.makedirs(os.path.join(descarga, "ffmpeg"))
|
||||
with open(os.path.join(descarga, "ffmpeg", "LICENSE.txt"), "w") as fh:
|
||||
fh.write("en el paquete completo aquí va ffmpeg")
|
||||
destino = instalar.sitio_definitivo()
|
||||
exe_d, exe_i = os.path.join(descarga, "OrganizadorLabstream.exe"), os.path.join(destino, "OrganizadorLabstream.exe")
|
||||
marca_d, marca_i = marca_de(os.path.join(descarga, "datos")), marca_de(os.path.join(destino, "datos"))
|
||||
try:
|
||||
proc = subprocess.Popen([exe_d], cwd=tmp)
|
||||
hs = espera(lambda: V._ventanas(marca_d), 40)
|
||||
if not hs:
|
||||
anota("el paquete abre desde Descargas", False)
|
||||
proc.kill()
|
||||
return
|
||||
h = hs[0]
|
||||
d = espera(lambda: dialogo_de(h), 25)
|
||||
pregunta = " ".join(texto(x) for x in hijos(d)) if d else ""
|
||||
anota("la primera vez ofrece instalarlo en el equipo", "Instalar OrganiLAB en este equipo" in pregunta, pregunta[8:70])
|
||||
if d:
|
||||
U.PostMessageW(d, WM_COMMAND, 6, 0) # «Sí»
|
||||
|
||||
def aviso_final():
|
||||
x = dialogo_de(h)
|
||||
return x if x and "quedó instalado" in " ".join(texto(y) for y in hijos(x)) else None
|
||||
d = espera(aviso_final, 90)
|
||||
anota("se copia a su sitio y lo dice", bool(d))
|
||||
if d:
|
||||
U.PostMessageW(d, WM_CLOSE, 0, 0) # solo tiene «Aceptar»
|
||||
hi = espera(lambda: V._ventanas(marca_i), 60)
|
||||
anota("se cierra y vuelve a abrirse solo desde su sitio", bool(hi) and espera(lambda: not V._ventanas(marca_d), 30) is True)
|
||||
try:
|
||||
proc.wait(30)
|
||||
except subprocess.TimeoutExpired:
|
||||
proc.kill()
|
||||
anota("en su sitio están el programa, sus datos y su ffmpeg",
|
||||
all(os.path.exists(os.path.join(destino, x)) for x in ("OrganizadorLabstream.exe", "runtime", "static", "datos\\config.json", "ffmpeg\\LICENSE.txt")))
|
||||
a = instalar.leer_acceso(instalar._accesos()[0]) if os.path.exists(instalar._accesos()[0]) else None
|
||||
anota("queda dado de alta desde su sitio (Inicio, Escritorio y Aplicaciones)",
|
||||
instalar.instalado_aqui(destino) and bool(a) and instalar._misma(a["exe"], exe_i) and os.path.exists(instalar._accesos()[1]),
|
||||
a["exe"] if a else "sin acceso directo")
|
||||
anota("del origen no se borra nada, y queda la nota de la mudanza", os.path.exists(exe_d) and bool(instalar.mudado(descarga)))
|
||||
if hi:
|
||||
try:
|
||||
with open(os.path.join(destino, "datos", "sesion.json"), encoding="utf-8") as fh:
|
||||
puerto = json.load(fh)["puerto"]
|
||||
e = json.loads(urllib.request.urlopen(f"http://127.0.0.1:{puerto}/api/estado", timeout=5).read())
|
||||
except Exception: # noqa
|
||||
e = {}
|
||||
anota("el instalado atiende con sus datos", e.get("congelado") is True and (e.get("config") or {}).get("instalacion", {}).get("ofrecida") is True,
|
||||
f"versión {e.get('version')}")
|
||||
subprocess.run([exe_d], cwd=tmp, timeout=40)
|
||||
time.sleep(2.5)
|
||||
anota("abrir la copia de Descargas abre la instalada", not V._ventanas(marca_d) and len(V._ventanas(marca_i)) == 1)
|
||||
U.PostMessageW(hi[0], WM_CLOSE, 0, 0)
|
||||
anota("y se cierra como siempre", espera(lambda: not V._ventanas(marca_i), 25) is True)
|
||||
time.sleep(2)
|
||||
finally:
|
||||
cerrar_las_de(V, marca_d, marca_i)
|
||||
for clave in (instalar.CLAVE, "Software\\OrganiLAB-ensayo"):
|
||||
try:
|
||||
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, clave)
|
||||
except OSError:
|
||||
pass
|
||||
os.environ.pop("ORGANILAB_ENSAYO", None)
|
||||
|
||||
|
||||
def main():
|
||||
guardia = threading.Timer(300, lambda: os._exit(3)) # pase lo que pase, no se queda colgada
|
||||
guardia.daemon = True
|
||||
@@ -360,6 +458,7 @@ def main():
|
||||
try:
|
||||
if "--paquete" in sys.argv:
|
||||
el_paquete(tmp, os.path.abspath(sys.argv[sys.argv.index("--paquete") + 1]))
|
||||
instalar_el_paquete(tmp, os.path.abspath(sys.argv[sys.argv.index("--paquete") + 1]))
|
||||
else:
|
||||
desde_el_codigo(tmp)
|
||||
finally:
|
||||
|
||||
Referencia en una nueva incidencia
Block a user