From 649351f68b83f2ba536886a7dc923730f091639c Mon Sep 17 00:00:00 2001 From: Josh Patra Date: Fri, 30 Jan 2026 13:21:23 -0500 Subject: [PATCH] feat: add automatic fallback support for SquidWTF endpoints - Decode 6 base64 URLs at startup (1 primary + 5 backups) - Automatic fallback when endpoint fails - All services try next endpoint on failure - Metadata, Download, and Validator all support fallback - Endpoints: triton.squid.wtf, wolf/hund/maus/vogel/katze.qqdl.site - Logs which endpoint is being used - Cycles through all endpoints before giving up --- allstarr/Program.cs | 30 ++-- .../SquidWTF/SquidWTFDownloadService.cs | 167 ++++++++++-------- .../SquidWTF/SquidWTFMetadataService.cs | 151 +++++++--------- .../SquidWTF/SquidWTFStartupValidator.cs | 70 ++++---- 4 files changed, 211 insertions(+), 207 deletions(-) diff --git a/allstarr/Program.cs b/allstarr/Program.cs index f3086bc..5149e25 100644 --- a/allstarr/Program.cs +++ b/allstarr/Program.cs @@ -16,13 +16,23 @@ using System.Text; var builder = WebApplication.CreateBuilder(args); -// Decode SquidWTF API base URL once at startup -var squidWtfApiBase = DecodeSquidWtfUrl(); -static string DecodeSquidWtfUrl() +// Decode SquidWTF API base URLs once at startup (primary + backups) +var squidWtfApiUrls = DecodeSquidWtfUrls(); +static List DecodeSquidWtfUrls() { - var encoded = "aHR0cHM6Ly90cml0b24uc3F1aWQud3Rm"; - var bytes = Convert.FromBase64String(encoded); - return Encoding.UTF8.GetString(bytes); + var encodedUrls = new[] + { + "aHR0cHM6Ly90cml0b24uc3F1aWQud3Rm", // Primary: triton.squid.wtf + "aHR0cHM6Ly93b2xmLnFxZGwuc2l0ZQ==", // Backup 1: wolf.qqdl.site + "aHR0cDovL2h1bmQucXFkbC5zaXRl", // Backup 2: hund.qqdl.site + "aHR0cHM6Ly9tYXVzLnFxZGwuc2l0ZQ==", // Backup 3: maus.qqdl.site + "aHR0cHM6Ly92b2dlbC5xcWRsLnNpdGU=", // Backup 4: vogel.qqdl.site + "aHR0cHM6Ly9rYXR6ZS5xcWRsLnNpdGU=" // Backup 5: katze.qqdl.site + }; + + return encodedUrls + .Select(encoded => Encoding.UTF8.GetString(Convert.FromBase64String(encoded))) + .ToList(); } // Determine backend type FIRST @@ -173,7 +183,7 @@ else if (musicService == MusicService.Deezer) } else if (musicService == MusicService.SquidWTF) { - // SquidWTF services - pass decoded URL + // SquidWTF services - pass decoded URLs with fallback support builder.Services.AddSingleton(sp => new SquidWTFMetadataService( sp.GetRequiredService(), @@ -181,7 +191,7 @@ else if (musicService == MusicService.SquidWTF) sp.GetRequiredService>(), sp.GetRequiredService>(), sp.GetRequiredService(), - squidWtfApiBase)); + squidWtfApiUrls)); builder.Services.AddSingleton(sp => new SquidWTFDownloadService( sp.GetRequiredService(), @@ -192,7 +202,7 @@ else if (musicService == MusicService.SquidWTF) sp.GetRequiredService>(), sp, sp.GetRequiredService>(), - squidWtfApiBase)); + squidWtfApiUrls)); } // Startup validation - register validators based on backend @@ -211,7 +221,7 @@ builder.Services.AddSingleton(sp => new SquidWTFStartupValidator( sp.GetRequiredService>(), sp.GetRequiredService().CreateClient(), - squidWtfApiBase)); + squidWtfApiUrls)); // Register orchestrator as hosted service builder.Services.AddHostedService(); diff --git a/allstarr/Services/SquidWTF/SquidWTFDownloadService.cs b/allstarr/Services/SquidWTF/SquidWTFDownloadService.cs index 85904c1..1e9e010 100644 --- a/allstarr/Services/SquidWTF/SquidWTFDownloadService.cs +++ b/allstarr/Services/SquidWTF/SquidWTFDownloadService.cs @@ -26,7 +26,8 @@ public class SquidWTFDownloadService : BaseDownloadService private DateTime _lastRequestTime = DateTime.MinValue; private readonly int _minRequestIntervalMs = 200; - private readonly string SquidWTFApiBase; + private readonly List _apiUrls; + private int _currentUrlIndex = 0; protected override string ProviderName => "squidwtf"; @@ -39,29 +40,48 @@ public class SquidWTFDownloadService : BaseDownloadService IOptions SquidWTFSettings, IServiceProvider serviceProvider, ILogger logger, - string apiBase) + List apiUrls) : base(configuration, localLibraryService, metadataService, subsonicSettings.Value, serviceProvider, logger) { _httpClient = httpClientFactory.CreateClient(); _squidwtfSettings = SquidWTFSettings.Value; - SquidWTFApiBase = apiBase; + _apiUrls = apiUrls; + } + + private async Task TryWithFallbackAsync(Func> action) + { + for (int attempt = 0; attempt < _apiUrls.Count; attempt++) + { + try + { + var baseUrl = _apiUrls[_currentUrlIndex]; + return await action(baseUrl); + } + catch (Exception ex) + { + Logger.LogWarning(ex, "Request failed with endpoint {Endpoint}, trying next...", _apiUrls[_currentUrlIndex]); + _currentUrlIndex = (_currentUrlIndex + 1) % _apiUrls.Count; + + if (attempt == _apiUrls.Count - 1) + { + Logger.LogError("All SquidWTF endpoints failed"); + throw; + } + } + } + throw new Exception("All SquidWTF endpoints failed"); } #region BaseDownloadService Implementation public override async Task IsAvailableAsync() { - try + return await TryWithFallbackAsync(async (baseUrl) => { - var response = await _httpClient.GetAsync(SquidWTFApiBase); + var response = await _httpClient.GetAsync(baseUrl); Console.WriteLine($"Response code from is available async: {response.IsSuccessStatusCode}"); return response.IsSuccessStatusCode; - } - catch (Exception ex) - { - Logger.LogWarning(ex, "SquidWTF service not available"); - return false; - } + }); } protected override string? ExtractExternalIdFromAlbumId(string albumId) @@ -137,74 +157,69 @@ public class SquidWTFDownloadService : BaseDownloadService { return await QueueRequestAsync(async () => { - // Map quality settings to Tidal's quality levels - var quality = _squidwtfSettings.Quality?.ToUpperInvariant() switch + return await TryWithFallbackAsync(async (baseUrl) => { - "FLAC" => "LOSSLESS", - "HI_RES" => "HI_RES_LOSSLESS", - "LOSSLESS" => "LOSSLESS", - "HIGH" => "HIGH", - "LOW" => "LOW", - _ => "LOSSLESS" // Default to lossless - }; - - var url = $"{SquidWTFApiBase}/track/?id={trackId}&quality={quality}"; + // Map quality settings to Tidal's quality levels + var quality = _squidwtfSettings.Quality?.ToUpperInvariant() switch + { + "FLAC" => "LOSSLESS", + "HI_RES" => "HI_RES_LOSSLESS", + "LOSSLESS" => "LOSSLESS", + "HIGH" => "HIGH", + "LOW" => "LOW", + _ => "LOSSLESS" // Default to lossless + }; + + var url = $"{baseUrl}/track/?id={trackId}&quality={quality}"; - Console.WriteLine($"%%%%%%%%%%%%%%%%%%% URL For downloads??: {url}"); + Console.WriteLine($"%%%%%%%%%%%%%%%%%%% URL For downloads??: {url}"); - try - { - var response = await _httpClient.GetAsync(url, cancellationToken); - response.EnsureSuccessStatusCode(); - - var json = await response.Content.ReadAsStringAsync(cancellationToken); - var doc = JsonDocument.Parse(json); - - if (!doc.RootElement.TryGetProperty("data", out var data)) - { - throw new Exception("Invalid response from API"); - } - - // Get the manifest (base64 encoded JSON containing the actual CDN URL) - var manifestBase64 = data.GetProperty("manifest").GetString() - ?? throw new Exception("No manifest in response"); - - // Decode the manifest - var manifestJson = Encoding.UTF8.GetString(Convert.FromBase64String(manifestBase64)); - var manifest = JsonDocument.Parse(manifestJson); - - // Extract the download URL from the manifest - if (!manifest.RootElement.TryGetProperty("urls", out var urls) || urls.GetArrayLength() == 0) - { - throw new Exception("No download URLs in manifest"); - } - - var downloadUrl = urls[0].GetString() - ?? throw new Exception("Download URL is null"); - - var mimeType = manifest.RootElement.TryGetProperty("mimeType", out var mimeTypeEl) - ? mimeTypeEl.GetString() - : "audio/flac"; - - var audioQuality = data.TryGetProperty("audioQuality", out var audioQualityEl) - ? audioQualityEl.GetString() - : "LOSSLESS"; - - Logger.LogDebug("Decoded manifest - URL: {Url}, MIME: {MimeType}, Quality: {Quality}", - downloadUrl, mimeType, audioQuality); - - return new DownloadResult - { - DownloadUrl = downloadUrl, - MimeType = mimeType ?? "audio/flac", - AudioQuality = audioQuality ?? "LOSSLESS" - }; - } - catch (Exception ex) - { - Logger.LogWarning(ex, "Failed to get track info"); - throw; - } + var response = await _httpClient.GetAsync(url, cancellationToken); + response.EnsureSuccessStatusCode(); + + var json = await response.Content.ReadAsStringAsync(cancellationToken); + var doc = JsonDocument.Parse(json); + + if (!doc.RootElement.TryGetProperty("data", out var data)) + { + throw new Exception("Invalid response from API"); + } + + // Get the manifest (base64 encoded JSON containing the actual CDN URL) + var manifestBase64 = data.GetProperty("manifest").GetString() + ?? throw new Exception("No manifest in response"); + + // Decode the manifest + var manifestJson = Encoding.UTF8.GetString(Convert.FromBase64String(manifestBase64)); + var manifest = JsonDocument.Parse(manifestJson); + + // Extract the download URL from the manifest + if (!manifest.RootElement.TryGetProperty("urls", out var urls) || urls.GetArrayLength() == 0) + { + throw new Exception("No download URLs in manifest"); + } + + var downloadUrl = urls[0].GetString() + ?? throw new Exception("Download URL is null"); + + var mimeType = manifest.RootElement.TryGetProperty("mimeType", out var mimeTypeEl) + ? mimeTypeEl.GetString() + : "audio/flac"; + + var audioQuality = data.TryGetProperty("audioQuality", out var audioQualityEl) + ? audioQualityEl.GetString() + : "LOSSLESS"; + + Logger.LogDebug("Decoded manifest - URL: {Url}, MIME: {MimeType}, Quality: {Quality}", + downloadUrl, mimeType, audioQuality); + + return new DownloadResult + { + DownloadUrl = downloadUrl, + MimeType = mimeType ?? "audio/flac", + AudioQuality = audioQuality ?? "LOSSLESS" + }; + }); }); } diff --git a/allstarr/Services/SquidWTF/SquidWTFMetadataService.cs b/allstarr/Services/SquidWTF/SquidWTFMetadataService.cs index a593353..e1e545e 100644 --- a/allstarr/Services/SquidWTF/SquidWTFMetadataService.cs +++ b/allstarr/Services/SquidWTF/SquidWTFMetadataService.cs @@ -21,7 +21,8 @@ public class SquidWTFMetadataService : IMusicMetadataService private readonly SubsonicSettings _settings; private readonly ILogger _logger; private readonly RedisCacheService _cache; - private readonly string BaseUrl; + private readonly List _apiUrls; + private int _currentUrlIndex = 0; public SquidWTFMetadataService( IHttpClientFactory httpClientFactory, @@ -29,24 +30,50 @@ public class SquidWTFMetadataService : IMusicMetadataService IOptions squidwtfSettings, ILogger logger, RedisCacheService cache, - string baseUrl) + List apiUrls) { _httpClient = httpClientFactory.CreateClient(); _settings = settings.Value; _logger = logger; _cache = cache; - BaseUrl = baseUrl; + _apiUrls = apiUrls; // Set up default headers _httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0"); } + + private string GetCurrentBaseUrl() => _apiUrls[_currentUrlIndex]; + + private async Task TryWithFallbackAsync(Func> action, T defaultValue) + { + for (int attempt = 0; attempt < _apiUrls.Count; attempt++) + { + try + { + var baseUrl = _apiUrls[_currentUrlIndex]; + return await action(baseUrl); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Request failed with endpoint {Endpoint}, trying next...", _apiUrls[_currentUrlIndex]); + _currentUrlIndex = (_currentUrlIndex + 1) % _apiUrls.Count; + + if (attempt == _apiUrls.Count - 1) + { + _logger.LogError("All SquidWTF endpoints failed"); + return defaultValue; + } + } + } + return defaultValue; + } public async Task> SearchSongsAsync(string query, int limit = 20) { - try + return await TryWithFallbackAsync(async (baseUrl) => { - var url = $"{BaseUrl}/search/?s={Uri.EscapeDataString(query)}"; + var url = $"{baseUrl}/search/?s={Uri.EscapeDataString(query)}"; var response = await _httpClient.GetAsync(url); if (!response.IsSuccessStatusCode) @@ -72,19 +99,14 @@ public class SquidWTFMetadataService : IMusicMetadataService } } return songs; - } - catch (Exception ex) - { - _logger.LogWarning(ex, "Failed to search songs for query: {Query}", query); - return new List(); - } + }, new List()); } public async Task> SearchAlbumsAsync(string query, int limit = 20) { - try + return await TryWithFallbackAsync(async (baseUrl) => { - var url = $"{BaseUrl}/search/?al={Uri.EscapeDataString(query)}"; + var url = $"{baseUrl}/search/?al={Uri.EscapeDataString(query)}"; var response = await _httpClient.GetAsync(url); if (!response.IsSuccessStatusCode) @@ -111,19 +133,14 @@ public class SquidWTFMetadataService : IMusicMetadataService } return albums; - } - catch (Exception ex) - { - _logger.LogWarning(ex, "Failed to search albums for query: {Query}", query); - return new List(); - } + }, new List()); } public async Task> SearchArtistsAsync(string query, int limit = 20) { - try + return await TryWithFallbackAsync(async (baseUrl) => { - var url = $"{BaseUrl}/search/?a={Uri.EscapeDataString(query)}"; + var url = $"{baseUrl}/search/?a={Uri.EscapeDataString(query)}"; var response = await _httpClient.GetAsync(url); if (!response.IsSuccessStatusCode) @@ -150,19 +167,14 @@ public class SquidWTFMetadataService : IMusicMetadataService } return artists; - } - catch (Exception ex) - { - _logger.LogWarning(ex, "Failed to search artists for query: {Query}", query); - return new List(); - } + }, new List()); } public async Task> SearchPlaylistsAsync(string query, int limit = 20) { - try + return await TryWithFallbackAsync(async (baseUrl) => { - var url = $"{BaseUrl}/search/?p={Uri.EscapeDataString(query)}"; + var url = $"{baseUrl}/search/?p={Uri.EscapeDataString(query)}"; var response = await _httpClient.GetAsync(url); if (!response.IsSuccessStatusCode) return new List(); @@ -180,11 +192,7 @@ public class SquidWTFMetadataService : IMusicMetadataService } } return playlists; - } - catch - { - return new List(); - } + }, new List()); } public async Task SearchAllAsync(string query, int songLimit = 20, int albumLimit = 20, int artistLimit = 20) @@ -210,10 +218,9 @@ public class SquidWTFMetadataService : IMusicMetadataService { if (externalProvider != "squidwtf") return null; - try + return await TryWithFallbackAsync(async (baseUrl) => { - // Use the /info endpoint for full track metadata - var url = $"{BaseUrl}/info/?id={externalId}"; + var url = $"{baseUrl}/info/?id={externalId}"; var response = await _httpClient.GetAsync(url); if (!response.IsSuccessStatusCode) return null; @@ -225,12 +232,7 @@ public class SquidWTFMetadataService : IMusicMetadataService return null; return ParseTidalTrackFull(track); - } - catch (Exception ex) - { - _logger.LogWarning(ex, "GetSongAsync Exception"); - return null; - } + }, (Song?)null); } public async Task GetAlbumAsync(string externalProvider, string externalId) @@ -242,10 +244,9 @@ public class SquidWTFMetadataService : IMusicMetadataService var cached = await _cache.GetAsync(cacheKey); if (cached != null) return cached; - try + return await TryWithFallbackAsync(async (baseUrl) => { - // Use the /info endpoint for full track metadata - var url = $"{BaseUrl}/album/?id={externalId}"; + var url = $"{baseUrl}/album/?id={externalId}"; var response = await _httpClient.GetAsync(url); if (!response.IsSuccessStatusCode) return null; @@ -279,12 +280,7 @@ public class SquidWTFMetadataService : IMusicMetadataService await _cache.SetAsync(cacheKey, album, TimeSpan.FromHours(24)); return album; - } - catch (Exception ex) - { - _logger.LogWarning(ex, "GetAlbumAsync Exception"); - return null; - } + }, (Album?)null); } public async Task GetArtistAsync(string externalProvider, string externalId) @@ -302,10 +298,9 @@ public class SquidWTFMetadataService : IMusicMetadataService return cached; } - try + return await TryWithFallbackAsync(async (baseUrl) => { - // Use the /info endpoint for full track metadata - var url = $"{BaseUrl}/artist/?f={externalId}"; + var url = $"{baseUrl}/artist/?f={externalId}"; _logger.LogInformation("Fetching artist from {Url}", url); var response = await _httpClient.GetAsync(url); @@ -366,25 +361,18 @@ public class SquidWTFMetadataService : IMusicMetadataService await _cache.SetAsync(cacheKey, artist, TimeSpan.FromHours(24)); return artist; - - } - catch (Exception ex) - { - _logger.LogWarning(ex, "GetArtistAsync Exception."); - return null; - } + }, (Artist?)null); } public async Task> GetArtistAlbumsAsync(string externalProvider, string externalId) { - - try + if (externalProvider != "squidwtf") return new List(); + + return await TryWithFallbackAsync(async (baseUrl) => { - if (externalProvider != "squidwtf") return new List(); - _logger.LogInformation("GetArtistAlbumsAsync called for SquidWTF artist {ExternalId}", externalId); - var url = $"{BaseUrl}/artist/?f={externalId}"; + var url = $"{baseUrl}/artist/?f={externalId}"; _logger.LogInformation("Fetching artist albums from URL: {Url}", url); var response = await _httpClient.GetAsync(url); @@ -418,21 +406,16 @@ public class SquidWTFMetadataService : IMusicMetadataService } return albums; - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to get SquidWTF artist albums for {ExternalId}", externalId); - return new List(); - } + }, new List()); } public async Task GetPlaylistAsync(string externalProvider, string externalId) { if (externalProvider != "squidwtf") return null; - try + return await TryWithFallbackAsync(async (baseUrl) => { - var url = $"{BaseUrl}/playlist/?id={externalId}"; + var url = $"{baseUrl}/playlist/?id={externalId}"; var response = await _httpClient.GetAsync(url); if (!response.IsSuccessStatusCode) return null; @@ -442,21 +425,16 @@ public class SquidWTFMetadataService : IMusicMetadataService if (playlistElement.TryGetProperty("error", out _)) return null; return ParseTidalPlaylist(playlistElement); - } - catch - { - return null; - } - + }, (ExternalPlaylist?)null); } public async Task> GetPlaylistTracksAsync(string externalProvider, string externalId) { if (externalProvider != "squidwtf") return new List(); - try + return await TryWithFallbackAsync(async (baseUrl) => { - var url = $"{BaseUrl}/playlist/?id={externalId}"; + var url = $"{baseUrl}/playlist/?id={externalId}"; var response = await _httpClient.GetAsync(url); if (!response.IsSuccessStatusCode) return new List(); @@ -507,12 +485,7 @@ public class SquidWTFMetadataService : IMusicMetadataService } } return songs; - } - catch - { - return new List(); - } - + }, new List()); } // --- Parser functions start here --- diff --git a/allstarr/Services/SquidWTF/SquidWTFStartupValidator.cs b/allstarr/Services/SquidWTF/SquidWTFStartupValidator.cs index 39b042b..0207104 100644 --- a/allstarr/Services/SquidWTF/SquidWTFStartupValidator.cs +++ b/allstarr/Services/SquidWTF/SquidWTFStartupValidator.cs @@ -12,15 +12,39 @@ namespace allstarr.Services.SquidWTF; public class SquidWTFStartupValidator : BaseStartupValidator { private readonly SquidWTFSettings _settings; - private readonly string _apiBase; + private readonly List _apiUrls; + private int _currentUrlIndex = 0; public override string ServiceName => "SquidWTF"; - public SquidWTFStartupValidator(IOptions settings, HttpClient httpClient, string apiBase) + public SquidWTFStartupValidator(IOptions settings, HttpClient httpClient, List apiUrls) : base(httpClient) { _settings = settings.Value; - _apiBase = apiBase; + _apiUrls = apiUrls; + } + + private async Task TryWithFallbackAsync(Func> action, T defaultValue) + { + for (int attempt = 0; attempt < _apiUrls.Count; attempt++) + { + try + { + var baseUrl = _apiUrls[_currentUrlIndex]; + return await action(baseUrl); + } + catch + { + WriteDetail($"Endpoint {_apiUrls[_currentUrlIndex]} failed, trying next..."); + _currentUrlIndex = (_currentUrlIndex + 1) % _apiUrls.Count; + + if (attempt == _apiUrls.Count - 1) + { + return defaultValue; + } + } + } + return defaultValue; } public override async Task ValidateAsync(CancellationToken cancellationToken) @@ -39,54 +63,36 @@ public class SquidWTFStartupValidator : BaseStartupValidator WriteStatus("SquidWTF Quality", quality, ConsoleColor.Cyan); - // Test connectivity - try + // Test connectivity with fallback + var result = await TryWithFallbackAsync(async (baseUrl) => { - var response = await _httpClient.GetAsync(_apiBase, cancellationToken); + var response = await _httpClient.GetAsync(baseUrl, cancellationToken); if (response.IsSuccessStatusCode) { - WriteStatus("SquidWTF API", "REACHABLE", ConsoleColor.Green); + WriteStatus("SquidWTF API", $"REACHABLE ({baseUrl})", ConsoleColor.Green); WriteDetail("No authentication required - powered by Tidal"); // Try a test search to verify functionality - await ValidateSearchFunctionality(cancellationToken); + await ValidateSearchFunctionality(baseUrl, cancellationToken); return ValidationResult.Success("SquidWTF validation completed"); } else { - WriteStatus("SquidWTF API", $"HTTP {(int)response.StatusCode}", ConsoleColor.Yellow); - WriteDetail("Service may be temporarily unavailable"); - return ValidationResult.Failure($"{response.StatusCode}", "SquidWTF returned code"); + throw new HttpRequestException($"HTTP {(int)response.StatusCode}"); } - } - catch (TaskCanceledException) - { - WriteStatus("SquidWTF API", "TIMEOUT", ConsoleColor.Yellow); - WriteDetail("Could not reach service within timeout period"); - return ValidationResult.Failure("-1", "SquidWTF connection timeout"); - } - catch (HttpRequestException ex) - { - WriteStatus("SquidWTF API", "UNREACHABLE", ConsoleColor.Red); - WriteDetail(ex.Message); - return ValidationResult.Failure("-1", $"Cannot connect to SquidWTF: {ex.Message}"); - } - catch (Exception ex) - { - WriteStatus("SquidWTF API", "ERROR", ConsoleColor.Red); - WriteDetail(ex.Message); - return ValidationResult.Failure("-1", $"Validation error: {ex.Message}"); - } + }, ValidationResult.Failure("-1", "All SquidWTF endpoints failed")); + + return result; } - private async Task ValidateSearchFunctionality(CancellationToken cancellationToken) + private async Task ValidateSearchFunctionality(string baseUrl, CancellationToken cancellationToken) { try { // Test search with a simple query - var searchUrl = $"{_apiBase}/search/?s=Taylor%20Swift"; + var searchUrl = $"{baseUrl}/search/?s=Taylor%20Swift"; var searchResponse = await _httpClient.GetAsync(searchUrl, cancellationToken); if (searchResponse.IsSuccessStatusCode)