├── .gitignore ├── README.md └── PlexMissingEpisodes.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | # VS Code 2 | .vscode/* 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PlexMissingEpisodes 2 | 3 | A PowerShell script that identifies missing TV show episodes in your Plex library by comparing against TheTVDB's episode database. 4 | 5 | ## Features 6 | 7 | - **Automatic Discovery**: Scans all TV show libraries in Plex without manual section configuration 8 | - **TheTVDB Integration**: Uses TheTVDB API v4 for accurate episode data 9 | - **Smart Episode Detection**: Handles multi-episode files and episode ranges (e.g., "S02E01-02") 10 | - **Flexible Configuration**: Configure via command-line parameters or script variables 11 | - **Dual Output Formats**: 12 | - **Standard**: Detailed reports with summaries and grouping 13 | - **Simple**: Clean one-line format perfect for searching and automation 14 | - **Flexible Output**: Console output with colors OR save directly to file with timestamp 15 | - **Flexible Filtering**: Process all shows, ignore specific shows, or focus on a single show 16 | - **Year Detection**: Automatically includes show year when available from Plex metadata 17 | - **Detailed Reporting**: Shows missing episodes organized by season with episode names 18 | - **Automation Friendly**: No user prompts when saving to file, perfect for scheduled tasks 19 | - **Metadata Validation**: Reports episodes with missing season/episode numbers 20 | - **MFA Support**: Supports authentication via Plex Token for users with Multi-Factor Authentication enabled 21 | - **Comprehensive Error Handling**: Robust authentication and API error management 22 | 23 | ## Quick Start 24 | 25 | 1. **Get TheTVDB API Key**: Register at [TheTVDB.com](https://thetvdb.com/api-information) and obtain your API key 26 | 2. **Configure Script**: Either edit the configuration variables in `PlexMissingEpisodes.ps1` OR pass parameters when running 27 | 3. **Run Script**: Execute the PowerShell script with or without parameters 28 | 29 | ## Configuration 30 | 31 | You can configure the script in two ways: 32 | 33 | ### Method 1: Command Line Parameters (Recommended) 34 | 35 | Pass configuration as parameters when running the script: 36 | 37 | ```powershell 38 | ./PlexMissingEpisodes.ps1 -ApiKey "your-api-key" -PlexServer "http://your-server:32400" -PlexUsername "username" -PlexPassword "password" 39 | ``` 40 | 41 | ### Method 2: Edit Script Variables 42 | 43 | Edit the default values in the script's configuration section (lines 45-67). Parameters will override these defaults when provided: 44 | 45 | ```powershell 46 | # TheTVDB Authentication - update the fallback values 47 | $TheTVDBAuthentication = @{ 48 | "apikey" = if ($ApiKey) { $ApiKey } else { "your-api-key-here" } 49 | "pin" = if ($Pin) { $Pin } else { "" } # Optional: Only needed for subscriber keys 50 | } 51 | 52 | # Plex Server Settings - update the fallback values 53 | if (-not $PlexServer) { $PlexServer = "http://your-plex-server:32400" } 54 | if (-not $PlexUsername) { $PlexUsername = 'your-username' } 55 | if (-not $PlexPassword) { $PlexPassword = 'your-password' } 56 | 57 | # Filtering Options - update the fallback values 58 | # Array of show names to ignore, example included 59 | $IgnoreList = if ($IgnoreShows) { $IgnoreShows } else { 60 | [system.collections.generic.list[string]]::new( 61 | # "Jeopardy!" 62 | ) 63 | } 64 | if (-not $SingleShowFilter) { $SingleShowFilter = "" } 65 | ``` 66 | 67 | **Note**: Parameters take precedence over script variables, allowing you to save defaults in the script while overriding specific values as needed. 68 | 69 | ## Parameters 70 | 71 | | Parameter | Type | Description | Example | 72 | |-----------|------|-------------|---------| 73 | | `-ApiKey` | String | TheTVDB API key (required) | `-ApiKey "abc123"` | 74 | | `-Pin` | String | TheTVDB Subscriber PIN (optional) | `-Pin "1234"` | 75 | | `-PlexServer` | String | Plex server URL | `-PlexServer "http://server:32400"` | 76 | | `-PlexUsername` | String | Plex username | `-PlexUsername "myuser"` | 77 | | `-PlexPassword` | String | Plex password | `-PlexPassword "mypass"` | 78 | | `-PlexToken` | String | Plex authentication token (bypasses login/MFA) | `-PlexToken "abc123xyz"` | 79 | | `-SingleShowFilter` | String | Process only this show (partial matching) | `-SingleShowFilter "Breaking Bad"` | 80 | | `-IgnoreShows` | String[] | Shows to ignore (comma-separated) | `-IgnoreShows "Show1","Show2"` | 81 | | `-OutputFile` | String | Save results to file instead of console | `-OutputFile "missing.txt"` | 82 | | `-SimpleOutput` | Switch | Use simple one-line format for easy searching | `-SimpleOutput` | 83 | 84 | ## Usage Examples 85 | 86 | ### Basic Usage (using script defaults) 87 | 88 | ```powershell 89 | ./PlexMissingEpisodes.ps1 90 | ``` 91 | 92 | ### With Parameters 93 | 94 | ```powershell 95 | # Minimal parameters (others use script defaults) 96 | ./PlexMissingEpisodes.ps1 -ApiKey "your-tvdb-api-key" 97 | 98 | # Full configuration via parameters 99 | ./PlexMissingEpisodes.ps1 -ApiKey "your-api-key" -PlexServer "http://server:32400" -PlexUsername "user" -PlexPassword "pass" 100 | 101 | # Using Plex Token (Required if MFA is enabled) 102 | ./PlexMissingEpisodes.ps1 -ApiKey "your-api-key" -PlexServer "http://server:32400" -PlexToken "your-plex-token" 103 | 104 | # Focus on specific show 105 | ./PlexMissingEpisodes.ps1 -SingleShowFilter "Breaking Bad" 106 | 107 | # Ignore multiple shows 108 | ./PlexMissingEpisodes.ps1 -IgnoreShows "Jeopardy!","Daily Show","News" 109 | 110 | # Save output directly to file (no "Press any key" prompt) 111 | ./PlexMissingEpisodes.ps1 -OutputFile "missing-episodes.txt" 112 | 113 | # Use simple output format for easy searching 114 | ./PlexMissingEpisodes.ps1 -SimpleOutput 115 | 116 | # Combine simple output with file saving 117 | ./PlexMissingEpisodes.ps1 -SimpleOutput -OutputFile "missing-simple.txt" 118 | ``` 119 | 120 | ### Save Output to File 121 | 122 | ```powershell 123 | # Using the OutputFile parameter (recommended - includes timestamp and no user prompt) 124 | ./PlexMissingEpisodes.ps1 -OutputFile "missing-episodes.txt" 125 | 126 | # Save simple format for automation/processing 127 | ./PlexMissingEpisodes.ps1 -SimpleOutput -OutputFile "missing-simple.txt" 128 | 129 | # Using shell redirection (alternative method) 130 | ./PlexMissingEpisodes.ps1 -ApiKey "your-key" | Out-File "missing-episodes.txt" 131 | ``` 132 | 133 | ### Get Help 134 | 135 | ```powershell 136 | Get-Help ./PlexMissingEpisodes.ps1 -Detailed 137 | ``` 138 | 139 | ## Requirements 140 | 141 | - **PowerShell 5.1+** 142 | - **Network Access**: Internet connection for TheTVDB API and Plex server access 143 | - **TheTVDB API Key**: Free registration required 144 | - **Plex Credentials**: Username/password or configure IP allowlist in Plex settings 145 | 146 | ## Output Formats 147 | 148 | The script provides two output formats: 149 | 150 | ### Standard Format (Default) 151 | 152 | - **Progress Tracking**: Real-time updates during library scanning 153 | - **Missing Episode Report**: Detailed breakdown by show and season 154 | - **Metadata Warnings**: Alerts for episodes with missing information 155 | - **Summary Statistics**: Total count of missing episodes 156 | 157 | ```text 158 | === MISSING EPISODES REPORT === 159 | Show Name 160 | Total missing episodes: 5 161 | Missing episodes by season: 162 | Season 1: 2 episodes 163 | S01E03 - Episode Title 164 | S01E07 - Another Episode 165 | Season 2: 3 episodes 166 | S02E01 - Season Premiere 167 | ... 168 | ``` 169 | 170 | ### Simple Format (`-SimpleOutput`) 171 | 172 | Perfect for automation, searching, and scripting with clean one-line entries: 173 | 174 | ```text 175 | What We Do in the Shadows (2019) - S03E06 - The Escape 176 | Breaking Bad (2008) - S02E03 - Bit by a Dead Bee 177 | The Office (2005) - S05E14 - Lecture Circuit: Part 1 178 | ``` 179 | 180 | **Format**: `Show Name (Year) - SXXEXX - Episode Title` 181 | 182 | - Includes year when available from Plex metadata 183 | - One line per missing episode 184 | - Easy to grep, sort, and process with other tools 185 | 186 | ## Practical Examples 187 | 188 | ### Scenario 1: First-time Setup 189 | 190 | If you're running the script for the first time and want to override the default credentials: 191 | 192 | ```powershell 193 | ./PlexMissingEpisodes.ps1 -ApiKey "your-tvdb-key" -PlexServer "http://192.168.1.100:32400" -PlexUsername "admin" -PlexPassword "securepass" 194 | ``` 195 | 196 | ### Scenario 2: Check Specific Show 197 | 198 | To quickly check missing episodes for a specific show without editing the script: 199 | 200 | ```powershell 201 | ./PlexMissingEpisodes.ps1 -SingleShowFilter "Stranger Things" 202 | ``` 203 | 204 | ### Scenario 3: Exclude Multiple Shows 205 | 206 | To run the check while ignoring several shows that you know have issues: 207 | 208 | ```powershell 209 | ./PlexMissingEpisodes.ps1 -IgnoreShows "The Daily Show","Saturday Night Live","Jeopardy!" 210 | ``` 211 | 212 | ### Scenario 4: Save Configuration, Override Specific Values 213 | 214 | Keep your default settings in the script but override just the show filter: 215 | 216 | ```powershell 217 | # Script has your default Plex/TVDB settings, but you want to check one show 218 | ./PlexMissingEpisodes.ps1 -SingleShowFilter "The Office" 219 | ``` 220 | 221 | ### Scenario 5: Simple Output for Easy Searching 222 | 223 | Use simple output format for automation and easy searching: 224 | 225 | ```powershell 226 | # Get simple output for all shows 227 | ./PlexMissingEpisodes.ps1 -SimpleOutput 228 | 229 | # Search for specific shows in simple output 230 | ./PlexMissingEpisodes.ps1 -SimpleOutput | grep "Breaking Bad" 231 | 232 | # Count missing episodes per show 233 | ./PlexMissingEpisodes.ps1 -SimpleOutput | cut -d' ' -f1-3 | sort | uniq -c 234 | 235 | # Save simple output to file for processing 236 | ./PlexMissingEpisodes.ps1 -SimpleOutput -OutputFile "missing-simple.txt" 237 | ``` 238 | 239 | ### Scenario 6: Automated Reports 240 | 241 | For scheduled tasks or automation where you don't want user interaction: 242 | 243 | ```powershell 244 | # Save to timestamped file automatically (no "Press any key" prompt) 245 | ./PlexMissingEpisodes.ps1 -OutputFile "reports/missing-episodes-$(Get-Date -Format 'yyyy-MM-dd').txt" 246 | 247 | # Run specific show check and save results 248 | ./PlexMissingEpisodes.ps1 -SingleShowFilter "Game of Thrones" -OutputFile "got-missing.txt" 249 | 250 | # Get simple format for automated processing 251 | ./PlexMissingEpisodes.ps1 -SimpleOutput -OutputFile "missing-simple.txt" 252 | ``` 253 | 254 | ## Troubleshooting 255 | 256 | - **Authentication Errors**: Verify TheTVDB API key and Plex credentials 257 | - **Network Issues**: Check Plex server URL and network connectivity 258 | - **Missing Shows**: Ensure shows have proper TVDB metadata in Plex 259 | - **Console Closes**: Run from PowerShell window or use output redirection 260 | - **Parameter Issues**: Use `Get-Help ./PlexMissingEpisodes.ps1` to see parameter syntax 261 | -------------------------------------------------------------------------------- /PlexMissingEpisodes.ps1: -------------------------------------------------------------------------------- 1 | # ============================================================================ 2 | # MIGRATED TO TheTVDB API v4 3 | # ============================================================================ 4 | # Key changes from v3 to v4: 5 | # - Authentication: Only requires 'apikey', optional 'pin' for user keys 6 | # - Base URL: https://api4.thetvdb.com/v4/ 7 | # - Episode endpoints: /series/{id}/episodes/default with pagination 8 | # - Field mapping: seasonNumber (was airedSeason), number (was airedEpisodeNumber), 9 | # name (was episodeName), aired (was firstAired) 10 | # - Token response: data.token (was token) 11 | # - Episode data: data.episodes (was data) 12 | # ============================================================================ 13 | 14 | [CmdletBinding()] 15 | param( 16 | [Parameter(HelpMessage = "TheTVDB API Key")] 17 | [string]$ApiKey, 18 | 19 | [Parameter(HelpMessage = "TheTVDB Subscriber PIN (optional)")] 20 | [string]$Pin, 21 | 22 | [Parameter(HelpMessage = "Plex Server URL (e.g., http://server:32400)")] 23 | [string]$PlexServer, 24 | 25 | [Parameter(HelpMessage = "Plex Username")] 26 | [string]$PlexUsername, 27 | 28 | [Parameter(HelpMessage = "Plex Password")] 29 | [string]$PlexPassword, 30 | 31 | [Parameter(HelpMessage = "Plex Authentication Token (optional - bypasses login)")] 32 | [string]$PlexToken, 33 | 34 | [Parameter(HelpMessage = "Single show filter - process only this show (supports partial matching)")] 35 | [string]$SingleShowFilter, 36 | 37 | [Parameter(HelpMessage = "Comma-separated list of show names to ignore")] 38 | [string[]]$IgnoreShows, 39 | 40 | [Parameter(HelpMessage = "Output file path to save results (optional - if not specified, output goes to console)")] 41 | [string]$OutputFile, 42 | 43 | [Parameter(HelpMessage = "Use simple output format: Show Name (Year) - SXXEXX - Title")] 44 | [switch]$SimpleOutput 45 | ) 46 | 47 | # ============================================================================ 48 | # Configuration Variables (can be overridden by parameters) 49 | # ============================================================================ 50 | 51 | # TheTVDB Authentication Information for API v4 52 | # API Key is required, PIN is optional (only needed for user-supported keys) 53 | $TheTVDBAuthentication = @{ 54 | "apikey" = if ($ApiKey) { $ApiKey } else { "" } 55 | "pin" = if ($Pin) { $Pin } else { "" } # Optional Subscriber PIN (only needed for user-supported keys) 56 | } 57 | 58 | # Plex Server Information - use parameters if provided, otherwise fall back to defaults 59 | if (-not $PlexServer) { $PlexServer = "" } 60 | if (-not $PlexUsername) { $PlexUsername = '' } 61 | if (-not $PlexPassword) { $PlexPassword = '' } 62 | 63 | # Array of show names to ignore, example included 64 | $IgnoreList = if ($IgnoreShows -and $IgnoreShows.Count -gt 0) { 65 | [system.collections.generic.list[string]]::new($IgnoreShows) 66 | } else { 67 | [system.collections.generic.list[string]]::new() 68 | } 69 | 70 | # Single show filter - if specified, only this show will be processed (supports partial matching) 71 | # Leave empty to process all shows (subject to IgnoreList) 72 | # Examples: "Jeopardy", "The Office", "Breaking Bad" 73 | if (-not $SingleShowFilter) { $SingleShowFilter = "" } 74 | 75 | # Function to parse episode ranges (e.g., "S02E01-02" -> episodes 1 and 2) 76 | function Get-EpisodeNumbers { 77 | param([string]$EpisodeString) 78 | 79 | $Episodes = @() 80 | 81 | # Handle range format like "S02E01-02" or "S02E01-E02" 82 | if ($EpisodeString -match 'S\d+E(\d+)[-]E?(\d+)') { 83 | $StartEp = [int]$matches[1] 84 | $EndEp = [int]$matches[2] 85 | for ($i = $StartEp; $i -le $EndEp; $i++) { 86 | $Episodes += $i 87 | } 88 | } 89 | # Handle single episode format like "S02E01" 90 | elseif ($EpisodeString -match 'S\d+E(\d+)') { 91 | $Episodes += [int]$matches[1] 92 | } 93 | 94 | return $Episodes 95 | } 96 | 97 | # Function to ensure console stays open when run directly 98 | function Wait-ForUserInput { 99 | # Clear any remaining progress bars 100 | Write-Progress -Activity "Completed" -Completed 101 | Write-Host "`nPress any key to exit..." -ForegroundColor Green 102 | $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") 103 | } 104 | 105 | # Set error action preference to stop on errors 106 | $ErrorActionPreference = "Stop" 107 | 108 | # Ignore Plex Certificate Issues 109 | if ($PlexServer -match "https") { 110 | Add-Type "using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } }" 111 | [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy 112 | } 113 | 114 | # Validate required configuration 115 | if ([string]::IsNullOrWhiteSpace($TheTVDBAuthentication.apikey)) { 116 | Write-Host -ForegroundColor Red "ERROR: TheTVDB API key is required. Please fill in the apikey." 117 | if ($Host.Name -eq "ConsoleHost" -and [Environment]::UserInteractive) { 118 | Wait-ForUserInput 119 | } 120 | exit 1 121 | } 122 | 123 | if ([string]::IsNullOrWhiteSpace($PlexServer)) { 124 | Write-Host -ForegroundColor Red "ERROR: Plex Server URL is required." 125 | if ($Host.Name -eq "ConsoleHost" -and [Environment]::UserInteractive) { 126 | Wait-ForUserInput 127 | } 128 | exit 1 129 | } 130 | 131 | # Main execution wrapped in try-catch 132 | try { 133 | Write-Host "Starting Plex Missing Episodes Check..." -ForegroundColor Green 134 | 135 | # Try to authenticate with TheTVDB API v4 to get a token 136 | try { 137 | # Prepare the authentication payload - remove pin if empty 138 | $authPayload = @{ "apikey" = $TheTVDBAuthentication.apikey } 139 | if (-not [string]::IsNullOrWhiteSpace($TheTVDBAuthentication.pin)) { 140 | $authPayload["pin"] = $TheTVDBAuthentication.pin 141 | } 142 | 143 | $TheTVDBToken = (Invoke-RestMethod -Uri "https://api4.thetvdb.com/v4/login" -Method Post -Body ($authPayload | ConvertTo-Json) -ContentType 'application/json').data.token 144 | Write-Host "Successfully authenticated with TheTVDB API v4" -ForegroundColor Green 145 | } 146 | catch { 147 | Write-Host -ForegroundColor Red "Failed to get TheTVDB API Token:" 148 | Write-Host -ForegroundColor Red $_ 149 | throw 150 | } 151 | 152 | # Create TheTVDB API Headers 153 | $TVDBHeaders = [System.Collections.Generic.Dictionary[[String], [String]]]::new() 154 | $TVDBHeaders.Add("Accept", "application/json") 155 | $TVDBHeaders.Add("Authorization", "Bearer $TheTVDBToken") 156 | 157 | # Create Plex Headers 158 | $PlexHeaders = [System.Collections.Generic.Dictionary[[String], [String]]]::new() 159 | $PlexHeaders.Add("X-Plex-Client-Identifier", "MissingTVEpisodes") 160 | $PlexHeaders.Add("X-Plex-Product", "PowerShell") 161 | $PlexHeaders.Add("X-Plex-Version", "V1") 162 | 163 | if (-not [string]::IsNullOrWhiteSpace($PlexToken)) { 164 | $PlexHeaders.Add("X-Plex-Token", $PlexToken) 165 | Write-Host "Using provided Plex Token" -ForegroundColor Green 166 | } 167 | else { 168 | $PlexHeaders.Add("Authorization", "Basic $([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$PlexUsername`:$PlexPassword")))") 169 | 170 | # Try to get Plex Token 171 | try { 172 | $PlexToken = (Invoke-RestMethod -Uri 'https://plex.tv/users/sign_in.json' -Method Post -Headers $PlexHeaders).user.authToken 173 | $PlexHeaders.Add("X-Plex-Token", $PlexToken) 174 | [void]$PlexHeaders.Remove("Authorization") 175 | Write-Host "Successfully authenticated with Plex" -ForegroundColor Green 176 | } 177 | catch { 178 | Write-Host -ForegroundColor Red "Failed to get Plex Auth Token:" 179 | Write-Host -ForegroundColor Red $_ 180 | throw 181 | } 182 | } 183 | 184 | # Try to get the Library IDs for TV Shows 185 | try { 186 | $TVKeys = ((Invoke-RestMethod -Uri "$PlexServer/library/sections" -Headers $PlexHeaders).MediaContainer.Directory | Where-Object type -eq "show").key 187 | Write-Host "Found $($TVKeys.Count) TV library section(s)" -ForegroundColor Green 188 | } 189 | catch { 190 | Write-Host -ForegroundColor Red "Failed to get Plex Library Sections:" 191 | if ($_.Exception.Response.StatusDescription -eq "Unauthorized") { 192 | Write-Host -ForegroundColor Red "Ensure that your source IP is configured under the 'List of IP addresses and networks that are allowed without auth' setting" 193 | } 194 | else { 195 | Write-Host -ForegroundColor Red $_ 196 | } 197 | throw 198 | } 199 | 200 | # Get all RatingKeys 201 | $RatingKeys = [System.Collections.Generic.List[int]]::new() 202 | $ProcessedShowCount = 0 203 | ForEach ($TVKey in $TVKeys) { 204 | $SeriesInfo = (Invoke-RestMethod -Uri "$PlexServer/library/sections/$TVKey/all/" -Headers $PlexHeaders).MediaContainer.Directory 205 | ForEach ($Series in $SeriesInfo) { 206 | $ShouldProcess = $false 207 | 208 | # Check if we should process this show 209 | if (-not [string]::IsNullOrWhiteSpace($SingleShowFilter)) { 210 | # Single show mode - only process shows matching the filter 211 | if ($Series.title -like "*$SingleShowFilter*") { 212 | $ShouldProcess = $true 213 | Write-Host "Found matching show: '$($Series.title)'" -ForegroundColor Cyan 214 | } 215 | } 216 | else { 217 | # Normal mode - process all shows except those in ignore list 218 | if ($IgnoreList -eq $null -or -not $IgnoreList.Contains($Series.title)) { 219 | $ShouldProcess = $true 220 | } 221 | } 222 | 223 | if ($ShouldProcess) { 224 | [void]$RatingKeys.Add($Series.ratingKey) 225 | $ProcessedShowCount++ 226 | } 227 | } 228 | } 229 | $RatingKeys = $RatingKeys | Sort-Object -Unique 230 | 231 | if (-not [string]::IsNullOrWhiteSpace($SingleShowFilter)) { 232 | Write-Host "Single show filter '$SingleShowFilter' - Found $($RatingKeys.Count) matching show(s) to process" -ForegroundColor Green 233 | } 234 | else { 235 | Write-Host "Found $($RatingKeys.Count) TV shows to process" -ForegroundColor Green 236 | } 237 | 238 | # Get all Show Data 239 | $PlexShows = @{ } 240 | $Progress = 0 241 | ForEach ($RatingKey in $RatingKeys) { 242 | $ShowData = (Invoke-RestMethod -Uri "$PlexServer/library/metadata/$RatingKey/" -Headers $PlexHeaders).MediaContainer.Directory 243 | $Progress++ 244 | 245 | # Ensure ShowData and title exist before using in Write-Progress 246 | if ($ShowData -and $ShowData.title) { 247 | Write-Progress -Activity "Collecting Show Data" -Status $ShowData.title -PercentComplete ($Progress / $RatingKeys.Count * 100) 248 | } 249 | else { 250 | Write-Progress -Activity "Collecting Show Data" -Status "Processing..." -PercentComplete ($Progress / $RatingKeys.Count * 100) 251 | } 252 | 253 | # Extract GUID - handle both old and new Plex agents 254 | $GUID = $null 255 | 256 | # Try to extract TVDB ID from new agent XML format 257 | try { 258 | if ($ShowData.InnerXml -match '