mirror of
https://github.com/Crunchy-DL/Crunchy-Downloader.git
synced 2026-04-25 18:52:53 +00:00
Add - Added background image option to the Appearance settings Add - Background Image Settings - Added new options to control opacity and blur radius Add - Added "Couldn't sync dubs" status if the syncing failed Add - Added functionality to combine multiple episodes from the same season into a single entry in the calendar Add - Added video resolution display next to dubs/subs in the downloads tab Add - Added Cloudflare check to image loading Add - Added hardsub selection if the current is not available Add - Added part size setting to configure the size of parts downloaded at the same time Add - Added quality override to history series Add - Added history marker to search results to indicate if a series is already in the user's history Add - Added seasons tab for seasonal releases (Spring, Summer, Fall, Winter) Add - Added potential releases and release times for the current day and the next week to the custom calendar Chg - Changed Calendar cards background color for improved visibility Chg - Combined Appearance settings into a single section in the settings tab Chg - Consolidated Debug settings into one settings expander for better organization Chg - Changed time sync to now check both the start and end of the video Chg - Changed encoding progress to be displayed by the progress bar Chg - Updated the functionality for hiding dubs in the custom calendar Chg - Adjusted Dub sync to improve accuracy, resolving issues where it failed for more episodes than expected Chg - Subtitles and dubs are now sorted according to the order selected in the MKV file Chg - Changed logout behavior to correctly log out if login fails when starting the downloader Chg - Changed that all downloaded files are removed if an in-progress download is removed from the queue Chg - Changed default profile image Chg - Updated used packages to the newest version Chg - Separated settings to separate tabs Fix - Fixed some series didn't get added to the history Fix - Fixed an issue with file path length that prevented some files from being accessed properly Fix - Fixed an issue where file names exceeded the maximum allowable length, causing errors Fix - Fixed an issue where refreshing a series could get stuck Fix - Fixed a crash that could happen with the syncing Fix - Fixed an issue where the download status showed "Done" while moving files from the temp folder Fix - Fixed an issue where cookies were not being utilized correctly Fix - Resolved issues with displaying dates in UTC format Fix - Fixed an issue with incorrect calendar grouping Fix - Fixed an issue with the previous week navigation in the calendar Fix - Fixed an issue where the calendar would not display correctly when not logged in Fix - Fixed incorrect FFmpeg check for other OS (Linux/macOS) Fix - Fixed an issue where image loading used a different HTTP client
210 lines
No EOL
7 KiB
C#
210 lines
No EOL
7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Diagnostics;
|
|
using System.Net.Http;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using CRD.Downloader.Crunchyroll;
|
|
using CRD.Utils.Sonarr.Models;
|
|
using CRD.Views;
|
|
|
|
namespace CRD.Utils.Sonarr;
|
|
|
|
public class SonarrClient{
|
|
private string? apiUrl;
|
|
|
|
private HttpClient httpClient;
|
|
|
|
private SonarrProperties properties;
|
|
|
|
public List<SonarrSeries> SonarrSeries =[];
|
|
|
|
#region Singelton
|
|
|
|
private static SonarrClient? _instance;
|
|
private static readonly object Padlock = new();
|
|
|
|
public static SonarrClient Instance{
|
|
get{
|
|
if (_instance == null){
|
|
lock (Padlock){
|
|
if (_instance == null){
|
|
_instance = new SonarrClient();
|
|
}
|
|
}
|
|
}
|
|
|
|
return _instance;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
public SonarrClient(){
|
|
httpClient = new HttpClient();
|
|
}
|
|
|
|
public async Task RefreshSonarr(){
|
|
await CheckSonarrSettings();
|
|
if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties is{ SonarrEnabled: true }){
|
|
SonarrSeries = await GetSeries();
|
|
CrunchyrollManager.Instance.History.MatchHistorySeriesWithSonarr(true);
|
|
|
|
foreach (var historySeries in CrunchyrollManager.Instance.HistoryList){
|
|
if (historySeries.SonarrSeriesId != null){
|
|
List<SonarrEpisode>? episodes = await GetEpisodes(int.Parse(historySeries.SonarrSeriesId));
|
|
historySeries.SonarrNextAirDate = CrunchyrollManager.Instance.History.GetNextAirDate(episodes);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
public async Task RefreshSonarrLite(){
|
|
await CheckSonarrSettings();
|
|
if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties is{ SonarrEnabled: true }){
|
|
SonarrSeries = await GetSeries();
|
|
CrunchyrollManager.Instance.History.MatchHistorySeriesWithSonarr(true);
|
|
}
|
|
}
|
|
|
|
public void SetApiUrl(){
|
|
if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null) properties = CrunchyrollManager.Instance.CrunOptions.SonarrProperties;
|
|
|
|
if (properties != null ){
|
|
apiUrl = $"http{(properties.UseSsl ? "s" : "")}://{(!string.IsNullOrEmpty(properties.Host) ? properties.Host : "localhost")}:{properties.Port}{(properties.UrlBase ?? "")}/api";
|
|
}
|
|
}
|
|
|
|
public async Task CheckSonarrSettings(){
|
|
|
|
SetApiUrl();
|
|
|
|
if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null){
|
|
CrunchyrollManager.Instance.CrunOptions.SonarrProperties.SonarrEnabled = false;
|
|
} else{
|
|
CrunchyrollManager.Instance.CrunOptions.SonarrProperties = new SonarrProperties(){SonarrEnabled = false};
|
|
return;
|
|
}
|
|
|
|
Debug.WriteLine($"[DEBUG] [SonarrClient.CheckSonarrSettings] Endpoint URL: '{apiUrl}'");
|
|
|
|
var request = CreateRequestMessage($"{apiUrl}", HttpMethod.Get);
|
|
HttpResponseMessage response;
|
|
|
|
try{
|
|
response = await httpClient.SendAsync(request);
|
|
response.EnsureSuccessStatusCode();
|
|
if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null) CrunchyrollManager.Instance.CrunOptions.SonarrProperties.SonarrEnabled = true;
|
|
} catch (Exception ex){
|
|
Debug.WriteLine($"[ERROR] [SonarrClient.GetJson] Endpoint URL: '{apiUrl}', {ex}");
|
|
if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null) CrunchyrollManager.Instance.CrunOptions.SonarrProperties.SonarrEnabled = false;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public async Task<List<SonarrSeries>> GetSeries(){
|
|
var json = await GetJson($"/v3/series{(true ? $"?includeSeasonImages={true}" : "")}");
|
|
|
|
List<SonarrSeries> series = [];
|
|
|
|
try{
|
|
series = Helpers.Deserialize<List<SonarrSeries>>(json,null) ?? [];
|
|
} catch (Exception e){
|
|
MainWindow.Instance.ShowError("Sonarr GetSeries error \n" + e);
|
|
Console.Error.WriteLine("Sonarr GetSeries error \n" + e);
|
|
}
|
|
|
|
return series;
|
|
}
|
|
|
|
public async Task<List<SonarrEpisode>> GetEpisodes(int seriesId){
|
|
var json = await GetJson($"/v3/episode?seriesId={seriesId}");
|
|
|
|
List<SonarrEpisode> episodes = [];
|
|
|
|
try{
|
|
episodes = Helpers.Deserialize<List<SonarrEpisode>>(json,null) ?? [];
|
|
} catch (Exception e){
|
|
MainWindow.Instance.ShowError("Sonarr GetEpisodes error \n" + e);
|
|
Console.Error.WriteLine("Sonarr GetEpisodes error \n" + e);
|
|
}
|
|
|
|
return episodes;
|
|
}
|
|
|
|
|
|
public async Task<SonarrEpisode> GetEpisode(int episodeId){
|
|
var json = await GetJson($"/v3/episode/id={episodeId}");
|
|
var episode = new SonarrEpisode();
|
|
try{
|
|
episode = Helpers.Deserialize<SonarrEpisode>(json,null) ?? new SonarrEpisode();
|
|
} catch (Exception e){
|
|
MainWindow.Instance.ShowError("Sonarr GetEpisode error \n" + e);
|
|
Console.Error.WriteLine("Sonarr GetEpisode error \n" + e);
|
|
}
|
|
|
|
return episode;
|
|
}
|
|
|
|
private async Task<string> GetJson(string endpointUrl){
|
|
Debug.WriteLine($"[DEBUG] [SonarrClient.PostJson] Endpoint URL: '{endpointUrl}'");
|
|
|
|
var request = CreateRequestMessage($"{apiUrl}{endpointUrl}", HttpMethod.Get);
|
|
HttpResponseMessage response;
|
|
var content = string.Empty;
|
|
|
|
try{
|
|
response = await httpClient.SendAsync(request);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
content = await response.Content.ReadAsStringAsync();
|
|
|
|
} catch (Exception ex){
|
|
Debug.WriteLine($"[ERROR] [SonarrClient.GetJson] Endpoint URL: '{endpointUrl}', {ex}");
|
|
}
|
|
|
|
|
|
if (!string.IsNullOrEmpty(content)) // Convert response to UTF8
|
|
content = Encoding.UTF8.GetString(Encoding.Default.GetBytes(content));
|
|
|
|
return content;
|
|
}
|
|
|
|
public HttpRequestMessage CreateRequestMessage(string uri, HttpMethod requestMethod, [Optional] NameValueCollection query){
|
|
UriBuilder uriBuilder = new UriBuilder(uri);
|
|
|
|
if (query != null){
|
|
uriBuilder.Query = query.ToString();
|
|
}
|
|
|
|
var request = new HttpRequestMessage(requestMethod, uriBuilder.ToString());
|
|
|
|
request.Headers.Add("X-Api-Key", properties.ApiKey);
|
|
|
|
request.Headers.UserAgent.ParseAdd($"{Assembly.GetExecutingAssembly().GetName().Name.Replace(" ", ".")}.v{Assembly.GetExecutingAssembly().GetName().Version}");
|
|
|
|
|
|
return request;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public class SonarrProperties(){
|
|
public string? Host{ get; set; }
|
|
public int Port{ get; set; }
|
|
public string? ApiKey{ get; set; }
|
|
public bool UseSsl{ get; set; }
|
|
|
|
public string? UrlBase{ get; set; }
|
|
|
|
public bool UseSonarrNumbering{ get; set; }
|
|
public bool SonarrEnabled{ get; set; }
|
|
} |