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;
|
||||
}
|
||||
|
||||
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){
|
||||
Console.Error.WriteLine("Missing CMS Access Token");
|
||||
return null;
|
||||
|
@ -366,7 +366,11 @@ public class CrSeries(){
|
|||
|
||||
query["preferred_audio_language"] = "ja-JP";
|
||||
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.Models;
|
||||
using CRD.Utils.Structs;
|
||||
using CRD.Utils.Structs.History;
|
||||
using CRD.ViewModels;
|
||||
using CRD.Views;
|
||||
using HtmlAgilityPack;
|
||||
|
@ -104,8 +105,9 @@ public class Crunchyroll{
|
|||
|
||||
public Crunchyroll(){
|
||||
CrunOptions = new CrDownloadOptions();
|
||||
Queue.CollectionChanged += UpdateItemListOnRemove;
|
||||
}
|
||||
|
||||
|
||||
public async Task Init(){
|
||||
_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){
|
||||
if (!forceUpdate && calendar.TryGetValue(weeksMondayDate, out var forDate)){
|
||||
|
@ -447,13 +484,12 @@ public class Crunchyroll{
|
|||
if (CrunOptions.RemoveFinishedDownload){
|
||||
Queue.Remove(data);
|
||||
}
|
||||
|
||||
Queue.Refresh();
|
||||
} else{
|
||||
Console.WriteLine("Skipping mux");
|
||||
}
|
||||
|
||||
ActiveDownloads--;
|
||||
Queue.Refresh();
|
||||
|
||||
if (CrunOptions.History && data.Data != null && data.Data.Count > 0){
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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(),
|
||||
|
@ -528,7 +564,7 @@ public class Crunchyroll{
|
|||
},
|
||||
CcTag = options.CcTag,
|
||||
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)){
|
||||
|
@ -1380,6 +1416,11 @@ public class Crunchyroll{
|
|||
writer.WriteEndElement(); // End Tags
|
||||
writer.WriteEndDocument();
|
||||
}
|
||||
|
||||
files.Add(new DownloadedMedia{
|
||||
Type = DownloadMediaType.Description,
|
||||
Path = fullPath,
|
||||
});
|
||||
}
|
||||
|
||||
Console.WriteLine($"{fileName} has been created with the description.");
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Media.Imaging;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CRD.Utils;
|
||||
using CRD.Utils.Sonarr;
|
||||
using CRD.Utils.Sonarr.Models;
|
||||
using CRD.Utils.Structs;
|
||||
using CRD.Utils.Structs.History;
|
||||
using CRD.Views;
|
||||
using DynamicData;
|
||||
using Newtonsoft.Json;
|
||||
using ReactiveUI;
|
||||
|
||||
|
@ -22,7 +27,7 @@ public class History(){
|
|||
public async Task UpdateSeries(string seriesId, string? seasonId){
|
||||
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){
|
||||
Console.Error.WriteLine("Parse Data Invalid");
|
||||
|
@ -64,7 +69,7 @@ public class History(){
|
|||
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
|
||||
|
||||
if (historySeries != null){
|
||||
var historySeason = historySeries.Seasons.Find(s => s.SeasonId == seasonId);
|
||||
var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == seasonId);
|
||||
|
||||
if (historySeason != null){
|
||||
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);
|
||||
|
||||
if (historySeries != null){
|
||||
var historySeason = historySeries.Seasons.Find(s => s.SeasonId == seasonId);
|
||||
var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == seasonId);
|
||||
|
||||
if (historySeason != null){
|
||||
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 downloadDirPath = "";
|
||||
|
||||
|
||||
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)){
|
||||
downloadDirPath = historySeries.SeriesDownloadPath;
|
||||
}
|
||||
|
@ -145,7 +150,7 @@ public class History(){
|
|||
var seriesId = episode.SeriesId;
|
||||
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
|
||||
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);
|
||||
if (series?.Data != null){
|
||||
|
@ -174,7 +179,7 @@ public class History(){
|
|||
|
||||
historySeries.Seasons.Add(newSeason);
|
||||
|
||||
historySeries.Seasons = historySeries.Seasons.OrderBy(s => s.SeasonNum != null ? int.Parse(s.SeasonNum) : 0).ToList();
|
||||
SortSeasons(historySeries);
|
||||
}
|
||||
|
||||
historySeries.UpdateNewEpisodes();
|
||||
|
@ -198,11 +203,7 @@ public class History(){
|
|||
historySeries.UpdateNewEpisodes();
|
||||
}
|
||||
|
||||
var sortedList = crunInstance.HistoryList.OrderBy(item => item.SeriesTitle).ToList();
|
||||
crunInstance.HistoryList.Clear();
|
||||
foreach (var item in sortedList){
|
||||
crunInstance.HistoryList.Add(item);
|
||||
}
|
||||
SortItems();
|
||||
|
||||
MatchHistorySeriesWithSonarr(false);
|
||||
await MatchHistoryEpisodesWithSonarr(false, historySeries);
|
||||
|
@ -215,7 +216,7 @@ public class History(){
|
|||
var seriesId = firstEpisode.SeriesId;
|
||||
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
|
||||
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);
|
||||
if (series?.Data != null){
|
||||
historySeries.SeriesTitle = series.Data.First().Title;
|
||||
|
@ -257,7 +258,7 @@ public class History(){
|
|||
|
||||
historySeries.Seasons.Add(newSeason);
|
||||
|
||||
historySeries.Seasons = historySeries.Seasons.OrderBy(s => s.SeasonNum != null ? int.Parse(s.SeasonNum) : 0).ToList();
|
||||
SortSeasons(historySeries);
|
||||
}
|
||||
|
||||
historySeries.UpdateNewEpisodes();
|
||||
|
@ -286,11 +287,7 @@ public class History(){
|
|||
historySeries.UpdateNewEpisodes();
|
||||
}
|
||||
|
||||
var sortedList = crunInstance.HistoryList.OrderBy(item => item.SeriesTitle).ToList();
|
||||
crunInstance.HistoryList.Clear();
|
||||
foreach (var item in sortedList){
|
||||
crunInstance.HistoryList.Add(item);
|
||||
}
|
||||
SortItems();
|
||||
|
||||
MatchHistorySeriesWithSonarr(false);
|
||||
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){
|
||||
// var series = await crunInstance.CrSeries.SeriesById(seriesId);
|
||||
|
||||
|
@ -389,6 +446,8 @@ public class History(){
|
|||
if (!string.IsNullOrEmpty(historySeries.SonarrSeriesId)){
|
||||
var episodes = await SonarrClient.Instance.GetEpisodes(int.Parse(historySeries.SonarrSeriesId));
|
||||
|
||||
historySeries.SonarrNextAirDate = GetNextAirDate(episodes);
|
||||
|
||||
List<HistoryEpisode> allHistoryEpisodes =[];
|
||||
|
||||
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){
|
||||
SonarrSeries? closestMatch = null;
|
||||
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")]
|
||||
Subtitle,
|
||||
|
||||
[EnumMember(Value = "Description")]
|
||||
Description,
|
||||
}
|
||||
|
||||
public enum ScaledBorderAndShadowSelection{
|
||||
|
@ -171,6 +174,18 @@ public enum ScaledBorderAndShadowSelection{
|
|||
ScaledBorderAndShadowNo,
|
||||
}
|
||||
|
||||
public enum HistoryViewType{
|
||||
Posters,
|
||||
Table,
|
||||
}
|
||||
|
||||
public enum SortingType{
|
||||
[EnumMember(Value = "Series Title")]
|
||||
SeriesTitle,
|
||||
[EnumMember(Value = "Next Air Date")]
|
||||
NextAirDate,
|
||||
}
|
||||
|
||||
public enum SonarrCoverType{
|
||||
Banner,
|
||||
FanArt,
|
||||
|
|
|
@ -71,6 +71,15 @@ public class HlsDownloader{
|
|||
_data.Offset = resumeData.Completed;
|
||||
_data.IsResume = true;
|
||||
} 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: {{ total: {resumeData.Total}, dled: {resumeData.Completed} }}, " +
|
||||
$"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){
|
||||
// Check if the path is absolute
|
||||
bool isAbsolute = Path.IsPathRooted(path);
|
||||
|
|
|
@ -141,10 +141,12 @@ public class Merger{
|
|||
|
||||
foreach (var aud in options.OnlyAudio){
|
||||
string trackName = aud.Language.Name;
|
||||
args.Add("--audio-tracks 0");
|
||||
args.Add("--no-video");
|
||||
args.Add($"--track-name 0:\"{trackName}\"");
|
||||
args.Add($"--language 0:{aud.Language.Code}");
|
||||
args.Add("--no-video");
|
||||
args.Add("--audio-tracks 0");
|
||||
|
||||
|
||||
|
||||
if (options.Defaults.Audio.Code == aud.Language.Code){
|
||||
args.Add("--default-track 0");
|
||||
|
@ -181,7 +183,7 @@ public class Merger{
|
|||
args.Add("--no-subtitles");
|
||||
}
|
||||
|
||||
if (options.Fonts != null && options.Fonts.Count > 0){
|
||||
if (options.Fonts is{ Count: > 0 }){
|
||||
foreach (var font in options.Fonts){
|
||||
args.Add($"--attachment-name \"{font.Name}\"");
|
||||
args.Add($"--attachment-mime-type \"{font.Mime}\"");
|
||||
|
@ -191,7 +193,7 @@ public class Merger{
|
|||
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}\"");
|
||||
}
|
||||
|
||||
|
@ -199,8 +201,8 @@ public class Merger{
|
|||
args.Add($"--title \"{options.VideoTitle}\"");
|
||||
}
|
||||
|
||||
if (options.MuxDescription){
|
||||
args.Add($"--global-tags \"{Path.Combine(Path.GetDirectoryName(options.Output), Path.GetFileNameWithoutExtension(options.Output))}.xml\"");
|
||||
if (options.Description is{ Count: > 0 }){
|
||||
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 + ".resume"));
|
||||
|
||||
if (options.MuxDescription){
|
||||
DeleteFile(Path.Combine(Path.GetDirectoryName(options.Output), Path.GetFileNameWithoutExtension(options.Output)) + ".xml");
|
||||
}
|
||||
|
||||
options.Description?.ForEach(chapter => DeleteFile(chapter.Path));
|
||||
|
||||
// Delete chapter files if any
|
||||
options.Chapters?.ForEach(chapter => DeleteFile(chapter.Path));
|
||||
|
||||
|
@ -319,7 +319,7 @@ public class MergerOptions{
|
|||
public MuxOptions Options{ get; set; }
|
||||
public Defaults Defaults{ get; set; }
|
||||
public bool mp3{ get; set; }
|
||||
public bool MuxDescription{ get; set; }
|
||||
public List<MergerInput> Description{ get; set; } = new List<MergerInput>();
|
||||
}
|
||||
|
||||
public class MuxOptions{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using CRD.Utils.Sonarr;
|
||||
using CRD.ViewModels;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace CRD.Utils.Structs;
|
||||
|
@ -143,4 +144,7 @@ public class CrDownloadOptions{
|
|||
[YamlMember(Alias = "download_dir_path", ApplyNamingConventions = false)]
|
||||
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;
|
||||
|
||||
public partial class AddDownloadPageViewModel : ViewModelBase{
|
||||
[ObservableProperty] public string _urlInput = "";
|
||||
[ObservableProperty] public string _buttonText = "Enter Url";
|
||||
[ObservableProperty] public bool _addAllEpisodes = false;
|
||||
[ObservableProperty]
|
||||
public string _urlInput = "";
|
||||
|
||||
[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> SelectedItems{ get; } = new();
|
||||
|
||||
[ObservableProperty] public ComboBoxItem _currentSelectedSeason;
|
||||
[ObservableProperty]
|
||||
public ComboBoxItem _currentSelectedSeason;
|
||||
|
||||
public ObservableCollection<ComboBoxItem> SeasonList{ get; } = new();
|
||||
|
||||
private Dictionary<string, List<ItemModel>> episodesBySeason = new();
|
||||
|
@ -79,7 +92,6 @@ public partial class AddDownloadPageViewModel : ViewModelBase{
|
|||
|
||||
if (currentSeriesList != null){
|
||||
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){
|
||||
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);
|
||||
UrlInput = "";
|
||||
selectedEpisodes.Clear();
|
||||
|
@ -122,7 +134,7 @@ public partial class AddDownloadPageViewModel : ViewModelBase{
|
|||
|
||||
if (match.Success){
|
||||
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){
|
||||
return;
|
||||
|
@ -130,7 +142,7 @@ public partial class AddDownloadPageViewModel : ViewModelBase{
|
|||
|
||||
ButtonEnabled = false;
|
||||
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;
|
||||
if (list != null){
|
||||
currentSeriesList = list;
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia;
|
||||
using Avalonia.Media.Imaging;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CRD.Downloader;
|
||||
using CRD.Utils;
|
||||
using CRD.Utils.Structs;
|
||||
|
||||
namespace CRD.ViewModels;
|
||||
|
@ -27,64 +22,23 @@ public partial class DownloadsPageViewModel : ViewModelBase{
|
|||
public bool _removeFinished;
|
||||
|
||||
public DownloadsPageViewModel(){
|
||||
UpdateListItems();
|
||||
Crunchyroll.Instance.UpdateDownloadListItems();
|
||||
Items = Crunchyroll.Instance.DownloadItemModels;
|
||||
AutoDownload = Crunchyroll.Instance.CrunOptions.AutoDownload;
|
||||
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){
|
||||
Crunchyroll.Instance.CrunOptions.AutoDownload = value;
|
||||
if (value){
|
||||
UpdateListItems();
|
||||
Crunchyroll.Instance.UpdateDownloadListItems();
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnRemoveFinishedChanged(bool value){
|
||||
Crunchyroll.Instance.CrunOptions.RemoveFinishedDownload = value;
|
||||
}
|
||||
|
||||
public void Cleanup(){
|
||||
Crunchyroll.Instance.Queue.CollectionChanged -= UpdateItemListOnRemove;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public partial class DownloadItemModel : INotifyPropertyChanged{
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
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.Input;
|
||||
using CRD.Downloader;
|
||||
using CRD.Utils;
|
||||
using CRD.Utils.Structs;
|
||||
using CRD.Utils.Structs.History;
|
||||
using CRD.Views;
|
||||
using DynamicData;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace CRD.ViewModels;
|
||||
|
@ -22,9 +32,84 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
[ObservableProperty]
|
||||
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(){
|
||||
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){
|
||||
if (historySeries.ThumbnailImage == null){
|
||||
historySeries.LoadImage();
|
||||
|
@ -32,13 +117,87 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
|
||||
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){
|
||||
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();
|
||||
_selectedSeries = null;
|
||||
}
|
||||
|
@ -81,6 +240,7 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
|
||||
FetchingData = false;
|
||||
RaisePropertyChanged(nameof(FetchingData));
|
||||
Crunchyroll.Instance.CrHistory.SortItems();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
@ -89,4 +249,65 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
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.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Platform.Storage;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
@ -7,6 +8,8 @@ using CommunityToolkit.Mvvm.Input;
|
|||
using CRD.Downloader;
|
||||
using CRD.Utils;
|
||||
using CRD.Utils.Sonarr;
|
||||
using CRD.Utils.Structs;
|
||||
using CRD.Utils.Structs.History;
|
||||
using CRD.Views;
|
||||
using ReactiveUI;
|
||||
|
||||
|
@ -30,14 +33,13 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
|||
if (_selectedSeries.ThumbnailImage == null){
|
||||
_selectedSeries.LoadImage();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(SelectedSeries.SonarrSeriesId)){
|
||||
SonarrAvailable = SelectedSeries.SonarrSeriesId.Length > 0;
|
||||
Crunchyroll.Instance.CrHistory.MatchHistoryEpisodesWithSonarr(true,SelectedSeries);
|
||||
CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList);
|
||||
} else{
|
||||
|
||||
if (!string.IsNullOrEmpty(SelectedSeries.SonarrSeriesId) && Crunchyroll.Instance.CrunOptions.SonarrProperties != null){
|
||||
SonarrAvailable = SelectedSeries.SonarrSeriesId.Length > 0 && Crunchyroll.Instance.CrunOptions.SonarrProperties.SonarrEnabled;
|
||||
}else{
|
||||
SonarrAvailable = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -72,23 +74,7 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
|||
_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]
|
||||
public async Task UpdateData(string? season){
|
||||
await SelectedSeries.FetchData(season);
|
||||
|
@ -98,7 +84,7 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
|||
|
||||
[RelayCommand]
|
||||
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){
|
||||
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>
|
||||
</Grid>
|
||||
|
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding SeasonName}"
|
||||
TextWrapping="NoWrap"
|
||||
<TextBlock HorizontalAlignment="Center" TextAlignment="Center" Text="{Binding SeasonName}"
|
||||
TextWrapping="Wrap"
|
||||
Height="40"
|
||||
Margin="0,0,0,0">
|
||||
<ToolTip.Tip>
|
||||
<TextBlock Text="{Binding SeasonName}" FontSize="15" />
|
||||
|
|
|
@ -10,15 +10,4 @@ public partial class DownloadsPageView : UserControl{
|
|||
public DownloadsPageView(){
|
||||
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:ui="clr-namespace:CRD.Utils.UI"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:history="clr-namespace:CRD.Utils.Structs.History"
|
||||
x:DataType="vm:HistoryPageViewModel"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="CRD.Views.HistoryPageView">
|
||||
|
||||
<UserControl.Resources>
|
||||
<ui:UiIntToVisibilityConverter x:Key="UiIntToVisibilityConverter" />
|
||||
<ui:UiSonarrIdToVisibilityConverter x:Key="UiSonarrIdToVisibilityConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
|
||||
<Grid>
|
||||
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Margin="20 0 0 0 ">
|
||||
<Button Command="{Binding RefreshAll}" Margin="10" IsEnabled="{Binding !FetchingData}">Refresh All</Button>
|
||||
<Button Command="{Binding AddMissingToQueue}" Margin="10" IsEnabled="{Binding !FetchingData}">Add To Queue</Button>
|
||||
<ToggleButton IsChecked="{Binding EditMode}" Margin="10" IsEnabled="{Binding !FetchingData}">Edit</ToggleButton>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" /> <!-- Takes up most space for the title -->
|
||||
<ColumnDefinition Width="*" />
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<ItemsPanelTemplate>
|
||||
|
@ -35,32 +143,68 @@
|
|||
</ListBox.ItemsPanel>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
||||
|
||||
<Grid>
|
||||
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" MaxWidth="250" Width="250"
|
||||
MaxHeight="400" Height="400" Margin="5">
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center"
|
||||
Width="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterWidth}"
|
||||
Height="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterHeight}"
|
||||
Margin="5">
|
||||
<Grid>
|
||||
<Image Source="{Binding ThumbnailImage}" Width="240" Height="360"></Image>
|
||||
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Right" IsVisible="{Binding NewEpisodes, Converter={StaticResource UiIntToVisibilityConverter}}">
|
||||
<TextBlock VerticalAlignment="Center" TextAlignment="Center" Margin="0 0 5 0" Width="30" Height="30"
|
||||
Background="Black" Opacity="0.8" Text="{Binding NewEpisodes}"
|
||||
Padding="0,5,0,0"/>
|
||||
</StackPanel>
|
||||
<Image Source="{Binding ThumbnailImage}"
|
||||
Width="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterImageWidth}"
|
||||
Height="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).PosterImageHeight}">
|
||||
</Image>
|
||||
|
||||
<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>
|
||||
<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">
|
||||
<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>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" MaxWidth="250" Width="250"
|
||||
MaxHeight="400" Height="400" Background="#90000000" IsVisible="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).EditMode}">
|
||||
<Button MaxWidth="250" Width="250" MaxHeight="400" Height="400" FontStyle="Italic"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).RemoveSeries}"
|
||||
CommandParameter="{Binding SeriesId}"
|
||||
IsEnabled="{Binding !$parent[UserControl].((vm:HistoryPageViewModel)DataContext).FetchingData}"
|
||||
>
|
||||
|
||||
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#90000000" IsVisible="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).EditMode}">
|
||||
|
||||
<Button
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
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>
|
||||
<TextBlock Text="Remove Series" FontSize="15" />
|
||||
</ToolTip.Tip>
|
||||
|
@ -68,22 +212,351 @@
|
|||
<controls:SymbolIcon Symbol="Delete" FontSize="32" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="250" Width="250"
|
||||
MaxHeight="400" Height="400" Background="#90000000" IsVisible="{Binding FetchingData}">
|
||||
<!-- <ProgressBar IsIndeterminate="{Binding FetchingData}" -->
|
||||
<!-- MaxWidth="100"> -->
|
||||
<!-- </ProgressBar> -->
|
||||
</Grid>
|
||||
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#90000000" IsVisible="{Binding FetchingData}">
|
||||
<controls:ProgressRing Width="100" Height="100"></controls:ProgressRing>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
|
||||
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</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>
|
||||
|
||||
|
||||
</UserControl>
|
|
@ -116,6 +116,9 @@ public partial class MainWindow : AppWindow{
|
|||
break;
|
||||
case "History":
|
||||
navView.Content = Activator.CreateInstance(typeof(HistoryPageViewModel));
|
||||
if ( navView.Content is HistoryPageViewModel){
|
||||
((HistoryPageViewModel)navView.Content).SetStorageProvider(StorageProvider);
|
||||
}
|
||||
navigationStack.Clear();
|
||||
navigationStack.Push(navView.Content);
|
||||
selectedNavVieItem = selectedItem;
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:vm="clr-namespace:CRD.ViewModels"
|
||||
xmlns:ui="clr-namespace:CRD.Utils.UI"
|
||||
xmlns:downloader="clr-namespace:CRD.Downloader"
|
||||
xmlns:history="clr-namespace:CRD.Utils.Structs.History"
|
||||
x:DataType="vm:SeriesPageViewModel"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="CRD.Views.SeriesPageView">
|
||||
|
@ -46,7 +45,7 @@
|
|||
|
||||
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
||||
BorderThickness="0" CornerRadius="50"
|
||||
Command="{Binding OpenCrPage}">
|
||||
Command="{Binding SelectedSeries.OpenCrPage}">
|
||||
<Grid>
|
||||
<controls:ImageIcon Source="../Assets/crunchy_icon_round.png" Width="30" Height="30" />
|
||||
</Grid>
|
||||
|
@ -55,13 +54,12 @@
|
|||
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
||||
BorderThickness="0" CornerRadius="50"
|
||||
IsVisible="{Binding SonarrAvailable}"
|
||||
Command="{Binding OpenSonarrPage}">
|
||||
Command="{Binding SelectedSeries.OpenSonarrPage}">
|
||||
<Grid>
|
||||
<controls:ImageIcon Source="../Assets/sonarr.png" Width="30" Height="30" />
|
||||
</Grid>
|
||||
</Button>
|
||||
|
||||
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
@ -138,7 +136,7 @@
|
|||
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
||||
BorderThickness="0" CornerRadius="50"
|
||||
IsVisible="{Binding !WasDownloaded}"
|
||||
Command="{Binding $parent[controls:SettingsExpander].((downloader:HistorySeason)DataContext).UpdateDownloaded}"
|
||||
Command="{Binding $parent[controls:SettingsExpander].((history:HistorySeason)DataContext).UpdateDownloaded}"
|
||||
CommandParameter="{Binding EpisodeId}">
|
||||
<Grid>
|
||||
<Ellipse Width="25" Height="25" Fill="Gray" />
|
||||
|
@ -149,7 +147,7 @@
|
|||
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
||||
BorderThickness="0" CornerRadius="50"
|
||||
IsVisible="{Binding WasDownloaded}"
|
||||
Command="{Binding $parent[controls:SettingsExpander].((downloader:HistorySeason)DataContext).UpdateDownloaded}"
|
||||
Command="{Binding $parent[controls:SettingsExpander].((history:HistorySeason)DataContext).UpdateDownloaded}"
|
||||
CommandParameter="{Binding EpisodeId}">
|
||||
<Grid>
|
||||
<Ellipse Width="25" Height="25" Fill="#21a556" />
|
||||
|
|
Loading…
Reference in New Issue