Add SQL query patterns documentation and Linux package build scripts

- Created `TV_SHOWS_SQL_QUERY_PATTERNS.md` to document SQL query patterns for TV shows, including performance issues and missing indexes.
- Added `README.md` for Linux package building, detailing steps for creating Debian and Red Hat packages.
- Implemented build scripts for Debian and Red Hat, including service files and post-installation hooks.
- Added necessary scripts for managing Jellyfin service lifecycle on both Debian and Red Hat systems.
- Included package specifications and installation instructions for both distributions.
This commit is contained in:
2026-07-14 10:32:56 -04:00
parent e51d3577ce
commit 8914f4dce9
29 changed files with 4123 additions and 31 deletions
+262
View File
@@ -0,0 +1,262 @@
# Linux Package Building
This directory contains scripts and templates for building Jellyfin Linux packages for both Debian and Red Hat-based distributions.
## Quick Start
### Build Both Packages
```bash
cd /home/wjones/projects/pgsql-jellyfin
chmod +x build-linux-packages.sh
./build-linux-packages.sh
```
Packages will be created in `build/packages/`.
### Build Specific Distribution
```bash
# Debian only
./build-linux-packages.sh --deb-only
# Red Hat only
./build-linux-packages.sh --rpm-only
# Specific version
./build-linux-packages.sh --version 11.0.0
```
## Directory Structure
```
scripts/
├── build-linux-packages.sh # Main automated build script
├── jellyfin.spec # Red Hat SPEC file template
├── debian/
│ ├── postinst.sh # Post-install hook (Debian)
│ ├── prerm.sh # Pre-remove hook (Debian)
│ ├── postrm.sh # Post-remove hook (Debian)
│ └── jellyfin.service # Systemd service file (Debian)
└── redhat/
├── post.sh # Post-install hook (Red Hat)
├── preun.sh # Pre-uninstall hook (Red Hat)
├── postun.sh # Post-uninstall hook (Red Hat)
└── jellyfin.service # Systemd service file (Red Hat)
```
## Prerequisites
### All Systems
- .NET SDK (for publishing the application)
- `fpm` (Effing Package Manager) for cross-distribution building
### Install FPM
**Debian/Ubuntu:**
```bash
sudo apt-get install ruby-dev
sudo gem install fpm
```
**Red Hat/CentOS/Fedora:**
```bash
sudo dnf install ruby-devel
sudo gem install fpm
```
## Package Specifications
### Publishing Parameters
Both distributions use identical .NET publishing parameters:
- **Runtime**: `linux-x64` (64-bit Linux)
- **Configuration**: `Release` (optimized build)
- **Self-contained**: `true` (includes .NET runtime)
- **Output location**: `/opt/jellyfin` (standard FHS compliant)
### Package Details
| Component | Debian | Red Hat |
|-----------|--------|---------|
| Extension | `.deb` | `.rpm` |
| Install command | `dpkg -i` / `apt install` | `dnf install` |
| Application dir | `/opt/jellyfin` | `/opt/jellyfin` |
| Config dir | `/etc/jellyfin` | `/etc/jellyfin` |
| Data dir | `/var/lib/jellyfin` | `/var/lib/jellyfin` |
| Log dir | `/var/log/jellyfin` | `/var/log/jellyfin` |
| Service file | `/etc/systemd/system/` | `/etc/systemd/system/` |
| User | `jellyfin` (system user) | `jellyfin` (system user) |
## Installation
### Debian
```bash
# Install package
sudo dpkg -i jellyfin-11.0.0-amd64.deb
sudo apt-get install -f # Fix dependencies if needed
# Start service
sudo systemctl start jellyfin
sudo systemctl status jellyfin
# View logs
sudo journalctl -u jellyfin -f
```
### Red Hat
```bash
# Install package
sudo dnf install jellyfin-11.0.0-1.x86_64.rpm
# Start service
sudo systemctl start jellyfin
sudo systemctl status jellyfin
# View logs
sudo journalctl -u jellyfin -f
```
## Verification
### Inspect Package Contents
**Debian:**
```bash
dpkg -c jellyfin-11.0.0-amd64.deb
```
**Red Hat:**
```bash
rpm -qpl jellyfin-11.0.0-1.x86_64.rpm
```
### Test Install/Remove Cycle
```bash
# Install
sudo dpkg -i jellyfin-11.0.0-amd64.deb # or dnf install for Red Hat
# Verify
sudo systemctl status jellyfin
# Remove
sudo dpkg -r jellyfin # or dnf remove for Red Hat
```
## Build Details
### What the Build Script Does
1. **Checks prerequisites** - Ensures .NET SDK and fpm are available
2. **Restores dependencies** - `dotnet restore Jellyfin.sln -r linux-x64`
3. **Builds solution** - `dotnet build Jellyfin.sln --configuration Release`
4. **Publishes application** - Creates self-contained linux-x64 binary
5. **Generates Debian package** - Creates `.deb` with hooks and metadata
6. **Generates Red Hat package** - Creates `.rpm` with hooks and metadata
7. **Reports completion** - Displays package paths and sizes
### Build Artifacts
Output location: `<project-root>/build/packages/`
- `jellyfin-11.0.0-amd64.deb` - Debian package
- `jellyfin-11.0.0-1.x86_64.rpm` - Red Hat package
## Hooks and Lifecycle
Both packages include installation and removal hooks:
### Installation
- Creates `jellyfin` system user if not exists
- Creates required directories (lib, log, cache)
- Sets proper ownership and permissions
- Enables and optionally starts systemd service
### Removal
- Stops systemd service
- Disables service from auto-start
- Removes `jellyfin` user and group
## Service Configuration
The included `jellyfin.service` file provides:
- **Auto-restart** on failure (5s delay)
- **Resource limits** (4GB max memory, 65k file descriptors)
- **Proper logging** (journald integration)
- **Directory configuration**:
- `--datadir=/var/lib/jellyfin` (configuration and metadata)
- `--logdir=/var/log/jellyfin` (application logs)
- `--cachedir=/var/cache/jellyfin` (temporary cache)
## Customization
### Change Version
Edit `SharedVersion.cs` or use build script flag:
```bash
./build-linux-packages.sh --version 12.0.0
```
### Modify Dependencies
Edit install scripts or update FPM dependencies in `build-linux-packages.sh`:
```bash
--depends "libssl3" \
--depends "libicu72" \
```
### Add Custom Hooks
Edit the respective hook scripts in `debian/` or `redhat/` directories.
## Documentation
For detailed information about the build process, see:
- [LINUX_PACKAGE_BUILD_GUIDE.md](../docs/LINUX_PACKAGE_BUILD_GUIDE.md) - Complete guide with examples
## Troubleshooting
### FPM not found
```bash
# Debian/Ubuntu
sudo apt-get install ruby-dev && sudo gem install fpm
# Red Hat/CentOS
sudo dnf install ruby-devel && sudo gem install fpm
```
### Missing dependencies during install
For Debian, run:
```bash
sudo apt-get install -f
```
For Red Hat, the package manager will automatically resolve.
### Permission denied when running script
```bash
chmod +x build-linux-packages.sh
```
### Check build details
Edit `build-linux-packages.sh` and remove `> /dev/null 2>&1` to see verbose output.
## Related Files
- **Build Script**: `build-linux-packages.sh`
- **SPEC Template**: `scripts/jellyfin.spec`
- **Debian Hooks**: `scripts/debian/{postinst,prerm,postrm}.sh`
- **Red Hat Hooks**: `scripts/redhat/{post,preun,postun}.sh`
- **Service Files**: Both distributions in `scripts/{debian,redhat}/jellyfin.service`
- **Complete Guide**: `docs/LINUX_PACKAGE_BUILD_GUIDE.md`
+31
View File
@@ -0,0 +1,31 @@
[Unit]
Description=Jellyfin Media Server
After=network.target
[Service]
Type=simple
User=jellyfin
Group=jellyfin
WorkingDirectory=/var/lib/jellyfin
# Run jellyfin with proper configuration directories
ExecStart=/opt/jellyfin/jellyfin \
--datadir=/var/lib/jellyfin \
--logdir=/var/log/jellyfin \
--cachedir=/var/cache/jellyfin
# Restart behavior
Restart=on-failure
RestartSec=5s
# Resource limits
LimitNOFILE=65536
MemoryMax=4G
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=jellyfin
[Install]
WantedBy=multi-user.target
+83
View File
@@ -0,0 +1,83 @@
#!/bin/bash
# Jellyfin postinst script - runs after package installation
set -e
# Create jellyfin user if it doesn't exist
if ! getent passwd jellyfin > /dev/null; then
useradd --system \
--home /var/lib/jellyfin \
--shell /bin/false \
--comment "Jellyfin Media Server" \
jellyfin
fi
# Create necessary directories
mkdir -p /var/lib/jellyfin
mkdir -p /var/log/jellyfin
mkdir -p /var/cache/jellyfin
mkdir -p /etc/jellyfin
# Set proper ownership and permissions
chown -R jellyfin:jellyfin /var/lib/jellyfin
chown -R jellyfin:jellyfin /var/log/jellyfin
chown -R jellyfin:jellyfin /var/cache/jellyfin
chown -R jellyfin:jellyfin /etc/jellyfin
chmod 755 /var/lib/jellyfin
chmod 755 /var/log/jellyfin
chmod 755 /var/cache/jellyfin
chmod 755 /etc/jellyfin
# Set permissions on the application directory
chown -R jellyfin:jellyfin /opt/jellyfin
chmod -R 755 /opt/jellyfin
# Create systemd service file if it doesn't exist
if [[ ! -f /etc/systemd/system/jellyfin.service ]]; then
cat > /etc/systemd/system/jellyfin.service << 'EOF'
[Unit]
Description=Jellyfin Media Server
After=network.target
[Service]
Type=simple
User=jellyfin
Group=jellyfin
WorkingDirectory=/var/lib/jellyfin
ExecStart=/opt/jellyfin/jellyfin --webdir=/opt/jellyfin/wwwroot --logdir=/var/log/jellyfin --datadir=/var/lib/jellyfin --cachedir=/var/cache/jellyfin
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
[Install]
WantedBy=multi-user.target
EOF
fi
# Reload systemd daemon
systemctl daemon-reload
# Enable and start the service
if [[ -f /etc/systemd/system/jellyfin.service ]]; then
systemctl enable jellyfin.service
systemctl restart jellyfin.service || true
fi
# Display post-installation message
echo ""
echo "=========================================="
echo "Jellyfin Media Server installed!"
echo "=========================================="
echo ""
echo "Service status: systemctl status jellyfin"
echo "View logs: journalctl -u jellyfin -f"
echo "Configuration: /etc/jellyfin/"
echo "Data: /var/lib/jellyfin/"
echo "Logs: /var/log/jellyfin/"
echo ""
echo "Access Jellyfin at: http://localhost:8096"
echo ""
exit 0
+34
View File
@@ -0,0 +1,34 @@
#!/bin/bash
set -e
# Jellyfin Post-Installation Script (Debian)
# Runs after package is installed
# Create jellyfin user/group if not exists
if ! id "jellyfin" &>/dev/null; then
echo "Creating jellyfin user and group..."
useradd -r -s /bin/false -d /var/lib/jellyfin jellyfin
fi
# Create necessary directories
mkdir -p /var/lib/jellyfin
mkdir -p /var/log/jellyfin
mkdir -p /etc/jellyfin
# Set ownership and permissions
chown -R jellyfin:jellyfin /opt/jellyfin
chown -R jellyfin:jellyfin /var/lib/jellyfin
chown -R jellyfin:jellyfin /var/log/jellyfin
chmod 755 /opt/jellyfin
chmod 750 /var/lib/jellyfin
chmod 750 /var/log/jellyfin
# Enable and start service if systemd is available
if command -v systemctl &> /dev/null; then
systemctl daemon-reload || true
systemctl enable jellyfin.service || true
echo "To start Jellyfin, run: sudo systemctl start jellyfin"
fi
echo "Jellyfin post-install complete!"
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
# Jellyfin postrm script - runs after package removal
set -e
# Remove systemd service file
if [[ -f /etc/systemd/system/jellyfin.service ]]; then
rm -f /etc/systemd/system/jellyfin.service
systemctl daemon-reload
fi
# Optional: Uncomment the lines below to completely remove user and data on uninstall
# WARNING: This will delete all Jellyfin data, libraries, and settings!
# Remove jellyfin user
# userdel jellyfin || true
# Remove data directories
# rm -rf /var/lib/jellyfin
# rm -rf /var/log/jellyfin
# rm -rf /var/cache/jellyfin
# rm -rf /etc/jellyfin
echo "Jellyfin Media Server uninstalled."
echo "Note: User data in /var/lib/jellyfin has been preserved."
echo "To completely remove all data, run:"
echo " sudo rm -rf /var/lib/jellyfin /var/log/jellyfin /var/cache/jellyfin /etc/jellyfin"
exit 0
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
# Jellyfin Post-Removal Script (Debian)
# Runs after package is removed
# Remove jellyfin user/group
if id "jellyfin" &>/dev/null; then
echo "Removing jellyfin user and group..."
userdel jellyfin || true
fi
echo "Jellyfin has been removed."
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
# Jellyfin prerm script - runs before package removal
set -e
# Stop the service
if [[ -f /etc/systemd/system/jellyfin.service ]]; then
systemctl stop jellyfin.service || true
systemctl disable jellyfin.service || true
fi
exit 0
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
set -e
# Jellyfin Pre-Removal Script (Debian)
# Runs before package is removed
# Stop service
if command -v systemctl &> /dev/null; then
echo "Stopping Jellyfin service..."
systemctl stop jellyfin.service || true
systemctl disable jellyfin.service || true
fi
+76
View File
@@ -0,0 +1,76 @@
Name: jellyfin
Version: 11.0.0
Release: 1%{?dist}
Summary: Jellyfin Media Server
License: GPL-2.0
URL: https://jellyfin.org
Source0: %{name}-%{version}.tar.gz
BuildArch: x86_64
BuildRequires: rpm-build
Requires: openssl-libs
Requires: libicu
Requires: fontconfig
Requires: systemd
%description
Jellyfin is a Free Software Media System that puts you in control of managing
and streaming your media. Run the Jellyfin server and access it from a web
browser, mobile app, media player, or other client application.
%prep
%setup -q
%build
# No build needed - we're packaging pre-built artifacts
%install
mkdir -p %{buildroot}/opt/jellyfin
mkdir -p %{buildroot}/var/lib/jellyfin
mkdir -p %{buildroot}/var/log/jellyfin
mkdir -p %{buildroot}/var/cache/jellyfin
mkdir -p %{buildroot}/etc/jellyfin
# Copy application files
cp -r . %{buildroot}/opt/jellyfin/
%pre
# Create jellyfin user/group if not exists
if ! id "jellyfin" &>/dev/null; then
useradd -r -s /bin/false -d /var/lib/jellyfin jellyfin
fi
%post
# Set ownership and permissions
chown -R jellyfin:jellyfin /opt/jellyfin
chown -R jellyfin:jellyfin /var/lib/jellyfin
chown -R jellyfin:jellyfin /var/log/jellyfin
chown -R jellyfin:jellyfin /var/cache/jellyfin
chmod 755 /opt/jellyfin
chmod 750 /var/lib/jellyfin
chmod 750 /var/log/jellyfin
chmod 750 /var/cache/jellyfin
# Enable and start service
systemctl daemon-reload || true
systemctl enable jellyfin.service || true
%preun
# Stop service before uninstall
systemctl stop jellyfin.service || true
systemctl disable jellyfin.service || true
%postun
# Remove user/group
userdel jellyfin || true
%files
/opt/jellyfin
%dir /var/lib/jellyfin
%dir /var/log/jellyfin
%dir /var/cache/jellyfin
%changelog
* Sun Jul 13 2026 Jellyfin Team <jellyfin@jellyfin.org> - 11.0.0-1
- Initial release
+156
View File
@@ -0,0 +1,156 @@
#!/bin/bash
# Jellyfin Debian Package Builder
# This script builds a .deb package from the current publishing configuration
# Usage: ./scripts/linux/build-deb-package.sh [--no-clean] [--output-dir PATH]
set -e
# Configuration
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
BUILD_TEMP_DIR=""
OUTPUT_DIR="${OUTPUT_DIR:-$PROJECT_ROOT/dist}"
CLEAN_AFTER=true
VERBOSE=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--no-clean)
CLEAN_AFTER=false
shift
;;
--output-dir)
OUTPUT_DIR="$2"
shift 2
;;
--verbose)
VERBOSE=true
shift
;;
*)
echo "Unknown option: $1"
echo "Usage: $0 [--no-clean] [--output-dir PATH] [--verbose]"
exit 1
;;
esac
done
# Functions
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*"
}
error() {
echo "[ERROR] $*" >&2
exit 1
}
cleanup() {
if [[ "$CLEAN_AFTER" == true ]] && [[ -n "$BUILD_TEMP_DIR" ]] && [[ -d "$BUILD_TEMP_DIR" ]]; then
log "Cleaning up temporary directory: $BUILD_TEMP_DIR"
rm -rf "$BUILD_TEMP_DIR"
fi
}
trap cleanup EXIT
# Extract version
log "Reading version from SharedVersion.cs..."
if [[ ! -f "$PROJECT_ROOT/SharedVersion.cs" ]]; then
error "SharedVersion.cs not found in $PROJECT_ROOT"
fi
VERSION=$(grep -oP 'Version\s*=\s*"\K[^"]+' "$PROJECT_ROOT/SharedVersion.cs")
if [[ -z "$VERSION" ]]; then
error "Could not extract version from SharedVersion.cs"
fi
log "Version: $VERSION"
# Create output directory
mkdir -p "$OUTPUT_DIR"
log "Output directory: $OUTPUT_DIR"
# Create temporary build directory
BUILD_TEMP_DIR=$(mktemp -d)
log "Temporary build directory: $BUILD_TEMP_DIR"
# Build and publish
cd "$PROJECT_ROOT"
log "Restoring dependencies..."
dotnet restore Jellyfin.sln -r linux-x64 > /dev/null 2>&1 || \
error "Failed to restore dependencies"
log "Building solution..."
dotnet build Jellyfin.sln --configuration Release > /dev/null 2>&1 || \
error "Failed to build solution"
log "Publishing Jellyfin.Server..."
if ! dotnet publish "Jellyfin.Server/Jellyfin.Server.csproj" \
--configuration Release \
--self-contained true \
--runtime linux-x64 \
--output "$BUILD_TEMP_DIR" > /dev/null 2>&1; then
error "Failed to publish Jellyfin.Server"
fi
# Verify published output
if [[ ! -f "$BUILD_TEMP_DIR/jellyfin" ]]; then
error "Published files not found in $BUILD_TEMP_DIR"
fi
log "Published files size: $(du -sh "$BUILD_TEMP_DIR" | cut -f1)"
# Create Debian package
PACKAGE_NAME="jellyfin-${VERSION}_amd64.deb"
PACKAGE_PATH="$OUTPUT_DIR/$PACKAGE_NAME"
log "Creating Debian package: $PACKAGE_NAME"
# Check if fpm is installed
if ! command -v fpm &> /dev/null; then
error "fpm is not installed. Install it with: sudo gem install fpm"
fi
# Build the package
if fpm \
-s dir \
-t deb \
-n jellyfin \
-v "$VERSION" \
-C "$BUILD_TEMP_DIR" \
-p "$PACKAGE_PATH" \
--license "LICENSE" \
--vendor "Jellyfin Contributors" \
--maintainer "Jellyfin Team <team@jellyfin.org>" \
--description "Jellyfin Media Server - a free software media server" \
--url "https://jellyfin.org" \
--architecture x86_64 \
--depends "libssl3" \
--depends "libicu72" \
--depends "libfontconfig1" \
--depends "libc6 (>= 2.31)" \
--before-remove "$PROJECT_ROOT/scripts/debian/prerm" \
--after-install "$PROJECT_ROOT/scripts/debian/postinst" \
--after-remove "$PROJECT_ROOT/scripts/debian/postrm" \
opt/=opt/jellyfin; then
log "Package created successfully!"
log "Package path: $PACKAGE_PATH"
log "Package size: $(du -sh "$PACKAGE_PATH" | cut -f1)"
# Display package information
log ""
log "Package Information:"
dpkg-deb -I "$PACKAGE_PATH" | sed 's/^/ /'
log ""
log "Installation command:"
log " sudo dpkg -i \"$PACKAGE_PATH\""
log ""
log "If dependencies are missing, run:"
log " sudo apt-get install -f"
else
error "Failed to create Debian package"
fi
+31
View File
@@ -0,0 +1,31 @@
[Unit]
Description=Jellyfin Media Server
After=network.target
[Service]
Type=simple
User=jellyfin
Group=jellyfin
WorkingDirectory=/var/lib/jellyfin
# Run jellyfin with proper configuration directories
ExecStart=/opt/jellyfin/jellyfin \
--datadir=/var/lib/jellyfin \
--logdir=/var/log/jellyfin \
--cachedir=/var/cache/jellyfin
# Restart behavior
Restart=on-failure
RestartSec=5s
# Resource limits
LimitNOFILE=65536
MemoryMax=4G
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=jellyfin
[Install]
WantedBy=multi-user.target
+37
View File
@@ -0,0 +1,37 @@
#!/bin/bash
set -e
# Jellyfin Post-Installation Script (Red Hat)
# Runs after package is installed
# Create jellyfin user/group if not exists
if ! id "jellyfin" &>/dev/null; then
echo "Creating jellyfin user and group..."
useradd -r -s /bin/false -d /var/lib/jellyfin jellyfin
fi
# Create necessary directories
mkdir -p /var/lib/jellyfin
mkdir -p /var/log/jellyfin
mkdir -p /var/cache/jellyfin
mkdir -p /etc/jellyfin
# Set ownership and permissions
chown -R jellyfin:jellyfin /opt/jellyfin
chown -R jellyfin:jellyfin /var/lib/jellyfin
chown -R jellyfin:jellyfin /var/log/jellyfin
chown -R jellyfin:jellyfin /var/cache/jellyfin
chmod 755 /opt/jellyfin
chmod 750 /var/lib/jellyfin
chmod 750 /var/log/jellyfin
chmod 750 /var/cache/jellyfin
# Enable and start service if systemd is available
if command -v systemctl &> /dev/null; then
systemctl daemon-reload || true
systemctl enable jellyfin.service || true
echo "To start Jellyfin, run: sudo systemctl start jellyfin"
fi
echo "Jellyfin post-install complete!"
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
# Jellyfin Post-Uninstall Script (Red Hat)
# Runs after package is removed
# Remove jellyfin user/group
if id "jellyfin" &>/dev/null; then
echo "Removing jellyfin user and group..."
userdel jellyfin || true
fi
echo "Jellyfin has been removed."
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
set -e
# Jellyfin Pre-Uninstall Script (Red Hat)
# Runs before package is removed
# Stop service
if command -v systemctl &> /dev/null; then
echo "Stopping Jellyfin service..."
systemctl stop jellyfin.service || true
systemctl disable jellyfin.service || true
fi