; Media Server NSIS Installer ; Cross-compilable: apt install nsis && makensis -DVERSION="1.0.0" installer.nsi !include "MUI2.nsh" !include "FileFunc.nsh" ; --- Configuration --- !define APPNAME "Media Server" !define EXENAME "media-server.bat" !define VBSNAME "start-hidden.vbs" !ifndef VERSION !define VERSION "0.0.0" !endif Name "${APPNAME} ${VERSION}" OutFile "build\MediaServer-v${VERSION}-setup.exe" InstallDir "$LOCALAPPDATA\${APPNAME}" RequestExecutionLevel user ; --- UI --- !define MUI_ICON "media_server\static\icons\icon.ico" !define MUI_UNICON "media_server\static\icons\icon.ico" !define MUI_ABORTWARNING !define MUI_FINISHPAGE_RUN "" !define MUI_FINISHPAGE_RUN_TEXT "Launch ${APPNAME}" !define MUI_FINISHPAGE_RUN_FUNCTION LaunchApp !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_LANGUAGE "English" ; --- Functions --- Function LaunchApp ExecShell "open" "wscript.exe" '"$INSTDIR\scripts\${VBSNAME}"' ; Give the server a moment to start, then open the UI in the default browser Sleep 2000 ExecShell "open" "http://localhost:8765/" FunctionEnd Function .onInit ; Check if server is running by trying to open its Python executable exclusively IfFileExists "$INSTDIR\python\python.exe" 0 done ClearErrors FileOpen $0 "$INSTDIR\python\python.exe" a IfErrors locked ; File opened fine — server is not running FileClose $0 Goto done locked: MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION \ "${APPNAME} is currently running.$\n$\nYes = Stop the server and continue$\nNo = Continue without stopping (may cause errors)$\nCancel = Abort installation" \ IDYES kill IDNO done Abort kill: nsExec::ExecToLog 'wmic process where "ExecutablePath like $\'%Media Server%python%$\'" call terminate' Sleep 2000 done: FunctionEnd ; --- Sections --- Section "!Core (required)" SecCore SectionIn RO SetOutPath "$INSTDIR" ; Wipe previous payload before extracting so stale files from an older ; version cannot survive an upgrade. Without this, in-place upgrades ; produce a half-old/half-new site-packages — e.g. an old PIL/_version.py ; alongside a new PIL/_imaging.pyd, which raises "_imaging extension was ; built for another version of Pillow" at runtime. config.yaml lives at ; $INSTDIR root and is preserved. RMDir /r "$INSTDIR\python" RMDir /r "$INSTDIR\app" RMDir /r "$INSTDIR\scripts" Delete "$INSTDIR\${EXENAME}" Delete "$INSTDIR\VERSION" Delete "$INSTDIR\config.example.yaml" ; Copy entire distribution File /r "dist\media-server\*.*" ; Create config.yaml from example if it doesn't already exist (preserve user config on upgrade) IfFileExists "$INSTDIR\config.yaml" +2 CopyFiles /SILENT "$INSTDIR\config.example.yaml" "$INSTDIR\config.yaml" ; Create uninstaller WriteUninstaller "$INSTDIR\uninstall.exe" ; Start Menu shortcuts CreateDirectory "$SMPROGRAMS\${APPNAME}" CreateShortcut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" \ "wscript.exe" '"$INSTDIR\scripts\${VBSNAME}"' \ "$INSTDIR\app\media_server\static\icons\icon.ico" 0 CreateShortcut "$SMPROGRAMS\${APPNAME}\${APPNAME} (Console).lnk" \ "$INSTDIR\${EXENAME}" "" \ "$INSTDIR\app\media_server\static\icons\icon.ico" 0 CreateShortcut "$SMPROGRAMS\${APPNAME}\Uninstall.lnk" \ "$INSTDIR\uninstall.exe" ; Registry for Add/Remove Programs WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \ "DisplayName" "${APPNAME}" WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \ "DisplayVersion" "${VERSION}" WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \ "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \ "InstallLocation" "$INSTDIR" WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \ "Publisher" "Alexei Dolgolyov" WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \ "NoModify" 1 WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \ "NoRepair" 1 ; Calculate installed size ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 IntFmt $0 "0x%08X" $0 WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \ "EstimatedSize" "$0" SectionEnd Section "Desktop shortcut" SecDesktop CreateShortcut "$DESKTOP\${APPNAME}.lnk" \ "wscript.exe" '"$INSTDIR\scripts\${VBSNAME}"' \ "$INSTDIR\app\media_server\static\icons\icon.ico" 0 SectionEnd Section "Start with Windows" SecAutostart ; Create Startup folder shortcut (runs hidden via VBS) CreateShortcut "$SMSTARTUP\${APPNAME}.lnk" \ "wscript.exe" '"$INSTDIR\scripts\${VBSNAME}"' \ "$INSTDIR\app\media_server\static\icons\icon.ico" 0 SectionEnd ; --- Section descriptions --- !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${SecCore} \ "Core application files, embedded Python, and Start Menu shortcuts." !insertmacro MUI_DESCRIPTION_TEXT ${SecDesktop} \ "Create a desktop shortcut to launch ${APPNAME}." !insertmacro MUI_DESCRIPTION_TEXT ${SecAutostart} \ "Automatically start ${APPNAME} when you log in to Windows." !insertmacro MUI_FUNCTION_DESCRIPTION_END ; --- Uninstaller --- Section "Uninstall" ; Stop running instance nsExec::ExecToLog 'wmic process where "ExecutablePath like $\'%Media Server%python%$\'" call terminate' nsExec::ExecToLog 'taskkill /F /IM media-server.exe' ; Remove application files RMDir /r "$INSTDIR\python" RMDir /r "$INSTDIR\app" RMDir /r "$INSTDIR\scripts" Delete "$INSTDIR\${EXENAME}" Delete "$INSTDIR\VERSION" Delete "$INSTDIR\uninstall.exe" ; Preserve config.yaml (user data) — only remove the example Delete "$INSTDIR\config.example.yaml" ; Remove shortcuts Delete "$DESKTOP\${APPNAME}.lnk" Delete "$SMSTARTUP\${APPNAME}.lnk" RMDir /r "$SMPROGRAMS\${APPNAME}" ; Remove registry DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" ; Remove install dir only if empty (config.yaml may remain) RMDir "$INSTDIR" SectionEnd