# Creating an Installer for Jellyfin

**Solution:** E:\Projects\pgsql-jellyfin  
**Target:** Windows Installer for PostgreSQL-enabled Jellyfin  
**Status:** Guide for implementation

---

## Overview

You can create professional installers for your Jellyfin solution using several methods. Here are your options ranked by recommendation:

---

## ✅ Recommended: WiX Toolset (Best for Windows)

### Why WiX?
- ✅ Industry standard for .NET applications
- ✅ Creates proper MSI installers
- ✅ Full Windows Installer features (services, registry, shortcuts)
- ✅ Supports upgrades and uninstalls
- ✅ Free and open source
- ✅ Integrates with Visual Studio

### Prerequisites
```powershell
# Install WiX Toolset
winget install --id WiX.Toolset

# Or download from:
# https://wixtoolset.org/
```

### Implementation Steps

#### Step 1: Create WiX Project

Add to your solution:
```xml
<!-- Jellyfin.Installer.csproj -->
<Project Sdk="WixToolset.Sdk/5.0.0">
  <PropertyGroup>
    <OutputType>Package</OutputType>
    <Platform>x64</Platform>
    <TargetFramework>net11.0</TargetFramework>
    <OutputName>JellyfinSetup-PostgreSQL-$(Version)</OutputName>
  </PropertyGroup>

  <ItemGroup>
    <!-- Reference the main Jellyfin.Server project -->
    <ProjectReference Include="..\Jellyfin.Server\Jellyfin.Server.csproj">
      <Private>true</Private>
      <DoNotHarvest>true</DoNotHarvest>
      <IncludeOutputGroupInVSIXContainer>true</IncludeOutputGroupInVSIXContainer>
    </ProjectReference>
  </ItemGroup>
</Project>
```

#### Step 2: Create Installer Definition (Product.wxs)

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
  <Package 
    Name="Jellyfin Server (PostgreSQL Edition)" 
    Manufacturer="Your Name"
    Version="11.0.0.0"
    UpgradeCode="PUT-YOUR-GUID-HERE">
    
    <MajorUpgrade 
      DowngradeErrorMessage="A newer version is already installed." />
    
    <Media Id="1" Cabinet="jellyfin.cab" EmbedCab="yes" />

    <!-- Installation Directory -->
    <StandardDirectory Id="ProgramFiles64Folder">
      <Directory Id="INSTALLFOLDER" Name="Jellyfin">
        <Directory Id="BinFolder" Name="bin" />
        <Directory Id="DataFolder" Name="data" />
      </Directory>
    </StandardDirectory>

    <!-- Program Data Directory -->
    <StandardDirectory Id="CommonAppDataFolder">
      <Directory Id="ProgramDataFolder" Name="jellyfin" />
    </StandardDirectory>

    <!-- Components -->
    <ComponentGroup Id="ProductComponents" Directory="BinFolder">
      <!-- Harvest all files from lib\Release\net11.0 -->
      <Component Id="JellyfinExe">
        <File Source="$(var.SolutionDir)lib\Release\net11.0\jellyfin.dll" />
        <File Source="$(var.SolutionDir)lib\Release\net11.0\jellyfin.exe" />
        <!-- Add more files... -->
      </Component>
    </ComponentGroup>

    <!-- Service Component -->
    <Component Id="JellyfinService" Directory="BinFolder">
      <ServiceInstall
        Id="JellyfinServiceInstaller"
        Type="ownProcess"
        Name="JellyfinPostgreSQL"
        DisplayName="Jellyfin Server (PostgreSQL)"
        Description="Jellyfin media server with PostgreSQL support"
        Start="auto"
        Account="LocalSystem"
        ErrorControl="normal"
        Arguments='--service'
        />
      <ServiceControl
        Id="JellyfinServiceControl"
        Start="install"
        Stop="both"
        Remove="uninstall"
        Name="JellyfinPostgreSQL"
        Wait="yes" />
    </Component>

    <!-- Start Menu Shortcuts -->
    <StandardDirectory Id="ProgramMenuFolder">
      <Directory Id="ApplicationProgramsFolder" Name="Jellyfin">
        <Component Id="ApplicationShortcuts">
          <Shortcut 
            Id="JellyfinShortcut"
            Name="Jellyfin Server (PostgreSQL)"
            Description="Start Jellyfin media server"
            Target="[BinFolder]jellyfin.exe"
            WorkingDirectory="BinFolder" />
          <RemoveFolder Id="CleanUpShortCut" On="uninstall" />
        </Component>
      </Directory>
    </StandardDirectory>

    <!-- Features -->
    <Feature Id="ProductFeature" Title="Jellyfin Server" Level="1">
      <ComponentGroupRef Id="ProductComponents" />
      <ComponentRef Id="JellyfinService" />
      <ComponentRef Id="ApplicationShortcuts" />
    </Feature>

    <!-- UI -->
    <WixVariable Id="WixUILicenseRtf" Value="License.rtf" />
    <UIRef Id="WixUI_InstallDir" />
    <Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
  </Package>
