MSI installer

Is it possible to add a msi installer to the next release? exe installers are fine for local installs but MSI’s are easier for network deployment(which i’ve been looking into as more uses start using shotcut at my office)

In the interim, would you have the option of using the portable zip version? As in, having a script wipe a user’s Shotcut program folder and copy in the new files from a network drive holding the portable version? Desktop icons would still be pointing to the correct location that way. Hacky, but could potentially work right away.

Right now I just go system to system to update them(it’s only 4 and one update a month or less) but as things change that number is likely to go up. I’d love to see ninite pickup shotcut as one of their supported applications as well but that’s a separate discussion(and on how many people request it from them really)

I doubt I will pick this up. Shotcut for Windows is built on Linux by cross-compiling and that includes the signing and installer using NSIS. However, I do unfortunately have a number of manual steps to notarize the macOS build and prepare the appx for the Microsoft Store. I could possibly add MSI if it is not too many steps, contributed by someone, and only requires Microsoft tools. Please try to build the MSI package using the portable zip or installed folder and submit it here along with instructions. Here is our simple NSIS installer script if you want to make it behave similarly and with same options:

Hi,

I’ve made a batch script which we use in our company to build MSI installers - deployable via GPO - from the official “ZIP” release of Shotcut.

Requirements you need to fulfull before running the “Do-your-own MSI installer” batch:

  • sed.exe (Unix sed for Windows)
    ** http x://sourceforge.net/projects/gnuwin32/files//sed/4.2.1/sed-4.2.1-bin.zip/download
    ** http x://sourceforge.net/projects/gnuwin32/files//sed/4.2.1/sed-4.2.1-dep.zip/download
  • WIX (Windows Installer Builder)
    ** https x://github.com/wixtoolset/wix3/releases
    ** I’m currently using wix_3.11.2.4516.exe
  • The files out of Shotcut’s official ZIP release
    ** https x://www.fosshub.com/Shotcut.html?dwl=shotcut-win64-[yymmdd].zip
    ** e.g. https://www.fosshub.com/Shotcut.html?dwl=shotcut-win64-201128.zip

Put up your files together like this:
image

Subdirectory “SourceDir” needs to contain the icon “shotcut.ico”. Extract the contents of the Shotcut ZIP release to the “SourceDir/” directory.

Download of the “do your own Shotcut MSI batch scripts”:

If you have set-up “sed.exe” and “WIX” on your PATH environment properly, the script should run fine because it’s designed “portable”.

Just double-click “wix_build_msi.cmd” and your MSI installer for the Shotcut version present in “SourceDir/” will be built and put into the same directory where the batch script resides. It will be named “Shotcut_v20.11.28.msi”, for example.

That’s it. Double click on the created MSI file and verify it installs properly before putting it on a GPO to deploy it in an AD domain. If Shotcut doesn’t change major things, the script will also work to build future releases into MSI.

Kind regards,
Catfriend1

1 Like

