8f860a8ec3
- All DLLs now output to lib\[Configuration]\[TargetFramework]\ at repo root (see Directory.Build.props) - .gitignore updated to exclude /lib/ - On first run, startup.json is auto-generated with OS-appropriate default paths (Windows, Linux, macOS, or portable) - Removes null/example config; generated config is immediately usable and clearly documented - Extensive new documentation: build output, startup.json logic, visual guides, and code proofs - Publish profile now deletes existing files for clean deploys - No breaking changes: existing startup.json files are preserved - Improves first-run UX, deployment, and cross-platform consistency
416 lines
13 KiB
Markdown
416 lines
13 KiB
Markdown
# How startup.json is Loaded and Used on Startup
|
|
|
|
## YES - The code DOES check startup.json on startup! ✅
|
|
|
|
Here's the complete flow:
|
|
|
|
---
|
|
|
|
## Startup Flow Diagram
|
|
|
|
```
|
|
┌─────────────────────────────────────────────┐
|
|
│ 1. Jellyfin Starts │
|
|
│ Program.Main() → StartApp() │
|
|
└──────────────────┬──────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────┐
|
|
│ 2. CreateApplicationPaths() │
|
|
│ (in StartupHelpers.cs) │
|
|
└──────────────────┬──────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────┐
|
|
│ 3. LoadStartupConfiguration() │
|
|
│ Searches for startup.json in: │
|
|
│ • Current directory │
|
|
│ • AppContext.BaseDirectory │
|
|
│ • AppContext.BaseDirectory/config │
|
|
└──────────────────┬──────────────────────────┘
|
|
│
|
|
┌─────────┴─────────┐
|
|
│ │
|
|
Found? Yes No
|
|
│ │
|
|
▼ ▼
|
|
┌──────────────────┐ ┌───────────────────────┐
|
|
│ 4a. Load JSON │ │ 4b. Create Default │
|
|
│ Return config│ │ (OS-specific) │
|
|
└────────┬─────────┘ └───────────┬───────────┘
|
|
│ │
|
|
└────────────┬───────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────┐
|
|
│ 5. Resolve Paths (Priority Order): │
|
|
│ 1. Command-line args │
|
|
│ 2. Environment variables │
|
|
│ 3. startup.json values ← HERE! │
|
|
│ 4. Built-in defaults │
|
|
└──────────────────┬──────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────┐
|
|
│ 6. Create ServerApplicationPaths object │
|
|
│ with resolved paths │
|
|
└─────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Code Flow Details
|
|
|
|
### Step 1: Startup Entry Point
|
|
**File:** `Jellyfin.Server/Program.cs`
|
|
|
|
```csharp
|
|
// Line 88-90
|
|
private static async Task StartApp(StartupOptions options)
|
|
{
|
|
ServerApplicationPaths appPaths = StartupHelpers.CreateApplicationPaths(options);
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Step 2: Load Configuration
|
|
**File:** `Jellyfin.Server/Helpers/StartupHelpers.cs`
|
|
|
|
```csharp
|
|
// Line 222-223
|
|
public static ServerApplicationPaths CreateApplicationPaths(StartupOptions options)
|
|
{
|
|
// Try to load startup configuration from file
|
|
var startupConfig = LoadStartupConfiguration();
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Step 3: Search for startup.json
|
|
**File:** `Jellyfin.Server/Helpers/StartupHelpers.cs`
|
|
|
|
```csharp
|
|
// Lines 78-112
|
|
private static IConfigurationRoot? LoadStartupConfiguration()
|
|
{
|
|
const string ConfigFileName = "startup.json";
|
|
|
|
// Search locations in priority order
|
|
var searchPaths = new[]
|
|
{
|
|
Path.Combine(Directory.GetCurrentDirectory(), ConfigFileName),
|
|
Path.Combine(AppContext.BaseDirectory, ConfigFileName),
|
|
Path.Combine(AppContext.BaseDirectory, "config", ConfigFileName)
|
|
};
|
|
|
|
foreach (var configPath in searchPaths)
|
|
{
|
|
if (File.Exists(configPath))
|
|
{
|
|
try
|
|
{
|
|
var config = new ConfigurationBuilder()
|
|
.AddJsonFile(configPath, optional: false, reloadOnChange: false)
|
|
.Build();
|
|
|
|
Console.WriteLine($"Loaded startup configuration from: {configPath}");
|
|
return config;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Warning: Failed to load startup configuration from {configPath}: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
// No configuration file found - create a default one
|
|
CreateDefaultStartupConfiguration();
|
|
return null;
|
|
}
|
|
```
|
|
|
|
### Step 4: Use startup.json Values
|
|
**File:** `Jellyfin.Server/Helpers/StartupHelpers.cs`
|
|
|
|
Each path is resolved with this priority chain:
|
|
|
|
```csharp
|
|
// DataDir example (lines 230-234)
|
|
var dataDir = options.DataDir // 1. Command-line
|
|
?? Environment.GetEnvironmentVariable("JELLYFIN_DATA_DIR") // 2. Environment
|
|
?? startupConfig?.GetValue<string>("Paths:DataDir") // 3. startup.json ← HERE!
|
|
?? Path.Join( // 4. Built-in default
|
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
|
"jellyfin");
|
|
|
|
// ConfigDir (lines 236-248)
|
|
var configDir = options.ConfigDir
|
|
?? Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR")
|
|
?? startupConfig?.GetValue<string>("Paths:ConfigDir"); // ← startup.json checked!
|
|
|
|
// CacheDir (lines 250-260)
|
|
var cacheDir = options.CacheDir
|
|
?? Environment.GetEnvironmentVariable("JELLYFIN_CACHE_DIR")
|
|
?? startupConfig?.GetValue<string>("Paths:CacheDir"); // ← startup.json checked!
|
|
|
|
// LogDir (lines 289-293)
|
|
var logDir = options.LogDir
|
|
?? Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR")
|
|
?? startupConfig?.GetValue<string>("Paths:LogDir"); // ← startup.json checked!
|
|
|
|
// TempDir (lines 295-301)
|
|
var tempDir = options.TempDir
|
|
?? Environment.GetEnvironmentVariable("JELLYFIN_TEMP_DIR")
|
|
?? startupConfig?.GetValue<string>("Paths:TempDir"); // ← startup.json checked!
|
|
|
|
// WebDir (lines 262-287)
|
|
var webDir = options.WebDir
|
|
?? Environment.GetEnvironmentVariable("JELLYFIN_WEB_DIR")
|
|
?? startupConfig?.GetValue<string>("Paths:WebDir"); // ← startup.json checked!
|
|
```
|
|
|
|
---
|
|
|
|
## Search Locations (In Order)
|
|
|
|
When looking for `startup.json`, the code checks these locations:
|
|
|
|
1. **Current working directory**
|
|
- Example: `E:\Projects\pgsql-jellyfin\startup.json`
|
|
- This is where you run `jellyfin` from
|
|
|
|
2. **Application base directory**
|
|
- Example: `E:\Projects\pgsql-jellyfin\Jellyfin.Server\bin\Release\net11.0\startup.json`
|
|
- Where the jellyfin.exe/dll is located
|
|
|
|
3. **Config subdirectory**
|
|
- Example: `E:\Projects\pgsql-jellyfin\Jellyfin.Server\bin\Release\net11.0\config\startup.json`
|
|
- Allows organizing config files
|
|
|
|
---
|
|
|
|
## Priority Order for Path Resolution
|
|
|
|
For **each path** (DataDir, ConfigDir, etc.), the code checks in this order:
|
|
|
|
```
|
|
┌──────────────────────────────────────┐
|
|
│ 1. Command-line argument │ Highest Priority
|
|
│ --datadir /custom/path │ (Always wins)
|
|
└──────────────────┬───────────────────┘
|
|
│ If not provided
|
|
▼
|
|
┌──────────────────────────────────────┐
|
|
│ 2. Environment variable │
|
|
│ JELLYFIN_DATA_DIR=/custom/path │
|
|
└──────────────────┬───────────────────┘
|
|
│ If not set
|
|
▼
|
|
┌──────────────────────────────────────┐
|
|
│ 3. startup.json value │ ← YOUR QUESTION!
|
|
│ "DataDir": "/custom/path" │ YES, IT CHECKS HERE!
|
|
└──────────────────┬───────────────────┘
|
|
│ If null or missing
|
|
▼
|
|
┌──────────────────────────────────────┐
|
|
│ 4. Built-in OS-specific default │ Lowest Priority
|
|
│ (code determines based on OS) │ (Fallback)
|
|
└──────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Console Output
|
|
|
|
### When startup.json is Found
|
|
```
|
|
Loaded startup configuration from: E:\Projects\pgsql-jellyfin\startup.json
|
|
```
|
|
|
|
### When startup.json is Not Found
|
|
```
|
|
Created default startup configuration at: E:\Projects\pgsql-jellyfin\startup.json
|
|
Using Windows defaults - using C:/ProgramData/jellyfin
|
|
You can customize this file to set different paths for Jellyfin.
|
|
```
|
|
|
|
---
|
|
|
|
## Real-World Examples
|
|
|
|
### Example 1: Using startup.json
|
|
|
|
**Your startup.json:**
|
|
```json
|
|
{
|
|
"Paths": {
|
|
"DataDir": "D:/Media/Jellyfin/Data",
|
|
"ConfigDir": "D:/Media/Jellyfin/Config",
|
|
"CacheDir": "E:/Cache/Jellyfin",
|
|
"LogDir": "D:/Logs/Jellyfin"
|
|
}
|
|
}
|
|
```
|
|
|
|
**What happens on startup:**
|
|
```csharp
|
|
// For DataDir:
|
|
options.DataDir // null (not provided)
|
|
?? Environment.GetEnvironmentVariable(...) // null (not set)
|
|
?? startupConfig?.GetValue("Paths:DataDir") // "D:/Media/Jellyfin/Data" ← USED!
|
|
|
|
// Result: DataDir = "D:/Media/Jellyfin/Data"
|
|
```
|
|
|
|
**Console output:**
|
|
```
|
|
Loaded startup configuration from: E:\Projects\pgsql-jellyfin\startup.json
|
|
[INF] Data directory: D:/Media/Jellyfin/Data
|
|
[INF] Config directory: D:/Media/Jellyfin/Config
|
|
[INF] Cache directory: E:/Cache/Jellyfin
|
|
[INF] Log directory: D:/Logs/Jellyfin
|
|
```
|
|
|
|
### Example 2: Override with Command-Line
|
|
|
|
**Your startup.json:**
|
|
```json
|
|
{
|
|
"Paths": {
|
|
"DataDir": "D:/Media/Jellyfin/Data"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Command:**
|
|
```powershell
|
|
jellyfin --datadir "F:/CustomData"
|
|
```
|
|
|
|
**What happens:**
|
|
```csharp
|
|
options.DataDir // "F:/CustomData" ← USED! (highest priority)
|
|
?? Environment.GetEnvironmentVariable(...) // (not checked)
|
|
?? startupConfig?.GetValue("Paths:DataDir") // (not checked)
|
|
|
|
// Result: DataDir = "F:/CustomData"
|
|
```
|
|
|
|
### Example 3: Override with Environment Variable
|
|
|
|
**Your startup.json:**
|
|
```json
|
|
{
|
|
"Paths": {
|
|
"DataDir": "D:/Media/Jellyfin/Data"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Environment:**
|
|
```powershell
|
|
$env:JELLYFIN_DATA_DIR = "G:/EnvData"
|
|
jellyfin
|
|
```
|
|
|
|
**What happens:**
|
|
```csharp
|
|
options.DataDir // null
|
|
?? Environment.GetEnvironmentVariable(...) // "G:/EnvData" ← USED!
|
|
?? startupConfig?.GetValue("Paths:DataDir") // (not checked)
|
|
|
|
// Result: DataDir = "G:/EnvData"
|
|
```
|
|
|
|
---
|
|
|
|
## Testing the Behavior
|
|
|
|
### Test 1: Verify startup.json is Loaded
|
|
```powershell
|
|
# Create a test startup.json
|
|
@"
|
|
{
|
|
"Paths": {
|
|
"DataDir": "E:/TEST_JELLYFIN_DATA",
|
|
"LogDir": "E:/TEST_JELLYFIN_LOGS"
|
|
}
|
|
}
|
|
"@ | Out-File startup.json
|
|
|
|
# Run Jellyfin (it will create these directories and use them)
|
|
.\jellyfin.exe
|
|
|
|
# Check console output - should see:
|
|
# "Loaded startup configuration from: E:\Projects\pgsql-jellyfin\startup.json"
|
|
```
|
|
|
|
### Test 2: Verify Priority Order
|
|
```powershell
|
|
# Set in startup.json
|
|
echo '{"Paths":{"DataDir":"E:/FROM_JSON"}}' > startup.json
|
|
|
|
# Override with environment variable
|
|
$env:JELLYFIN_DATA_DIR = "E:/FROM_ENV"
|
|
|
|
# Run Jellyfin
|
|
.\jellyfin.exe
|
|
|
|
# Result: Will use E:/FROM_ENV (environment wins over startup.json)
|
|
```
|
|
|
|
### Test 3: Verify Search Locations
|
|
```powershell
|
|
# Put startup.json in current directory
|
|
echo '{"Paths":{"DataDir":"E:/CURRENT_DIR"}}' > startup.json
|
|
|
|
# Also put one in base directory
|
|
echo '{"Paths":{"DataDir":"E:/BASE_DIR"}}' > .\bin\Release\net11.0\startup.json
|
|
|
|
# Run from current directory
|
|
.\jellyfin.exe
|
|
|
|
# Result: Uses E:/CURRENT_DIR (current directory checked first)
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
### ✅ YES - startup.json IS Checked on Startup!
|
|
|
|
**When:** During application initialization in `CreateApplicationPaths()`
|
|
|
|
**Where:** Searches 3 locations (current dir, base dir, config subdir)
|
|
|
|
**Priority:** 3rd in the resolution chain:
|
|
1. Command-line args (highest)
|
|
2. Environment variables
|
|
3. **startup.json** ← HERE
|
|
4. Built-in defaults (lowest)
|
|
|
|
**What's Read:** All `Paths:*` values:
|
|
- `Paths:DataDir`
|
|
- `Paths:ConfigDir`
|
|
- `Paths:CacheDir`
|
|
- `Paths:LogDir`
|
|
- `Paths:TempDir`
|
|
- `Paths:WebDir`
|
|
|
|
**Confirmation:** Console output shows "Loaded startup configuration from: [path]" when found
|
|
|
|
---
|
|
|
|
## Code References
|
|
|
|
| Function | File | Purpose |
|
|
|----------|------|---------|
|
|
| `StartApp()` | Program.cs:88 | Entry point |
|
|
| `CreateApplicationPaths()` | StartupHelpers.cs:222 | Path resolution |
|
|
| `LoadStartupConfiguration()` | StartupHelpers.cs:78 | Loads startup.json |
|
|
| `CreateDefaultStartupConfiguration()` | StartupHelpers.cs:116 | Creates default if missing |
|
|
|
|
---
|
|
|
|
**Verified:** The code absolutely checks and uses startup.json on every startup! ✅
|