</Wix>
```

#### Step 3: Build the Installer

```powershell
# Add WiX project to solution
dotnet new wix -n Jellyfin.Installer -o Jellyfin.Installer
dotnet sln add Jellyfin.Installer/Jellyfin.Installer.csproj

# Build installer
dotnet build Jellyfin.Installer --configuration Release

# Output: Jellyfin.Installer\bin\Release\JellyfinSetup-PostgreSQL-11.0.0.msi
```

---

## ⚡ Quick Option: Inno Setup (Easier but Less Features)

### Why Inno Setup?
- ✅ Very easy to use
- ✅ Script-based (no XML)
- ✅ Small installer size
- ✅ Free
- ❌ Less Windows Installer integration than WiX

### Prerequisites
```powershell
# Download from: https://jrsoftware.org/isinfo.php
winget install --id JRSoftware.InnoSetup
```

### Implementation: Create Setup Script (jellyfin-setup.iss)

```pascal
; Jellyfin PostgreSQL Edition Installer
[Setup]
AppName=Jellyfin Server (PostgreSQL Edition)
AppVersion=11.0.0
DefaultDirName={autopf}\Jellyfin
DefaultGroupName=Jellyfin
OutputDir=installer-output
OutputBaseFilename=JellyfinSetup-PostgreSQL-11.0.0
Compression=lzma2
SolidCompression=yes
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64
PrivilegesRequired=admin
SetupIconFile=jellyfin.ico

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "Create a &desktop icon"; GroupDescription: "Additional icons:"
Name: "service"; Description: "Install as Windows Service"; GroupDescription: "Service Options:"; Flags: unchecked

[Files]
; Copy all files from lib\Release\net11.0
Source: "lib\Release\net11.0\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; Copy startup configuration
Source: "Jellyfin.Server\Resources\Configuration\startup.windows.json"; DestDir: "{app}"; DestName: "startup.json"; Flags: onlyifdoesntexist

[Dirs]
; Create program data directories
Name: "{commonappdata}\jellyfin"; Permissions: users-modify
Name: "{commonappdata}\jellyfin\data"; Permissions: users-modify
Name: "{commonappdata}\jellyfin\log"; Permissions: users-modify
Name: "{commonappdata}\jellyfin\cache"; Permissions: users-modify

[Icons]
Name: "{group}\Jellyfin Server"; Filename: "{app}\jellyfin.exe"
Name: "{group}\Jellyfin Web Interface"; Filename: "http://localhost:8096"
Name: "{commondesktop}\Jellyfin Server"; Filename: "{app}\jellyfin.exe"; Tasks: desktopicon

[Run]
; Open web interface after install
Filename: "http://localhost:8096"; Description: "Open Jellyfin Web Interface"; Flags: postinstall shellexec skipifsilent

; Install as service (optional)
Filename: "sc.exe"; Parameters: "create JellyfinPostgreSQL binPath= ""{app}\jellyfin.exe --service"" start= auto"; Tasks: service; Flags: runhidden
Filename: "sc.exe"; Parameters: "description JellyfinPostgreSQL ""Jellyfin media server with PostgreSQL support"""; Tasks: service; Flags: runhidden
Filename: "sc.exe"; Parameters: "start JellyfinPostgreSQL"; Tasks: service; Flags: runhidden waituntilterminated

[UninstallRun]
; Stop and remove service if it exists
Filename: "sc.exe"; Parameters: "stop JellyfinPostgreSQL"; Flags: runhidden
Filename: "sc.exe"; Parameters: "delete JellyfinPostgreSQL"; Flags: runhidden

