Add - Added a button to manually match Sonarr series
Add - Available dubs/subs overall to history series - not all dubs/subs are available for every season/episode Fix - Subscription end date was in UTC, causing the program to not recognize an active premium subscription https://github.com/Crunchy-DL/Crunchy-Downloader/issues/74
This commit is contained in:
parent
29376c59a6
commit
6aa10cb2c2
|
@ -10,6 +10,13 @@
|
||||||
<crd:ViewLocator />
|
<crd:ViewLocator />
|
||||||
</Application.DataTemplates>
|
</Application.DataTemplates>
|
||||||
|
|
||||||
|
<Application.Resources>
|
||||||
|
<x:Double x:Key="ContentDialogMinWidth">500</x:Double>
|
||||||
|
<x:Double x:Key="ContentDialogMaxWidth">1500</x:Double>
|
||||||
|
<x:Double x:Key="ContentDialogMinHeight">150</x:Double>
|
||||||
|
<x:Double x:Key="ContentDialogMaxHeight">700</x:Double>
|
||||||
|
</Application.Resources>
|
||||||
|
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<sty:FluentAvaloniaTheme PreferSystemTheme="True" PreferUserAccentColor="True"/>
|
<sty:FluentAvaloniaTheme PreferSystemTheme="True" PreferUserAccentColor="True"/>
|
||||||
<StyleInclude Source="avares://CRD/Styling/ControlsGalleryStyles.axaml" />
|
<StyleInclude Source="avares://CRD/Styling/ControlsGalleryStyles.axaml" />
|
||||||
|
|
|
@ -153,7 +153,6 @@ public class CalendarManager{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CalendarWeek week = new CalendarWeek();
|
CalendarWeek week = new CalendarWeek();
|
||||||
week.CalendarDays = new List<CalendarDay>();
|
week.CalendarDays = new List<CalendarDay>();
|
||||||
|
|
||||||
|
@ -181,10 +180,6 @@ public class CalendarManager{
|
||||||
foreach (var crBrowseEpisode in newEpisodes){
|
foreach (var crBrowseEpisode in newEpisodes){
|
||||||
var targetDate = CrunchyrollManager.Instance.CrunOptions.CalendarFilterByAirDate ? crBrowseEpisode.EpisodeMetadata.EpisodeAirDate : crBrowseEpisode.LastPublic;
|
var targetDate = CrunchyrollManager.Instance.CrunOptions.CalendarFilterByAirDate ? crBrowseEpisode.EpisodeMetadata.EpisodeAirDate : crBrowseEpisode.LastPublic;
|
||||||
|
|
||||||
if (targetDate.Kind == DateTimeKind.Utc){
|
|
||||||
targetDate = targetDate.ToLocalTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CrunchyrollManager.Instance.CrunOptions.CalendarHideDubs && crBrowseEpisode.EpisodeMetadata.SeasonTitle != null &&
|
if (CrunchyrollManager.Instance.CrunOptions.CalendarHideDubs && crBrowseEpisode.EpisodeMetadata.SeasonTitle != null &&
|
||||||
(crBrowseEpisode.EpisodeMetadata.SeasonTitle.EndsWith("Dub)") || crBrowseEpisode.EpisodeMetadata.AudioLocale != Locale.JaJp)){
|
(crBrowseEpisode.EpisodeMetadata.SeasonTitle.EndsWith("Dub)") || crBrowseEpisode.EpisodeMetadata.AudioLocale != Locale.JaJp)){
|
||||||
continue;
|
continue;
|
||||||
|
@ -218,6 +213,18 @@ public class CalendarManager{
|
||||||
calendarDay.CalendarEpisodes?.Add(calEpisode);
|
calendarDay.CalendarEpisodes?.Add(calEpisode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var weekCalendarDay in week.CalendarDays){
|
||||||
|
if (weekCalendarDay.CalendarEpisodes != null)
|
||||||
|
weekCalendarDay.CalendarEpisodes = weekCalendarDay.CalendarEpisodes
|
||||||
|
.OrderBy(e => e.DateTime)
|
||||||
|
.ThenBy(e => e.SeasonName)
|
||||||
|
.ThenBy(e => {
|
||||||
|
double parsedNumber;
|
||||||
|
return double.TryParse(e.EpisodeNumber, out parsedNumber) ? parsedNumber : double.MinValue;
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class CrAuth{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void JsonTokenToFileAndVariable(string content){
|
private void JsonTokenToFileAndVariable(string content){
|
||||||
crunInstance.Token = JsonConvert.DeserializeObject<CrToken>(content, crunInstance.SettingsJsonSerializerSettings);
|
crunInstance.Token = Helpers.Deserialize<CrToken>(content, crunInstance.SettingsJsonSerializerSettings);
|
||||||
|
|
||||||
|
|
||||||
if (crunInstance.Token != null && crunInstance.Token.expires_in != null){
|
if (crunInstance.Token != null && crunInstance.Token.expires_in != null){
|
||||||
|
|
|
@ -166,7 +166,7 @@ public class CrunchyrollManager{
|
||||||
if (File.Exists(CfgManager.PathCrHistory)){
|
if (File.Exists(CfgManager.PathCrHistory)){
|
||||||
var decompressedJson = CfgManager.DecompressJsonFile(CfgManager.PathCrHistory);
|
var decompressedJson = CfgManager.DecompressJsonFile(CfgManager.PathCrHistory);
|
||||||
if (!string.IsNullOrEmpty(decompressedJson)){
|
if (!string.IsNullOrEmpty(decompressedJson)){
|
||||||
HistoryList = JsonConvert.DeserializeObject<ObservableCollection<HistorySeries>>(decompressedJson) ?? new ObservableCollection<HistorySeries>();
|
HistoryList = Helpers.Deserialize<ObservableCollection<HistorySeries>>(decompressedJson,CrunchyrollManager.Instance.SettingsJsonSerializerSettings) ?? new ObservableCollection<HistorySeries>();
|
||||||
|
|
||||||
foreach (var historySeries in HistoryList){
|
foreach (var historySeries in HistoryList){
|
||||||
historySeries.Init();
|
historySeries.Init();
|
||||||
|
@ -1611,7 +1611,7 @@ public class CrunchyrollManager{
|
||||||
Data = new List<Dictionary<string, Dictionary<string, StreamDetails>>>()
|
Data = new List<Dictionary<string, Dictionary<string, StreamDetails>>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
var playStream = JsonConvert.DeserializeObject<CrunchyStreamData>(responseContent, SettingsJsonSerializerSettings);
|
var playStream = Helpers.Deserialize<CrunchyStreamData>(responseContent, SettingsJsonSerializerSettings);
|
||||||
if (playStream == null) return temppbData;
|
if (playStream == null) return temppbData;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(playStream.Token)){
|
if (!string.IsNullOrEmpty(playStream.Token)){
|
||||||
|
@ -1758,7 +1758,7 @@ public class CrunchyrollManager{
|
||||||
showRequestResponse = await HttpClientReq.Instance.SendHttpRequest(showRequest);
|
showRequestResponse = await HttpClientReq.Instance.SendHttpRequest(showRequest);
|
||||||
|
|
||||||
if (showRequestResponse.IsOk){
|
if (showRequestResponse.IsOk){
|
||||||
CrunchyOldChapter chapterData = JsonConvert.DeserializeObject<CrunchyOldChapter>(showRequestResponse.ResponseContent);
|
CrunchyOldChapter chapterData = Helpers.Deserialize<CrunchyOldChapter>(showRequestResponse.ResponseContent,SettingsJsonSerializerSettings);
|
||||||
|
|
||||||
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||||
|
|
||||||
|
|
|
@ -379,12 +379,22 @@ public class History(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CrSeriesBase? cachedSeries;
|
||||||
|
|
||||||
private async Task RefreshSeriesData(string seriesId, HistorySeries historySeries){
|
private async Task RefreshSeriesData(string seriesId, HistorySeries historySeries){
|
||||||
var series = await crunInstance.CrSeries.SeriesById(seriesId, string.IsNullOrEmpty(crunInstance.CrunOptions.HistoryLang) ? crunInstance.DefaultLocale : crunInstance.CrunOptions.HistoryLang, true);
|
if (cachedSeries == null || (cachedSeries.Data != null && cachedSeries.Data.First().Id != seriesId)){
|
||||||
if (series?.Data != null){
|
cachedSeries = await crunInstance.CrSeries.SeriesById(seriesId, string.IsNullOrEmpty(crunInstance.CrunOptions.HistoryLang) ? crunInstance.DefaultLocale : crunInstance.CrunOptions.HistoryLang, true);
|
||||||
historySeries.SeriesDescription = series.Data.First().Description;
|
} else{
|
||||||
historySeries.ThumbnailImageUrl = GetSeriesThumbnail(series);
|
return;
|
||||||
historySeries.SeriesTitle = series.Data.First().Title;
|
}
|
||||||
|
|
||||||
|
if (cachedSeries?.Data != null){
|
||||||
|
var series = cachedSeries.Data.First();
|
||||||
|
historySeries.SeriesDescription = series.Description;
|
||||||
|
historySeries.ThumbnailImageUrl = GetSeriesThumbnail(cachedSeries);
|
||||||
|
historySeries.SeriesTitle = series.Title;
|
||||||
|
historySeries.HistorySeriesAvailableDubLang = series.AudioLocales;
|
||||||
|
historySeries.HistorySeriesAvailableSoftSubs = series.SubtitleLocales;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,13 +768,13 @@ public class History(){
|
||||||
return highestSimilarity < 0.8 ? null : closestMatch;
|
return highestSimilarity < 0.8 ? null : closestMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double CalculateSimilarity(string source, string target){
|
public double CalculateSimilarity(string source, string target){
|
||||||
int distance = LevenshteinDistance(source, target);
|
int distance = LevenshteinDistance(source, target);
|
||||||
return 1.0 - (double)distance / Math.Max(source.Length, target.Length);
|
return 1.0 - (double)distance / Math.Max(source.Length, target.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int LevenshteinDistance(string source, string target){
|
private int LevenshteinDistance(string source, string target){
|
||||||
if (string.IsNullOrEmpty(source)){
|
if (string.IsNullOrEmpty(source)){
|
||||||
return string.IsNullOrEmpty(target) ? 0 : target.Length;
|
return string.IsNullOrEmpty(target) ? 0 : target.Length;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ using System.Security.Cryptography;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CRD.Downloader;
|
using CRD.Downloader;
|
||||||
|
using CRD.Downloader.Crunchyroll;
|
||||||
using CRD.Utils.Parser.Utils;
|
using CRD.Utils.Parser.Utils;
|
||||||
using CRD.Utils.Structs;
|
using CRD.Utils.Structs;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
@ -62,7 +63,7 @@ public class HlsDownloader{
|
||||||
try{
|
try{
|
||||||
Console.WriteLine("Resume data found! Trying to resume...");
|
Console.WriteLine("Resume data found! Trying to resume...");
|
||||||
string resumeFileContent = File.ReadAllText($"{fn}.resume");
|
string resumeFileContent = File.ReadAllText($"{fn}.resume");
|
||||||
var resumeData = JsonConvert.DeserializeObject<ResumeData>(resumeFileContent);
|
var resumeData = Helpers.Deserialize<ResumeData>(resumeFileContent, null);
|
||||||
|
|
||||||
if (resumeData != null){
|
if (resumeData != null){
|
||||||
if (resumeData.Total == _data.M3U8Json?.Segments.Count &&
|
if (resumeData.Total == _data.M3U8Json?.Segments.Count &&
|
||||||
|
|
|
@ -9,6 +9,7 @@ using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
|
using CRD.Utils.JsonConv;
|
||||||
using CRD.Utils.Structs;
|
using CRD.Utils.Structs;
|
||||||
using CRD.Utils.Structs.Crunchyroll.Music;
|
using CRD.Utils.Structs.Crunchyroll.Music;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
@ -16,15 +17,11 @@ using Newtonsoft.Json;
|
||||||
namespace CRD.Utils;
|
namespace CRD.Utils;
|
||||||
|
|
||||||
public class Helpers{
|
public class Helpers{
|
||||||
/// <summary>
|
|
||||||
/// Deserializes a JSON string into a specified .NET type.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the object to deserialize to.</typeparam>
|
|
||||||
/// <param name="json">The JSON string to deserialize.</param>
|
|
||||||
/// <param name="serializerSettings">The settings for deserialization if null default settings will be used</param>
|
|
||||||
/// <returns>The deserialized object of type T.</returns>
|
|
||||||
public static T? Deserialize<T>(string json, JsonSerializerSettings? serializerSettings){
|
public static T? Deserialize<T>(string json, JsonSerializerSettings? serializerSettings){
|
||||||
try{
|
try{
|
||||||
|
serializerSettings ??= new JsonSerializerSettings();
|
||||||
|
serializerSettings.Converters.Add(new UtcToLocalTimeConverter());
|
||||||
|
|
||||||
return JsonConvert.DeserializeObject<T>(json, serializerSettings);
|
return JsonConvert.DeserializeObject<T>(json, serializerSettings);
|
||||||
} catch (JsonException ex){
|
} catch (JsonException ex){
|
||||||
Console.Error.WriteLine($"Error deserializing JSON: {ex.Message}");
|
Console.Error.WriteLine($"Error deserializing JSON: {ex.Message}");
|
||||||
|
@ -55,26 +52,6 @@ public class Helpers{
|
||||||
return dialogue;
|
return dialogue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ExtractDialogue(string[] lines, int startLine){
|
|
||||||
var dialogueBuilder = new StringBuilder();
|
|
||||||
|
|
||||||
for (int i = startLine; i < lines.Length && !string.IsNullOrWhiteSpace(lines[i]); i++){
|
|
||||||
if (!lines[i].Contains("-->") && !lines[i].StartsWith("STYLE")){
|
|
||||||
string line = lines[i].Trim();
|
|
||||||
// Remove HTML tags and keep the inner text
|
|
||||||
line = Regex.Replace(line, @"<[^>]+>", "");
|
|
||||||
dialogueBuilder.Append(line + "\\N");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the last newline character
|
|
||||||
if (dialogueBuilder.Length > 0){
|
|
||||||
dialogueBuilder.Length -= 2; // Remove the last "\N"
|
|
||||||
}
|
|
||||||
|
|
||||||
return dialogueBuilder.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void OpenUrl(string url){
|
public static void OpenUrl(string url){
|
||||||
try{
|
try{
|
||||||
Process.Start(new ProcessStartInfo{
|
Process.Start(new ProcessStartInfo{
|
||||||
|
@ -423,4 +400,5 @@ public class Helpers{
|
||||||
|
|
||||||
return languageGroups;
|
return languageGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace CRD.Utils.JsonConv;
|
||||||
|
|
||||||
|
public class UtcToLocalTimeConverter : JsonConverter<DateTime>{
|
||||||
|
public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer){
|
||||||
|
return reader.Value switch{
|
||||||
|
null => DateTime.MinValue,
|
||||||
|
DateTime dateTime when dateTime.Kind == DateTimeKind.Utc => dateTime.ToLocalTime(),
|
||||||
|
DateTime dateTime => dateTime,
|
||||||
|
_ => throw new JsonSerializationException($"Unexpected token parsing date. Expected DateTime, got {reader.Value.GetType()}.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer){
|
||||||
|
writer.WriteValue(value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -129,7 +129,10 @@ public class SonarrSeries{
|
||||||
/// The images.
|
/// The images.
|
||||||
/// </value>
|
/// </value>
|
||||||
[JsonProperty("images")]
|
[JsonProperty("images")]
|
||||||
public List<SonarrImage> Images{ get; set; }
|
public List<SonarrImage>? Images{ get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string ImageUrl{ get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the type of the series.
|
/// Gets or sets the type of the series.
|
||||||
|
|
|
@ -109,7 +109,7 @@ public class SonarrClient{
|
||||||
List<SonarrSeries> series = [];
|
List<SonarrSeries> series = [];
|
||||||
|
|
||||||
try{
|
try{
|
||||||
series = JsonConvert.DeserializeObject<List<SonarrSeries>>(json) ?? [];
|
series = Helpers.Deserialize<List<SonarrSeries>>(json,null) ?? [];
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
MainWindow.Instance.ShowError("Sonarr GetSeries error \n" + e);
|
MainWindow.Instance.ShowError("Sonarr GetSeries error \n" + e);
|
||||||
Console.Error.WriteLine("Sonarr GetSeries error \n" + e);
|
Console.Error.WriteLine("Sonarr GetSeries error \n" + e);
|
||||||
|
@ -124,7 +124,7 @@ public class SonarrClient{
|
||||||
List<SonarrEpisode> episodes = [];
|
List<SonarrEpisode> episodes = [];
|
||||||
|
|
||||||
try{
|
try{
|
||||||
episodes = JsonConvert.DeserializeObject<List<SonarrEpisode>>(json) ?? [];
|
episodes = Helpers.Deserialize<List<SonarrEpisode>>(json,null) ?? [];
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
MainWindow.Instance.ShowError("Sonarr GetEpisodes error \n" + e);
|
MainWindow.Instance.ShowError("Sonarr GetEpisodes error \n" + e);
|
||||||
Console.Error.WriteLine("Sonarr GetEpisodes error \n" + e);
|
Console.Error.WriteLine("Sonarr GetEpisodes error \n" + e);
|
||||||
|
@ -138,7 +138,7 @@ public class SonarrClient{
|
||||||
var json = await GetJson($"/v3/episode/id={episodeId}");
|
var json = await GetJson($"/v3/episode/id={episodeId}");
|
||||||
var episode = new SonarrEpisode();
|
var episode = new SonarrEpisode();
|
||||||
try{
|
try{
|
||||||
episode = JsonConvert.DeserializeObject<SonarrEpisode>(json) ?? new SonarrEpisode();
|
episode = Helpers.Deserialize<SonarrEpisode>(json,null) ?? new SonarrEpisode();
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
MainWindow.Instance.ShowError("Sonarr GetEpisode error \n" + e);
|
MainWindow.Instance.ShowError("Sonarr GetEpisode error \n" + e);
|
||||||
Console.Error.WriteLine("Sonarr GetEpisode error \n" + e);
|
Console.Error.WriteLine("Sonarr GetEpisode error \n" + e);
|
||||||
|
|
|
@ -14,7 +14,7 @@ public class StreamError{
|
||||||
|
|
||||||
public static StreamError? FromJson(string json){
|
public static StreamError? FromJson(string json){
|
||||||
try{
|
try{
|
||||||
return JsonConvert.DeserializeObject<StreamError>(json);
|
return Helpers.Deserialize<StreamError>(json,null);
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
Console.Error.WriteLine(e);
|
Console.Error.WriteLine(e);
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -52,6 +52,12 @@ public class HistorySeries : INotifyPropertyChanged{
|
||||||
[JsonProperty("history_series_add_date")]
|
[JsonProperty("history_series_add_date")]
|
||||||
public DateTime? HistorySeriesAddDate{ get; set; }
|
public DateTime? HistorySeriesAddDate{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("history_series_available_soft_subs")]
|
||||||
|
public List<string> HistorySeriesAvailableSoftSubs{ get; set; } =[];
|
||||||
|
|
||||||
|
[JsonProperty("history_series_available_dub_lang")]
|
||||||
|
public List<string> HistorySeriesAvailableDubLang{ get; set; } =[];
|
||||||
|
|
||||||
[JsonProperty("history_series_soft_subs_override")]
|
[JsonProperty("history_series_soft_subs_override")]
|
||||||
public List<string> HistorySeriesSoftSubsOverride{ get; set; } =[];
|
public List<string> HistorySeriesSoftSubsOverride{ get; set; } =[];
|
||||||
|
|
||||||
|
|
|
@ -49,9 +49,9 @@ public class Updater : INotifyPropertyChanged{
|
||||||
public async Task<bool> CheckForUpdatesAsync(){
|
public async Task<bool> CheckForUpdatesAsync(){
|
||||||
try{
|
try{
|
||||||
using (var client = new HttpClient()){
|
using (var client = new HttpClient()){
|
||||||
client.DefaultRequestHeaders.Add("User-Agent", "C# App"); // GitHub API requires a user agent
|
client.DefaultRequestHeaders.Add("User-Agent", "C# App");
|
||||||
var response = await client.GetStringAsync(apiEndpoint);
|
var response = await client.GetStringAsync(apiEndpoint);
|
||||||
var releaseInfo = JsonConvert.DeserializeObject<dynamic>(response);
|
var releaseInfo = Helpers.Deserialize<dynamic>(response,null);
|
||||||
|
|
||||||
var latestVersion = releaseInfo.tag_name;
|
var latestVersion = releaseInfo.tag_name;
|
||||||
downloadUrl = releaseInfo.assets[0].browser_download_url;
|
downloadUrl = releaseInfo.assets[0].browser_download_url;
|
||||||
|
@ -63,11 +63,11 @@ public class Updater : INotifyPropertyChanged{
|
||||||
if (latestVersion != currentVersion){
|
if (latestVersion != currentVersion){
|
||||||
Console.WriteLine("Update available: " + latestVersion + " - Current Version: " + currentVersion);
|
Console.WriteLine("Update available: " + latestVersion + " - Current Version: " + currentVersion);
|
||||||
return true;
|
return true;
|
||||||
} else{
|
}
|
||||||
|
|
||||||
Console.WriteLine("No updates available.");
|
Console.WriteLine("No updates available.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
Console.Error.WriteLine("Failed to get Update information");
|
Console.Error.WriteLine("Failed to get Update information");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -111,7 +111,7 @@ public partial class AccountPageViewModel : ViewModelBase{
|
||||||
CloseButtonText = "Close"
|
CloseButtonText = "Close"
|
||||||
};
|
};
|
||||||
|
|
||||||
var viewModel = new ContentDialogInputLoginViewModel(dialog, this);
|
var viewModel = new Utils.ContentDialogInputLoginViewModel(dialog, this);
|
||||||
dialog.Content = new ContentDialogInputLoginView(){
|
dialog.Content = new ContentDialogInputLoginView(){
|
||||||
DataContext = viewModel
|
DataContext = viewModel
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,10 @@ using CRD.Utils;
|
||||||
using CRD.Utils.Sonarr;
|
using CRD.Utils.Sonarr;
|
||||||
using CRD.Utils.Structs;
|
using CRD.Utils.Structs;
|
||||||
using CRD.Utils.Structs.History;
|
using CRD.Utils.Structs.History;
|
||||||
|
using CRD.ViewModels.Utils;
|
||||||
using CRD.Views;
|
using CRD.Views;
|
||||||
|
using CRD.Views.Utils;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace CRD.ViewModels;
|
namespace CRD.ViewModels;
|
||||||
|
@ -28,24 +31,38 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public static bool _sonarrAvailable;
|
public static bool _sonarrAvailable;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public static bool _sonarrConnected;
|
||||||
|
|
||||||
private IStorageProvider? _storageProvider;
|
private IStorageProvider? _storageProvider;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _availableDubs;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _availableSubs;
|
||||||
|
|
||||||
public SeriesPageViewModel(){
|
public SeriesPageViewModel(){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_selectedSeries = CrunchyrollManager.Instance.SelectedSeries;
|
_selectedSeries = CrunchyrollManager.Instance.SelectedSeries;
|
||||||
|
|
||||||
if (_selectedSeries.ThumbnailImage == null){
|
if (_selectedSeries.ThumbnailImage == null){
|
||||||
_selectedSeries.LoadImage();
|
_selectedSeries.LoadImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(SelectedSeries.SonarrSeriesId) && CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null){
|
if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null){
|
||||||
SonarrAvailable = SelectedSeries.SonarrSeriesId.Length > 0 && CrunchyrollManager.Instance.CrunOptions.SonarrProperties.SonarrEnabled;
|
SonarrConnected = CrunchyrollManager.Instance.CrunOptions.SonarrProperties.SonarrEnabled;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(SelectedSeries.SonarrSeriesId)){
|
||||||
|
SonarrAvailable = SelectedSeries.SonarrSeriesId.Length > 0 && SonarrConnected;
|
||||||
} else{
|
} else{
|
||||||
SonarrAvailable = false;
|
SonarrAvailable = false;
|
||||||
}
|
}
|
||||||
|
} else{
|
||||||
|
SonarrConnected = SonarrAvailable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AvailableDubs = "Available Dubs: " + string.Join(", ", SelectedSeries.HistorySeriesAvailableDubLang);
|
||||||
|
AvailableSubs = "Available Subs: " + string.Join(", ", SelectedSeries.HistorySeriesAvailableSoftSubs);
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
|
@ -79,6 +96,44 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
||||||
_storageProvider = storageProvider ?? throw new ArgumentNullException(nameof(storageProvider));
|
_storageProvider = storageProvider ?? throw new ArgumentNullException(nameof(storageProvider));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
public async Task MatchSonarrSeries_Button(){
|
||||||
|
var dialog = new ContentDialog(){
|
||||||
|
Title = "Sonarr Matching",
|
||||||
|
PrimaryButtonText = "Save",
|
||||||
|
CloseButtonText = "Close",
|
||||||
|
FullSizeDesired = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var viewModel = new ContentDialogSonarrMatchViewModel(dialog, SelectedSeries.SonarrSeriesId,SelectedSeries.SeriesTitle);
|
||||||
|
dialog.Content = new ContentDialogSonarrMatchView(){
|
||||||
|
DataContext = viewModel
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialogResult = await dialog.ShowAsync();
|
||||||
|
|
||||||
|
if (dialogResult == ContentDialogResult.Primary){
|
||||||
|
SelectedSeries.SonarrSeriesId = viewModel.CurrentSonarrSeries.Id.ToString();
|
||||||
|
SelectedSeries.SonarrTvDbId = viewModel.CurrentSonarrSeries.TvdbId.ToString();
|
||||||
|
SelectedSeries.SonarrSlugTitle = viewModel.CurrentSonarrSeries.TitleSlug;
|
||||||
|
|
||||||
|
if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null){
|
||||||
|
SonarrConnected = CrunchyrollManager.Instance.CrunOptions.SonarrProperties.SonarrEnabled;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(SelectedSeries.SonarrSeriesId)){
|
||||||
|
SonarrAvailable = SelectedSeries.SonarrSeriesId.Length > 0 && SonarrConnected;
|
||||||
|
} else{
|
||||||
|
SonarrAvailable = false;
|
||||||
|
}
|
||||||
|
} else{
|
||||||
|
SonarrConnected = SonarrAvailable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateData("");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
public async Task DownloadSeasonAll(HistorySeason season){
|
public async Task DownloadSeasonAll(HistorySeason season){
|
||||||
|
@ -112,6 +167,9 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
||||||
|
|
||||||
SelectedSeries.Seasons.Refresh();
|
SelectedSeries.Seasons.Refresh();
|
||||||
|
|
||||||
|
AvailableDubs = "Available Dubs: " + string.Join(", ", SelectedSeries.HistorySeriesAvailableDubLang);
|
||||||
|
AvailableSubs = "Available Subs: " + string.Join(", ", SelectedSeries.HistorySeriesAvailableSoftSubs);
|
||||||
|
|
||||||
// MessageBus.Current.SendMessage(new NavigationMessage(typeof(SeriesPageViewModel), false, true));
|
// MessageBus.Current.SendMessage(new NavigationMessage(typeof(SeriesPageViewModel), false, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +180,7 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
||||||
SelectedSeries.Seasons.Remove(objectToRemove);
|
SelectedSeries.Seasons.Remove(objectToRemove);
|
||||||
CfgManager.UpdateHistoryFile();
|
CfgManager.UpdateHistoryFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageBus.Current.SendMessage(new NavigationMessage(typeof(SeriesPageViewModel), false, true));
|
MessageBus.Current.SendMessage(new NavigationMessage(typeof(SeriesPageViewModel), false, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CRD.Downloader;
|
|
||||||
using CRD.Downloader.Crunchyroll;
|
using CRD.Downloader.Crunchyroll;
|
||||||
using CRD.Utils;
|
|
||||||
using CRD.Utils.Structs;
|
using CRD.Utils.Structs;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
|
|
||||||
namespace CRD.ViewModels;
|
namespace CRD.ViewModels.Utils;
|
||||||
|
|
||||||
public partial class ContentDialogInputLoginViewModel : ViewModelBase{
|
public partial class ContentDialogInputLoginViewModel : ViewModelBase{
|
||||||
private readonly ContentDialog dialog;
|
private readonly ContentDialog dialog;
|
|
@ -0,0 +1,101 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CRD.Downloader.Crunchyroll;
|
||||||
|
using CRD.Utils;
|
||||||
|
using CRD.Utils.Sonarr;
|
||||||
|
using CRD.Utils.Sonarr.Models;
|
||||||
|
using DynamicData;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
|
||||||
|
namespace CRD.ViewModels.Utils;
|
||||||
|
|
||||||
|
public partial class ContentDialogSonarrMatchViewModel : ViewModelBase{
|
||||||
|
private readonly ContentDialog dialog;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private SonarrSeries _currentSonarrSeries;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private Bitmap? _currentSeriesImage;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private SonarrSeries _selectedItem;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private ObservableCollection<SonarrSeries> _sonarrSeriesList = new();
|
||||||
|
|
||||||
|
public ContentDialogSonarrMatchViewModel(ContentDialog dialog, string? currentSonarrId, string? seriesTitle){
|
||||||
|
if (dialog is null){
|
||||||
|
throw new ArgumentNullException(nameof(dialog));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dialog = dialog;
|
||||||
|
dialog.Closed += DialogOnClosed;
|
||||||
|
dialog.PrimaryButtonClick += SaveButton;
|
||||||
|
|
||||||
|
CurrentSonarrSeries = SonarrClient.Instance.SonarrSeries.Find(e => e.Id.ToString() == currentSonarrId) ?? new SonarrSeries(){ Title = "No series matched" };
|
||||||
|
|
||||||
|
SetImageUrl(CurrentSonarrSeries);
|
||||||
|
|
||||||
|
LoadList(seriesTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveButton(ContentDialog sender, ContentDialogButtonClickEventArgs args){
|
||||||
|
dialog.PrimaryButtonClick -= SaveButton;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadList(string? title){
|
||||||
|
var list = PopulateSeriesList(title);
|
||||||
|
SonarrSeriesList.AddRange(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SonarrSeries> PopulateSeriesList(string? title){
|
||||||
|
var seriesList = SonarrClient.Instance.SonarrSeries.ToList();
|
||||||
|
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(title)){
|
||||||
|
seriesList.Sort((series1, series2) => {
|
||||||
|
double similarity1 = Helpers.CalculateCosineSimilarity(series1.Title.ToLower(), title.ToLower());
|
||||||
|
double similarity2 = Helpers.CalculateCosineSimilarity(series2.Title.ToLower(), title.ToLower());
|
||||||
|
|
||||||
|
return similarity2.CompareTo(similarity1);
|
||||||
|
});
|
||||||
|
} else{
|
||||||
|
seriesList.Sort((series1, series2) => string.Compare(series1.Title, series2.Title, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
seriesList = seriesList.Take(20).ToList();
|
||||||
|
|
||||||
|
foreach (var sonarrSeries in seriesList){
|
||||||
|
SetImageUrl(sonarrSeries);
|
||||||
|
}
|
||||||
|
|
||||||
|
return seriesList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetImageUrl(SonarrSeries sonarrSeries){
|
||||||
|
var properties = CrunchyrollManager.Instance.CrunOptions.SonarrProperties;
|
||||||
|
if (properties == null || sonarrSeries.Images == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseUrl = "";
|
||||||
|
baseUrl = $"http{(properties.UseSsl ? "s" : "")}://{(!string.IsNullOrEmpty(properties.Host) ? properties.Host : "localhost")}:{properties.Port}{(properties.UrlBase ?? "")}";
|
||||||
|
|
||||||
|
sonarrSeries.ImageUrl = baseUrl + sonarrSeries.Images.Find(e => e.CoverType == SonarrCoverType.Poster)?.Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
partial void OnSelectedItemChanged(SonarrSeries value){
|
||||||
|
CurrentSonarrSeries = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DialogOnClosed(ContentDialog sender, ContentDialogClosedEventArgs args){
|
||||||
|
dialog.Closed -= DialogOnClosed;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CRD.Downloader;
|
|
||||||
using CRD.Utils.Structs;
|
|
||||||
using CRD.Utils.Updater;
|
using CRD.Utils.Updater;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
|
|
||||||
namespace CRD.ViewModels;
|
namespace CRD.ViewModels.Utils;
|
||||||
|
|
||||||
public partial class ContentDialogUpdateViewModel : ViewModelBase{
|
public partial class ContentDialogUpdateViewModel : ViewModelBase{
|
||||||
private readonly ContentDialog dialog;
|
private readonly ContentDialog dialog;
|
|
@ -9,6 +9,7 @@ using FluentAvalonia.Core;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
using FluentAvalonia.UI.Windowing;
|
using FluentAvalonia.UI.Windowing;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
using ContentDialogUpdateViewModel = CRD.ViewModels.Utils.ContentDialogUpdateViewModel;
|
||||||
|
|
||||||
namespace CRD.Views;
|
namespace CRD.Views;
|
||||||
|
|
||||||
|
|
|
@ -27,23 +27,36 @@
|
||||||
|
|
||||||
<Button Grid.Row="0" Grid.Column="0" Command="{Binding NavBack}" Margin="0 0 0 10">Back</Button>
|
<Button Grid.Row="0" Grid.Column="0" Command="{Binding NavBack}" Margin="0 0 0 10">Back</Button>
|
||||||
|
|
||||||
<Image Grid.Row="1" Grid.Column="0" Margin="10" Source="{Binding SelectedSeries.ThumbnailImage}" Width="240"
|
<ScrollViewer Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
|
||||||
|
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
|
||||||
|
<Grid Margin="10" VerticalAlignment="Top" >
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Image Grid.Column="0" Margin="10" Source="{Binding SelectedSeries.ThumbnailImage}" Width="240"
|
||||||
Height="360">
|
Height="360">
|
||||||
</Image>
|
</Image>
|
||||||
|
|
||||||
|
|
||||||
<Grid Grid.Row="1" Grid.Column="1">
|
<Grid Grid.Column="1">
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<TextBlock Grid.Row="0" FontSize="50" Text="{Binding SelectedSeries.SeriesTitle}"></TextBlock>
|
<TextBlock Grid.Row="0" FontSize="50" Text="{Binding SelectedSeries.SeriesTitle}"></TextBlock>
|
||||||
<TextBlock Grid.Row="1" FontSize="20" TextWrapping="Wrap" Text="{Binding SelectedSeries.SeriesDescription}"></TextBlock>
|
<TextBlock Grid.Row="1" FontSize="20" TextWrapping="Wrap" Text="{Binding SelectedSeries.SeriesDescription}"></TextBlock>
|
||||||
<StackPanel Grid.Row="3" Orientation="Vertical">
|
<TextBlock Grid.Row="3" FontSize="15" Opacity="0.8" TextWrapping="Wrap" Text="{Binding AvailableDubs}"></TextBlock>
|
||||||
|
<TextBlock Grid.Row="4" FontSize="15" Opacity="0.8" TextWrapping="Wrap" Text="{Binding AvailableSubs}"></TextBlock>
|
||||||
|
<StackPanel Grid.Row="5" Orientation="Vertical">
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" Margin="0 10 10 10">
|
<StackPanel Orientation="Horizontal" Margin="0 10 10 10">
|
||||||
|
|
||||||
|
@ -190,6 +203,18 @@
|
||||||
</Popup>
|
</Popup>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel IsVisible="{Binding EditMode}">
|
||||||
|
<Button Width="30" Height="30" Margin="0 0 10 0"
|
||||||
|
BorderThickness="0"
|
||||||
|
IsVisible="{Binding SonarrConnected}"
|
||||||
|
Command="{Binding MatchSonarrSeries_Button}">
|
||||||
|
<Grid>
|
||||||
|
<controls:ImageIcon Source="../Assets/sonarr.png" Width="25" Height="25" />
|
||||||
|
</Grid>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
@ -197,9 +222,9 @@
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
<ScrollViewer Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
|
|
||||||
<ItemsControl ItemsSource="{Binding SelectedSeries.Seasons}">
|
<ItemsControl ItemsSource="{Binding SelectedSeries.Seasons}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
|
@ -485,6 +510,11 @@
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:vm="clr-namespace:CRD.ViewModels"
|
xmlns:vm="clr-namespace:CRD.ViewModels"
|
||||||
x:DataType="vm:ContentDialogInputLoginViewModel"
|
xmlns:utils="clr-namespace:CRD.ViewModels.Utils"
|
||||||
|
x:DataType="utils:ContentDialogInputLoginViewModel"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="CRD.Views.Utils.ContentDialogInputLoginView">
|
x:Class="CRD.Views.Utils.ContentDialogInputLoginView">
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
<controls:ContentDialog xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:vm="clr-namespace:CRD.ViewModels.Utils"
|
||||||
|
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:models="clr-namespace:CRD.Utils.Sonarr.Models"
|
||||||
|
xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia"
|
||||||
|
x:DataType="vm:ContentDialogSonarrMatchViewModel"
|
||||||
|
x:Class="CRD.Views.Utils.ContentDialogSonarrMatchView">
|
||||||
|
|
||||||
|
<Grid HorizontalAlignment="Stretch" MaxHeight="500" >
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Border Grid.Column="0" Grid.Row="1" CornerRadius="10" Background="{DynamicResource ButtonBackground}">
|
||||||
|
<Grid Margin="10" VerticalAlignment="Top">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<!-- Image -->
|
||||||
|
<Image Grid.Column="0" Margin="10" asyncImageLoader:ImageLoader.Source="{Binding CurrentSonarrSeries.ImageUrl}" MaxWidth="120" MaxHeight="180"></Image>
|
||||||
|
|
||||||
|
<Grid Grid.Column="1" Margin="10">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" /> <!-- Takes up most space for the title -->
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<!-- Takes up space as needed for the time -->
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding CurrentSonarrSeries.Title}" FontWeight="Bold"
|
||||||
|
FontSize="16"
|
||||||
|
TextWrapping="Wrap" VerticalAlignment="Center" />
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding CurrentSonarrSeries.Year}" FontStyle="Italic"
|
||||||
|
HorizontalAlignment="Right" VerticalAlignment="Center" />
|
||||||
|
<TextBlock Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"
|
||||||
|
Text="{Binding CurrentSonarrSeries.Overview}"
|
||||||
|
FontStyle="Italic" Opacity="0.8" TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" FontSize="1" Text=" " Width="1500" Opacity="0" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- <Rectangle Grid.Row="2" Width="1500" Height="0" Fill="Gray" Margin="10,0" /> -->
|
||||||
|
<!-- <TextBlock Grid.Column="0" Grid.Row="2" Text="Series"></TextBlock> -->
|
||||||
|
|
||||||
|
<ListBox Grid.Row="3" SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding SonarrSeriesList}">
|
||||||
|
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type models:SonarrSeries}">
|
||||||
|
<StackPanel>
|
||||||
|
<Border Padding="10" Margin="5" BorderThickness="1">
|
||||||
|
<Grid Margin="10" VerticalAlignment="Top">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<!-- Image -->
|
||||||
|
<asyncImageLoader:AdvancedImage Grid.Column="0" MaxWidth="120" MaxHeight="180" Source="{Binding ImageUrl}"
|
||||||
|
Stretch="Fill" />
|
||||||
|
|
||||||
|
<!-- Text Content -->
|
||||||
|
<Grid Grid.Column="1" Margin="10" VerticalAlignment="Top">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" /> <!-- Takes up most space for the title -->
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<!-- Takes up space as needed for the time -->
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="{Binding Title}" FontWeight="Bold"
|
||||||
|
FontSize="16"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Year}" FontStyle="Italic"
|
||||||
|
HorizontalAlignment="Right" TextWrapping="Wrap" />
|
||||||
|
<TextBlock Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"
|
||||||
|
Text="{Binding Overview}"
|
||||||
|
FontStyle="Italic" Opacity="0.8" TextWrapping="Wrap" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
<Border Background="LightGray" Height="1" Margin="0,5" HorizontalAlignment="Stretch" />
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
|
||||||
|
</ListBox>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</controls:ContentDialog>
|
|
@ -0,0 +1,11 @@
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace CRD.Views.Utils;
|
||||||
|
|
||||||
|
public partial class ContentDialogSonarrMatchView : UserControl{
|
||||||
|
public ContentDialogSonarrMatchView(){
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,8 @@
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:vm="clr-namespace:CRD.ViewModels"
|
xmlns:vm="clr-namespace:CRD.ViewModels"
|
||||||
x:DataType="vm:ContentDialogUpdateViewModel"
|
xmlns:utils="clr-namespace:CRD.ViewModels.Utils"
|
||||||
|
x:DataType="utils:ContentDialogUpdateViewModel"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="CRD.Views.Utils.ContentDialogUpdateView">
|
x:Class="CRD.Views.Utils.ContentDialogUpdateView">
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue