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:
@@ -22,6 +22,7 @@ import ctypes
|
||||
import time
|
||||
import queue
|
||||
import shutil
|
||||
import socket
|
||||
import stat as stat_mod
|
||||
import hashlib
|
||||
import sqlite3
|
||||
@@ -39,7 +40,7 @@ import actualizador
|
||||
asistente.O = sys.modules[__name__]
|
||||
actualizador.O = sys.modules[__name__]
|
||||
|
||||
VERSION = "3.0.0"
|
||||
VERSION = "3.1.0"
|
||||
|
||||
BASE = os.path.dirname(os.path.abspath(sys.executable if getattr(sys, "frozen", False) else __file__))
|
||||
# Empaquetado: el programa corre con el motor que viaja en su propia carpeta. OrganizadorLabstream.exe es
|
||||
@@ -3751,6 +3752,19 @@ def estado_carpeta(ruta):
|
||||
}
|
||||
|
||||
|
||||
class ServidorLocal(ThreadingHTTPServer):
|
||||
"""El servidor de la interfaz, con el puerto para él solo. ThreadingHTTPServer pide «reutilizar dirección»,
|
||||
y en Windows eso significa que dos programas pueden escuchar a la vez en el mismo puerto y las peticiones
|
||||
van a cualquiera de los dos: dos OrganiLAB con datos distintos (el instalado y una copia suelta, o el de
|
||||
ensayo y el de verdad) se contestaban el uno por el otro sin que nada fallara a la vista."""
|
||||
allow_reuse_address = False
|
||||
|
||||
def server_bind(self):
|
||||
if hasattr(socket, "SO_EXCLUSIVEADDRUSE"):
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
|
||||
super().server_bind()
|
||||
|
||||
|
||||
class Manejador(BaseHTTPRequestHandler):
|
||||
protocol_version = "HTTP/1.1"
|
||||
|
||||
@@ -4132,7 +4146,7 @@ class Manejador(BaseHTTPRequestHandler):
|
||||
AYUDA = """OrganiLAB (Organizador Labstream) — uso por comando
|
||||
|
||||
OrganizadorLabstream.exe abre OrganiLAB en su ventana
|
||||
OrganizadorLabstream.exe instalar lo pone en el menú Inicio y en el Escritorio (desinstalar lo quita)
|
||||
OrganizadorLabstream.exe instalar lo instala en su sitio: carpeta de programas, menú Inicio y Escritorio (desinstalar lo quita)
|
||||
OrganizadorLabstream.exe "D:\\Clientes\\Carpeta" (o arrastra la carpeta encima del .exe) ASISTENTE: la escanea con barra
|
||||
de progreso, da el diagnóstico por tipo de archivo y pregunta paso a paso qué hacer
|
||||
OrganizadorLabstream.exe asistente "ruta" [--si] [--simular] lo mismo por comando (--si acepta lo recomendado sin preguntar)
|
||||
@@ -4359,6 +4373,7 @@ SOLTADOS = {"ts": 0, "rutas": []}
|
||||
SESION_PATH = os.path.join(DATOS, "sesion.json") # qué copia tiene abiertos estos datos, y en qué puerto atiende
|
||||
_CANDADO = [] # el de «una sola copia abierta»; vive lo que el proceso
|
||||
SILENCIO_NAVEGADOR = 180 # segundos sin que la página pregunte para dar el navegador por cerrado
|
||||
RELEVO = [] # el .exe que toma el relevo al salir (tras instalarse en su sitio)
|
||||
|
||||
|
||||
def _al_soltar(rutas):
|
||||
@@ -4471,7 +4486,8 @@ def _vigilar(v, fin):
|
||||
|
||||
def _recien_abierta(v):
|
||||
"""Con la interfaz ya a la vista: si el programa está dado de alta en Windows, pone al día su versión y
|
||||
sus accesos directos; y la primera vez que se abre un paquete en un equipo, ofrece darlo de alta."""
|
||||
sus accesos directos; y la primera vez que se abre un paquete en un equipo, ofrece instalarlo: copiarlo a
|
||||
su sitio definitivo, darlo de alta y seguir desde allí."""
|
||||
try:
|
||||
import ventana
|
||||
import instalar
|
||||
@@ -4480,14 +4496,35 @@ def _recien_abierta(v):
|
||||
oferta = CONFIG.get("instalacion") or {}
|
||||
if not CONGELADO or os.environ.get("ORGANIZADOR_PUERTO") or oferta.get("ofrecida") or instalar.instalado_aqui(BASE):
|
||||
return
|
||||
if instalar.instalado() and not instalar.instalado_aqui(BASE):
|
||||
return # ya hay otro instalado en este equipo: esta copia va por libre
|
||||
CONFIG["instalacion"] = {**oferta, "ofrecida": True} # se pregunta una sola vez
|
||||
guardar_json(CONFIG_PATH, CONFIG)
|
||||
time.sleep(1.5)
|
||||
if v.mensaje("¿Quieres tener OrganiLAB en el menú Inicio y en el Escritorio, como un programa más de Windows?\n\n"
|
||||
"No copia nada: el programa se queda en esta carpeta. Se quita desde Configuración › Aplicaciones.",
|
||||
ventana.MB_YESNO | ventana.MB_ICONQUESTION) == ventana.IDYES:
|
||||
instalar.instalar(BASE, VERSION)
|
||||
v.mensaje("Listo. Búscalo como «OrganiLAB» en el menú Inicio, o ábrelo desde el Escritorio.")
|
||||
if not instalar.hay_que_mudar(BASE): # ya está en su sitio: solo falta darlo de alta
|
||||
if v.mensaje("¿Quieres tener OrganiLAB en el menú Inicio y en el Escritorio, como un programa más de Windows?\n\n"
|
||||
"Se quita desde Configuración › Aplicaciones.", ventana.MB_YESNO | ventana.MB_ICONQUESTION) == ventana.IDYES:
|
||||
instalar.instalar(BASE, VERSION)
|
||||
v.mensaje("Listo. Búscalo como «OrganiLAB» en el menú Inicio, o ábrelo desde el Escritorio.")
|
||||
return
|
||||
if v.mensaje("¿Instalar OrganiLAB en este equipo?\n\nSe copia a tu carpeta de programas, queda en el menú Inicio y en el "
|
||||
"Escritorio, y se quita desde Configuración › Aplicaciones. La carpeta desde la que lo has abierto ya no hará falta.",
|
||||
ventana.MB_YESNO | ventana.MB_ICONQUESTION) != ventana.IDYES:
|
||||
return
|
||||
v.evaluar("modal(" + json.dumps("Instalando OrganiLAB…") + ", " + json.dumps(
|
||||
'<p><span class="spin"></span> Copiando el programa a su sitio definitivo. Tarda unos segundos; al terminar se abre solo desde allí.</p>') + ", null)")
|
||||
try:
|
||||
r = instalar.poner(BASE, VERSION, avance=v.progreso)
|
||||
except Exception as ex: # noqa
|
||||
v.progreso(None)
|
||||
v.evaluar("document.querySelector('#modal').classList.remove('on')")
|
||||
v.mensaje(f"No se pudo instalar: {ex}\n\nEl programa sigue funcionando desde esta carpeta.", ventana.MB_OK | ventana.MB_ICONWARNING)
|
||||
return
|
||||
v.progreso(None)
|
||||
v.mensaje("OrganiLAB quedó instalado.\n\nAhora se cierra y se abre desde su sitio. La carpeta desde la que lo abriste "
|
||||
"ya no hace falta: puedes borrarla.")
|
||||
RELEVO.append(os.path.join(r["base"], "OrganizadorLabstream.exe"))
|
||||
v.cerrar()
|
||||
except Exception as ex: # noqa
|
||||
print("no se pudo dar de alta en Windows:", ex)
|
||||
|
||||
@@ -4539,8 +4576,20 @@ def _atender_navegador():
|
||||
def main():
|
||||
global PUERTO
|
||||
args_tmp = [a for a in sys.argv[1:] if not a.startswith("--")]
|
||||
if CONGELADO and "--puente" not in sys.argv:
|
||||
import instalar
|
||||
instalada = instalar.mudado(BASE)
|
||||
if instalada: # desde esta carpeta ya se instaló el programa: se abre el instalado
|
||||
if sys.argv[1:]:
|
||||
sys.exit(subprocess.call([instalada] + sys.argv[1:]))
|
||||
subprocess.Popen([instalada], cwd=os.path.dirname(instalada), close_fds=True)
|
||||
return
|
||||
if args_tmp and args_tmp[0].lower() in ("instalar", "desinstalar") and "--puente" not in sys.argv:
|
||||
import instalar # hablan con cuadros de mensaje: no hace falta consola
|
||||
if args_tmp[0].lower() == "instalar" and instalar.hay_que_mudar(BASE) and not _soy_la_unica():
|
||||
import ventana # la mudanza se lleva los datos: con el programa abierto se quedarían a medias
|
||||
ctypes.windll.user32.MessageBoxW(None, "Cierra OrganiLAB antes de instalarlo.", "OrganiLAB", ventana.MB_OK | ventana.MB_ICONINFORMATION)
|
||||
sys.exit(1)
|
||||
sys.exit(instalar.principal(args_tmp[0].lower(), BASE, VERSION, silencioso="--si" in sys.argv))
|
||||
if args_tmp and "--puente" not in sys.argv:
|
||||
_asegurar_consola()
|
||||
@@ -4583,13 +4632,14 @@ def main():
|
||||
if huerfanas:
|
||||
print(f"{huerfanas} conversiones quedaron a medias la última vez: vuelven a la cola en espera (Reanudar).")
|
||||
try:
|
||||
srv = ThreadingHTTPServer(("127.0.0.1", PUERTO), Manejador)
|
||||
srv = ServidorLocal(("127.0.0.1", PUERTO), Manejador)
|
||||
except OSError:
|
||||
if os.environ.get("ORGANIZADOR_PUERTO"):
|
||||
print(f"El puerto {PUERTO} está ocupado por otro programa.")
|
||||
sys.exit(1)
|
||||
# el puerto de siempre lo tiene otro programa: se toma uno libre (a la ventana le da igual cuál sea)
|
||||
srv = ThreadingHTTPServer(("127.0.0.1", 0), Manejador)
|
||||
# el puerto de siempre lo tiene otro programa (u otro OrganiLAB con otros datos): se toma uno libre,
|
||||
# que a la ventana le da igual cuál sea
|
||||
srv = ServidorLocal(("127.0.0.1", 0), Manejador)
|
||||
PUERTO = srv.server_address[1]
|
||||
url = f"http://127.0.0.1:{PUERTO}/"
|
||||
guardar_json(SESION_PATH, {"pid": os.getpid(), "puerto": PUERTO})
|
||||
@@ -4620,6 +4670,9 @@ def main():
|
||||
while en_marcha(): # la ventana se fue con trabajo pendiente: se acaba antes de salir
|
||||
time.sleep(1)
|
||||
srv.shutdown()
|
||||
srv.server_close()
|
||||
if RELEVO: # se acaba de instalar en su sitio: sigue desde allí
|
||||
subprocess.Popen([RELEVO[0]], cwd=os.path.dirname(RELEVO[0]), close_fds=True)
|
||||
finally:
|
||||
try:
|
||||
os.remove(SESION_PATH)
|
||||
|
||||
Referencia en una nueva incidencia
Block a user