[Code]
// Custom code for install dialogs
function InitializeSetup(): Boolean;
begin
  Result := True;
  // Check for .NET 11 runtime
  if not RegKeyExists(HKLM, 'SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedhost') then
  begin
    MsgBox('.NET 11 Runtime is required. Please install it first.', mbError, MB_OK);
    Result := False;
  end;
end;
```

### Build Inno Setup Installer

```powershell
# Compile the script
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" jellyfin-setup.iss

# Output: installer-output\JellyfinSetup-PostgreSQL-11.0.0.exe
```

---

## 🚀 Modern Option: MSIX (Windows 10/11 Only)

### Why MSIX?
- ✅ Modern Windows app packaging
- ✅ Automatic updates via Microsoft Store
- ✅ Clean install/uninstall
- ✅ Sandboxed security
- ❌ Windows 10 1809+ only
- ❌ Requires code signing certificate

### Implementation: Add MSIX Project

```xml
<!-- Jellyfin.Package.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net11.0-windows10.0.19041.0</TargetFramework>
    <GenerateAppxPackageOnBuild>true</GenerateAppxPackageOnBuild>
    <AppxPackageSigningEnabled>true</AppxPackageSigningEnabled>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\Jellyfin.Server\Jellyfin.Server.csproj" />
  </ItemGroup>

  <ItemGroup>
    <Content Include="Images\StoreLogo.png" />
    <Content Include="Images\Square44x44Logo.png" />
    <Content Include="Images\Square150x150Logo.png" />
    <Content Include="Images\Wide310x150Logo.png" />
  </ItemGroup>
</Project>
```

---

## 📦 Simple Option: Self-Contained Publish + ZIP

### Why Zip?
- ✅ Simplest approach
- ✅ No installer needed
- ✅ Portable
- ❌ No service install
- ❌ No shortcuts
- ❌ No uninstaller

### Implementation

```powershell
# Publish self-contained
dotnet publish Jellyfin.Server `
  --configuration Release `
  --runtime win-x64 `
  --self-contained true `
  --output "publish\jellyfin-pgsql-win-x64" `
  /p:PublishSingleFile=false `
  /p:IncludeNativeLibrariesForSelfExtract=true

# Create ZIP
Compress-Archive `
  -Path "publish\jellyfin-pgsql-win-x64\*" `
  -DestinationPath "jellyfin-pgsql-11.0.0-win-x64.zip"
```

### Include README in ZIP

```markdown
# Jellyfin Server (PostgreSQL Edition)

## Installation

1. Extract this ZIP to C:\Jellyfin (or any location)
2. Run `jellyfin.exe`
3. Open http://localhost:8096 in your browser
4. Follow setup wizard

## Install as Windows Service

Run as Administrator:
```cmd
sc create JellyfinPostgreSQL binPath= "C:\Jellyfin\jellyfin.exe --service" start= auto
sc description JellyfinPostgreSQL "Jellyfin media server with PostgreSQL"
sc start JellyfinPostgreSQL
```

## Uninstall Service

```cmd
sc stop JellyfinPostgreSQL
sc delete JellyfinPostgreSQL
```

## Configuration

Edit `startup.json` to customize paths.
```

---

## 🎯 Recommended Approach for You

### For Production Distribution: **WiX Toolset**

**Why:**
- Professional MSI installer
- Windows Service support
- Proper upgrade handling
- Includes PostgreSQL setup guidance

**Next Steps:**

1. **Install WiX**
   ```powershell
   winget install --id WiX.Toolset
   ```

2. **Create Installer Project**
   ```powershell
   mkdir Jellyfin.Installer
   cd Jellyfin.Installer
   # Create Product.wxs file (see template above)
   ```

3. **Add to Solution**
   ```powershell
   dotnet sln add Jellyfin.Installer/Jellyfin.Installer.csproj
   ```

4. **Build**
   ```powershell
   dotnet build Jellyfin.Installer --configuration Release
   ```

### For Quick Testing: **Inno Setup**

**Why:**
- Fast to create
- Easy to customize
- Good for internal testing

**Next Steps:**

1. **Install Inno Setup**
   ```powershell
   winget install JRSoftware.InnoSetup
   ```

2. **Create Script**
   - Use template above
   - Save as `jellyfin-setup.iss`

3. **Compile**
   ```powershell
   "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" jellyfin-setup.iss
   ```

---

## Additional Installer Features to Consider

### PostgreSQL Database Setup

Add to installer:

```xml
<!-- WiX: Check for PostgreSQL -->
<Property Id="POSTGRESQLINSTALLED">
  <RegistrySearch Id="PostgreSQLSearch" 
    Root="HKLM" 
    Key="SOFTWARE\PostgreSQL\Installations\postgresql-x64-16"
    Name="Version"
    Type="raw" />
