Add - Added Table view to history tab
Add - Added Sorting to history tab Add - Added next air date to history posters Add - Added scale slider to history tab for posters Chg - Auto download always starts new downloads even when you are not on the download queue tab Chg - Small size adjustment for the text in the calendar tab Fix - Simultaneous downloads set to 1 and auto download didn't work together Fix - Finished downloads didn't resume correctly
This commit is contained in:
parent
89c7b1021f
commit
9d66eb34c9
|
@ -356,7 +356,7 @@ public class CrSeries(){
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CrSeriesSearch?> ParseSeriesById(string id,string? locale){
|
public async Task<CrSeriesSearch?> ParseSeriesById(string id,string? locale,bool forced = false){
|
||||||
if (crunInstance.CmsToken?.Cms == null){
|
if (crunInstance.CmsToken?.Cms == null){
|
||||||
Console.Error.WriteLine("Missing CMS Access Token");
|
Console.Error.WriteLine("Missing CMS Access Token");
|
||||||
return null;
|
return null;
|
||||||
|
@ -366,7 +366,11 @@ public class CrSeries(){
|
||||||
|
|
||||||
query["preferred_audio_language"] = "ja-JP";
|
query["preferred_audio_language"] = "ja-JP";
|
||||||
if (!string.IsNullOrEmpty(locale)){
|
if (!string.IsNullOrEmpty(locale)){
|
||||||
query["locale"] = Languages.Locale2language(locale).CrLocale;
|
query["locale"] = Languages.Locale2language(locale).CrLocale;
|
||||||
|
if (forced){
|
||||||
|
query["force_locale"] = Languages.Locale2language(locale).CrLocale;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ using CRD.Utils.Muxing;
|
||||||
using CRD.Utils.Sonarr;
|
using CRD.Utils.Sonarr;
|
||||||
using CRD.Utils.Sonarr.Models;
|
using CRD.Utils.Sonarr.Models;
|
||||||
using CRD.Utils.Structs;
|
using CRD.Utils.Structs;
|
||||||
|
using CRD.Utils.Structs.History;
|
||||||
using CRD.ViewModels;
|
using CRD.ViewModels;
|
||||||
using CRD.Views;
|
using CRD.Views;
|
||||||
using HtmlAgilityPack;
|
using HtmlAgilityPack;
|
||||||
|
@ -104,8 +105,9 @@ public class Crunchyroll{
|
||||||
|
|
||||||
public Crunchyroll(){
|
public Crunchyroll(){
|
||||||
CrunOptions = new CrDownloadOptions();
|
CrunOptions = new CrDownloadOptions();
|
||||||
|
Queue.CollectionChanged += UpdateItemListOnRemove;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Init(){
|
public async Task Init(){
|
||||||
_widevine = Widevine.Instance;
|
_widevine = Widevine.Instance;
|
||||||
|
|
||||||
|
@ -199,6 +201,41 @@ public class Crunchyroll{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateItemListOnRemove(object? sender, NotifyCollectionChangedEventArgs e){
|
||||||
|
if (e.Action == NotifyCollectionChangedAction.Remove){
|
||||||
|
if (e.OldItems != null)
|
||||||
|
foreach (var eOldItem in e.OldItems){
|
||||||
|
var downloadItem = DownloadItemModels.FirstOrDefault(e => e.epMeta.Equals(eOldItem));
|
||||||
|
if (downloadItem != null){
|
||||||
|
DownloadItemModels.Remove(downloadItem);
|
||||||
|
} else{
|
||||||
|
Console.Error.WriteLine("Failed to Remove Episode from list");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateDownloadListItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateDownloadListItems(){
|
||||||
|
var list = Queue;
|
||||||
|
|
||||||
|
foreach (CrunchyEpMeta crunchyEpMeta in list){
|
||||||
|
var downloadItem = DownloadItemModels.FirstOrDefault(e => e.epMeta.Equals(crunchyEpMeta));
|
||||||
|
if (downloadItem != null){
|
||||||
|
downloadItem.Refresh();
|
||||||
|
} else{
|
||||||
|
downloadItem = new DownloadItemModel(crunchyEpMeta);
|
||||||
|
downloadItem.LoadImage();
|
||||||
|
DownloadItemModels.Add(downloadItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (downloadItem is{ isDownloading: false, Error: false } && CrunOptions.AutoDownload && ActiveDownloads < CrunOptions.SimultaneousDownloads){
|
||||||
|
downloadItem.StartDownload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<CalendarWeek> GetCalendarForDate(string weeksMondayDate, bool forceUpdate){
|
public async Task<CalendarWeek> GetCalendarForDate(string weeksMondayDate, bool forceUpdate){
|
||||||
if (!forceUpdate && calendar.TryGetValue(weeksMondayDate, out var forDate)){
|
if (!forceUpdate && calendar.TryGetValue(weeksMondayDate, out var forDate)){
|
||||||
|
@ -447,13 +484,12 @@ public class Crunchyroll{
|
||||||
if (CrunOptions.RemoveFinishedDownload){
|
if (CrunOptions.RemoveFinishedDownload){
|
||||||
Queue.Remove(data);
|
Queue.Remove(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
Queue.Refresh();
|
|
||||||
} else{
|
} else{
|
||||||
Console.WriteLine("Skipping mux");
|
Console.WriteLine("Skipping mux");
|
||||||
}
|
}
|
||||||
|
|
||||||
ActiveDownloads--;
|
ActiveDownloads--;
|
||||||
|
Queue.Refresh();
|
||||||
|
|
||||||
if (CrunOptions.History && data.Data != null && data.Data.Count > 0){
|
if (CrunOptions.History && data.Data != null && data.Data.Count > 0){
|
||||||
CrHistory.SetAsDownloaded(data.ShowId, data.SeasonId, data.Data.First().MediaId);
|
CrHistory.SetAsDownloaded(data.ShowId, data.SeasonId, data.Data.First().MediaId);
|
||||||
|
@ -506,7 +542,7 @@ public class Crunchyroll{
|
||||||
Console.Error.WriteLine("No xml description file found to mux description");
|
Console.Error.WriteLine("No xml description file found to mux description");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var merger = new Merger(new MergerOptions{
|
var merger = new Merger(new MergerOptions{
|
||||||
OnlyVid = data.Where(a => a.Type == DownloadMediaType.Video).Select(a => new MergerInput{ Language = a.Lang, Path = a.Path ?? string.Empty }).ToList(),
|
OnlyVid = data.Where(a => a.Type == DownloadMediaType.Video).Select(a => new MergerInput{ Language = a.Lang, Path = a.Path ?? string.Empty }).ToList(),
|
||||||
|
@ -528,7 +564,7 @@ public class Crunchyroll{
|
||||||
},
|
},
|
||||||
CcTag = options.CcTag,
|
CcTag = options.CcTag,
|
||||||
mp3 = muxToMp3,
|
mp3 = muxToMp3,
|
||||||
MuxDescription = muxDesc
|
Description = data.Where(a => a.Type == DownloadMediaType.Description).Select(a => new MergerInput{ Path = a.Path ?? string.Empty }).ToList(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!File.Exists(CfgManager.PathFFMPEG)){
|
if (!File.Exists(CfgManager.PathFFMPEG)){
|
||||||
|
@ -1380,6 +1416,11 @@ public class Crunchyroll{
|
||||||
writer.WriteEndElement(); // End Tags
|
writer.WriteEndElement(); // End Tags
|
||||||
writer.WriteEndDocument();
|
writer.WriteEndDocument();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
files.Add(new DownloadedMedia{
|
||||||
|
Type = DownloadMediaType.Description,
|
||||||
|
Path = fullPath,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"{fileName} has been created with the description.");
|
Console.WriteLine($"{fileName} has been created with the description.");
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
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 CommunityToolkit.Mvvm.Input;
|
||||||
using CRD.Utils;
|
using CRD.Utils;
|
||||||
using CRD.Utils.Sonarr;
|
using CRD.Utils.Sonarr;
|
||||||
using CRD.Utils.Sonarr.Models;
|
using CRD.Utils.Sonarr.Models;
|
||||||
using CRD.Utils.Structs;
|
using CRD.Utils.Structs;
|
||||||
|
using CRD.Utils.Structs.History;
|
||||||
using CRD.Views;
|
using CRD.Views;
|
||||||
|
using DynamicData;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
|
@ -22,7 +27,7 @@ public class History(){
|
||||||
public async Task UpdateSeries(string seriesId, string? seasonId){
|
public async Task UpdateSeries(string seriesId, string? seasonId){
|
||||||
await crunInstance.CrAuth.RefreshToken(true);
|
await crunInstance.CrAuth.RefreshToken(true);
|
||||||
|
|
||||||
CrSeriesSearch? parsedSeries = await crunInstance.CrSeries.ParseSeriesById(seriesId, "ja");
|
CrSeriesSearch? parsedSeries = await crunInstance.CrSeries.ParseSeriesById(seriesId, "ja", true);
|
||||||
|
|
||||||
if (parsedSeries == null){
|
if (parsedSeries == null){
|
||||||
Console.Error.WriteLine("Parse Data Invalid");
|
Console.Error.WriteLine("Parse Data Invalid");
|
||||||
|
@ -64,7 +69,7 @@ public class History(){
|
||||||
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
|
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
|
||||||
|
|
||||||
if (historySeries != null){
|
if (historySeries != null){
|
||||||
var historySeason = historySeries.Seasons.Find(s => s.SeasonId == seasonId);
|
var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == seasonId);
|
||||||
|
|
||||||
if (historySeason != null){
|
if (historySeason != null){
|
||||||
var historyEpisode = historySeason.EpisodesList.Find(e => e.EpisodeId == episodeId);
|
var historyEpisode = historySeason.EpisodesList.Find(e => e.EpisodeId == episodeId);
|
||||||
|
@ -84,7 +89,7 @@ public class History(){
|
||||||
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
|
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
|
||||||
|
|
||||||
if (historySeries != null){
|
if (historySeries != null){
|
||||||
var historySeason = historySeries.Seasons.Find(s => s.SeasonId == seasonId);
|
var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == seasonId);
|
||||||
|
|
||||||
if (historySeason != null){
|
if (historySeason != null){
|
||||||
var historyEpisode = historySeason.EpisodesList.Find(e => e.EpisodeId == episodeId);
|
var historyEpisode = historySeason.EpisodesList.Find(e => e.EpisodeId == episodeId);
|
||||||
|
@ -102,9 +107,9 @@ public class History(){
|
||||||
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
|
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
|
||||||
|
|
||||||
var downloadDirPath = "";
|
var downloadDirPath = "";
|
||||||
|
|
||||||
if (historySeries != null){
|
if (historySeries != null){
|
||||||
var historySeason = historySeries.Seasons.Find(s => s.SeasonId == seasonId);
|
var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == seasonId);
|
||||||
if (!string.IsNullOrEmpty(historySeries.SeriesDownloadPath)){
|
if (!string.IsNullOrEmpty(historySeries.SeriesDownloadPath)){
|
||||||
downloadDirPath = historySeries.SeriesDownloadPath;
|
downloadDirPath = historySeries.SeriesDownloadPath;
|
||||||
}
|
}
|
||||||
|
@ -145,7 +150,7 @@ public class History(){
|
||||||
var seriesId = episode.SeriesId;
|
var seriesId = episode.SeriesId;
|
||||||
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
|
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
|
||||||
if (historySeries != null){
|
if (historySeries != null){
|
||||||
var historySeason = historySeries.Seasons.Find(s => s.SeasonId == episode.SeasonId);
|
var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == episode.SeasonId);
|
||||||
|
|
||||||
var series = await crunInstance.CrSeries.SeriesById(seriesId);
|
var series = await crunInstance.CrSeries.SeriesById(seriesId);
|
||||||
if (series?.Data != null){
|
if (series?.Data != null){
|
||||||
|
@ -174,7 +179,7 @@ public class History(){
|
||||||
|
|
||||||
historySeries.Seasons.Add(newSeason);
|
historySeries.Seasons.Add(newSeason);
|
||||||
|
|
||||||
historySeries.Seasons = historySeries.Seasons.OrderBy(s => s.SeasonNum != null ? int.Parse(s.SeasonNum) : 0).ToList();
|
SortSeasons(historySeries);
|
||||||
}
|
}
|
||||||
|
|
||||||
historySeries.UpdateNewEpisodes();
|
historySeries.UpdateNewEpisodes();
|
||||||
|
@ -198,11 +203,7 @@ public class History(){
|
||||||
historySeries.UpdateNewEpisodes();
|
historySeries.UpdateNewEpisodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
var sortedList = crunInstance.HistoryList.OrderBy(item => item.SeriesTitle).ToList();
|
SortItems();
|
||||||
crunInstance.HistoryList.Clear();
|
|
||||||
foreach (var item in sortedList){
|
|
||||||
crunInstance.HistoryList.Add(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
MatchHistorySeriesWithSonarr(false);
|
MatchHistorySeriesWithSonarr(false);
|
||||||
await MatchHistoryEpisodesWithSonarr(false, historySeries);
|
await MatchHistoryEpisodesWithSonarr(false, historySeries);
|
||||||
|
@ -215,7 +216,7 @@ public class History(){
|
||||||
var seriesId = firstEpisode.SeriesId;
|
var seriesId = firstEpisode.SeriesId;
|
||||||
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
|
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
|
||||||
if (historySeries != null){
|
if (historySeries != null){
|
||||||
var historySeason = historySeries.Seasons.Find(s => s.SeasonId == firstEpisode.SeasonId);
|
var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == firstEpisode.SeasonId);
|
||||||
var series = await crunInstance.CrSeries.SeriesById(seriesId);
|
var series = await crunInstance.CrSeries.SeriesById(seriesId);
|
||||||
if (series?.Data != null){
|
if (series?.Data != null){
|
||||||
historySeries.SeriesTitle = series.Data.First().Title;
|
historySeries.SeriesTitle = series.Data.First().Title;
|
||||||
|
@ -257,7 +258,7 @@ public class History(){
|
||||||
|
|
||||||
historySeries.Seasons.Add(newSeason);
|
historySeries.Seasons.Add(newSeason);
|
||||||
|
|
||||||
historySeries.Seasons = historySeries.Seasons.OrderBy(s => s.SeasonNum != null ? int.Parse(s.SeasonNum) : 0).ToList();
|
SortSeasons(historySeries);
|
||||||
}
|
}
|
||||||
|
|
||||||
historySeries.UpdateNewEpisodes();
|
historySeries.UpdateNewEpisodes();
|
||||||
|
@ -286,11 +287,7 @@ public class History(){
|
||||||
historySeries.UpdateNewEpisodes();
|
historySeries.UpdateNewEpisodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
var sortedList = crunInstance.HistoryList.OrderBy(item => item.SeriesTitle).ToList();
|
SortItems();
|
||||||
crunInstance.HistoryList.Clear();
|
|
||||||
foreach (var item in sortedList){
|
|
||||||
crunInstance.HistoryList.Add(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
MatchHistorySeriesWithSonarr(false);
|
MatchHistorySeriesWithSonarr(false);
|
||||||
await MatchHistoryEpisodesWithSonarr(false, historySeries);
|
await MatchHistoryEpisodesWithSonarr(false, historySeries);
|
||||||
|
@ -298,6 +295,66 @@ public class History(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SortSeasons(HistorySeries series){
|
||||||
|
var sortedSeasons = series.Seasons
|
||||||
|
.OrderBy(s => s.SeasonNum != null ? int.Parse(s.SeasonNum) : 0)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
series.Seasons.Clear();
|
||||||
|
|
||||||
|
foreach (var season in sortedSeasons){
|
||||||
|
series.Seasons.Add(season);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SortItems(){
|
||||||
|
var currentSortingType = Crunchyroll.Instance.CrunOptions.HistoryPageProperties?.SelectedSorting ?? SortingType.SeriesTitle;
|
||||||
|
switch (currentSortingType){
|
||||||
|
case SortingType.SeriesTitle:
|
||||||
|
var sortedList = Crunchyroll.Instance.HistoryList.OrderBy(s => s.SeriesTitle).ToList();
|
||||||
|
|
||||||
|
Crunchyroll.Instance.HistoryList.Clear();
|
||||||
|
|
||||||
|
Crunchyroll.Instance.HistoryList.AddRange(sortedList);
|
||||||
|
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SortingType.NextAirDate:
|
||||||
|
|
||||||
|
DateTime today = DateTime.UtcNow.Date;
|
||||||
|
|
||||||
|
var sortedSeriesDates = Crunchyroll.Instance.HistoryList
|
||||||
|
.OrderByDescending(s => s.SonarrNextAirDate == "Today")
|
||||||
|
.ThenBy(s => s.SonarrNextAirDate == "Today" ? s.SeriesTitle : null)
|
||||||
|
.ThenBy(s => {
|
||||||
|
var date = ParseDate(s.SonarrNextAirDate, today);
|
||||||
|
return date.HasValue ? date.Value : DateTime.MaxValue;
|
||||||
|
})
|
||||||
|
.ThenBy(s => s.SeriesTitle)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
Crunchyroll.Instance.HistoryList.Clear();
|
||||||
|
|
||||||
|
Crunchyroll.Instance.HistoryList.AddRange(sortedSeriesDates);
|
||||||
|
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime? ParseDate(string dateStr, DateTime today){
|
||||||
|
if (dateStr == "Today"){
|
||||||
|
return today;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DateTime.TryParseExact(dateStr, "dd.MM.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime date)){
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private string GetSeriesThumbnail(CrSeriesBase series){
|
private string GetSeriesThumbnail(CrSeriesBase series){
|
||||||
// var series = await crunInstance.CrSeries.SeriesById(seriesId);
|
// var series = await crunInstance.CrSeries.SeriesById(seriesId);
|
||||||
|
|
||||||
|
@ -389,6 +446,8 @@ public class History(){
|
||||||
if (!string.IsNullOrEmpty(historySeries.SonarrSeriesId)){
|
if (!string.IsNullOrEmpty(historySeries.SonarrSeriesId)){
|
||||||
var episodes = await SonarrClient.Instance.GetEpisodes(int.Parse(historySeries.SonarrSeriesId));
|
var episodes = await SonarrClient.Instance.GetEpisodes(int.Parse(historySeries.SonarrSeriesId));
|
||||||
|
|
||||||
|
historySeries.SonarrNextAirDate = GetNextAirDate(episodes);
|
||||||
|
|
||||||
List<HistoryEpisode> allHistoryEpisodes =[];
|
List<HistoryEpisode> allHistoryEpisodes =[];
|
||||||
|
|
||||||
foreach (var historySeriesSeason in historySeries.Seasons){
|
foreach (var historySeriesSeason in historySeries.Seasons){
|
||||||
|
@ -451,6 +510,29 @@ public class History(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetNextAirDate(List<SonarrEpisode> episodes){
|
||||||
|
DateTime today = DateTime.UtcNow.Date;
|
||||||
|
|
||||||
|
// Check if any episode air date matches today
|
||||||
|
var todayEpisode = episodes.FirstOrDefault(e => e.AirDateUtc.Date == today);
|
||||||
|
if (todayEpisode != null){
|
||||||
|
return "Today";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the next episode date
|
||||||
|
var nextEpisode = episodes
|
||||||
|
.Where(e => e.AirDateUtc.Date > today)
|
||||||
|
.OrderBy(e => e.AirDateUtc.Date)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (nextEpisode != null){
|
||||||
|
return nextEpisode.AirDateUtc.ToString("dd.MM.yyyy");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no future episode date is found
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
private SonarrSeries? FindClosestMatch(string title){
|
private SonarrSeries? FindClosestMatch(string title){
|
||||||
SonarrSeries? closestMatch = null;
|
SonarrSeries? closestMatch = null;
|
||||||
double highestSimilarity = 0.0;
|
double highestSimilarity = 0.0;
|
||||||
|
@ -543,218 +625,3 @@ public class NumericStringPropertyComparer : IComparer<HistoryEpisode>{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HistorySeries : INotifyPropertyChanged{
|
|
||||||
[JsonProperty("series_title")]
|
|
||||||
public string? SeriesTitle{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("series_id")]
|
|
||||||
public string? SeriesId{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("sonarr_series_id")]
|
|
||||||
public string? SonarrSeriesId{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("sonarr_tvdb_id")]
|
|
||||||
public string? SonarrTvDbId{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("sonarr_slug_title")]
|
|
||||||
public string? SonarrSlugTitle{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("series_description")]
|
|
||||||
public string? SeriesDescription{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("series_thumbnail_url")]
|
|
||||||
public string? ThumbnailImageUrl{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("series_new_episodes")]
|
|
||||||
public int NewEpisodes{ get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public Bitmap? ThumbnailImage{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("series_season_list")]
|
|
||||||
public required List<HistorySeason> Seasons{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("series_download_path")]
|
|
||||||
public string? SeriesDownloadPath{ get; set; }
|
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public bool FetchingData{ get; set; }
|
|
||||||
|
|
||||||
public async Task LoadImage(){
|
|
||||||
try{
|
|
||||||
using (var client = new HttpClient()){
|
|
||||||
var response = await client.GetAsync(ThumbnailImageUrl);
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
using (var stream = await response.Content.ReadAsStreamAsync()){
|
|
||||||
ThumbnailImage = new Bitmap(stream);
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ThumbnailImage)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ex){
|
|
||||||
// Handle exceptions
|
|
||||||
Console.Error.WriteLine("Failed to load image: " + ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateNewEpisodes(){
|
|
||||||
int count = 0;
|
|
||||||
bool foundWatched = false;
|
|
||||||
|
|
||||||
// Iterate over the Seasons list from the end to the beginning
|
|
||||||
for (int i = Seasons.Count - 1; i >= 0 && !foundWatched; i--){
|
|
||||||
if (Seasons[i].SpecialSeason == true){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over the Episodes from the end to the beginning
|
|
||||||
for (int j = Seasons[i].EpisodesList.Count - 1; j >= 0 && !foundWatched; j--){
|
|
||||||
if (Seasons[i].EpisodesList[j].SpecialEpisode){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Seasons[i].EpisodesList[j].WasDownloaded){
|
|
||||||
count++;
|
|
||||||
} else{
|
|
||||||
foundWatched = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NewEpisodes = count;
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NewEpisodes)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetFetchingData(){
|
|
||||||
FetchingData = true;
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task AddNewMissingToDownloads(){
|
|
||||||
bool foundWatched = false;
|
|
||||||
|
|
||||||
// Iterate over the Seasons list from the end to the beginning
|
|
||||||
for (int i = Seasons.Count - 1; i >= 0 && !foundWatched; i--){
|
|
||||||
if (Seasons[i].SpecialSeason == true){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over the Episodes from the end to the beginning
|
|
||||||
for (int j = Seasons[i].EpisodesList.Count - 1; j >= 0 && !foundWatched; j--){
|
|
||||||
if (Seasons[i].EpisodesList[j].SpecialEpisode){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Seasons[i].EpisodesList[j].WasDownloaded){
|
|
||||||
//ADD to download queue
|
|
||||||
await Seasons[i].EpisodesList[j].DownloadEpisode();
|
|
||||||
} else{
|
|
||||||
foundWatched = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task FetchData(string? seasonId){
|
|
||||||
FetchingData = true;
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData)));
|
|
||||||
await Crunchyroll.Instance.CrHistory.UpdateSeries(SeriesId, seasonId);
|
|
||||||
FetchingData = false;
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData)));
|
|
||||||
Crunchyroll.Instance.CrHistory.MatchHistoryEpisodesWithSonarr(false, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class HistorySeason : INotifyPropertyChanged{
|
|
||||||
[JsonProperty("season_title")]
|
|
||||||
public string? SeasonTitle{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("season_id")]
|
|
||||||
public string? SeasonId{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("season_cr_season_number")]
|
|
||||||
public string? SeasonNum{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("season_special_season")]
|
|
||||||
public bool? SpecialSeason{ get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public string CombinedProperty => SpecialSeason ?? false ? $"Specials {SeasonNum}" : $"Season {SeasonNum}";
|
|
||||||
|
|
||||||
[JsonProperty("season_downloaded_episodes")]
|
|
||||||
public int DownloadedEpisodes{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("season_episode_list")]
|
|
||||||
public required List<HistoryEpisode> EpisodesList{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("series_download_path")]
|
|
||||||
public string? SeasonDownloadPath{ get; set; }
|
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
|
||||||
|
|
||||||
public void UpdateDownloaded(string? EpisodeId){
|
|
||||||
if (!string.IsNullOrEmpty(EpisodeId)){
|
|
||||||
EpisodesList.First(e => e.EpisodeId == EpisodeId).ToggleWasDownloaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
DownloadedEpisodes = EpisodesList.FindAll(e => e.WasDownloaded).Count;
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DownloadedEpisodes)));
|
|
||||||
CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateDownloaded(){
|
|
||||||
DownloadedEpisodes = EpisodesList.FindAll(e => e.WasDownloaded).Count;
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DownloadedEpisodes)));
|
|
||||||
CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class HistoryEpisode : INotifyPropertyChanged{
|
|
||||||
[JsonProperty("episode_title")]
|
|
||||||
public string? EpisodeTitle{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("episode_id")]
|
|
||||||
public string? EpisodeId{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("episode_cr_episode_number")]
|
|
||||||
public string? Episode{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("episode_cr_episode_description")]
|
|
||||||
public string? EpisodeDescription{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("episode_cr_season_number")]
|
|
||||||
public string? EpisodeSeasonNum{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("episode_was_downloaded")]
|
|
||||||
public bool WasDownloaded{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("episode_special_episode")]
|
|
||||||
public bool SpecialEpisode{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("sonarr_episode_id")]
|
|
||||||
public string? SonarrEpisodeId{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("sonarr_has_file")]
|
|
||||||
public bool SonarrHasFile{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("sonarr_episode_number")]
|
|
||||||
public string? SonarrEpisodeNumber{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("sonarr_season_number")]
|
|
||||||
public string? SonarrSeasonNumber{ get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("sonarr_absolut_number")]
|
|
||||||
public string? SonarrAbsolutNumber{ get; set; }
|
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
|
||||||
|
|
||||||
public void ToggleWasDownloaded(){
|
|
||||||
WasDownloaded = !WasDownloaded;
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(WasDownloaded)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DownloadEpisode(){
|
|
||||||
await Crunchyroll.Instance.AddEpisodeToQue(EpisodeId, Crunchyroll.Instance.DefaultLocale, Crunchyroll.Instance.CrunOptions.DubLang);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -163,6 +163,9 @@ public enum DownloadMediaType{
|
||||||
|
|
||||||
[EnumMember(Value = "Subtitle")]
|
[EnumMember(Value = "Subtitle")]
|
||||||
Subtitle,
|
Subtitle,
|
||||||
|
|
||||||
|
[EnumMember(Value = "Description")]
|
||||||
|
Description,
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ScaledBorderAndShadowSelection{
|
public enum ScaledBorderAndShadowSelection{
|
||||||
|
@ -171,6 +174,18 @@ public enum ScaledBorderAndShadowSelection{
|
||||||
ScaledBorderAndShadowNo,
|
ScaledBorderAndShadowNo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum HistoryViewType{
|
||||||
|
Posters,
|
||||||
|
Table,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SortingType{
|
||||||
|
[EnumMember(Value = "Series Title")]
|
||||||
|
SeriesTitle,
|
||||||
|
[EnumMember(Value = "Next Air Date")]
|
||||||
|
NextAirDate,
|
||||||
|
}
|
||||||
|
|
||||||
public enum SonarrCoverType{
|
public enum SonarrCoverType{
|
||||||
Banner,
|
Banner,
|
||||||
FanArt,
|
FanArt,
|
||||||
|
|
|
@ -71,6 +71,15 @@ public class HlsDownloader{
|
||||||
_data.Offset = resumeData.Completed;
|
_data.Offset = resumeData.Completed;
|
||||||
_data.IsResume = true;
|
_data.IsResume = true;
|
||||||
} else{
|
} else{
|
||||||
|
|
||||||
|
if (resumeData.Total == _data.M3U8Json?.Segments.Count &&
|
||||||
|
resumeData.Completed == resumeData.Total &&
|
||||||
|
!double.IsNaN(resumeData.Completed)){
|
||||||
|
|
||||||
|
Console.WriteLine("Already finished");
|
||||||
|
return (Ok: true, _data.Parts);
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine("Resume data is wrong!");
|
Console.WriteLine("Resume data is wrong!");
|
||||||
Console.WriteLine($"Resume: {{ total: {resumeData.Total}, dled: {resumeData.Completed} }}, " +
|
Console.WriteLine($"Resume: {{ total: {resumeData.Total}, dled: {resumeData.Completed} }}, " +
|
||||||
$"Current: {{ total: {_data.M3U8Json?.Segments.Count} }}");
|
$"Current: {{ total: {_data.M3U8Json?.Segments.Count} }}");
|
||||||
|
|
|
@ -27,6 +27,17 @@ public class Helpers{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void OpenUrl(string url){
|
||||||
|
try{
|
||||||
|
Process.Start(new ProcessStartInfo{
|
||||||
|
FileName = url,
|
||||||
|
UseShellExecute = true
|
||||||
|
});
|
||||||
|
} catch (Exception e){
|
||||||
|
Console.Error.WriteLine($"An error occurred while trying to open URL - {url} : {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void EnsureDirectoriesExist(string path){
|
public static void EnsureDirectoriesExist(string path){
|
||||||
// Check if the path is absolute
|
// Check if the path is absolute
|
||||||
bool isAbsolute = Path.IsPathRooted(path);
|
bool isAbsolute = Path.IsPathRooted(path);
|
||||||
|
|
|
@ -141,10 +141,12 @@ public class Merger{
|
||||||
|
|
||||||
foreach (var aud in options.OnlyAudio){
|
foreach (var aud in options.OnlyAudio){
|
||||||
string trackName = aud.Language.Name;
|
string trackName = aud.Language.Name;
|
||||||
|
args.Add("--audio-tracks 0");
|
||||||
|
args.Add("--no-video");
|
||||||
args.Add($"--track-name 0:\"{trackName}\"");
|
args.Add($"--track-name 0:\"{trackName}\"");
|
||||||
args.Add($"--language 0:{aud.Language.Code}");
|
args.Add($"--language 0:{aud.Language.Code}");
|
||||||
args.Add("--no-video");
|
|
||||||
args.Add("--audio-tracks 0");
|
|
||||||
|
|
||||||
if (options.Defaults.Audio.Code == aud.Language.Code){
|
if (options.Defaults.Audio.Code == aud.Language.Code){
|
||||||
args.Add("--default-track 0");
|
args.Add("--default-track 0");
|
||||||
|
@ -181,7 +183,7 @@ public class Merger{
|
||||||
args.Add("--no-subtitles");
|
args.Add("--no-subtitles");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.Fonts != null && options.Fonts.Count > 0){
|
if (options.Fonts is{ Count: > 0 }){
|
||||||
foreach (var font in options.Fonts){
|
foreach (var font in options.Fonts){
|
||||||
args.Add($"--attachment-name \"{font.Name}\"");
|
args.Add($"--attachment-name \"{font.Name}\"");
|
||||||
args.Add($"--attachment-mime-type \"{font.Mime}\"");
|
args.Add($"--attachment-mime-type \"{font.Mime}\"");
|
||||||
|
@ -191,7 +193,7 @@ public class Merger{
|
||||||
args.Add("--no-attachments");
|
args.Add("--no-attachments");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.Chapters != null && options.Chapters.Count > 0){
|
if (options.Chapters is{ Count: > 0 }){
|
||||||
args.Add($"--chapters \"{options.Chapters[0].Path}\"");
|
args.Add($"--chapters \"{options.Chapters[0].Path}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,8 +201,8 @@ public class Merger{
|
||||||
args.Add($"--title \"{options.VideoTitle}\"");
|
args.Add($"--title \"{options.VideoTitle}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.MuxDescription){
|
if (options.Description is{ Count: > 0 }){
|
||||||
args.Add($"--global-tags \"{Path.Combine(Path.GetDirectoryName(options.Output), Path.GetFileNameWithoutExtension(options.Output))}.xml\"");
|
args.Add($"--global-tags \"{options.Description[0].Path}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -242,10 +244,8 @@ public class Merger{
|
||||||
allMediaFiles.ForEach(file => DeleteFile(file.Path));
|
allMediaFiles.ForEach(file => DeleteFile(file.Path));
|
||||||
allMediaFiles.ForEach(file => DeleteFile(file.Path + ".resume"));
|
allMediaFiles.ForEach(file => DeleteFile(file.Path + ".resume"));
|
||||||
|
|
||||||
if (options.MuxDescription){
|
options.Description?.ForEach(chapter => DeleteFile(chapter.Path));
|
||||||
DeleteFile(Path.Combine(Path.GetDirectoryName(options.Output), Path.GetFileNameWithoutExtension(options.Output)) + ".xml");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete chapter files if any
|
// Delete chapter files if any
|
||||||
options.Chapters?.ForEach(chapter => DeleteFile(chapter.Path));
|
options.Chapters?.ForEach(chapter => DeleteFile(chapter.Path));
|
||||||
|
|
||||||
|
@ -319,7 +319,7 @@ public class MergerOptions{
|
||||||
public MuxOptions Options{ get; set; }
|
public MuxOptions Options{ get; set; }
|
||||||
public Defaults Defaults{ get; set; }
|
public Defaults Defaults{ get; set; }
|
||||||
public bool mp3{ get; set; }
|
public bool mp3{ get; set; }
|
||||||
public bool MuxDescription{ get; set; }
|
public List<MergerInput> Description{ get; set; } = new List<MergerInput>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MuxOptions{
|
public class MuxOptions{
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using CRD.Utils.Sonarr;
|
using CRD.Utils.Sonarr;
|
||||||
|
using CRD.ViewModels;
|
||||||
using YamlDotNet.Serialization;
|
using YamlDotNet.Serialization;
|
||||||
|
|
||||||
namespace CRD.Utils.Structs;
|
namespace CRD.Utils.Structs;
|
||||||
|
@ -143,4 +144,7 @@ public class CrDownloadOptions{
|
||||||
[YamlMember(Alias = "download_dir_path", ApplyNamingConventions = false)]
|
[YamlMember(Alias = "download_dir_path", ApplyNamingConventions = false)]
|
||||||
public string? DownloadDirPath{ get; set; }
|
public string? DownloadDirPath{ get; set; }
|
||||||
|
|
||||||
|
[YamlMember(Alias = "history_page_properties", ApplyNamingConventions = false)]
|
||||||
|
public HistoryPageProperties? HistoryPageProperties{ get; set; }
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CRD.Downloader;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace CRD.Utils.Structs.History;
|
||||||
|
|
||||||
|
public class HistoryEpisode : INotifyPropertyChanged{
|
||||||
|
[JsonProperty("episode_title")]
|
||||||
|
public string? EpisodeTitle{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("episode_id")]
|
||||||
|
public string? EpisodeId{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("episode_cr_episode_number")]
|
||||||
|
public string? Episode{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("episode_cr_episode_description")]
|
||||||
|
public string? EpisodeDescription{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("episode_cr_season_number")]
|
||||||
|
public string? EpisodeSeasonNum{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("episode_was_downloaded")]
|
||||||
|
public bool WasDownloaded{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("episode_special_episode")]
|
||||||
|
public bool SpecialEpisode{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("sonarr_episode_id")]
|
||||||
|
public string? SonarrEpisodeId{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("sonarr_has_file")]
|
||||||
|
public bool SonarrHasFile{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("sonarr_episode_number")]
|
||||||
|
public string? SonarrEpisodeNumber{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("sonarr_season_number")]
|
||||||
|
public string? SonarrSeasonNumber{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("sonarr_absolut_number")]
|
||||||
|
public string? SonarrAbsolutNumber{ get; set; }
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
public void ToggleWasDownloaded(){
|
||||||
|
WasDownloaded = !WasDownloaded;
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(WasDownloaded)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DownloadEpisode(){
|
||||||
|
await Crunchyroll.Instance.AddEpisodeToQue(EpisodeId, Crunchyroll.Instance.DefaultLocale, Crunchyroll.Instance.CrunOptions.DubLang);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using CRD.Downloader;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace CRD.Utils.Structs.History;
|
||||||
|
|
||||||
|
public class HistorySeason : INotifyPropertyChanged{
|
||||||
|
[JsonProperty("season_title")]
|
||||||
|
public string? SeasonTitle{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("season_id")]
|
||||||
|
public string? SeasonId{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("season_cr_season_number")]
|
||||||
|
public string? SeasonNum{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("season_special_season")]
|
||||||
|
public bool? SpecialSeason{ get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string CombinedProperty => SpecialSeason ?? false ? $"Specials {SeasonNum}" : $"Season {SeasonNum}";
|
||||||
|
|
||||||
|
[JsonProperty("season_downloaded_episodes")]
|
||||||
|
public int DownloadedEpisodes{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("season_episode_list")]
|
||||||
|
public required List<HistoryEpisode> EpisodesList{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("series_download_path")]
|
||||||
|
public string? SeasonDownloadPath{ get; set; }
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
public void UpdateDownloaded(string? EpisodeId){
|
||||||
|
if (!string.IsNullOrEmpty(EpisodeId)){
|
||||||
|
EpisodesList.First(e => e.EpisodeId == EpisodeId).ToggleWasDownloaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadedEpisodes = EpisodesList.FindAll(e => e.WasDownloaded).Count;
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DownloadedEpisodes)));
|
||||||
|
CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateDownloaded(){
|
||||||
|
DownloadedEpisodes = EpisodesList.FindAll(e => e.WasDownloaded).Count;
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DownloadedEpisodes)));
|
||||||
|
CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
|
using CRD.Downloader;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace CRD.Utils.Structs.History;
|
||||||
|
|
||||||
|
public class HistorySeries : INotifyPropertyChanged{
|
||||||
|
[JsonProperty("series_title")]
|
||||||
|
public string? SeriesTitle{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("series_id")]
|
||||||
|
public string? SeriesId{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("sonarr_series_id")]
|
||||||
|
public string? SonarrSeriesId{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("sonarr_tvdb_id")]
|
||||||
|
public string? SonarrTvDbId{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("sonarr_slug_title")]
|
||||||
|
public string? SonarrSlugTitle{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("sonarr_next_air_date")]
|
||||||
|
public string? SonarrNextAirDate{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("series_description")]
|
||||||
|
public string? SeriesDescription{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("series_thumbnail_url")]
|
||||||
|
public string? ThumbnailImageUrl{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("series_new_episodes")]
|
||||||
|
public int NewEpisodes{ get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public Bitmap? ThumbnailImage{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("series_season_list")]
|
||||||
|
public required ObservableCollection<HistorySeason> Seasons{ get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("series_download_path")]
|
||||||
|
public string? SeriesDownloadPath{ get; set; }
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public bool FetchingData{ get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public bool EditModeEnabled{
|
||||||
|
get => _editModeEnabled;
|
||||||
|
set{
|
||||||
|
if (_editModeEnabled != value){
|
||||||
|
_editModeEnabled = value;
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(EditModeEnabled)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
private bool _editModeEnabled;
|
||||||
|
|
||||||
|
public async Task LoadImage(){
|
||||||
|
try{
|
||||||
|
using (var client = new HttpClient()){
|
||||||
|
var response = await client.GetAsync(ThumbnailImageUrl);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
using (var stream = await response.Content.ReadAsStreamAsync()){
|
||||||
|
ThumbnailImage = new Bitmap(stream);
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ThumbnailImage)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex){
|
||||||
|
// Handle exceptions
|
||||||
|
Console.Error.WriteLine("Failed to load image: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateNewEpisodes(){
|
||||||
|
int count = 0;
|
||||||
|
bool foundWatched = false;
|
||||||
|
|
||||||
|
// Iterate over the Seasons list from the end to the beginning
|
||||||
|
for (int i = Seasons.Count - 1; i >= 0 && !foundWatched; i--){
|
||||||
|
if (Seasons[i].SpecialSeason == true){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the Episodes from the end to the beginning
|
||||||
|
for (int j = Seasons[i].EpisodesList.Count - 1; j >= 0 && !foundWatched; j--){
|
||||||
|
if (Seasons[i].EpisodesList[j].SpecialEpisode){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Seasons[i].EpisodesList[j].WasDownloaded){
|
||||||
|
count++;
|
||||||
|
} else{
|
||||||
|
foundWatched = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NewEpisodes = count;
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NewEpisodes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFetchingData(){
|
||||||
|
FetchingData = true;
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddNewMissingToDownloads(){
|
||||||
|
bool foundWatched = false;
|
||||||
|
|
||||||
|
// Iterate over the Seasons list from the end to the beginning
|
||||||
|
for (int i = Seasons.Count - 1; i >= 0 && !foundWatched; i--){
|
||||||
|
if (Seasons[i].SpecialSeason == true){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the Episodes from the end to the beginning
|
||||||
|
for (int j = Seasons[i].EpisodesList.Count - 1; j >= 0 && !foundWatched; j--){
|
||||||
|
if (Seasons[i].EpisodesList[j].SpecialEpisode){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Seasons[i].EpisodesList[j].WasDownloaded){
|
||||||
|
//ADD to download queue
|
||||||
|
await Seasons[i].EpisodesList[j].DownloadEpisode();
|
||||||
|
} else{
|
||||||
|
foundWatched = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task FetchData(string? seasonId){
|
||||||
|
FetchingData = true;
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData)));
|
||||||
|
await Crunchyroll.Instance.CrHistory.UpdateSeries(SeriesId, seasonId);
|
||||||
|
FetchingData = false;
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData)));
|
||||||
|
Crunchyroll.Instance.CrHistory.MatchHistoryEpisodesWithSonarr(false, this);
|
||||||
|
UpdateNewEpisodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveSeason(string? season){
|
||||||
|
HistorySeason? objectToRemove = Seasons.FirstOrDefault(se => se.SeasonId == season) ?? null;
|
||||||
|
if (objectToRemove != null){
|
||||||
|
Seasons.Remove(objectToRemove);
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Seasons)));
|
||||||
|
}
|
||||||
|
|
||||||
|
CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenSonarrPage(){
|
||||||
|
var sonarrProp = Crunchyroll.Instance.CrunOptions.SonarrProperties;
|
||||||
|
|
||||||
|
if (sonarrProp == null) return;
|
||||||
|
|
||||||
|
Helpers.OpenUrl($"http{(sonarrProp.UseSsl ? "s" : "")}://{sonarrProp.Host}:{sonarrProp.Port}{(sonarrProp.UrlBase ?? "")}/series/{SonarrSlugTitle}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenCrPage(){
|
||||||
|
Helpers.OpenUrl($"https://www.crunchyroll.com/series/{SeriesId}");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using CRD.Downloader;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
|
||||||
|
namespace CRD.Utils.UI;
|
||||||
|
|
||||||
|
public class UiSonarrIdToVisibilityConverter : IValueConverter{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture){
|
||||||
|
if (value is string stringValue){
|
||||||
|
return Crunchyroll.Instance.CrunOptions.SonarrProperties != null && (stringValue.Length > 0 && Crunchyroll.Instance.CrunOptions.SonarrProperties.SonarrEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){
|
||||||
|
throw new NotImplementedException("This converter only works for one-way binding");
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,17 +19,30 @@ using ReactiveUI;
|
||||||
namespace CRD.ViewModels;
|
namespace CRD.ViewModels;
|
||||||
|
|
||||||
public partial class AddDownloadPageViewModel : ViewModelBase{
|
public partial class AddDownloadPageViewModel : ViewModelBase{
|
||||||
[ObservableProperty] public string _urlInput = "";
|
[ObservableProperty]
|
||||||
[ObservableProperty] public string _buttonText = "Enter Url";
|
public string _urlInput = "";
|
||||||
[ObservableProperty] public bool _addAllEpisodes = false;
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public string _buttonText = "Enter Url";
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public bool _addAllEpisodes = false;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public bool _buttonEnabled = false;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public bool _allButtonEnabled = false;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public bool _showLoading = false;
|
||||||
|
|
||||||
[ObservableProperty] public bool _buttonEnabled = false;
|
|
||||||
[ObservableProperty] public bool _allButtonEnabled = false;
|
|
||||||
[ObservableProperty] public bool _showLoading = false;
|
|
||||||
public ObservableCollection<ItemModel> Items{ get; } = new();
|
public ObservableCollection<ItemModel> Items{ get; } = new();
|
||||||
public ObservableCollection<ItemModel> SelectedItems{ get; } = new();
|
public ObservableCollection<ItemModel> SelectedItems{ get; } = new();
|
||||||
|
|
||||||
[ObservableProperty] public ComboBoxItem _currentSelectedSeason;
|
[ObservableProperty]
|
||||||
|
public ComboBoxItem _currentSelectedSeason;
|
||||||
|
|
||||||
public ObservableCollection<ComboBoxItem> SeasonList{ get; } = new();
|
public ObservableCollection<ComboBoxItem> SeasonList{ get; } = new();
|
||||||
|
|
||||||
private Dictionary<string, List<ItemModel>> episodesBySeason = new();
|
private Dictionary<string, List<ItemModel>> episodesBySeason = new();
|
||||||
|
@ -79,7 +92,6 @@ public partial class AddDownloadPageViewModel : ViewModelBase{
|
||||||
|
|
||||||
if (currentSeriesList != null){
|
if (currentSeriesList != null){
|
||||||
Crunchyroll.Instance.AddSeriesToQueue(currentSeriesList.Value, new CrunchyMultiDownload(Crunchyroll.Instance.CrunOptions.DubLang, AddAllEpisodes, false, selectedEpisodes));
|
Crunchyroll.Instance.AddSeriesToQueue(currentSeriesList.Value, new CrunchyMultiDownload(Crunchyroll.Instance.CrunOptions.DubLang, AddAllEpisodes, false, selectedEpisodes));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -106,7 +118,7 @@ public partial class AddDownloadPageViewModel : ViewModelBase{
|
||||||
|
|
||||||
if (match.Success){
|
if (match.Success){
|
||||||
var locale = match.Groups[1].Value; // Capture the locale part
|
var locale = match.Groups[1].Value; // Capture the locale part
|
||||||
var id = match.Groups[2].Value; // Capture the ID part
|
var id = match.Groups[2].Value; // Capture the ID part
|
||||||
Crunchyroll.Instance.AddEpisodeToQue(id, locale, Crunchyroll.Instance.CrunOptions.DubLang);
|
Crunchyroll.Instance.AddEpisodeToQue(id, locale, Crunchyroll.Instance.CrunOptions.DubLang);
|
||||||
UrlInput = "";
|
UrlInput = "";
|
||||||
selectedEpisodes.Clear();
|
selectedEpisodes.Clear();
|
||||||
|
@ -122,7 +134,7 @@ public partial class AddDownloadPageViewModel : ViewModelBase{
|
||||||
|
|
||||||
if (match.Success){
|
if (match.Success){
|
||||||
var locale = match.Groups[1].Value; // Capture the locale part
|
var locale = match.Groups[1].Value; // Capture the locale part
|
||||||
var id = match.Groups[2].Value; // Capture the ID part
|
var id = match.Groups[2].Value; // Capture the ID part
|
||||||
|
|
||||||
if (id.Length != 9){
|
if (id.Length != 9){
|
||||||
return;
|
return;
|
||||||
|
@ -130,7 +142,7 @@ public partial class AddDownloadPageViewModel : ViewModelBase{
|
||||||
|
|
||||||
ButtonEnabled = false;
|
ButtonEnabled = false;
|
||||||
ShowLoading = true;
|
ShowLoading = true;
|
||||||
var list = await Crunchyroll.Instance.CrSeries.ListSeriesId(id,"", new CrunchyMultiDownload(Crunchyroll.Instance.CrunOptions.DubLang, true));
|
var list = await Crunchyroll.Instance.CrSeries.ListSeriesId(id, "", new CrunchyMultiDownload(Crunchyroll.Instance.CrunOptions.DubLang, true));
|
||||||
ShowLoading = false;
|
ShowLoading = false;
|
||||||
if (list != null){
|
if (list != null){
|
||||||
currentSeriesList = list;
|
currentSeriesList = list;
|
||||||
|
|
|
@ -1,18 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Avalonia;
|
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using CRD.Downloader;
|
using CRD.Downloader;
|
||||||
using CRD.Utils;
|
|
||||||
using CRD.Utils.Structs;
|
using CRD.Utils.Structs;
|
||||||
|
|
||||||
namespace CRD.ViewModels;
|
namespace CRD.ViewModels;
|
||||||
|
@ -27,64 +22,23 @@ public partial class DownloadsPageViewModel : ViewModelBase{
|
||||||
public bool _removeFinished;
|
public bool _removeFinished;
|
||||||
|
|
||||||
public DownloadsPageViewModel(){
|
public DownloadsPageViewModel(){
|
||||||
UpdateListItems();
|
Crunchyroll.Instance.UpdateDownloadListItems();
|
||||||
Items = Crunchyroll.Instance.DownloadItemModels;
|
Items = Crunchyroll.Instance.DownloadItemModels;
|
||||||
AutoDownload = Crunchyroll.Instance.CrunOptions.AutoDownload;
|
AutoDownload = Crunchyroll.Instance.CrunOptions.AutoDownload;
|
||||||
RemoveFinished = Crunchyroll.Instance.CrunOptions.RemoveFinishedDownload;
|
RemoveFinished = Crunchyroll.Instance.CrunOptions.RemoveFinishedDownload;
|
||||||
Crunchyroll.Instance.Queue.CollectionChanged += UpdateItemListOnRemove;
|
|
||||||
// Items.Add(new DownloadItemModel{Title = "Test - S1E1"});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateItemListOnRemove(object? sender, NotifyCollectionChangedEventArgs e){
|
|
||||||
if (e.Action == NotifyCollectionChangedAction.Remove){
|
|
||||||
if (e.OldItems != null)
|
|
||||||
foreach (var eOldItem in e.OldItems){
|
|
||||||
var downloadItem = Crunchyroll.Instance.DownloadItemModels.FirstOrDefault(e => e.epMeta.Equals(eOldItem));
|
|
||||||
if (downloadItem != null){
|
|
||||||
Crunchyroll.Instance.DownloadItemModels.Remove(downloadItem);
|
|
||||||
} else{
|
|
||||||
Console.Error.WriteLine("Failed to Remove Episode from list");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateListItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void UpdateListItems(){
|
|
||||||
var list = Crunchyroll.Instance.Queue;
|
|
||||||
|
|
||||||
foreach (CrunchyEpMeta crunchyEpMeta in list){
|
|
||||||
var downloadItem = Crunchyroll.Instance.DownloadItemModels.FirstOrDefault(e => e.epMeta.Equals(crunchyEpMeta));
|
|
||||||
if (downloadItem != null){
|
|
||||||
downloadItem.Refresh();
|
|
||||||
} else{
|
|
||||||
downloadItem = new DownloadItemModel(crunchyEpMeta);
|
|
||||||
downloadItem.LoadImage();
|
|
||||||
Crunchyroll.Instance.DownloadItemModels.Add(downloadItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downloadItem is{ isDownloading: false, Error: false } && Crunchyroll.Instance.CrunOptions.AutoDownload && Crunchyroll.Instance.ActiveDownloads < Crunchyroll.Instance.CrunOptions.SimultaneousDownloads){
|
|
||||||
downloadItem.StartDownload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnAutoDownloadChanged(bool value){
|
partial void OnAutoDownloadChanged(bool value){
|
||||||
Crunchyroll.Instance.CrunOptions.AutoDownload = value;
|
Crunchyroll.Instance.CrunOptions.AutoDownload = value;
|
||||||
if (value){
|
if (value){
|
||||||
UpdateListItems();
|
Crunchyroll.Instance.UpdateDownloadListItems();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnRemoveFinishedChanged(bool value){
|
partial void OnRemoveFinishedChanged(bool value){
|
||||||
Crunchyroll.Instance.CrunOptions.RemoveFinishedDownload = value;
|
Crunchyroll.Instance.CrunOptions.RemoveFinishedDownload = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cleanup(){
|
|
||||||
Crunchyroll.Instance.Queue.CollectionChanged -= UpdateItemListOnRemove;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class DownloadItemModel : INotifyPropertyChanged{
|
public partial class DownloadItemModel : INotifyPropertyChanged{
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Platform.Storage;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using CRD.Downloader;
|
using CRD.Downloader;
|
||||||
using CRD.Utils;
|
using CRD.Utils;
|
||||||
|
using CRD.Utils.Structs;
|
||||||
|
using CRD.Utils.Structs.History;
|
||||||
using CRD.Views;
|
using CRD.Views;
|
||||||
|
using DynamicData;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace CRD.ViewModels;
|
namespace CRD.ViewModels;
|
||||||
|
@ -22,9 +32,84 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public static bool _editMode;
|
public static bool _editMode;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public double _scaleValue;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public ComboBoxItem _selectedView;
|
||||||
|
|
||||||
|
public ObservableCollection<ComboBoxItem> ViewsList{ get; } =[];
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public ComboBoxItem _selectedSorting;
|
||||||
|
|
||||||
|
public ObservableCollection<ComboBoxItem> SortingList{ get; } =[];
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public double _posterWidth;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public double _posterHeight;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public double _posterImageWidth;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public double _posterImageHeight;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public double _posterTextSize;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public Thickness _cornerMargin;
|
||||||
|
|
||||||
|
private HistoryViewType currentViewType = HistoryViewType.Posters;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public bool _isPosterViewSelected = false;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public bool _isTableViewSelected = false;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public static bool _viewSelectionOpen;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public static bool _sortingSelectionOpen;
|
||||||
|
|
||||||
|
private IStorageProvider _storageProvider;
|
||||||
|
|
||||||
|
private SortingType currentSortingType = SortingType.NextAirDate;
|
||||||
|
|
||||||
public HistoryPageViewModel(){
|
public HistoryPageViewModel(){
|
||||||
Items = Crunchyroll.Instance.HistoryList;
|
Items = Crunchyroll.Instance.HistoryList;
|
||||||
|
|
||||||
|
HistoryPageProperties? properties = Crunchyroll.Instance.CrunOptions.HistoryPageProperties;
|
||||||
|
|
||||||
|
currentViewType = properties?.SelectedView ?? HistoryViewType.Posters;
|
||||||
|
currentSortingType = properties?.SelectedSorting ?? SortingType.SeriesTitle;
|
||||||
|
ScaleValue = properties?.ScaleValue ?? 0.73;
|
||||||
|
|
||||||
|
foreach (HistoryViewType viewType in Enum.GetValues(typeof(HistoryViewType))){
|
||||||
|
var combobox = new ComboBoxItem{ Content = viewType };
|
||||||
|
ViewsList.Add(combobox);
|
||||||
|
if (viewType == currentViewType){
|
||||||
|
SelectedView = combobox;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (SortingType sortingType in Enum.GetValues(typeof(SortingType))){
|
||||||
|
var combobox = new ComboBoxItem{ Content = sortingType.GetEnumMemberValue() };
|
||||||
|
SortingList.Add(combobox);
|
||||||
|
if (sortingType == currentSortingType){
|
||||||
|
SelectedSorting = combobox;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IsPosterViewSelected = currentViewType == HistoryViewType.Posters;
|
||||||
|
IsTableViewSelected = currentViewType == HistoryViewType.Table;
|
||||||
|
|
||||||
|
|
||||||
foreach (var historySeries in Items){
|
foreach (var historySeries in Items){
|
||||||
if (historySeries.ThumbnailImage == null){
|
if (historySeries.ThumbnailImage == null){
|
||||||
historySeries.LoadImage();
|
historySeries.LoadImage();
|
||||||
|
@ -32,13 +117,87 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
||||||
|
|
||||||
historySeries.UpdateNewEpisodes();
|
historySeries.UpdateNewEpisodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Crunchyroll.Instance.CrHistory.SortItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void UpdateSettings(){
|
||||||
|
if (Crunchyroll.Instance.CrunOptions.HistoryPageProperties != null){
|
||||||
|
Crunchyroll.Instance.CrunOptions.HistoryPageProperties.ScaleValue = ScaleValue;
|
||||||
|
Crunchyroll.Instance.CrunOptions.HistoryPageProperties.SelectedView = currentViewType;
|
||||||
|
Crunchyroll.Instance.CrunOptions.HistoryPageProperties.SelectedSorting = currentSortingType;
|
||||||
|
} else{
|
||||||
|
Crunchyroll.Instance.CrunOptions.HistoryPageProperties = new HistoryPageProperties(){ ScaleValue = ScaleValue, SelectedView = currentViewType, SelectedSorting = currentSortingType };
|
||||||
|
}
|
||||||
|
|
||||||
|
CfgManager.WriteSettingsToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnSelectedViewChanged(ComboBoxItem value){
|
||||||
|
if (Enum.TryParse(value.Content + "", out HistoryViewType viewType)){
|
||||||
|
currentViewType = viewType;
|
||||||
|
IsPosterViewSelected = currentViewType == HistoryViewType.Posters;
|
||||||
|
IsTableViewSelected = currentViewType == HistoryViewType.Table;
|
||||||
|
} else{
|
||||||
|
Console.Error.WriteLine("Invalid viewtype selected");
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewSelectionOpen = false;
|
||||||
|
UpdateSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnSelectedSortingChanged(ComboBoxItem value){
|
||||||
|
if (TryParseEnum<SortingType>(value.Content + "", out var sortingType)){
|
||||||
|
currentSortingType = sortingType;
|
||||||
|
Crunchyroll.Instance.CrHistory.SortItems();
|
||||||
|
} else{
|
||||||
|
Console.Error.WriteLine("Invalid viewtype selected");
|
||||||
|
}
|
||||||
|
|
||||||
|
SortingSelectionOpen = false;
|
||||||
|
UpdateSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryParseEnum<T>(string value, out T result) where T : struct, Enum{
|
||||||
|
foreach (var field in typeof(T).GetFields()){
|
||||||
|
var attribute = field.GetCustomAttribute<EnumMemberAttribute>();
|
||||||
|
if (attribute != null && attribute.Value == value){
|
||||||
|
result = (T)field.GetValue(null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
partial void OnScaleValueChanged(double value){
|
||||||
|
double t = (ScaleValue - 0.5) / (1 - 0.5);
|
||||||
|
|
||||||
|
PosterHeight = Math.Clamp(225 + t * (410 - 225), 225, 410);
|
||||||
|
PosterWidth = 250 * ScaleValue;
|
||||||
|
PosterImageHeight = 360 * ScaleValue;
|
||||||
|
PosterImageWidth = 240 * ScaleValue;
|
||||||
|
|
||||||
|
|
||||||
|
double posterTextSizeCalc = 11 + t * (15 - 11);
|
||||||
|
|
||||||
|
PosterTextSize = Math.Clamp(posterTextSizeCalc, 11, 15);
|
||||||
|
CornerMargin = new Thickness(0, 0, Math.Clamp(3 + t * (5 - 3), 3, 5), 0);
|
||||||
|
UpdateSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
partial void OnSelectedSeriesChanged(HistorySeries value){
|
partial void OnSelectedSeriesChanged(HistorySeries value){
|
||||||
Crunchyroll.Instance.SelectedSeries = value;
|
Crunchyroll.Instance.SelectedSeries = value;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(value.SonarrSeriesId) && Crunchyroll.Instance.CrunOptions.SonarrProperties is{ SonarrEnabled: true }){
|
||||||
|
Crunchyroll.Instance.CrHistory.MatchHistoryEpisodesWithSonarr(true, SelectedSeries);
|
||||||
|
CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList);
|
||||||
|
}
|
||||||
|
|
||||||
NavToSeries();
|
NavToSeries();
|
||||||
_selectedSeries = null;
|
_selectedSeries = null;
|
||||||
}
|
}
|
||||||
|
@ -81,6 +240,7 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
||||||
|
|
||||||
FetchingData = false;
|
FetchingData = false;
|
||||||
RaisePropertyChanged(nameof(FetchingData));
|
RaisePropertyChanged(nameof(FetchingData));
|
||||||
|
Crunchyroll.Instance.CrHistory.SortItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
|
@ -89,4 +249,65 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
||||||
await Items[i].AddNewMissingToDownloads();
|
await Items[i].AddNewMissingToDownloads();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
public async Task OpenFolderDialogAsyncSeason(HistorySeason? season){
|
||||||
|
if (_storageProvider == null){
|
||||||
|
Console.Error.WriteLine("StorageProvider must be set before using the dialog.");
|
||||||
|
throw new InvalidOperationException("StorageProvider must be set before using the dialog.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var result = await _storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions{
|
||||||
|
Title = "Select Folder"
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.Count > 0){
|
||||||
|
var selectedFolder = result[0];
|
||||||
|
// Do something with the selected folder path
|
||||||
|
Console.WriteLine($"Selected folder: {selectedFolder.Path.LocalPath}");
|
||||||
|
|
||||||
|
if (season != null){
|
||||||
|
season.SeasonDownloadPath = selectedFolder.Path.LocalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
public async Task OpenFolderDialogAsyncSeries(HistorySeries? series){
|
||||||
|
if (_storageProvider == null){
|
||||||
|
Console.Error.WriteLine("StorageProvider must be set before using the dialog.");
|
||||||
|
throw new InvalidOperationException("StorageProvider must be set before using the dialog.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var result = await _storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions{
|
||||||
|
Title = "Select Folder"
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.Count > 0){
|
||||||
|
var selectedFolder = result[0];
|
||||||
|
// Do something with the selected folder path
|
||||||
|
Console.WriteLine($"Selected folder: {selectedFolder.Path.LocalPath}");
|
||||||
|
|
||||||
|
if (series != null){
|
||||||
|
series.SeriesDownloadPath = selectedFolder.Path.LocalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void SetStorageProvider(IStorageProvider storageProvider){
|
||||||
|
_storageProvider = storageProvider ?? throw new ArgumentNullException(nameof(storageProvider));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HistoryPageProperties(){
|
||||||
|
public SortingType? SelectedSorting{ get; set; }
|
||||||
|
public HistoryViewType SelectedView{ get; set; }
|
||||||
|
public double? ScaleValue{ get; set; }
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
@ -7,6 +8,8 @@ using CommunityToolkit.Mvvm.Input;
|
||||||
using CRD.Downloader;
|
using CRD.Downloader;
|
||||||
using CRD.Utils;
|
using CRD.Utils;
|
||||||
using CRD.Utils.Sonarr;
|
using CRD.Utils.Sonarr;
|
||||||
|
using CRD.Utils.Structs;
|
||||||
|
using CRD.Utils.Structs.History;
|
||||||
using CRD.Views;
|
using CRD.Views;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
|
@ -30,14 +33,13 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
||||||
if (_selectedSeries.ThumbnailImage == null){
|
if (_selectedSeries.ThumbnailImage == null){
|
||||||
_selectedSeries.LoadImage();
|
_selectedSeries.LoadImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(SelectedSeries.SonarrSeriesId)){
|
if (!string.IsNullOrEmpty(SelectedSeries.SonarrSeriesId) && Crunchyroll.Instance.CrunOptions.SonarrProperties != null){
|
||||||
SonarrAvailable = SelectedSeries.SonarrSeriesId.Length > 0;
|
SonarrAvailable = SelectedSeries.SonarrSeriesId.Length > 0 && Crunchyroll.Instance.CrunOptions.SonarrProperties.SonarrEnabled;
|
||||||
Crunchyroll.Instance.CrHistory.MatchHistoryEpisodesWithSonarr(true,SelectedSeries);
|
}else{
|
||||||
CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList);
|
|
||||||
} else{
|
|
||||||
SonarrAvailable = false;
|
SonarrAvailable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,23 +74,7 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
||||||
_storageProvider = storageProvider ?? throw new ArgumentNullException(nameof(storageProvider));
|
_storageProvider = storageProvider ?? throw new ArgumentNullException(nameof(storageProvider));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
public void OpenSonarrPage(){
|
|
||||||
var sonarrProp = Crunchyroll.Instance.CrunOptions.SonarrProperties;
|
|
||||||
|
|
||||||
if (sonarrProp == null) return;
|
|
||||||
|
|
||||||
OpenUrl($"http{(sonarrProp.UseSsl ? "s" : "")}://{sonarrProp.Host}:{sonarrProp.Port}{(sonarrProp.UrlBase ?? "")}/series/{SelectedSeries.SonarrSlugTitle}");
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
public void OpenCrPage(){
|
|
||||||
|
|
||||||
OpenUrl($"https://www.crunchyroll.com/series/{SelectedSeries.SeriesId}");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
public async Task UpdateData(string? season){
|
public async Task UpdateData(string? season){
|
||||||
await SelectedSeries.FetchData(season);
|
await SelectedSeries.FetchData(season);
|
||||||
|
@ -98,7 +84,7 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
public void RemoveSeason(string? season){
|
public void RemoveSeason(string? season){
|
||||||
HistorySeason? objectToRemove = SelectedSeries.Seasons.Find(se => se.SeasonId == season) ?? null;
|
HistorySeason? objectToRemove = SelectedSeries.Seasons.FirstOrDefault(se => se.SeasonId == season) ?? null;
|
||||||
if (objectToRemove != null){
|
if (objectToRemove != null){
|
||||||
SelectedSeries.Seasons.Remove(objectToRemove);
|
SelectedSeries.Seasons.Remove(objectToRemove);
|
||||||
}
|
}
|
||||||
|
@ -115,14 +101,5 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void OpenUrl(string url){
|
|
||||||
try{
|
|
||||||
Process.Start(new ProcessStartInfo{
|
|
||||||
FileName = url,
|
|
||||||
UseShellExecute = true
|
|
||||||
});
|
|
||||||
} catch (Exception e){
|
|
||||||
Console.Error.WriteLine($"An error occurred while trying to open URL - {url} : {e.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -124,8 +124,9 @@
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TextBlock HorizontalAlignment="Center" Text="{Binding SeasonName}"
|
<TextBlock HorizontalAlignment="Center" TextAlignment="Center" Text="{Binding SeasonName}"
|
||||||
TextWrapping="NoWrap"
|
TextWrapping="Wrap"
|
||||||
|
Height="40"
|
||||||
Margin="0,0,0,0">
|
Margin="0,0,0,0">
|
||||||
<ToolTip.Tip>
|
<ToolTip.Tip>
|
||||||
<TextBlock Text="{Binding SeasonName}" FontSize="15" />
|
<TextBlock Text="{Binding SeasonName}" FontSize="15" />
|
||||||
|
|
|
@ -10,15 +10,4 @@ public partial class DownloadsPageView : UserControl{
|
||||||
public DownloadsPageView(){
|
public DownloadsPageView(){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e){
|
|
||||||
base.OnDetachedFromVisualTree(e);
|
|
||||||
if (DataContext is DownloadsPageViewModel vm){
|
|
||||||
vm.Cleanup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Button_OnClick(object? sender, RoutedEventArgs e){
|
|
||||||
// Crunchy.Instance.TestMethode();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -5,28 +5,136 @@
|
||||||
xmlns:vm="clr-namespace:CRD.ViewModels"
|
xmlns:vm="clr-namespace:CRD.ViewModels"
|
||||||
xmlns:ui="clr-namespace:CRD.Utils.UI"
|
xmlns:ui="clr-namespace:CRD.Utils.UI"
|
||||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:history="clr-namespace:CRD.Utils.Structs.History"
|
||||||
x:DataType="vm:HistoryPageViewModel"
|
x:DataType="vm:HistoryPageViewModel"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="CRD.Views.HistoryPageView">
|
x:Class="CRD.Views.HistoryPageView">
|
||||||
|
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<ui:UiIntToVisibilityConverter x:Key="UiIntToVisibilityConverter" />
|
<ui:UiIntToVisibilityConverter x:Key="UiIntToVisibilityConverter" />
|
||||||
|
<ui:UiSonarrIdToVisibilityConverter x:Key="UiSonarrIdToVisibilityConverter" />
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Margin="20 0 0 0 ">
|
<Grid.ColumnDefinitions>
|
||||||
<Button Command="{Binding RefreshAll}" Margin="10" IsEnabled="{Binding !FetchingData}">Refresh All</Button>
|
<ColumnDefinition Width="Auto" /> <!-- Takes up most space for the title -->
|
||||||
<Button Command="{Binding AddMissingToQueue}" Margin="10" IsEnabled="{Binding !FetchingData}">Add To Queue</Button>
|
<ColumnDefinition Width="*" />
|
||||||
<ToggleButton IsChecked="{Binding EditMode}" Margin="10" IsEnabled="{Binding !FetchingData}">Edit</ToggleButton>
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Background="#2a2a2a"></StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal">
|
||||||
|
|
||||||
|
<Button Width="70" Height="70" Background="Transparent" BorderThickness="0" Margin="5 0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Command="{Binding RefreshAll}"
|
||||||
|
IsEnabled="{Binding !FetchingData}">
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<controls:SymbolIcon Symbol="Sync" FontSize="32" />
|
||||||
|
<TextBlock Text="Refresh Filtered" TextWrapping="Wrap" FontSize="12"></TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button Width="70" Height="70" Background="Transparent" BorderThickness="0" Margin="5 0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Command="{Binding AddMissingToQueue}"
|
||||||
|
IsEnabled="{Binding !FetchingData}">
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<controls:SymbolIcon Symbol="Import" FontSize="32" />
|
||||||
|
<TextBlock Text="Add To Queue" TextWrapping="Wrap" FontSize="12"></TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<ToggleButton Width="70" Height="70" Background="Transparent" BorderThickness="0" Margin="5 0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
IsChecked="{Binding EditMode}"
|
||||||
|
IsEnabled="{Binding !FetchingData}">
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<controls:SymbolIcon Symbol="Edit" FontSize="32" />
|
||||||
|
<TextBlock Text="Edit" TextWrapping="Wrap" FontSize="12"></TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</ToggleButton>
|
||||||
|
|
||||||
|
<!-- <Button Command="{Binding RefreshAll}" Margin="10" IsEnabled="{Binding !FetchingData}">Refresh All</Button> -->
|
||||||
|
<!-- <Button Command="{Binding AddMissingToQueue}" Margin="10 0" IsEnabled="{Binding !FetchingData}">Add To Queue</Button> -->
|
||||||
|
<!-- <ToggleButton IsChecked="{Binding EditMode}" Margin="10 0" IsEnabled="{Binding !FetchingData}">Edit</ToggleButton> -->
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<ListBox Grid.Row="1" Grid.Column="0" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedSeries}" Margin="5">
|
<StackPanel Grid.Row="0" Grid.Column="2" Orientation="Horizontal">
|
||||||
|
|
||||||
|
<Slider VerticalAlignment="Center" Minimum="0.5" Maximum="1" Width="100"
|
||||||
|
Value="{Binding ScaleValue}"
|
||||||
|
IsVisible="{Binding IsPosterViewSelected}">
|
||||||
|
</Slider>
|
||||||
|
|
||||||
|
<StackPanel>
|
||||||
|
<ToggleButton x:Name="DropdownButtonViews" Width="70" Height="70" Background="Transparent"
|
||||||
|
BorderThickness="0" Margin="5 0" VerticalAlignment="Center"
|
||||||
|
IsEnabled="{Binding !FetchingData}"
|
||||||
|
IsChecked="{Binding ViewSelectionOpen}">
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<controls:SymbolIcon Symbol="View" FontSize="32" />
|
||||||
|
<TextBlock Text="View" FontSize="12"></TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</ToggleButton>
|
||||||
|
<Popup IsLightDismissEnabled="True"
|
||||||
|
IsOpen="{Binding IsChecked, ElementName=DropdownButtonViews, Mode=TwoWay}"
|
||||||
|
Placement="BottomEdgeAlignedRight"
|
||||||
|
PlacementTarget="{Binding ElementName=DropdownButtonViews}">
|
||||||
|
<Border BorderThickness="1" Background="{DynamicResource ComboBoxDropDownBackground}">
|
||||||
|
<ListBox SelectionMode="Single" Width="210"
|
||||||
|
MaxHeight="400"
|
||||||
|
ItemsSource="{Binding ViewsList}" SelectedItem="{Binding SelectedView}">
|
||||||
|
</ListBox>
|
||||||
|
</Border>
|
||||||
|
</Popup>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
|
||||||
|
<StackPanel>
|
||||||
|
<ToggleButton x:Name="DropdownButtonSorting" Width="70" Height="70" Background="Transparent"
|
||||||
|
BorderThickness="0" Margin="5 0" VerticalAlignment="Center"
|
||||||
|
IsEnabled="{Binding !FetchingData}"
|
||||||
|
IsChecked="{Binding SortingSelectionOpen}">
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<controls:SymbolIcon Symbol="Sort" FontSize="32" />
|
||||||
|
<TextBlock Text="Sort" FontSize="12"></TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</ToggleButton>
|
||||||
|
<Popup IsLightDismissEnabled="True"
|
||||||
|
IsOpen="{Binding IsChecked, ElementName=DropdownButtonSorting, Mode=TwoWay}"
|
||||||
|
Placement="BottomEdgeAlignedRight"
|
||||||
|
PlacementTarget="{Binding ElementName=DropdownButtonSorting}">
|
||||||
|
<Border BorderThickness="1" Background="{DynamicResource ComboBoxDropDownBackground}">
|
||||||
|
<ListBox SelectionMode="Single" Width="210"
|
||||||
|
MaxHeight="400"
|
||||||
|
ItemsSource="{Binding SortingList}" SelectedItem="{Binding SelectedSorting}">
|
||||||
|
</ListBox>
|
||||||
|
</Border>
|
||||||
|
</Popup>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Button Width="70" Height="70" Background="Transparent" BorderThickness="0" Margin="5 0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
IsEnabled="False">
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<controls:SymbolIcon Symbol="Filter" FontSize="32" />
|
||||||
|
<TextBlock Text="Filter" FontSize="12"></TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
|
||||||
|
<ListBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" ItemsSource="{Binding Items}"
|
||||||
|
SelectedItem="{Binding SelectedSeries}"
|
||||||
|
Margin="5" IsVisible="{Binding IsPosterViewSelected}">
|
||||||
|
|
||||||
<ListBox.ItemsPanel>
|
<ListBox.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
|
@ -35,32 +143,68 @@
|
||||||
</ListBox.ItemsPanel>
|
</ListBox.ItemsPanel>
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
|
<StackPanel Orientation="Vertical" HorizontalAlignment="Center"
|
||||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" MaxWidth="250" Width="250"
|
Width="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterWidth}"
|
||||||
MaxHeight="400" Height="400" Margin="5">
|
Height="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterHeight}"
|
||||||
|
Margin="5">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Image Source="{Binding ThumbnailImage}" Width="240" Height="360"></Image>
|
<Image Source="{Binding ThumbnailImage}"
|
||||||
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Right" IsVisible="{Binding NewEpisodes, Converter={StaticResource UiIntToVisibilityConverter}}">
|
Width="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterImageWidth}"
|
||||||
<TextBlock VerticalAlignment="Center" TextAlignment="Center" Margin="0 0 5 0" Width="30" Height="30"
|
Height="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterImageHeight}">
|
||||||
Background="Black" Opacity="0.8" Text="{Binding NewEpisodes}"
|
</Image>
|
||||||
Padding="0,5,0,0"/>
|
|
||||||
</StackPanel>
|
<Border VerticalAlignment="Top" HorizontalAlignment="Right" CornerRadius="0 0 0 10"
|
||||||
|
Background="#f78c25" Opacity="1"
|
||||||
|
Margin="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).CornerMargin} "
|
||||||
|
IsVisible="{Binding NewEpisodes, Converter={StaticResource UiIntToVisibilityConverter}}">
|
||||||
|
<TextBlock VerticalAlignment="Center" TextAlignment="Center" Width="30"
|
||||||
|
Height="30"
|
||||||
|
Text="{Binding NewEpisodes}"
|
||||||
|
Padding="0,5,0,0" />
|
||||||
|
</Border>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<TextBlock HorizontalAlignment="Center" Text="{Binding SeriesTitle}" TextWrapping="NoWrap" MaxWidth="240"
|
|
||||||
|
<TextBlock HorizontalAlignment="Center" TextAlignment="Center"
|
||||||
|
Text="{Binding SeriesTitle}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Width="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterImageWidth}"
|
||||||
|
FontSize="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterTextSize}"
|
||||||
|
Height="35"
|
||||||
Margin="4,0,4,0">
|
Margin="4,0,4,0">
|
||||||
|
<ToolTip.Tip>
|
||||||
|
<TextBlock Text="{Binding SeriesTitle}" FontSize="15" />
|
||||||
|
</ToolTip.Tip>
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<TextBlock HorizontalAlignment="Center" TextAlignment="Center"
|
||||||
|
Text="{Binding SonarrNextAirDate}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Width="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterImageWidth}"
|
||||||
|
FontSize="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterTextSize}"
|
||||||
|
MaxHeight="20"
|
||||||
|
Margin="4,0,4,0">
|
||||||
|
<ToolTip.Tip>
|
||||||
|
<TextBlock Text="{Binding SonarrNextAirDate}" FontSize="15" />
|
||||||
|
</ToolTip.Tip>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" MaxWidth="250" Width="250"
|
|
||||||
MaxHeight="400" Height="400" Background="#90000000" IsVisible="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).EditMode}">
|
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#90000000" IsVisible="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).EditMode}">
|
||||||
<Button MaxWidth="250" Width="250" MaxHeight="400" Height="400" FontStyle="Italic"
|
|
||||||
VerticalAlignment="Center"
|
<Button
|
||||||
Command="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).RemoveSeries}"
|
Background="Transparent"
|
||||||
CommandParameter="{Binding SeriesId}"
|
BorderThickness="0"
|
||||||
IsEnabled="{Binding !$parent[UserControl].((vm:HistoryPageViewModel)DataContext).FetchingData}"
|
Width="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterWidth}"
|
||||||
>
|
Height="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterHeight}"
|
||||||
|
FontStyle="Italic"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Command="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).RemoveSeries}"
|
||||||
|
CommandParameter="{Binding SeriesId}"
|
||||||
|
IsEnabled="{Binding !$parent[UserControl].((vm:HistoryPageViewModel)DataContext).FetchingData}">
|
||||||
<ToolTip.Tip>
|
<ToolTip.Tip>
|
||||||
<TextBlock Text="Remove Series" FontSize="15" />
|
<TextBlock Text="Remove Series" FontSize="15" />
|
||||||
</ToolTip.Tip>
|
</ToolTip.Tip>
|
||||||
|
@ -68,22 +212,351 @@
|
||||||
<controls:SymbolIcon Symbol="Delete" FontSize="32" />
|
<controls:SymbolIcon Symbol="Delete" FontSize="32" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
</StackPanel>
|
</Grid>
|
||||||
|
|
||||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="250" Width="250"
|
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#90000000" IsVisible="{Binding FetchingData}">
|
||||||
MaxHeight="400" Height="400" Background="#90000000" IsVisible="{Binding FetchingData}">
|
|
||||||
<!-- <ProgressBar IsIndeterminate="{Binding FetchingData}" -->
|
|
||||||
<!-- MaxWidth="100"> -->
|
|
||||||
<!-- </ProgressBar> -->
|
|
||||||
<controls:ProgressRing Width="100" Height="100"></controls:ProgressRing>
|
<controls:ProgressRing Width="100" Height="100"></controls:ProgressRing>
|
||||||
</StackPanel>
|
</Grid>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" IsVisible="{Binding IsTableViewSelected}">
|
||||||
|
|
||||||
|
<ScrollViewer>
|
||||||
|
<ItemsControl ItemsSource="{Binding Items}"
|
||||||
|
IsEnabled="{Binding !FetchingData}"
|
||||||
|
|
||||||
|
Margin="5" IsVisible="{Binding IsTableViewSelected}">
|
||||||
|
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<controls:SettingsExpander
|
||||||
|
Header="{Binding .}"
|
||||||
|
ItemsSource="{Binding .}"
|
||||||
|
IsExpanded="False">
|
||||||
|
<controls:SettingsExpander.HeaderTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type history:HistorySeries}">
|
||||||
|
|
||||||
|
<Grid Margin="10">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1" Grid.Column="0">
|
||||||
|
<!-- Define a row with auto height to match the image height -->
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<!-- Define columns if needed -->
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<!-- Image element -->
|
||||||
|
<Image Margin="10"
|
||||||
|
Source="{Binding ThumbnailImage}"
|
||||||
|
Width="120"
|
||||||
|
Height="180" />
|
||||||
|
|
||||||
|
<!-- Border element aligned within the same grid cell, overlaying the image -->
|
||||||
|
<Border VerticalAlignment="Top" HorizontalAlignment="Right"
|
||||||
|
CornerRadius="0 0 0 10"
|
||||||
|
Background="#f78c25" Opacity="1"
|
||||||
|
Margin="10"
|
||||||
|
IsVisible="{Binding NewEpisodes, Converter={StaticResource UiIntToVisibilityConverter}}"
|
||||||
|
Grid.Row="0" Grid.Column="0">
|
||||||
|
<TextBlock VerticalAlignment="Center" TextAlignment="Center"
|
||||||
|
Width="30" Height="30"
|
||||||
|
Text="{Binding NewEpisodes}"
|
||||||
|
Padding="0,5,0,0" />
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
|
<Grid Grid.Row="1" Grid.Column="1">
|
||||||
|
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" FontSize="25" Text="{Binding SeriesTitle}"></TextBlock>
|
||||||
|
<TextBlock Grid.Row="1" FontSize="15" TextWrapping="Wrap"
|
||||||
|
Text="{Binding SeriesDescription}">
|
||||||
|
</TextBlock>
|
||||||
|
<StackPanel Grid.Row="3" Orientation="Vertical">
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0 10 10 10">
|
||||||
|
|
||||||
|
<Button Width="34" Height="34" Margin="0 0 10 0"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="0" CornerRadius="50"
|
||||||
|
Command="{Binding OpenCrPage}">
|
||||||
|
<Grid>
|
||||||
|
<controls:ImageIcon
|
||||||
|
Source="../Assets/crunchy_icon_round.png"
|
||||||
|
Width="30"
|
||||||
|
Height="30" />
|
||||||
|
</Grid>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button Width="34" Height="34" Margin="0 0 10 0"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="0" CornerRadius="50"
|
||||||
|
IsVisible="{Binding SonarrSeriesId, Converter={StaticResource UiSonarrIdToVisibilityConverter}}"
|
||||||
|
Command="{Binding OpenSonarrPage}">
|
||||||
|
<Grid>
|
||||||
|
<controls:ImageIcon Source="../Assets/sonarr.png"
|
||||||
|
Width="30" Height="30" />
|
||||||
|
</Grid>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Button Command="{Binding FetchData}" Margin="0 0 5 10">Fetch Series</Button>
|
||||||
|
<ToggleButton x:Name="SeriesEditModeToggle" IsChecked="{Binding EditModeEnabled}" Margin="0 0 5 10">Edit</ToggleButton>
|
||||||
|
|
||||||
|
<Button Margin="0 0 5 10" FontStyle="Italic"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Command="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).OpenFolderDialogAsyncSeries}"
|
||||||
|
CommandParameter="{Binding .}">
|
||||||
|
<ToolTip.Tip>
|
||||||
|
<TextBlock Text="{Binding SeriesDownloadPath}"
|
||||||
|
FontSize="15" />
|
||||||
|
</ToolTip.Tip>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<controls:SymbolIcon Symbol="Folder" FontSize="18" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
|
</DataTemplate>
|
||||||
|
</controls:SettingsExpander.HeaderTemplate>
|
||||||
|
|
||||||
|
|
||||||
|
<controls:SettingsExpanderItem>
|
||||||
|
<ScrollViewer>
|
||||||
|
<ItemsControl ItemsSource="{Binding Seasons}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type history:HistorySeason}">
|
||||||
|
|
||||||
|
<controls:SettingsExpander
|
||||||
|
Header="{Binding CombinedProperty}"
|
||||||
|
ItemsSource="{Binding EpisodesList}"
|
||||||
|
|
||||||
|
Description="{Binding SeasonTitle}"
|
||||||
|
IsExpanded="False">
|
||||||
|
|
||||||
|
|
||||||
|
<controls:SettingsExpander.ItemTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type history:HistoryEpisode}">
|
||||||
|
|
||||||
|
<Grid VerticalAlignment="Center">
|
||||||
|
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<TextBlock Text="E"></TextBlock>
|
||||||
|
<TextBlock Text="{Binding Episode}"></TextBlock>
|
||||||
|
<TextBlock Text=" - "></TextBlock>
|
||||||
|
<TextBlock Text="{Binding EpisodeTitle}"></TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="1"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
|
||||||
|
<StackPanel VerticalAlignment="Center"
|
||||||
|
Margin="0 0 5 0"
|
||||||
|
IsVisible="{Binding $parent[ItemsControl].((vm:SeriesPageViewModel)DataContext).SonarrAvailable}">
|
||||||
|
|
||||||
|
|
||||||
|
<controls:ImageIcon
|
||||||
|
IsVisible="{Binding SonarrHasFile}"
|
||||||
|
Source="../Assets/sonarr.png"
|
||||||
|
Width="25"
|
||||||
|
Height="25" />
|
||||||
|
|
||||||
|
<controls:ImageIcon
|
||||||
|
IsVisible="{Binding !SonarrHasFile}"
|
||||||
|
Source="../Assets/sonarr_inactive.png"
|
||||||
|
Width="25"
|
||||||
|
Height="25" />
|
||||||
|
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
|
||||||
|
<Button Width="34" Height="34"
|
||||||
|
Margin="0 0 10 0" Background="Transparent"
|
||||||
|
BorderThickness="0" CornerRadius="50"
|
||||||
|
IsVisible="{Binding !WasDownloaded}"
|
||||||
|
Command="{Binding $parent[controls:SettingsExpander].((history:HistorySeason)DataContext).UpdateDownloaded}"
|
||||||
|
CommandParameter="{Binding EpisodeId}">
|
||||||
|
<Grid>
|
||||||
|
<Ellipse Width="25" Height="25"
|
||||||
|
Fill="Gray" />
|
||||||
|
<controls:SymbolIcon Symbol="Checkmark"
|
||||||
|
FontSize="18" />
|
||||||
|
</Grid>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button Width="34" Height="34"
|
||||||
|
Margin="0 0 10 0" Background="Transparent"
|
||||||
|
BorderThickness="0" CornerRadius="50"
|
||||||
|
IsVisible="{Binding WasDownloaded}"
|
||||||
|
Command="{Binding $parent[controls:SettingsExpander].((history:HistorySeason)DataContext).UpdateDownloaded}"
|
||||||
|
CommandParameter="{Binding EpisodeId}">
|
||||||
|
<Grid>
|
||||||
|
<Ellipse Width="25" Height="25"
|
||||||
|
Fill="#21a556" />
|
||||||
|
<controls:SymbolIcon Symbol="Checkmark"
|
||||||
|
FontSize="18" />
|
||||||
|
</Grid>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button Margin="0 0 5 0" FontStyle="Italic"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Command="{Binding DownloadEpisode}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<controls:SymbolIcon Symbol="Download"
|
||||||
|
FontSize="18" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</controls:SettingsExpander.ItemTemplate>
|
||||||
|
|
||||||
|
<controls:SettingsExpander.Footer>
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<TextBlock Text="{Binding DownloadedEpisodes}" VerticalAlignment="Center"></TextBlock>
|
||||||
|
<TextBlock Text="/" VerticalAlignment="Center"></TextBlock>
|
||||||
|
<TextBlock Text="{Binding EpisodesList.Count}" VerticalAlignment="Center"></TextBlock>
|
||||||
|
|
||||||
|
<Button Margin="10 0 0 0" FontStyle="Italic"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Command="{Binding $parent[ScrollViewer].((history:HistorySeries)DataContext).FetchData}"
|
||||||
|
CommandParameter="{Binding SeasonId}">
|
||||||
|
<ToolTip.Tip>
|
||||||
|
<TextBlock Text="Fetch Season" FontSize="15" />
|
||||||
|
</ToolTip.Tip>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<controls:SymbolIcon Symbol="Refresh"
|
||||||
|
FontSize="18" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button Margin="10 0 0 0" FontStyle="Italic"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Command="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).OpenFolderDialogAsyncSeason}"
|
||||||
|
CommandParameter="{Binding .}">
|
||||||
|
<ToolTip.Tip>
|
||||||
|
<TextBlock Text="{Binding SeasonDownloadPath}"
|
||||||
|
FontSize="15" />
|
||||||
|
</ToolTip.Tip>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<controls:SymbolIcon Symbol="Folder"
|
||||||
|
FontSize="18" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button Margin="10 0 0 0" FontStyle="Italic"
|
||||||
|
IsVisible="{Binding $parent[ScrollViewer].((history:HistorySeries)DataContext).EditModeEnabled}"
|
||||||
|
Command="{Binding $parent[ScrollViewer].((history:HistorySeries)DataContext).RemoveSeason}"
|
||||||
|
CommandParameter="{Binding SeasonId}"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<ToolTip.Tip>
|
||||||
|
<TextBlock Text="Remove Season" FontSize="15" />
|
||||||
|
</ToolTip.Tip>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<controls:SymbolIcon Symbol="Delete"
|
||||||
|
FontSize="18" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</controls:SettingsExpander.Footer>
|
||||||
|
|
||||||
|
</controls:SettingsExpander>
|
||||||
|
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
</controls:SettingsExpanderItem>
|
||||||
|
|
||||||
|
</controls:SettingsExpander>
|
||||||
|
|
||||||
|
<Grid IsVisible="{Binding FetchingData}"
|
||||||
|
Background="#90000000"
|
||||||
|
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||||
|
<controls:ProgressRing Width="100" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid IsVisible="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).EditMode}"
|
||||||
|
Background="#90000000"
|
||||||
|
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||||
|
<Button
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="0"
|
||||||
|
FontStyle="Italic"
|
||||||
|
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
||||||
|
Command="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).RemoveSeries}"
|
||||||
|
CommandParameter="{Binding SeriesId}"
|
||||||
|
IsEnabled="{Binding !$parent[UserControl].((vm:HistoryPageViewModel)DataContext).FetchingData}">
|
||||||
|
<ToolTip.Tip>
|
||||||
|
<TextBlock Text="Remove Series" FontSize="15" />
|
||||||
|
</ToolTip.Tip>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<controls:SymbolIcon Symbol="Delete" FontSize="32" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
</UserControl>
|
</UserControl>
|
|
@ -116,6 +116,9 @@ public partial class MainWindow : AppWindow{
|
||||||
break;
|
break;
|
||||||
case "History":
|
case "History":
|
||||||
navView.Content = Activator.CreateInstance(typeof(HistoryPageViewModel));
|
navView.Content = Activator.CreateInstance(typeof(HistoryPageViewModel));
|
||||||
|
if ( navView.Content is HistoryPageViewModel){
|
||||||
|
((HistoryPageViewModel)navView.Content).SetStorageProvider(StorageProvider);
|
||||||
|
}
|
||||||
navigationStack.Clear();
|
navigationStack.Clear();
|
||||||
navigationStack.Push(navView.Content);
|
navigationStack.Push(navView.Content);
|
||||||
selectedNavVieItem = selectedItem;
|
selectedNavVieItem = selectedItem;
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
xmlns:vm="clr-namespace:CRD.ViewModels"
|
xmlns:vm="clr-namespace:CRD.ViewModels"
|
||||||
xmlns:ui="clr-namespace:CRD.Utils.UI"
|
xmlns:history="clr-namespace:CRD.Utils.Structs.History"
|
||||||
xmlns:downloader="clr-namespace:CRD.Downloader"
|
|
||||||
x:DataType="vm:SeriesPageViewModel"
|
x:DataType="vm:SeriesPageViewModel"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="CRD.Views.SeriesPageView">
|
x:Class="CRD.Views.SeriesPageView">
|
||||||
|
@ -46,7 +45,7 @@
|
||||||
|
|
||||||
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
||||||
BorderThickness="0" CornerRadius="50"
|
BorderThickness="0" CornerRadius="50"
|
||||||
Command="{Binding OpenCrPage}">
|
Command="{Binding SelectedSeries.OpenCrPage}">
|
||||||
<Grid>
|
<Grid>
|
||||||
<controls:ImageIcon Source="../Assets/crunchy_icon_round.png" Width="30" Height="30" />
|
<controls:ImageIcon Source="../Assets/crunchy_icon_round.png" Width="30" Height="30" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -55,13 +54,12 @@
|
||||||
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
||||||
BorderThickness="0" CornerRadius="50"
|
BorderThickness="0" CornerRadius="50"
|
||||||
IsVisible="{Binding SonarrAvailable}"
|
IsVisible="{Binding SonarrAvailable}"
|
||||||
Command="{Binding OpenSonarrPage}">
|
Command="{Binding SelectedSeries.OpenSonarrPage}">
|
||||||
<Grid>
|
<Grid>
|
||||||
<controls:ImageIcon Source="../Assets/sonarr.png" Width="30" Height="30" />
|
<controls:ImageIcon Source="../Assets/sonarr.png" Width="30" Height="30" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
|
@ -138,7 +136,7 @@
|
||||||
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
||||||
BorderThickness="0" CornerRadius="50"
|
BorderThickness="0" CornerRadius="50"
|
||||||
IsVisible="{Binding !WasDownloaded}"
|
IsVisible="{Binding !WasDownloaded}"
|
||||||
Command="{Binding $parent[controls:SettingsExpander].((downloader:HistorySeason)DataContext).UpdateDownloaded}"
|
Command="{Binding $parent[controls:SettingsExpander].((history:HistorySeason)DataContext).UpdateDownloaded}"
|
||||||
CommandParameter="{Binding EpisodeId}">
|
CommandParameter="{Binding EpisodeId}">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Ellipse Width="25" Height="25" Fill="Gray" />
|
<Ellipse Width="25" Height="25" Fill="Gray" />
|
||||||
|
@ -149,7 +147,7 @@
|
||||||
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
||||||
BorderThickness="0" CornerRadius="50"
|
BorderThickness="0" CornerRadius="50"
|
||||||
IsVisible="{Binding WasDownloaded}"
|
IsVisible="{Binding WasDownloaded}"
|
||||||
Command="{Binding $parent[controls:SettingsExpander].((downloader:HistorySeason)DataContext).UpdateDownloaded}"
|
Command="{Binding $parent[controls:SettingsExpander].((history:HistorySeason)DataContext).UpdateDownloaded}"
|
||||||
CommandParameter="{Binding EpisodeId}">
|
CommandParameter="{Binding EpisodeId}">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Ellipse Width="25" Height="25" Fill="#21a556" />
|
<Ellipse Width="25" Height="25" Fill="#21a556" />
|
||||||
|
|
Loading…
Reference in New Issue