For reference, the above mentioned fileset contains:

  • WIX WXS build template

    <?xml version="1.0" encoding="utf-8"?> <?define Property_ProductVersion = "BATCH_PRODUCTVERSION" ?> <?define Property_ProductCode = "*" ?> <?define Property_UpgradeCode = "2cb47c71-5bd9-4615-bf3f-714f9a83b185" ?> <?define Property_ProductName = "BATCH_PRODUCTNAME" ?> <?define Property_ProductManufacturer = "BATCH_MANUFACTURER" ?> <?define Property_ProductArchitecture = "BATCH_ARCHITECTURE" ?>
          <Icon Id="BATCH_PRODUCTEXE" SourceFile="SourceDir\$(var.Property_ProductName).ico" />
          <Property Id='ARPPRODUCTICON' Value='BATCH_PRODUCTEXE' />
    
          <Feature Id="DefaultFeature" Title="Main Feature" Level="1">
              <ComponentGroupRef Id="ProductComponentGroup" />
          </Feature>
    
          <Media Id="1" Cabinet="simple.cab" EmbedCab="yes" />
    
          <!-- The InstallExecuteSequence table describes the order that actions will be executed during       -->
          <!-- installation, repair and uninstall of this product.                                             -->
          <InstallExecuteSequence>
              <RemoveExistingProducts Before="InstallInitialize" />
              <LaunchConditions After="AppSearch"/>
          </InstallExecuteSequence>
    
          <UI />
      </Product>
    
  • WIX HEAT transform template

    <?xml version="1.0" encoding="utf-8"?>

    <xsl:stylesheet
    version=“1.0”
    xmlns=“http REMOVE_BECAUSE_OF_FORUM_LINK_RESTRICTION ://schemas.microsoft.com/wix/2006/wi”
    xmlns:xsl=“http REMOVE_BECAUSE_OF_FORUM_LINK_RESTRICTION ://www.w3.org/1999/XSL/Transform”
    xmlns:wix=“http REMOVE_BECAUSE_OF_FORUM_LINK_RESTRICTION ://schemas.microsoft.com/wix/2006/wi”
    xmlns:str=“http REMOVE_BECAUSE_OF_FORUM_LINK_RESTRICTION ://xsltsl.org/string”
    exclude-result-prefixes=“wix str” >

      <xsl:output
      	encoding="utf-8"
      	method="xml"
      	version="1.0"
      	indent="yes" />
    
      <xsl:template match='wix:Component[contains(wix:File/@Source, "SourceDir\BATCH_PRODUCTEXE") and not(substring-after(wix:File/@Source, "SourceDir\BATCH_PRODUCTEXE"))]'>
      	<xsl:copy>
      		<xsl:apply-templates select="@*|node()"/>
      		<xsl:comment>Added by HEAT transform</xsl:comment>
      		<Shortcut 
      			Id="ProdcutExeShortcut" 
      			Icon="BATCH_PRODUCTEXE"
      			Name="BATCH_PRODUCTNAME" 
      			Directory="ProgramMenuFolder" 
      			Advertise="yes">
      			<xsl:attribute name="WorkingDirectory">ProductDir</xsl:attribute>
      		</Shortcut>
    
      		<!-- Capabilities keys for Vista/7 "Set Program Access and Defaults" -->
      		<RegistryValue Root="HKLM" Key="SOFTWARE\BATCH_PRODUCTNAME\Capabilities" Name="ApplicationDescription" Value="BATCH_PRODUCTNAME" Type="string" />
      		<RegistryValue Root="HKLM" Key="SOFTWARE\BATCH_PRODUCTNAME\Capabilities" Name="ApplicationIcon" Value="[ProductDir]BATCH_PRODUCTEXE,0" Type="string" />
      		<RegistryValue Root="HKLM" Key="SOFTWARE\BATCH_PRODUCTNAME\Capabilities" Name="ApplicationName" Value="BATCH_PRODUCTNAME" Type="string" />
      		<RegistryValue Root="HKLM" Key="SOFTWARE\BATCH_PRODUCTNAME\Capabilities\DefaultIcon" Value="[ProductDir]BATCH_PRODUCTEXE,1" Type="string" />
      		<RegistryValue Root="HKLM" Key="SOFTWARE\BATCH_PRODUCTNAME\Capabilities\FileAssociations" Name=".BATCH_PRIMARY_FILE_ASSOCIATION" Value="BATCH_PRIMARY_ASSOCIATION_DESC" Type="string" />
      		<RegistryValue Root="HKLM" Key="SOFTWARE\BATCH_PRODUCTNAME\Capabilities\MIMEAssociations" Name="application/BATCH_PRIMARY_FILE_ASSOCIATION" Value="BATCH_PRIMARY_ASSOCIATION_DESC" Type="string" />
      		<RegistryValue Root="HKLM" Key="SOFTWARE\BATCH_PRODUCTNAME\Capabilities\shell\Open\command" Value="&quot;[ProductDir]BATCH_PRODUCTEXE&quot; &quot;%1&quot;" Type="string" />
      		<RegistryValue Root="HKLM" Key="SOFTWARE\RegisteredApplications" Name="BATCH_PRODUCTNAME" Value="SOFTWARE\BATCH_PRODUCTNAME\Capabilities" Type="string" />
    
      		<!-- App Paths to support Start,Run -> "myapp" -->
      		<RegistryValue Root="HKLM" Key="SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\BATCH_PRODUCTEXE" Value="[ProductDir]BATCH_PRODUCTEXE" Type="string" />
      		<RegistryValue Root="HKLM" Key="SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\BATCH_PRODUCTEXE" Name="Path" Value="[ProductDir]" Type="string" />
    
      		<!-- Extend to the "open with" list + Win7 jump menu pinning  -->
      		<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\Applications\BATCH_PRODUCTEXE\SupportedTypes" Name=".BATCH_PRIMARY_FILE_ASSOCIATION" Value="" Type="string" />
      		<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\Applications\BATCH_PRODUCTEXE\shell\open" Name="FriendlyAppName" Value="BATCH_PRODUCTNAME" Type="string" />
    
      		<!-- BATCH_PRIMARY_ASSOCIATION_DESC ProgID -->
      		<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\BATCH_PRIMARY_ASSOCIATION_DESC" Name="FriendlyTypeName" Value="BATCH_PRODUCTNAME" Type="string" />
      		<ProgId Id="BATCH_PRIMARY_ASSOCIATION_DESC" Description="BATCH_PRODUCTNAME" Icon="BATCH_PRODUCTEXE" Advertise="yes">
      			<Extension Id="BATCH_PRIMARY_FILE_ASSOCIATION">
      				<Verb Id="open" Argument="&quot;%1&quot;" />
      				<MIME Advertise="yes" ContentType="application/BATCH_PRIMARY_FILE_ASSOCIATION" Default="yes" />
      			</Extension>
      		</ProgId>
      	</xsl:copy>
      </xsl:template>
    
      <!-- identity template -->
      <xsl:template match="@*|node()">
      	<xsl:copy>
      	  <xsl:apply-templates select="@*|node()"/>
      	</xsl:copy>
      </xsl:template>
    
      <xsl:template match='/'>
      	<xsl:comment>*** DO NOT EDIT: Generated by heat.exe; transformed by ProgComponentGroup.xsl</xsl:comment>
      	<xsl:apply-templates select="@*|node()"/>
      </xsl:template>
    

    </xsl:stylesheet>

  • BATCH script “wix_build_msi.cmd”

    @echo off
    setlocal enabledelayedexpansion
    REM
    SET SCRIPT_PATH=%~dps0
    cd /d “%SCRIPT_PATH%”
    cls
    REM
    REM Consts.
    SET DO_CLEANUP=1
    SET PRODUCT_PRIMARY_FILE_ASSOCIATION=mlt
    SET PRODUCT_PRIMARY_ASSOCIATION_DESC=Shotcut.Project
    SET PRODUCT_NAME=Shotcut
    SET PRODUCT_MANUFACTURER=Meltytech, LLC
    REM
    REM Attention - PRODUCT_EXE is CASE SENSITIVE - If wrong, HEAT XLST will NOT work.
    SET PRODUCT_EXE=shotcut.exe
    SET PRODUCT_ARCHITECTURE=x64
    REM
    SET “TEMP_FILE_PREFIX=%PRODUCT_NAME%”
    REM
    REM Runtime Vars.
    SET WIX_TOOLKIT_DIR_NAME=
    for /f “tokens=" %%A in ('dir /b /a:d "%ProgramFiles(x86)%\Wix Toolset v”’) do SET WIX_TOOLKIT_DIR_NAME=%%A
    IF NOT DEFINED WIX_TOOLKIT_DIR_NAME echo [ERROR] Could not resolve WIX_TOOLKIT_DIR_NAME. & goto :EOS
    SET WIX_TOOLKIT_BIN_PATH=%ProgramFiles(x86)%%WIX_TOOLKIT_DIR_NAME%\bin
    echo [INFO] WIX_TOOLKIT_BIN_PATH=%WIX_TOOLKIT_BIN_PATH%
    REM
    SET WIX_CANDLE_BIN="%WIX_TOOLKIT_BIN_PATH%\candle.exe"
    SET WIX_HEAT_BIN="%WIX_TOOLKIT_BIN_PATH%\heat.exe"
    SET WIX_LIGHT_BIN="%WIX_TOOLKIT_BIN_PATH%\light.exe"
    REM
    REM Input files
    SET WIX_INPUT_SCRIPT_TEMPLATE="%SCRIPT_PATH%%TEMP_FILE_PREFIX%.wxs.template"
    SET WIX_INPUT_SCRIPT="%SCRIPT_PATH%%TEMP_FILE_PREFIX%.wxs"
    SET WIX_FILES_SCRIPT="%SCRIPT_PATH%Files.wxs"
    SET WIX_HEAT_TRANSFORM_TEMPLATE="%SCRIPT_PATH%%TEMP_FILE_PREFIX%.heat.xlst.template"
    SET WIX_HEAT_TRANSFORM="%SCRIPT_PATH%%TEMP_FILE_PREFIX%.heat.xlst"
    REM
    SET PRODUCT_EXE_PRODUCTVERSION=
    for /f “tokens=2 delims=#” %%A in ('TYPE SourceDir%PRODUCT_EXE% 2^>NUL: ^| %SCRIPT_PATH%psreplace “Shotcut version \0([0-9]{2}.[0-9]{2}.[0-9]{2})\0” “VERSION#$1#VERSION” ^| find “VERSION#"’) do SET PRODUCT_EXE_PRODUCTVERSION=%%A
    IF NOT DEFINED PRODUCT_EXE_PRODUCTVERSION echo [ERROR] Could not determine ProductVersion by directly reading the EXE. & pause & goto :eof
    echo [INFO] PRODUCT_EXE_PRODUCTVERSION=[%PRODUCT_EXE_PRODUCTVERSION%]
    echo [INFO] PRODUCT_NAME=[%PRODUCT_NAME%]
    REM
    REM Main.
    REM
    REM Update "BATCH
    " in WIX input and transform scripts with "PRODUCT_” from here.
    type %WIX_INPUT_SCRIPT_TEMPLATE% | sed -e “s/BATCH_PRODUCTVERSION/%PRODUCT_EXE_PRODUCTVERSION%/g” -e “s/BATCH_PRODUCTNAME/%PRODUCT_NAME%/g” -e “s/BATCH_PRODUCTEXE/%PRODUCT_EXE%/g” -e “s/BATCH_MANUFACTURER/%PRODUCT_MANUFACTURER%/g” -e “s/BATCH_ARCHITECTURE/%PRODUCT_ARCHITECTURE%/g” -e “s/BATCH_PRIMARY_FILE_ASSOCIATION/%PRODUCT_PRIMARY_FILE_ASSOCIATION%/g” -e “s/BATCH_PRIMARY_ASSOCIATION_DESC/%PRODUCT_PRIMARY_ASSOCIATION_DESC%/g” > %WIX_INPUT_SCRIPT%
    REM
    type %WIX_HEAT_TRANSFORM_TEMPLATE% | sed -e “s/BATCH_PRODUCTVERSION/%PRODUCT_EXE_PRODUCTVERSION%/g” -e “s/BATCH_PRODUCTNAME/%PRODUCT_NAME%/g” -e “s/BATCH_PRODUCTEXE/%PRODUCT_EXE%/g” -e “s/BATCH_MANUFACTURER/%PRODUCT_MANUFACTURER%/g” -e “s/BATCH_ARCHITECTURE/%PRODUCT_ARCHITECTURE%/g” -e “s/BATCH_PRIMARY_FILE_ASSOCIATION/%PRODUCT_PRIMARY_FILE_ASSOCIATION%/g” -e “s/BATCH_PRIMARY_ASSOCIATION_DESC/%PRODUCT_PRIMARY_ASSOCIATION_DESC%/g” > %WIX_HEAT_TRANSFORM%
    REM
    REM Output files
    SET PRODUCT_WIX_OBJ="%SCRIPT_PATH%%TEMP_FILE_PREFIX%.wixobj"
    SET FILES_WIX_OBJ="%SCRIPT_PATH%Files.wixobj"
    REM
    REM Get “Property_ProductVersion” from WXS script.
    SET MSI_PRODUCT_VERSION=
    for /f delims^=^"^ tokens^=2 %%A in (‘type %WIX_INPUT_SCRIPT% 2^>NUL: ^| find /i “Property_ProductVersion =”’) do SET MSI_PRODUCT_VERSION=%%A
    IF NOT DEFINED MSI_PRODUCT_VERSION echo [ERROR] Could not extract MSI_PRODUCT_VERSION from WXS script. & goto :EOS
    echo [INFO] ProductVersion=[v%MSI_PRODUCT_VERSION%]
    REM
    REM Output files
    SET WIX_PDB="%SCRIPT_PATH%%TEMP_FILE_PREFIX%_v%MSI_PRODUCT_VERSION%.wixpdb"
    SET MSI_TARGET="%SCRIPT_PATH%%TEMP_FILE_PREFIX%_v%MSI_PRODUCT_VERSION%.msi"
    REM
    del /f %MSI_TARGET% 2> NUL:
    REM
    REM Exec HEAT
    echo [INFO] Building file list …
    %WIX_HEAT_BIN% dir “%SCRIPT_PATH%SourceDir” -ag -cg “ProductComponentGroup” -dr “ProductDir” -sreg -sfrag -srd -arch %PRODUCT_ARCHITECTURE% -t “%WIX_HEAT_TRANSFORM%” -out “%WIX_FILES_SCRIPT%” *
    if NOT EXIST %WIX_FILES_SCRIPT% echo [ERROR] Failed to build WIX_FILES_SCRIPT in step #1 & goto :EOS
    REM
    REM Verify HEAT Transform
    echo [INFO] Verifying HEAT XLST transform …
    type “%WIX_FILES_SCRIPT%” | find /i “Added by HEAT transform” 1> NUL: || (echo [ERROR] XLST transform FAILED. & goto :EOS)
    REM
    REM Exec CANDLE
    echo [INFO] Running LINT …
    %WIX_CANDLE_BIN% -nologo -arch %PRODUCT_ARCHITECTURE% -out “%SCRIPT_PATH%” -ext WixUiExtension -ext WixUtilExtension %WIX_INPUT_SCRIPT% %WIX_FILES_SCRIPT% -sw1150
    IF NOT EXIST %PRODUCT_WIX_OBJ% echo [ERROR] Failed to build PRODUCT_WIX_OBJ in step #2 & goto :EOS
    IF NOT EXIST %FILES_WIX_OBJ% echo [ERROR] Failed to build FILES_WIX_OBJ in step #2 & goto :EOS
    REM
    REM Exec LIGHT
    echo [INFO] Merging MSI …
    %WIX_LIGHT_BIN% -nologo “*.wixobj” -out %MSI_TARGET% -ext WixUiExtension -ext WixUtilExtension -sw1076
    del /f “%WIX_PDB%” 2> NUL:
    REM
    if NOT EXIST %MSI_TARGET% echo [ERROR] Failed to merge MSI in step #3 & goto :EOS
    REM
    goto :EOS

    :cleanUp
    REM
    IF NOT “%DO_CLEANUP%” == “1” goto :eof
    REM
    echo [INFO] Cleanup previous build …
    IF DEFINED PRODUCT_WIX_OBJ del /f “%PRODUCT_WIX_OBJ%” 2> NUL:
    IF DEFINED FILES_WIX_OBJ del /f “%FILES_WIX_OBJ%” 2> NUL:
    IF DEFINED WIX_INPUT_SCRIPT del /f “%WIX_INPUT_SCRIPT%” 2> NUL:
    IF DEFINED WIX_FILES_SCRIPT del /f “%WIX_FILES_SCRIPT%” 2> NUL:
    IF DEFINED WIX_HEAT_TRANSFORM del /f “%WIX_HEAT_TRANSFORM%” 2> NUL:
    IF DEFINED WIX_PDB del /f “%WIX_PDB%” 2> NUL:
    REM
    goto :eof

    :EOS
    REM
    call :cleanUp
    REM
    pause
    goto :eof

For completeness: A successful MSI build using the batch script looks like this.

image

image