using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Jellyfin.Plugin.SRFPlay.Api; using Jellyfin.Plugin.SRFPlay.Api.Models; using Jellyfin.Plugin.SRFPlay.Configuration; using Jellyfin.Plugin.SRFPlay.Services; using Microsoft.Extensions.Logging; namespace Jellyfin.Plugin.SRFPlay.Tests { class Program { static async Task Main(string[] args) { // Run the new Play v3 API tests await TestPlayV3Api.RunTests(); Console.WriteLine("\n\n"); Console.WriteLine("================================================================="); Console.WriteLine("OLD API TESTS (DEPRECATED - Expected to fail)"); Console.WriteLine("=================================================================\n"); Console.WriteLine("=== SRFPlay Plugin Test Suite (Old API) ===\n"); // Setup logging using var loggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); builder.SetMinimumLevel(LogLevel.Warning); // Only show warnings and errors }); var apiClient = new SRFApiClient(loggerFactory); var streamResolver = new StreamUrlResolver(loggerFactory.CreateLogger()); var cancellationToken = CancellationToken.None; // Test 1: Check API connectivity Console.WriteLine("[Test 1] API Connectivity Test"); Console.WriteLine("-------------------------------"); try { var latestVideos = await apiClient.GetLatestVideosAsync("srf", cancellationToken); if (latestVideos?.ChapterList != null && latestVideos.ChapterList.Any()) { Console.WriteLine($"✓ Successfully fetched latest videos: {latestVideos.ChapterList.Count} items"); Console.WriteLine($" First video: {latestVideos.ChapterList[0]?.Title}"); } else { Console.WriteLine("✗ Failed: No videos returned"); } } catch (Exception ex) { Console.WriteLine($"✗ Error: {ex.Message}"); } Console.WriteLine(); // Test 2: Check trending videos Console.WriteLine("[Test 2] Trending Videos Test"); Console.WriteLine("-----------------------------"); try { var trendingVideos = await apiClient.GetTrendingVideosAsync("srf", cancellationToken); if (trendingVideos?.ChapterList != null && trendingVideos.ChapterList.Any()) { Console.WriteLine($"✓ Successfully fetched trending videos: {trendingVideos.ChapterList.Count} items"); Console.WriteLine($" First video: {trendingVideos.ChapterList[0]?.Title}"); } else { Console.WriteLine("✗ Failed: No videos returned"); } } catch (Exception ex) { Console.WriteLine($"✗ Error: {ex.Message}"); } Console.WriteLine(); // Test 3: Get specific video and check stream URL Console.WriteLine("[Test 3] Stream URL Resolution Test"); Console.WriteLine("-----------------------------------"); try { var latestVideos = await apiClient.GetLatestVideosAsync("srf", cancellationToken); if (latestVideos?.ChapterList != null && latestVideos.ChapterList.Any()) { var firstChapter = latestVideos.ChapterList[0]; Console.WriteLine($"Testing with: {firstChapter.Title}"); Console.WriteLine($"URN: {firstChapter.Urn}"); // Fetch full media composition for this URN var mediaComposition = await apiClient.GetMediaCompositionByUrnAsync(firstChapter.Urn, cancellationToken); if (mediaComposition?.ChapterList != null && mediaComposition.ChapterList.Any()) { var chapter = mediaComposition.ChapterList[0]; // Check if content is playable bool hasPlayableContent = streamResolver.HasPlayableContent(chapter); Console.WriteLine($"Has playable content: {hasPlayableContent}"); // Check if content is expired bool isExpired = streamResolver.IsContentExpired(chapter); Console.WriteLine($"Is expired: {isExpired}"); // Try to get stream URL if (hasPlayableContent && !isExpired) { var streamUrl = streamResolver.GetStreamUrl(chapter, Jellyfin.Plugin.SRFPlay.Configuration.QualityPreference.Auto); if (!string.IsNullOrEmpty(streamUrl)) { Console.WriteLine($"✓ Stream URL resolved: {streamUrl.Substring(0, Math.Min(80, streamUrl.Length))}..."); // Show available resources if (chapter.ResourceList != null) { Console.WriteLine($" Available resources: {chapter.ResourceList.Count}"); foreach (var resource in chapter.ResourceList.Take(5)) { var hasDrm = resource.DrmList != null && resource.DrmList.ToString() != "[]"; Console.WriteLine($" - {resource.Quality} ({resource.Protocol}) {(hasDrm ? "[DRM]" : "[No DRM]")}"); } } } else { Console.WriteLine("✗ Failed to resolve stream URL"); } } else { if (!hasPlayableContent) Console.WriteLine("✗ Content is not playable (likely DRM protected)"); if (isExpired) Console.WriteLine($"✗ Content is expired (ValidTo: {chapter.ValidTo})"); } } else { Console.WriteLine("✗ Failed to fetch media composition"); } } } catch (Exception ex) { Console.WriteLine($"✗ Error: {ex.Message}"); Console.WriteLine($"Stack trace: {ex.StackTrace}"); } Console.WriteLine(); // Test 4: Check multiple videos for playability Console.WriteLine("[Test 4] Multiple Videos Playability Check"); Console.WriteLine("------------------------------------------"); try { var latestVideos = await apiClient.GetLatestVideosAsync("srf", cancellationToken); if (latestVideos?.ChapterList != null) { int totalVideos = Math.Min(10, latestVideos.ChapterList.Count); int playableCount = 0; int expiredCount = 0; int drmCount = 0; Console.WriteLine($"Checking {totalVideos} videos...\n"); for (int i = 0; i < totalVideos; i++) { var chapter = latestVideos.ChapterList[i]; var mediaComp = await apiClient.GetMediaCompositionByUrnAsync(chapter.Urn, cancellationToken); if (mediaComp?.ChapterList != null && mediaComp.ChapterList.Any()) { var fullChapter = mediaComp.ChapterList[0]; bool hasPlayable = streamResolver.HasPlayableContent(fullChapter); bool isExpired = streamResolver.IsContentExpired(fullChapter); string status; if (isExpired) { status = "EXPIRED"; expiredCount++; } else if (!hasPlayable) { status = "DRM"; drmCount++; } else { status = "PLAYABLE"; playableCount++; } Console.WriteLine($"{i + 1}. [{status}] {fullChapter.Title}"); } } Console.WriteLine(); Console.WriteLine($"Summary:"); Console.WriteLine($" Playable: {playableCount}/{totalVideos} ({(playableCount * 100.0 / totalVideos):F1}%)"); Console.WriteLine($" DRM Protected: {drmCount}/{totalVideos} ({(drmCount * 100.0 / totalVideos):F1}%)"); Console.WriteLine($" Expired: {expiredCount}/{totalVideos} ({(expiredCount * 100.0 / totalVideos):F1}%)"); } } catch (Exception ex) { Console.WriteLine($"✗ Error: {ex.Message}"); } Console.WriteLine(); // Test 5: Test library content discovery (simulates what would be in the library) Console.WriteLine("[Test 5] Library Content Discovery Simulation"); Console.WriteLine("----------------------------------------------"); try { var latestVideos = await apiClient.GetLatestVideosAsync("srf", cancellationToken); var trendingVideos = await apiClient.GetTrendingVideosAsync("srf", cancellationToken); var allUrns = new HashSet(); if (latestVideos?.ChapterList != null) { foreach (var chapter in latestVideos.ChapterList) { if (!string.IsNullOrEmpty(chapter.Urn)) allUrns.Add(chapter.Urn); } } if (trendingVideos?.ChapterList != null) { foreach (var chapter in trendingVideos.ChapterList) { if (!string.IsNullOrEmpty(chapter.Urn)) allUrns.Add(chapter.Urn); } } Console.WriteLine($"✓ Latest videos available: {latestVideos?.ChapterList?.Count ?? 0}"); Console.WriteLine($"✓ Trending videos available: {trendingVideos?.ChapterList?.Count ?? 0}"); Console.WriteLine($"✓ Total unique items for library (deduplicated): {allUrns.Count}"); } catch (Exception ex) { Console.WriteLine($"✗ Error: {ex.Message}"); } Console.WriteLine(); // Test 6: Test different business units Console.WriteLine("[Test 6] Business Units Test"); Console.WriteLine("----------------------------"); var businessUnits = new[] { "srf", "rts", "rsi", "rtr", "swi" }; foreach (var bu in businessUnits) { try { var videos = await apiClient.GetLatestVideosAsync(bu, cancellationToken); if (videos?.ChapterList != null && videos.ChapterList.Any()) { Console.WriteLine($"✓ {bu.ToUpper()}: {videos.ChapterList.Count} videos available"); } else { Console.WriteLine($"✗ {bu.ToUpper()}: No videos found"); } } catch (Exception ex) { Console.WriteLine($"✗ {bu.ToUpper()}: Error - {ex.Message}"); } } Console.WriteLine(); Console.WriteLine("=== Test Suite Complete ==="); } } }