From acb945c2ef30a333432263c2a42a2a7ea1fafb56 Mon Sep 17 00:00:00 2001 From: Labstream Date: Sun, 4 Oct 2026 12:37:25 -0500 Subject: [PATCH] =?UTF-8?q?3.1.1:=20la=20versi=C3=B3n=20que=20ense=C3=B1a?= =?UTF-8?q?=20Windows=20no=20depende=20de=20los=20accesos=20directos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Al actualizar a la 3.1.0 la instalación de Munka por su propio botón, Configuración › Aplicaciones siguió diciendo 3.0.0. Tras actualizarse se reescribían los dos accesos directos (uno vive en el Escritorio de OneDrive) antes de apuntar la versión, y si uno no se dejaba reescribir, la versión no se apuntaba. instalar.al_dia ya no toca un acceso que abre lo que debe, uno que no se deja no impide lo demás, y la versión se apunta siempre. Con prueba. Co-Authored-By: Claude Fable 5.1 --- README.md | 8 ++++++++ instalar.py | 45 ++++++++++++++++++++++++++++----------------- manifiesto.json | 22 +++++++++++----------- organizador.py | 2 +- probar.py | 16 ++++++++++++++++ 5 files changed, 64 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 19383c5..8afdd20 100644 --- a/README.md +++ b/README.md @@ -735,6 +735,14 @@ que viaja con cada versión; una prueba comprueba que cada pestaña tiene su apa Pruebas: `probar.py` 19 de 19, `prueba_funcional.py` 26 de 26, `prueba_ventana.py` 18 desde el código y, con el paquete, 8 del arranque y 9 de la instalación. +3.1.1, la versión que enseña Windows no depende de los accesos directos. Al actualizar la instalación de +Munka a la 3.1.0 por su propio botón, todo fue bien menos un detalle: Configuración › Aplicaciones siguió +diciendo 3.0.0. No se llegó a ver el error (sin consola no queda escrito), pero entre notar la versión vieja y +apuntar la nueva solo había una cosa: reescribir los dos accesos directos, y uno vive en el Escritorio de +OneDrive. Si uno no se dejaba reescribir en ese momento, la versión no se apuntaba; repetido a mano un rato +después, y en el equipo de ensayo, funcionó. Ahora `instalar.al_dia` no toca un acceso que ya abre lo que +debe, uno que no se deja no impide lo demás, y la versión se apunta siempre. Con prueba. + Probado el 9 sep 2026 sobre una copia de ensayo: plan → aplicar → deshacer → volver a aplicar deja el árbol exacto; conversión ffmpeg verificada y reemplazo con original a `_Para borrar`. El puente de Resolve conectó con Resolve Studio 21.1 pero no pudo abrir proyecto porque Resolve estaba en el Gestor de proyectos: diff --git a/instalar.py b/instalar.py index 422051c..6841972 100644 --- a/instalar.py +++ b/instalar.py @@ -274,23 +274,24 @@ def instalado_aqui(base): return bool(i) and os.path.normcase(os.path.normpath(i["base"])) == os.path.normcase(os.path.normpath(base)) -def instalar(base, version, solo_los_que_hay=False): - """Da de alta el programa de `base`. Con `solo_los_que_hay` no repone un acceso directo que alguien - quitó a propósito: solo pone al día los que siguen ahí (es lo que se hace tras cada actualización).""" +def _apuntar(base, version): + """La línea del programa en Configuración › Aplicaciones.""" exe, args = orden(base) - icono = os.path.join(base, "static", "organilab.ico") - hechos = [] - for ruta in _accesos(): - if solo_los_que_hay and not os.path.exists(ruta): - continue - hechos.append(acceso_directo(ruta, exe, args, base, icono)) with winreg.CreateKey(winreg.HKEY_CURRENT_USER, CLAVE) as k: for nombre, valor in (("DisplayName", NOMBRE), ("DisplayVersion", version), ("Publisher", "Labstream"), - ("DisplayIcon", icono), ("InstallLocation", base), + ("DisplayIcon", os.path.join(base, "static", "organilab.ico")), ("InstallLocation", base), ("UninstallString", f'"{exe}" {args} desinstalar'.replace(" ", " "))): winreg.SetValueEx(k, nombre, 0, winreg.REG_SZ, valor) for nombre, valor in (("NoModify", 1), ("NoRepair", 1), ("EstimatedSize", _peso_kb(base))): winreg.SetValueEx(k, nombre, 0, winreg.REG_DWORD, valor) + + +def instalar(base, version): + """Da de alta el programa de `base`: sus dos accesos directos y su línea en Aplicaciones.""" + exe, args = orden(base) + icono = os.path.join(base, "static", "organilab.ico") + hechos = [acceso_directo(ruta, exe, args, base, icono) for ruta in _accesos()] + _apuntar(base, version) return {"base": base, "accesos": hechos, "orden": f'"{exe}" {args}'.strip()} @@ -307,17 +308,27 @@ def poner(base, version, avance=None): def al_dia(base, version): - """Tras una actualización: si este programa está dado de alta, Windows tiene que enseñar la versión - nueva y los accesos directos tienen que apuntar a lo que ahora lo abre.""" + """Tras una actualización: si este programa está dado de alta, Windows tiene que enseñar la versión nueva + y los accesos directos tienen que apuntar a lo que ahora lo abre. Devuelve True si hubo que cambiar algo.""" i = instalado() if not i or not instalado_aqui(base): return False exe, args = orden(base) - viejos = [a for a in map(leer_acceso, (r for r in _accesos() if os.path.exists(r))) if a] - if i["version"] == version and all(os.path.normcase(a["exe"]) == os.path.normcase(exe) and a["args"] == args for a in viejos): - return False - instalar(base, version, solo_los_que_hay=True) - return True + cambio = i["version"] != version + for ruta in _accesos(): + if not os.path.exists(ruta): + continue # el que alguien quitó a propósito no se repone + try: + a = leer_acceso(ruta) + if a and _misma(a["exe"], exe) and a["args"] == args: + continue # ya abre lo que tiene que abrir: no se toca + acceso_directo(ruta, exe, args, base, os.path.join(base, "static", "organilab.ico")) + cambio = True + except OSError: + pass # un acceso que ahora no se deja reescribir no impide lo demás + if cambio: + _apuntar(base, version) + return cambio def desinstalar(base=None): diff --git a/manifiesto.json b/manifiesto.json index 30f533e..10f5958 100644 --- a/manifiesto.json +++ b/manifiesto.json @@ -1,19 +1,19 @@ { "nombre": "OrganiLAB", - "version": "3.1.0", - "builtAt": "2026-10-04T17:23:53.000Z", - "archivo": "OrganizadorLabstream-portatil-3.1.0.zip", - "size": 195387012, - "sha256": "1c80a369bcdc8f07c5a9db1dec1bd95dcd390a8e65714ec60251388544ee6523", + "version": "3.1.1", + "builtAt": "2026-10-04T17:34:05.000Z", + "archivo": "OrganizadorLabstream-portatil-3.1.1.zip", + "size": 195387259, + "sha256": "117da3af85e14222893a34c7f992cd7853536bdef81f634b8453742e33aebdde", "app": { - "archivo": "OrganizadorLabstream-app-3.1.0.zip", - "size": 11574433, - "sha256": "cedd68ade3f61ccd2958e3578bf46be8b92e20d73aade3b7e455585e53898d3e", - "url": "/api/herramientas/OrganizadorLabstream-app-3.1.0.zip" + "archivo": "OrganizadorLabstream-app-3.1.1.zip", + "size": 11574680, + "sha256": "05b9e0f5d35ac1244d8b1b0eb6aa7caefc9799b8d910f3d3ad42d8e22e3c3a2e", + "url": "/api/herramientas/OrganizadorLabstream-app-3.1.1.zip" }, - "notes": "El programa se instala solo en su sitio definitivo (tu carpeta de programas) y trae un manual dentro, en «Manual». Además, dos copias ya no pueden compartir puerto.", + "notes": "El programa se instala solo en su sitio definitivo (tu carpeta de programas) y trae un manual dentro, en «Manual». Dos copias ya no pueden compartir puerto, y tras actualizarse Windows enseña siempre la versión nueva.", "firma": { "alg": "rsa2048-pkcs1v15-sha256", - "valor": "1ca46a8c6af640d62da3b8a97500661ae2d1e5442b66fb9ba9ee5268e3cfd85c1c1b6c2949484742adbdc758788bb5df323654b6a8533dd1ce3a93e9b974b0870bfa7c2f74fec4f6ef922cf1228c2d290a09a54dd62cabe846cdb40ed79276cc36940283e39db57ccc5f392daaf074672546f38cc3342cbc569d76723d365c2807755d95e8f129fb5e309952da27f645ef6130af7abb3f1356f895a92745ae890ad0c5b9d2a2139bc85ea5b66bcd3b2385d74c2d4b2bc438acf474e8dde041951d0c8f9949b39bf214d38ce3f0a80451f9c1681a9e3e53c2d28abf3e570201cc624b248213392ada7c892743a26c2be40d5170149d72d2dd3cd8bd92e3bff5d7" + "valor": "09ce52a3885faebe34bbb4a8210f460cfa345da578fcb9e73316aec5bdb55ec9977f6cbb9f3d3f8e8925755d6dce94310219d046210fcbcba082134fd088208a9b830086c3bbcbff6acd04259348b70871c935fceb457362619a406f99da2b8abafd69dba7e1400020b90fd7a211d42e72cd49cd0294fbc8fc30e8fbae0ba431e5881f68a6607de7ae44ceadb5b22b007c7a4e5a95347ebdab5b1a645c4814b51a01f95c57e2c0caf9a5a0ccf35ca2f9f53117788b509ff42f4fd8800fe9f38eecb5ee421f3e6dc366b48d344291b21c1b50677fb50e8621733b30d932e0f055b07571b74a0273e528797ae3853851ce35e0eb9a42fe14ae34ca6ea7a6ff2029" } } \ No newline at end of file diff --git a/organizador.py b/organizador.py index c993381..afd9051 100644 --- a/organizador.py +++ b/organizador.py @@ -40,7 +40,7 @@ import actualizador asistente.O = sys.modules[__name__] actualizador.O = sys.modules[__name__] -VERSION = "3.1.0" +VERSION = "3.1.1" 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 diff --git a/probar.py b/probar.py index c01bc77..e67364a 100644 --- a/probar.py +++ b/probar.py @@ -150,6 +150,22 @@ class Windows(unittest.TestCase): self.assertTrue(instalar.al_dia(base, "3.0.1")) self.assertEqual(instalar.instalado()["version"], "3.0.1") self.assertFalse(os.path.exists(escritorio)) + # un acceso directo que no se deja reescribir (pasó con el del Escritorio en OneDrive) no impide + # apuntar la versión nueva; y uno que ya abre lo que debe ni se toca + open(os.path.join(base, "OrganizadorLabstream.exe"), "wb").close() # ahora el programa se abre con otra cosa + de_verdad, intentos = instalar.acceso_directo, [] + instalar.acceso_directo = lambda *a, **k: (intentos.append(a[0]), (_ for _ in ()).throw(OSError("ocupado")))[1] + try: + self.assertTrue(instalar.al_dia(base, "3.0.2")) + finally: + instalar.acceso_directo = de_verdad + self.assertEqual(intentos, [inicio]) + self.assertEqual(instalar.instalado()["version"], "3.0.2") + self.assertTrue(instalar.al_dia(base, "3.0.2")) # ahora sí se deja: lo pone al día + self.assertTrue(instalar._misma(instalar.leer_acceso(inicio)["exe"], os.path.join(base, "OrganizadorLabstream.exe"))) + self.assertFalse(instalar.al_dia(base, "3.0.2")) # y ya no queda nada que cambiar + os.remove(os.path.join(base, "OrganizadorLabstream.exe")) + instalar.al_dia(base, "3.0.2") # quitarlo desde otra copia del programa no toca lo de esta; desde esta, sí self.assertEqual(instalar.desinstalar(tmp)["accesos"], []) self.assertTrue(instalar.instalado_aqui(base))