</Property>

<Condition Message="PostgreSQL 12+ is required. Please install PostgreSQL first.">
  Installed OR POSTGRESQLINSTALLED
</Condition>
```

### Firewall Rules

```xml
<!-- Add Windows Firewall exception -->
<Component Id="FirewallException">
  <fire:FirewallException Id="JellyfinFirewall"
    Name="Jellyfin Server"
    Port="8096"
    Protocol="tcp"
    Scope="any" />
</Component>
```

### Database Migration Wizard

Include a post-install wizard for:
- PostgreSQL connection setup
- Database migration from SQLite
- Initial configuration

---

## Testing Your Installer

### Test Checklist

- [ ] Fresh Windows install
- [ ] Upgrade from previous version
- [ ] Silent install: `msiexec /i JellyfinSetup.msi /quiet`
- [ ] Custom install directory
- [ ] Service starts automatically
- [ ] Firewall rules created
- [ ] Shortcuts work
- [ ] Uninstall removes everything
- [ ] Data preserved on upgrade

### Test Commands

```powershell
# Install silently
msiexec /i JellyfinSetup-PostgreSQL-11.0.0.msi /quiet /l*v install.log

# Uninstall silently
msiexec /x JellyfinSetup-PostgreSQL-11.0.0.msi /quiet

# Upgrade
msiexec /i JellyfinSetup-PostgreSQL-11.0.1.msi /quiet
```

---

## Distribution

### Code Signing (Recommended)

Get a code signing certificate:
- DigiCert
- Sectigo
- SignPath (free for open source)

Sign your installer:
```powershell
signtool sign /f certificate.pfx /p password /t http://timestamp.digicert.com JellyfinSetup.msi
```

### Release Strategy

1. **GitHub Releases**
   ```yaml
   # .github/workflows/release.yml
   - name: Build Installer
     run: dotnet build Jellyfin.Installer --configuration Release
   
   - name: Upload Release Asset
     uses: actions/upload-release-asset@v1
     with:
       asset_path: Jellyfin.Installer/bin/Release/JellyfinSetup.msi
       asset_name: jellyfin-pgsql-windows-${{ github.ref }}.msi
   ```

2. **Versioning**
   - Use semantic versioning: 11.0.0
   - Include in filename: JellyfinSetup-PostgreSQL-11.0.0.msi
   - Update UpgradeCode for major versions only

---

## Quick Start Script

I can create a PowerShell script to help you set up the installer project:

```powershell
# setup-installer.ps1
param(
    [Parameter(Mandatory=$false)]
    [ValidateSet("WiX", "InnoSetup", "Both")]
    [string]$Type = "InnoSetup"
)

Write-Host "Setting up Jellyfin installer project..." -ForegroundColor Cyan

# Your current setup
$solutionRoot = "E:\Projects\pgsql-jellyfin"
$libFolder = "$solutionRoot\lib\Release\net11.0"

if ($Type -eq "InnoSetup" -or $Type -eq "Both") {
    # Create Inno Setup script
    # ... (see template above)
}

if ($Type -eq "WiX" -or $Type -eq "Both") {
    # Create WiX project
    # ... (see template above)
}

Write-Host "Installer setup complete!" -ForegroundColor Green
```

---

## Summary

**Recommended for You: Inno Setup** (Quick start)
- Easiest to implement immediately
- Creates professional .exe installer
- Includes service install option

**Future: Migrate to WiX** (Production)
- Industry standard
- Better upgrade handling
- Full MSI features

**Want me to create the installer project for you?** Just let me know which approach you prefer!

---

**Files Needed:**
- [ ] jellyfin.ico (icon file)
- [ ] License.rtf (license text)
- [ ] README.md (user instructions)
- [ ] PostgreSQL setup guide

**Estimated Time:**
- Inno Setup: 30 minutes
- WiX Setup: 2-4 hours
- MSIX: 1-2 hours

Let me know which installer type you'd like me to help you implement!
