mirror of
https://github.com/Crunchy-DL/Crunchy-Downloader.git
synced 2026-01-11 20:10:26 +00:00
Add - Added sorting to seasons tab
Add - Added a download subs button to the history Chg - Changed CDM error message to include a GitHub wiki link Chg - Changed the style of settings tabs Chg - Changed the date format in the seasons tab Fix - Fixed Crunchyroll seasons numbering issue (e.g., Blue Exorcist) Fix - Fixed window slightly moving on startup Fix - Fixed a bug in the upcoming episodes display for the next week
This commit is contained in:
parent
5c200b3199
commit
51e3102503
25 changed files with 540 additions and 205 deletions
|
|
@ -153,7 +153,9 @@ public class CalendarManager{
|
|||
|
||||
|
||||
public async Task<CalendarWeek> BuildCustomCalendar(DateTime calTargetDate, bool forceUpdate){
|
||||
await LoadAnilistUpcoming();
|
||||
if (CrunchyrollManager.Instance.CrunOptions.CalendarShowUpcomingEpisodes){
|
||||
await LoadAnilistUpcoming();
|
||||
}
|
||||
|
||||
if (!forceUpdate && calendar.TryGetValue("C" + calTargetDate.ToString("yyyy-MM-dd"), out var forDate)){
|
||||
return forDate;
|
||||
|
|
@ -303,15 +305,16 @@ public class CalendarManager{
|
|||
}
|
||||
}
|
||||
|
||||
if (CrunchyrollManager.Instance.CrunOptions.CalendarShowUpcomingEpisodes){
|
||||
foreach (var calendarDay in week.CalendarDays){
|
||||
if (calendarDay.DateTime.Date >= DateTime.Now.Date){
|
||||
if (ProgramManager.Instance.AnilistUpcoming.ContainsKey(calendarDay.DateTime.ToString("yyyy-MM-dd"))){
|
||||
var list = ProgramManager.Instance.AnilistUpcoming[calendarDay.DateTime.ToString("yyyy-MM-dd")];
|
||||
|
||||
foreach (var calendarDay in week.CalendarDays){
|
||||
if (calendarDay.DateTime.Date >= DateTime.Now.Date){
|
||||
if (ProgramManager.Instance.AnilistUpcoming.ContainsKey(calendarDay.DateTime.ToString("yyyy-MM-dd"))){
|
||||
var list = ProgramManager.Instance.AnilistUpcoming[calendarDay.DateTime.ToString("yyyy-MM-dd")];
|
||||
|
||||
foreach (var calendarEpisode in list.Where(calendarEpisode => calendarDay.DateTime.Date == calendarEpisode.DateTime.Date)
|
||||
.Where(calendarEpisode => calendarDay.CalendarEpisodes.All(ele => ele.CrSeriesID != calendarEpisode.CrSeriesID))){
|
||||
calendarDay.CalendarEpisodes.Add(calendarEpisode);
|
||||
foreach (var calendarEpisode in list.Where(calendarEpisode => calendarDay.DateTime.Date == calendarEpisode.DateTime.Date)
|
||||
.Where(calendarEpisode => calendarDay.CalendarEpisodes.All(ele => ele.CrSeriesID != calendarEpisode.CrSeriesID))){
|
||||
calendarDay.CalendarEpisodes.Add(calendarEpisode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -320,7 +323,8 @@ public class CalendarManager{
|
|||
foreach (var weekCalendarDay in week.CalendarDays){
|
||||
if (weekCalendarDay.CalendarEpisodes.Count > 0)
|
||||
weekCalendarDay.CalendarEpisodes = weekCalendarDay.CalendarEpisodes
|
||||
.OrderBy(e => e.DateTime)
|
||||
.OrderBy(e => e.AnilistEpisode) // False first, then true
|
||||
.ThenBy(e => e.DateTime)
|
||||
.ThenBy(e => e.SeasonName)
|
||||
.ThenBy(e => {
|
||||
double parsedNumber;
|
||||
|
|
@ -331,9 +335,9 @@ public class CalendarManager{
|
|||
}
|
||||
|
||||
|
||||
foreach (var day in week.CalendarDays){
|
||||
if (day.CalendarEpisodes != null) day.CalendarEpisodes = day.CalendarEpisodes.OrderBy(e => e.DateTime).ToList();
|
||||
}
|
||||
// foreach (var day in week.CalendarDays){
|
||||
// if (day.CalendarEpisodes != null) day.CalendarEpisodes = day.CalendarEpisodes.OrderBy(e => e.DateTime).ToList();
|
||||
// }
|
||||
|
||||
calendar["C" + calTargetDate.ToString("yyyy-MM-dd")] = week;
|
||||
|
||||
|
|
@ -437,38 +441,32 @@ public class CalendarManager{
|
|||
if (match.Success){
|
||||
crunchyrollID = match.Groups[1].Value;
|
||||
|
||||
calEp.CrSeriesID = crunchyrollID;
|
||||
AdjustReleaseTimeToHistory(calEp, crunchyrollID);
|
||||
} else{
|
||||
Uri uri = new Uri(url);
|
||||
|
||||
if (CrunchyrollManager.Instance.CrunOptions.History){
|
||||
var historySeries = CrunchyrollManager.Instance.HistoryList.FirstOrDefault(item => item.SeriesId == crunchyrollID);
|
||||
if (uri.Host == "www.crunchyroll.com"
|
||||
&& uri.AbsolutePath != "/"
|
||||
&& (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)){
|
||||
HttpRequestMessage getUrlRequest = new HttpRequestMessage(HttpMethod.Head, url);
|
||||
|
||||
if (historySeries != null){
|
||||
var oldestRelease = DateTime.MinValue;
|
||||
foreach (var historySeriesSeason in historySeries.Seasons){
|
||||
if (historySeriesSeason.EpisodesList.Any()){
|
||||
var releaseDate = historySeriesSeason.EpisodesList.Last().EpisodeCrPremiumAirDate;
|
||||
string? finalUrl = "";
|
||||
|
||||
if (releaseDate.HasValue && oldestRelease < releaseDate.Value){
|
||||
oldestRelease = releaseDate.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
try{
|
||||
HttpResponseMessage getUrlResponse = await HttpClientReq.Instance.GetHttpClient().SendAsync(getUrlRequest);
|
||||
|
||||
if (oldestRelease != DateTime.MinValue){
|
||||
calEp.DateTime = new DateTime(
|
||||
calEp.DateTime.Year,
|
||||
calEp.DateTime.Month,
|
||||
calEp.DateTime.Day,
|
||||
oldestRelease.Hour,
|
||||
oldestRelease.Minute,
|
||||
oldestRelease.Second,
|
||||
calEp.DateTime.Kind
|
||||
);
|
||||
}
|
||||
finalUrl = getUrlResponse.RequestMessage?.RequestUri?.ToString();
|
||||
} catch (Exception ex){
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
|
||||
Match match2 = Regex.Match(finalUrl ?? string.Empty, pattern);
|
||||
if (match2.Success){
|
||||
crunchyrollID = match2.Groups[1].Value;
|
||||
|
||||
AdjustReleaseTimeToHistory(calEp, crunchyrollID);
|
||||
}
|
||||
}
|
||||
} else{
|
||||
crunchyrollID = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -487,6 +485,45 @@ public class CalendarManager{
|
|||
}
|
||||
}
|
||||
|
||||
private static void AdjustReleaseTimeToHistory(CalendarEpisode calEp, string crunchyrollId){
|
||||
calEp.CrSeriesID = crunchyrollId;
|
||||
|
||||
if (CrunchyrollManager.Instance.CrunOptions.History){
|
||||
var historySeries = CrunchyrollManager.Instance.HistoryList.FirstOrDefault(item => item.SeriesId == crunchyrollId);
|
||||
|
||||
if (historySeries != null){
|
||||
var oldestRelease = DateTime.MinValue;
|
||||
foreach (var historySeriesSeason in historySeries.Seasons){
|
||||
if (historySeriesSeason.EpisodesList.Any()){
|
||||
var releaseDate = historySeriesSeason.EpisodesList.Last().EpisodeCrPremiumAirDate;
|
||||
|
||||
if (releaseDate.HasValue && oldestRelease < releaseDate.Value){
|
||||
oldestRelease = releaseDate.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oldestRelease != DateTime.MinValue){
|
||||
var adjustedDate = new DateTime(
|
||||
calEp.DateTime.Year,
|
||||
calEp.DateTime.Month,
|
||||
calEp.DateTime.Day,
|
||||
oldestRelease.Hour,
|
||||
oldestRelease.Minute,
|
||||
oldestRelease.Second,
|
||||
calEp.DateTime.Kind
|
||||
);
|
||||
|
||||
if ((adjustedDate - oldestRelease).TotalDays is < 6 and > 1){
|
||||
adjustedDate = oldestRelease.AddDays(7);
|
||||
}
|
||||
|
||||
calEp.DateTime = adjustedDate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Query
|
||||
|
||||
private string query = @"query ($weekStart: Int, $weekEnd: Int, $page: Int) {
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@ public class CrunchyrollManager{
|
|||
options.FfmpegOptions = new();
|
||||
options.DefaultAudio = "ja-JP";
|
||||
options.DefaultSub = "en-US";
|
||||
options.QualityAudio = "best";
|
||||
options.QualityVideo = "best";
|
||||
options.CcTag = "CC";
|
||||
options.CcSubsFont = "Trebuchet MS";
|
||||
options.FsRetryTime = 5;
|
||||
|
|
@ -150,11 +152,9 @@ public class CrunchyrollManager{
|
|||
Username = "???",
|
||||
Avatar = "crbrand_avatars_logo_marks_mangagirl_taupe.png",
|
||||
PreferredContentAudioLanguage = "ja-JP",
|
||||
PreferredContentSubtitleLanguage = "de-DE",
|
||||
PreferredContentSubtitleLanguage = DefaultLocale,
|
||||
HasPremium = false,
|
||||
};
|
||||
|
||||
Console.WriteLine($"CDM available: {_widevine.canDecrypt}");
|
||||
}
|
||||
|
||||
public async Task Init(){
|
||||
|
|
@ -254,10 +254,10 @@ public class CrunchyrollManager{
|
|||
|
||||
QueueManager.Instance.Queue.Refresh();
|
||||
|
||||
var fileNameAndPath = CrunOptions.DownloadToTempFolder
|
||||
var fileNameAndPath = options.DownloadToTempFolder
|
||||
? Path.Combine(res.TempFolderPath ?? string.Empty, res.FileName ?? string.Empty)
|
||||
: Path.Combine(res.FolderPath ?? string.Empty, res.FileName ?? string.Empty);
|
||||
if (CrunOptions is{ DlVideoOnce: false, KeepDubsSeperate: true }){
|
||||
if (options is{ DlVideoOnce: false, KeepDubsSeperate: true }){
|
||||
var groupByDub = Helpers.GroupByLanguageWithSubtitles(res.Data);
|
||||
var mergers = new List<Merger>();
|
||||
foreach (var keyValue in groupByDub){
|
||||
|
|
@ -277,7 +277,8 @@ public class CrunchyrollManager{
|
|||
SyncTiming = options.SyncTiming,
|
||||
CcTag = options.CcTag,
|
||||
KeepAllVideos = true,
|
||||
MuxDescription = options.IncludeVideoDescription
|
||||
MuxDescription = options.IncludeVideoDescription,
|
||||
DlVideoOnce = options.DlVideoOnce
|
||||
},
|
||||
fileNameAndPath);
|
||||
|
||||
|
|
@ -293,7 +294,7 @@ public class CrunchyrollManager{
|
|||
foreach (var merger in mergers){
|
||||
merger.CleanUp();
|
||||
|
||||
if (CrunOptions.IsEncodeEnabled){
|
||||
if (options.IsEncodeEnabled){
|
||||
data.DownloadProgress = new DownloadProgress(){
|
||||
IsDownloading = true,
|
||||
Percent = 100,
|
||||
|
|
@ -304,11 +305,11 @@ public class CrunchyrollManager{
|
|||
|
||||
QueueManager.Instance.Queue.Refresh();
|
||||
|
||||
await Helpers.RunFFmpegWithPresetAsync(merger?.options.Output, FfmpegEncoding.GetPreset(CrunOptions.EncodingPresetName), data);
|
||||
await Helpers.RunFFmpegWithPresetAsync(merger?.options.Output, FfmpegEncoding.GetPreset(options.EncodingPresetName), data);
|
||||
}
|
||||
|
||||
if (CrunOptions.DownloadToTempFolder){
|
||||
await MoveFromTempFolder(merger, data, res.TempFolderPath, res.Data.Where(e => e.Type == DownloadMediaType.Subtitle));
|
||||
if (options.DownloadToTempFolder){
|
||||
await MoveFromTempFolder(merger, data, options, res.TempFolderPath, res.Data.Where(e => e.Type == DownloadMediaType.Subtitle));
|
||||
}
|
||||
}
|
||||
} else{
|
||||
|
|
@ -328,7 +329,8 @@ public class CrunchyrollManager{
|
|||
SyncTiming = options.SyncTiming,
|
||||
CcTag = options.CcTag,
|
||||
KeepAllVideos = true,
|
||||
MuxDescription = options.IncludeVideoDescription
|
||||
MuxDescription = options.IncludeVideoDescription,
|
||||
DlVideoOnce = options.DlVideoOnce
|
||||
},
|
||||
fileNameAndPath);
|
||||
|
||||
|
|
@ -338,7 +340,7 @@ public class CrunchyrollManager{
|
|||
result.merger.CleanUp();
|
||||
}
|
||||
|
||||
if (CrunOptions.IsEncodeEnabled){
|
||||
if (options.IsEncodeEnabled){
|
||||
data.DownloadProgress = new DownloadProgress(){
|
||||
IsDownloading = true,
|
||||
Percent = 100,
|
||||
|
|
@ -349,11 +351,11 @@ public class CrunchyrollManager{
|
|||
|
||||
QueueManager.Instance.Queue.Refresh();
|
||||
|
||||
await Helpers.RunFFmpegWithPresetAsync(result.merger?.options.Output, FfmpegEncoding.GetPreset(CrunOptions.EncodingPresetName), data);
|
||||
await Helpers.RunFFmpegWithPresetAsync(result.merger?.options.Output, FfmpegEncoding.GetPreset(options.EncodingPresetName), data);
|
||||
}
|
||||
|
||||
if (CrunOptions.DownloadToTempFolder){
|
||||
await MoveFromTempFolder(result.merger, data, res.TempFolderPath, res.Data.Where(e => e.Type == DownloadMediaType.Subtitle));
|
||||
if (options.DownloadToTempFolder){
|
||||
await MoveFromTempFolder(result.merger, data, options, res.TempFolderPath, res.Data.Where(e => e.Type == DownloadMediaType.Subtitle));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -367,19 +369,19 @@ public class CrunchyrollManager{
|
|||
Doing = "Done" + (syncError ? " - Couldn't sync dubs" : "")
|
||||
};
|
||||
|
||||
if (CrunOptions.RemoveFinishedDownload && !syncError){
|
||||
if (options.RemoveFinishedDownload && !syncError){
|
||||
QueueManager.Instance.Queue.Remove(data);
|
||||
}
|
||||
} else{
|
||||
Console.WriteLine("Skipping mux");
|
||||
res.Data.ForEach(file => Helpers.DeleteFile(file.Path + ".resume"));
|
||||
if (CrunOptions.DownloadToTempFolder){
|
||||
if (options.DownloadToTempFolder){
|
||||
if (string.IsNullOrEmpty(res.TempFolderPath) || !Directory.Exists(res.TempFolderPath)){
|
||||
Console.WriteLine("Invalid or non-existent temp folder path.");
|
||||
} else{
|
||||
// Move files
|
||||
foreach (var downloadedMedia in res.Data){
|
||||
await MoveFile(downloadedMedia.Path ?? string.Empty, res.TempFolderPath, data.DownloadPath ?? CfgManager.PathVIDEOS_DIR);
|
||||
await MoveFile(downloadedMedia.Path ?? string.Empty, res.TempFolderPath, data.DownloadPath ?? CfgManager.PathVIDEOS_DIR, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -393,7 +395,7 @@ public class CrunchyrollManager{
|
|||
Doing = "Done - Skipped muxing"
|
||||
};
|
||||
|
||||
if (CrunOptions.RemoveFinishedDownload){
|
||||
if (options.RemoveFinishedDownload){
|
||||
QueueManager.Instance.Queue.Remove(data);
|
||||
}
|
||||
}
|
||||
|
|
@ -402,7 +404,7 @@ public class CrunchyrollManager{
|
|||
QueueManager.Instance.ActiveDownloads--;
|
||||
QueueManager.Instance.Queue.Refresh();
|
||||
|
||||
if (CrunOptions.History && data.Data != null && data.Data.Count > 0){
|
||||
if (options.History && data.Data != null && data.Data.Count > 0){
|
||||
History.SetAsDownloaded(data.ShowId, data.SeasonId, data.Data.First().MediaId);
|
||||
}
|
||||
|
||||
|
|
@ -412,8 +414,8 @@ public class CrunchyrollManager{
|
|||
|
||||
#region Temp Files Move
|
||||
|
||||
private async Task MoveFromTempFolder(Merger? merger, CrunchyEpMeta data, string tempFolderPath, IEnumerable<DownloadedMedia> subtitles){
|
||||
if (!CrunOptions.DownloadToTempFolder) return;
|
||||
private async Task MoveFromTempFolder(Merger? merger, CrunchyEpMeta data, CrDownloadOptions options, string tempFolderPath, IEnumerable<DownloadedMedia> subtitles){
|
||||
if (!options.DownloadToTempFolder) return;
|
||||
|
||||
data.DownloadProgress = new DownloadProgress{
|
||||
IsDownloading = true,
|
||||
|
|
@ -431,15 +433,15 @@ public class CrunchyrollManager{
|
|||
}
|
||||
|
||||
// Move the main output file
|
||||
await MoveFile(merger?.options.Output ?? string.Empty, tempFolderPath, data.DownloadPath ?? CfgManager.PathVIDEOS_DIR);
|
||||
await MoveFile(merger?.options.Output ?? string.Empty, tempFolderPath, data.DownloadPath ?? CfgManager.PathVIDEOS_DIR, options);
|
||||
|
||||
// Move the subtitle files
|
||||
foreach (var downloadedMedia in subtitles){
|
||||
await MoveFile(downloadedMedia.Path ?? string.Empty, tempFolderPath, data.DownloadPath ?? CfgManager.PathVIDEOS_DIR);
|
||||
await MoveFile(downloadedMedia.Path ?? string.Empty, tempFolderPath, data.DownloadPath ?? CfgManager.PathVIDEOS_DIR, options);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task MoveFile(string sourcePath, string tempFolderPath, string downloadPath){
|
||||
private async Task MoveFile(string sourcePath, string tempFolderPath, string downloadPath, CrDownloadOptions options){
|
||||
if (string.IsNullOrEmpty(sourcePath) || !File.Exists(sourcePath)){
|
||||
// Console.Error.WriteLine("Source file does not exist or path is invalid.");
|
||||
return;
|
||||
|
|
@ -454,8 +456,8 @@ public class CrunchyrollManager{
|
|||
var fileName = sourcePath[tempFolderPath.Length..].TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
var destinationFolder = !string.IsNullOrEmpty(downloadPath)
|
||||
? downloadPath
|
||||
: !string.IsNullOrEmpty(CrunOptions.DownloadDirPath)
|
||||
? CrunOptions.DownloadDirPath
|
||||
: !string.IsNullOrEmpty(options.DownloadDirPath)
|
||||
? options.DownloadDirPath
|
||||
: CfgManager.PathVIDEOS_DIR;
|
||||
|
||||
var destinationPath = Path.Combine(destinationFolder ?? string.Empty, fileName ?? string.Empty);
|
||||
|
|
@ -539,7 +541,7 @@ public class CrunchyrollManager{
|
|||
Subtitles = data.Where(a => a.Type == DownloadMediaType.Subtitle).Select(a => new SubtitleInput
|
||||
{ File = a.Path ?? string.Empty, Language = a.Language, ClosedCaption = a.Cc ?? false, Signs = a.Signs ?? false, RelatedVideoDownloadMedia = a.RelatedVideoDownloadMedia }).ToList(),
|
||||
KeepAllVideos = options.KeepAllVideos,
|
||||
Fonts = FontsManager.Instance.MakeFontsList(CfgManager.PathFONTS_DIR, subsList),
|
||||
Fonts = FontsManager.Instance.MakeFontsList(CfgManager.PathFONTS_DIR, subsList),
|
||||
Chapters = data.Where(a => a.Type == DownloadMediaType.Chapters).Select(a => new MergerInput{ Language = a.Lang, Path = a.Path ?? string.Empty }).ToList(),
|
||||
VideoTitle = options.VideoTitle,
|
||||
Options = new MuxOptions(){
|
||||
|
|
@ -565,7 +567,7 @@ public class CrunchyrollManager{
|
|||
|
||||
bool isMuxed, syncError = false;
|
||||
|
||||
if (options.SyncTiming && CrunOptions.DlVideoOnce){
|
||||
if (options is{ SyncTiming: true, DlVideoOnce: true }){
|
||||
var basePath = merger.options.OnlyVid.First().Path;
|
||||
var syncVideosList = data.Where(a => a.Type == DownloadMediaType.SyncVideo).ToList();
|
||||
|
||||
|
|
@ -666,13 +668,13 @@ public class CrunchyrollManager{
|
|||
}
|
||||
|
||||
if (!_widevine.canDecrypt){
|
||||
Console.Error.WriteLine("L3 key files missing");
|
||||
MainWindow.Instance.ShowError("Can't find CDM files in the Widevine folder.\nFor more information, please check the FAQ section in the Wiki on the GitHub page.");
|
||||
Console.Error.WriteLine("CDM files missing");
|
||||
MainWindow.Instance.ShowError("Can't find CDM files in the Widevine folder.\nFor more information, please check the FAQ section in the Wiki on the GitHub page.", true);
|
||||
return new DownloadResponse{
|
||||
Data = new List<DownloadedMedia>(),
|
||||
Error = true,
|
||||
FileName = "./unknown",
|
||||
ErrorText = "Missing L3 Key"
|
||||
ErrorText = "Missing CDM files"
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -715,8 +717,8 @@ public class CrunchyrollManager{
|
|||
|
||||
string currentMediaId = (epMeta.MediaId.Contains(':') ? epMeta.MediaId.Split(':')[1] : epMeta.MediaId);
|
||||
|
||||
fileDir = CrunOptions.DownloadToTempFolder ? !string.IsNullOrEmpty(CrunOptions.DownloadTempDirPath)
|
||||
? Path.Combine(CrunOptions.DownloadTempDirPath, Helpers.GetValidFolderName(currentMediaId))
|
||||
fileDir = options.DownloadToTempFolder ? !string.IsNullOrEmpty(options.DownloadTempDirPath)
|
||||
? Path.Combine(options.DownloadTempDirPath, Helpers.GetValidFolderName(currentMediaId))
|
||||
: Path.Combine(CfgManager.PathTEMP_DIR, Helpers.GetValidFolderName(currentMediaId)) :
|
||||
!string.IsNullOrEmpty(data.DownloadPath) ? data.DownloadPath :
|
||||
!string.IsNullOrEmpty(options.DownloadDirPath) ? options.DownloadDirPath : CfgManager.PathVIDEOS_DIR;
|
||||
|
|
@ -788,7 +790,7 @@ public class CrunchyrollManager{
|
|||
#endregion
|
||||
|
||||
|
||||
var fetchPlaybackData = await FetchPlaybackData(mediaId, mediaGuid, data.Music);
|
||||
var fetchPlaybackData = await FetchPlaybackData(options, mediaId, mediaGuid, data.Music);
|
||||
|
||||
if (!fetchPlaybackData.IsOk){
|
||||
if (!fetchPlaybackData.IsOk && fetchPlaybackData.error != string.Empty){
|
||||
|
|
@ -888,7 +890,7 @@ public class CrunchyrollManager{
|
|||
Console.Error.WriteLine("Try hardsubs stream: " + string.Join(", ", hsLangs));
|
||||
}
|
||||
|
||||
if (dlVideoOnce && CrunOptions.DlVideoOnce){
|
||||
if (dlVideoOnce && options.DlVideoOnce){
|
||||
streams = streams.Where((s) => {
|
||||
if (s.HardsubLang != "-"){
|
||||
return false;
|
||||
|
|
@ -1054,6 +1056,11 @@ public class CrunchyrollManager{
|
|||
videos.Sort((a, b) => a.quality.width.CompareTo(b.quality.width));
|
||||
audios.Sort((a, b) => a.bandwidth.CompareTo(b.bandwidth));
|
||||
|
||||
if (string.IsNullOrEmpty(data.VideoQuality)){
|
||||
Console.Error.WriteLine("Warning: VideoQuality is null or empty. Defaulting to 'best' quality.");
|
||||
data.VideoQuality = "best";
|
||||
}
|
||||
|
||||
int chosenVideoQuality;
|
||||
if (options.DlVideoOnce && dlVideoOnce && options.SyncTiming){
|
||||
chosenVideoQuality = 1;
|
||||
|
|
@ -1062,7 +1069,7 @@ public class CrunchyrollManager{
|
|||
} else if (data.VideoQuality == "worst"){
|
||||
chosenVideoQuality = 1;
|
||||
} else{
|
||||
var tempIndex = videos.FindIndex(a => a.quality.height + "" == data.VideoQuality.Replace("p", ""));
|
||||
var tempIndex = videos.FindIndex(a => a.quality.height + "" == data.VideoQuality?.Replace("p", ""));
|
||||
if (tempIndex < 0){
|
||||
chosenVideoQuality = videos.Count;
|
||||
} else{
|
||||
|
|
@ -1473,11 +1480,15 @@ public class CrunchyrollManager{
|
|||
Console.WriteLine("Downloading skipped!");
|
||||
}
|
||||
}
|
||||
} else if (options.Novids && options.Noaudio){
|
||||
} else if (options is{ Novids: true, Noaudio: true }){
|
||||
|
||||
variables.Add(new Variable("height", 360, false));
|
||||
variables.Add(new Variable("width", 640, false));
|
||||
|
||||
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers, options.Override).ToArray());
|
||||
}
|
||||
|
||||
if (compiledChapters.Count > 0){
|
||||
if (compiledChapters.Count > 0 && options is not{ Novids: true, Noaudio: true }){
|
||||
try{
|
||||
// Parsing and constructing the file names
|
||||
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers, options.Override).ToArray());
|
||||
|
|
@ -1536,16 +1547,12 @@ public class CrunchyrollManager{
|
|||
} else{
|
||||
Console.WriteLine("Subtitles downloading skipped!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// await Task.Delay(options.Waittime);
|
||||
}
|
||||
}
|
||||
|
||||
// variables.Add(new Variable("height", quality == 0 ? plQuality.Last().RESOLUTION.Height : plQuality[quality - 1].RESOLUTION.Height, false));
|
||||
// variables.Add(new Variable("width", quality == 0 ? plQuality.Last().RESOLUTION.Width : plQuality[quality - 1].RESOLUTION.Width, false));
|
||||
|
||||
|
||||
if (options.IncludeVideoDescription){
|
||||
string fullPath = (Path.IsPathRooted(fileName) ? fileName : Path.Combine(fileDir, fileName)) + ".xml";
|
||||
|
||||
|
|
@ -1590,7 +1597,7 @@ public class CrunchyrollManager{
|
|||
}
|
||||
|
||||
var tempFolderPath = "";
|
||||
if (CrunOptions.DownloadToTempFolder){
|
||||
if (options.DownloadToTempFolder){
|
||||
tempFolderPath = fileDir;
|
||||
fileDir = !string.IsNullOrEmpty(data.DownloadPath) ? data.DownloadPath :
|
||||
!string.IsNullOrEmpty(options.DownloadDirPath) ? options.DownloadDirPath : CfgManager.PathVIDEOS_DIR;
|
||||
|
|
@ -1745,7 +1752,7 @@ public class CrunchyrollManager{
|
|||
}
|
||||
|
||||
File.WriteAllText(sxData.Path, subsAssReqResponse.ResponseContent);
|
||||
Console.WriteLine($"Subtitle downloaded: ${sxData.File}");
|
||||
Console.WriteLine($"Subtitle downloaded: {sxData.File}");
|
||||
files.Add(new DownloadedMedia{
|
||||
Type = DownloadMediaType.Subtitle,
|
||||
Cc = isCc,
|
||||
|
|
@ -1788,10 +1795,10 @@ public class CrunchyrollManager{
|
|||
M3U8Json videoJson = new M3U8Json{
|
||||
Segments = chosenVideoSegments.segments.Cast<dynamic>().ToList()
|
||||
};
|
||||
|
||||
|
||||
data.downloadedFiles.Add(chosenVideoSegments.pssh != null ? $"{tempTsFile}.video.enc.m4s" : $"{tsFile}.video.m4s");
|
||||
data.downloadedFiles.Add(chosenVideoSegments.pssh != null ? $"{tempTsFile}.video.enc.m4s.resume" : $"{tsFile}.video.m4s.resume");
|
||||
|
||||
|
||||
var videoDownloader = new HlsDownloader(new HlsOptions{
|
||||
Output = chosenVideoSegments.pssh != null ? $"{tempTsFile}.video.enc.m4s" : $"{tsFile}.video.m4s",
|
||||
Timeout = options.Timeout,
|
||||
|
|
@ -1844,7 +1851,7 @@ public class CrunchyrollManager{
|
|||
M3U8Json audioJson = new M3U8Json{
|
||||
Segments = chosenAudioSegments.segments.Cast<dynamic>().ToList()
|
||||
};
|
||||
|
||||
|
||||
data.downloadedFiles.Add(chosenAudioSegments.pssh != null ? $"{tempTsFile}.audio.enc.m4s" : $"{tsFile}.audio.m4s");
|
||||
data.downloadedFiles.Add(chosenAudioSegments.pssh != null ? $"{tempTsFile}.audio.enc.m4s.resume" : $"{tsFile}.audio.m4s.resume");
|
||||
|
||||
|
|
@ -1866,13 +1873,13 @@ public class CrunchyrollManager{
|
|||
|
||||
#region Fetch Playback Data
|
||||
|
||||
private async Task<(bool IsOk, PlaybackData pbData, string error)> FetchPlaybackData(string mediaId, string mediaGuidId, bool music){
|
||||
private async Task<(bool IsOk, PlaybackData pbData, string error)> FetchPlaybackData(CrDownloadOptions options, string mediaId, string mediaGuidId, bool music){
|
||||
var temppbData = new PlaybackData{
|
||||
Total = 0,
|
||||
Data = new Dictionary<string, StreamDetails>()
|
||||
};
|
||||
|
||||
var playbackEndpoint = $"https://cr-play-service.prd.crunchyrollsvc.com/v1/{(music ? "music/" : "")}{mediaGuidId}/{CrunOptions.StreamEndpoint}/play";
|
||||
var playbackEndpoint = $"https://cr-play-service.prd.crunchyrollsvc.com/v1/{(music ? "music/" : "")}{mediaGuidId}/{options.StreamEndpoint}/play";
|
||||
var playbackRequestResponse = await SendPlaybackRequestAsync(playbackEndpoint);
|
||||
|
||||
if (!playbackRequestResponse.IsOk){
|
||||
|
|
|
|||
|
|
@ -302,6 +302,7 @@ public class History(){
|
|||
historyEpisode.EpisodeId = crunchyEpisode.Id;
|
||||
historyEpisode.Episode = crunchyEpisode.Episode;
|
||||
historyEpisode.EpisodeSeasonNum = Helpers.ExtractNumberAfterS(crunchyEpisode.Identifier) ?? crunchyEpisode.SeasonNumber + "";
|
||||
historyEpisode.EpisodeCrPremiumAirDate = crunchyEpisode.PremiumAvailableDate;
|
||||
|
||||
historyEpisode.HistoryEpisodeAvailableDubLang = Languages.SortListByLangList(langList);
|
||||
historyEpisode.HistoryEpisodeAvailableSoftSubs = Languages.SortListByLangList(crunchyEpisode.SubtitleLocales);
|
||||
|
|
@ -333,8 +334,7 @@ public class History(){
|
|||
newSeason.EpisodesList.Sort(new NumericStringPropertyComparer());
|
||||
|
||||
await RefreshSeriesData(seriesId, historySeries);
|
||||
|
||||
|
||||
|
||||
historySeries.Seasons.Add(newSeason);
|
||||
historySeries.UpdateNewEpisodes();
|
||||
historySeries.Init();
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ public class QueueManager{
|
|||
}
|
||||
|
||||
|
||||
public async Task CrAddEpisodeToQueue(string epId, string crLocale, List<string> dubLang, bool updateHistory = false){
|
||||
public async Task CrAddEpisodeToQueue(string epId, string crLocale, List<string> dubLang, bool updateHistory = false, bool onlySubs = false){
|
||||
await CrunchyrollManager.Instance.CrAuth.RefreshToken(true);
|
||||
|
||||
var episodeL = await CrunchyrollManager.Instance.CrEpisode.ParseEpisodeById(epId, crLocale);
|
||||
|
|
@ -100,7 +100,7 @@ public class QueueManager{
|
|||
|
||||
var sList = await CrunchyrollManager.Instance.CrEpisode.EpisodeData((CrunchyEpisode)episodeL, updateHistory);
|
||||
|
||||
(HistoryEpisode? historyEpisode, List<string> dublist, List<string> sublist, string downloadDirPath,string videoQuality) historyEpisode = (null, [], [], "","");
|
||||
(HistoryEpisode? historyEpisode, List<string> dublist, List<string> sublist, string downloadDirPath, string videoQuality) historyEpisode = (null, [], [], "", "");
|
||||
|
||||
if (CrunchyrollManager.Instance.CrunOptions.History){
|
||||
var episode = sList.EpisodeAndLanguages.Items.First();
|
||||
|
|
@ -142,9 +142,11 @@ public class QueueManager{
|
|||
}
|
||||
|
||||
selected.VideoQuality = !string.IsNullOrEmpty(historyEpisode.videoQuality) ? historyEpisode.videoQuality : CrunchyrollManager.Instance.CrunOptions.QualityVideo;
|
||||
|
||||
|
||||
selected.DownloadSubs = historyEpisode.sublist.Count > 0 ? historyEpisode.sublist : CrunchyrollManager.Instance.CrunOptions.DlSubs;
|
||||
|
||||
selected.OnlySubs = onlySubs;
|
||||
|
||||
Queue.Add(selected);
|
||||
|
||||
if (selected.Data.Count < dubLang.Count){
|
||||
|
|
@ -154,7 +156,8 @@ public class QueueManager{
|
|||
var languages = sList.EpisodeAndLanguages.Items.Select((a, index) =>
|
||||
$"{(a.IsPremiumOnly ? "+ " : "")}{sList.EpisodeAndLanguages.Langs.ElementAtOrDefault(index).CrLocale ?? "Unknown"}").ToArray();
|
||||
|
||||
Console.Error.WriteLine($"{selected.SeasonTitle} - Season {selected.Season} - {selected.EpisodeTitle} dubs - [{string.Join(", ", languages)}] subs - [{string.Join(", ", selected.AvailableSubs ?? [])}]");
|
||||
Console.Error.WriteLine(
|
||||
$"{selected.SeasonTitle} - Season {selected.Season} - {selected.EpisodeTitle} dubs - [{string.Join(", ", languages)}] subs - [{string.Join(", ", selected.AvailableSubs ??[])}]");
|
||||
MessageBus.Current.SendMessage(new ToastMessage($"Added episode to the queue but couldn't find all selected dubs", ToastType.Warning, 2));
|
||||
} else{
|
||||
Console.WriteLine("Added Episode to Queue");
|
||||
|
|
@ -167,7 +170,7 @@ public class QueueManager{
|
|||
var languages = sList.EpisodeAndLanguages.Items.Select((a, index) =>
|
||||
$"{(a.IsPremiumOnly ? "+ " : "")}{sList.EpisodeAndLanguages.Langs.ElementAtOrDefault(index).CrLocale ?? "Unknown"}").ToArray();
|
||||
|
||||
Console.Error.WriteLine($"{selected.SeasonTitle} - Season {selected.Season} - {selected.EpisodeTitle} dubs - [{string.Join(", ", languages)}] subs - [{string.Join(", ", selected.AvailableSubs ?? [])}]");
|
||||
Console.Error.WriteLine($"{selected.SeasonTitle} - Season {selected.Season} - {selected.EpisodeTitle} dubs - [{string.Join(", ", languages)}] subs - [{string.Join(", ", selected.AvailableSubs ??[])}]");
|
||||
MessageBus.Current.SendMessage(new ToastMessage($"Couldn't add episode to the queue with current dub settings", ToastType.Error, 2));
|
||||
}
|
||||
} else{
|
||||
|
|
@ -180,6 +183,7 @@ public class QueueManager{
|
|||
|
||||
if (movieMeta != null){
|
||||
movieMeta.DownloadSubs = CrunchyrollManager.Instance.CrunOptions.DlSubs;
|
||||
movieMeta.OnlySubs = onlySubs;
|
||||
Queue.Add(movieMeta);
|
||||
|
||||
Console.WriteLine("Added Movie to Queue");
|
||||
|
|
@ -255,7 +259,7 @@ public class QueueManager{
|
|||
}
|
||||
|
||||
var subLangList = CrunchyrollManager.Instance.History.GetSubList(crunchyEpMeta.ShowId, crunchyEpMeta.SeasonId);
|
||||
|
||||
|
||||
crunchyEpMeta.VideoQuality = !string.IsNullOrEmpty(subLangList.videoQuality) ? subLangList.videoQuality : CrunchyrollManager.Instance.CrunOptions.QualityVideo;
|
||||
crunchyEpMeta.DownloadSubs = subLangList.sublist.Count > 0 ? subLangList.sublist : CrunchyrollManager.Instance.CrunOptions.DlSubs;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,7 @@
|
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:uip="using:FluentAvalonia.UI.Controls.Primitives">
|
||||
<!--
|
||||
NavView style in MainView for main app navigation
|
||||
While you are free to copy this into your own apps
|
||||
if you want an MS store like NavView, this will NOT
|
||||
be an officially supported thing in the main library
|
||||
-->
|
||||
|
||||
<Style Selector="ui|NavigationView.SampleAppNav">
|
||||
<Setter Property="IsPaneToggleButtonVisible" Value="False" />
|
||||
<Setter Property="OpenPaneLength" Value="72" />
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ public class Widevine{
|
|||
Console.Error.WriteLine("Widevine: " + e);
|
||||
canDecrypt = false;
|
||||
}
|
||||
|
||||
Console.WriteLine($"CDM available: {canDecrypt}");
|
||||
}
|
||||
|
||||
public async Task<List<ContentKey>> getKeys(string? pssh, string licenseServer, Dictionary<string, string> authData){
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ public class Helpers{
|
|||
return default;
|
||||
}
|
||||
|
||||
public static T DeepCopy<T>(T obj){
|
||||
var json = JsonConvert.SerializeObject(obj);
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
}
|
||||
|
||||
public static string ConvertTimeFormat(string time){
|
||||
var timeParts = time.Split(':', '.');
|
||||
int hours = int.Parse(timeParts[0]);
|
||||
|
|
@ -71,7 +76,7 @@ public class Helpers{
|
|||
}
|
||||
|
||||
public static void EnsureDirectoriesExist(string path){
|
||||
Console.WriteLine($"Check if path exists: {path}");
|
||||
// Console.WriteLine($"Check if path exists: {path}");
|
||||
|
||||
// Check if the path is absolute
|
||||
bool isAbsolute = Path.IsPathRooted(path);
|
||||
|
|
@ -507,22 +512,30 @@ public class Helpers{
|
|||
}
|
||||
|
||||
public static string? ExtractNumberAfterS(string input){
|
||||
// Regular expression pattern to match |S followed by a number and optionally C followed by another number
|
||||
string pattern = @"\|S(\d+)(?:C(\d+))?";
|
||||
// Regular expression pattern to match |S followed by a number and optionally C or P followed by another number
|
||||
string pattern = @"\|S(\d+)(?:C(\d+)|P(\d+))?";
|
||||
Match match = Regex.Match(input, pattern);
|
||||
|
||||
if (match.Success){
|
||||
string sNumber = match.Groups[1].Value;
|
||||
string cNumber = match.Groups[2].Value;
|
||||
string sNumber = match.Groups[1].Value; // Extract the S number
|
||||
string cNumber = match.Groups[2].Value; // Extract the C number if present
|
||||
string pNumber = match.Groups[3].Value; // Extract the P number if present
|
||||
|
||||
if (!string.IsNullOrEmpty(cNumber)){
|
||||
// Case for C: Return S + . + C
|
||||
return $"{sNumber}.{cNumber}";
|
||||
} else if (!string.IsNullOrEmpty(pNumber)){
|
||||
// Case for P: Increment S by P - 1
|
||||
if (int.TryParse(sNumber, out int sNumeric) && int.TryParse(pNumber, out int pNumeric)){
|
||||
return (sNumeric + (pNumeric - 1)).ToString();
|
||||
}
|
||||
} else{
|
||||
// Return only S if no C or P is present
|
||||
return sNumber;
|
||||
}
|
||||
} else{
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -459,6 +459,7 @@ public class CrunchyMuxOptions{
|
|||
public LanguageItem DefaultAudio{ get; set; }
|
||||
public string CcTag{ get; set; }
|
||||
public bool SyncTiming{ get; set; }
|
||||
public bool DlVideoOnce{ get; set; }
|
||||
}
|
||||
|
||||
public class MergerOptions{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.Media.Imaging;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Newtonsoft.Json;
|
||||
|
|
@ -9,7 +10,7 @@ public partial class AnilistSeries : ObservableObject{
|
|||
public int Id{ get; set; }
|
||||
public int? IdMal{ get; set; }
|
||||
public Title Title{ get; set; }
|
||||
public Date StartDate{ get; set; }
|
||||
public Date? StartDate{ get; set; }
|
||||
public Date EndDate{ get; set; }
|
||||
public string Status{ get; set; }
|
||||
public string Season{ get; set; }
|
||||
|
|
@ -37,13 +38,25 @@ public partial class AnilistSeries : ObservableObject{
|
|||
|
||||
[JsonIgnore]
|
||||
public Bitmap? ThumbnailImage{ get; set; }
|
||||
|
||||
|
||||
[JsonIgnore]
|
||||
public string StartDateForm => $"{StartDate.Day}.{StartDate.Month}.{StartDate.Year}";
|
||||
|
||||
public string StartDateForm{
|
||||
get{
|
||||
if (StartDate == null)
|
||||
return string.Empty;
|
||||
|
||||
|
||||
var cultureInfo = System.Globalization.CultureInfo.InvariantCulture;
|
||||
string monthAbbreviation = cultureInfo.DateTimeFormat.GetAbbreviatedMonthName(StartDate.Month);
|
||||
|
||||
return string.Format("{0:00}.{1}.{2}", StartDate.Day, monthAbbreviation, StartDate.Year);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[JsonIgnore]
|
||||
public string? CrunchyrollID;
|
||||
|
||||
|
||||
[JsonIgnore]
|
||||
[ObservableProperty]
|
||||
public bool _hasCrID;
|
||||
|
|
@ -51,7 +64,6 @@ public partial class AnilistSeries : ObservableObject{
|
|||
[JsonIgnore]
|
||||
[ObservableProperty]
|
||||
public bool _isInHistory;
|
||||
|
||||
}
|
||||
|
||||
public class Title{
|
||||
|
|
@ -61,9 +73,20 @@ public class Title{
|
|||
}
|
||||
|
||||
public class Date{
|
||||
public int? Year{ get; set; }
|
||||
public int? Month{ get; set; }
|
||||
public int? Day{ get; set; }
|
||||
public int Year{ get; set; }
|
||||
public int Month{ get; set; }
|
||||
public int Day{ get; set; }
|
||||
|
||||
public DateTime? ToDateTime(){
|
||||
if (Year == 0 || Month == 0 || Day == 0)
|
||||
return DateTime.MinValue;
|
||||
|
||||
try{
|
||||
return new DateTime(Year, Month, Day);
|
||||
} catch{
|
||||
return DateTime.MinValue;;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CoverImage{
|
||||
|
|
|
|||
|
|
@ -80,6 +80,9 @@ public class CrDownloadOptions{
|
|||
|
||||
[YamlMember(Alias = "history_page_properties", ApplyNamingConventions = false)]
|
||||
public HistoryPageProperties? HistoryPageProperties{ get; set; }
|
||||
|
||||
[YamlMember(Alias = "seasons_page_properties", ApplyNamingConventions = false)]
|
||||
public SeasonsPageProperties? SeasonsPageProperties{ get; set; }
|
||||
|
||||
[YamlMember(Alias = "download_speed_limit", ApplyNamingConventions = false)]
|
||||
public int DownloadSpeedLimit{ get; set; }
|
||||
|
|
@ -225,7 +228,10 @@ public class CrDownloadOptions{
|
|||
|
||||
[YamlMember(Alias = "calendar_filter_by_air_date", ApplyNamingConventions = false)]
|
||||
public bool CalendarFilterByAirDate{ get; set; }
|
||||
|
||||
|
||||
[YamlMember(Alias = "calendar_show_upcoming_episodes", ApplyNamingConventions = false)]
|
||||
public bool CalendarShowUpcomingEpisodes{ get; set; }
|
||||
|
||||
[YamlMember(Alias = "stream_endpoint", ApplyNamingConventions = false)]
|
||||
public string? StreamEndpoint{ get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -259,6 +259,8 @@ public class CrunchyEpMeta{
|
|||
public string Resolution{ get; set; }
|
||||
|
||||
public List<string> downloadedFiles{ get; set; } =[];
|
||||
|
||||
public bool OnlySubs{ get; set; }
|
||||
}
|
||||
|
||||
public class DownloadProgress{
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ using Newtonsoft.Json;
|
|||
namespace CRD.Utils.Structs.History;
|
||||
|
||||
public class HistoryEpisode : INotifyPropertyChanged{
|
||||
|
||||
[JsonProperty("episode_title")]
|
||||
public string? EpisodeTitle{ get; set; }
|
||||
|
||||
|
|
@ -24,7 +23,7 @@ public class HistoryEpisode : INotifyPropertyChanged{
|
|||
|
||||
[JsonProperty("episode_cr_season_number")]
|
||||
public string? EpisodeSeasonNum{ get; set; }
|
||||
|
||||
|
||||
[JsonProperty("episode_cr_premium_air_date")]
|
||||
public DateTime? EpisodeCrPremiumAirDate{ get; set; }
|
||||
|
||||
|
|
@ -48,7 +47,7 @@ public class HistoryEpisode : INotifyPropertyChanged{
|
|||
|
||||
[JsonProperty("sonarr_absolut_number")]
|
||||
public string? SonarrAbsolutNumber{ get; set; }
|
||||
|
||||
|
||||
[JsonProperty("history_episode_available_soft_subs")]
|
||||
public List<string> HistoryEpisodeAvailableSoftSubs{ get; set; } =[];
|
||||
|
||||
|
|
@ -77,8 +76,9 @@ public class HistoryEpisode : INotifyPropertyChanged{
|
|||
CfgManager.UpdateHistoryFile();
|
||||
}
|
||||
|
||||
public async Task DownloadEpisode(){
|
||||
await QueueManager.Instance.CrAddEpisodeToQueue(EpisodeId, string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.HistoryLang) ? CrunchyrollManager.Instance.DefaultLocale : CrunchyrollManager.Instance.CrunOptions.HistoryLang,
|
||||
CrunchyrollManager.Instance.CrunOptions.DubLang);
|
||||
public async Task DownloadEpisode(bool onlySubs = false){
|
||||
await QueueManager.Instance.CrAddEpisodeToQueue(EpisodeId,
|
||||
string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.HistoryLang) ? CrunchyrollManager.Instance.DefaultLocale : CrunchyrollManager.Instance.CrunOptions.HistoryLang,
|
||||
CrunchyrollManager.Instance.CrunOptions.DubLang, false, onlySubs);
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,9 @@ public partial class CalendarPageViewModel : ViewModelBase{
|
|||
|
||||
[ObservableProperty]
|
||||
private bool _filterByAirDate;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _showUpcomingEpisodes;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hideDubs;
|
||||
|
|
@ -79,6 +82,7 @@ public partial class CalendarPageViewModel : ViewModelBase{
|
|||
CustomCalendar = CrunchyrollManager.Instance.CrunOptions.CustomCalendar;
|
||||
HideDubs = CrunchyrollManager.Instance.CrunOptions.CalendarHideDubs;
|
||||
FilterByAirDate = CrunchyrollManager.Instance.CrunOptions.CalendarFilterByAirDate;
|
||||
ShowUpcomingEpisodes = CrunchyrollManager.Instance.CrunOptions.CalendarShowUpcomingEpisodes;
|
||||
|
||||
ComboBoxItem? dubfilter = CalendarDubFilter.FirstOrDefault(a => a.Content != null && (string)a.Content == CrunchyrollManager.Instance.CrunOptions.CalendarDubFilter) ?? null;
|
||||
CurrentCalendarDubFilter = dubfilter ?? CalendarDubFilter[0];
|
||||
|
|
@ -292,6 +296,15 @@ public partial class CalendarPageViewModel : ViewModelBase{
|
|||
CrunchyrollManager.Instance.CrunOptions.CalendarFilterByAirDate = value;
|
||||
CfgManager.WriteSettingsToFile();
|
||||
}
|
||||
|
||||
partial void OnShowUpcomingEpisodesChanged(bool value){
|
||||
if (loading){
|
||||
return;
|
||||
}
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.CalendarShowUpcomingEpisodes = value;
|
||||
CfgManager.WriteSettingsToFile();
|
||||
}
|
||||
|
||||
partial void OnCurrentCalendarDubFilterChanged(ComboBoxItem? value){
|
||||
if (loading){
|
||||
|
|
|
|||
|
|
@ -186,7 +186,15 @@ public partial class DownloadItemModel : INotifyPropertyChanged{
|
|||
epMeta.DownloadProgress.IsDownloading = true;
|
||||
Paused = !epMeta.Paused && !isDownloading || epMeta.Paused;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Paused)));
|
||||
await CrunchyrollManager.Instance.DownloadEpisode(epMeta, CrunchyrollManager.Instance.CrunOptions);
|
||||
|
||||
CrDownloadOptions newOptions = Helpers.DeepCopy(CrunchyrollManager.Instance.CrunOptions);
|
||||
|
||||
if (epMeta.OnlySubs){
|
||||
newOptions.Novids = true;
|
||||
newOptions.Noaudio = true;
|
||||
}
|
||||
|
||||
await CrunchyrollManager.Instance.DownloadEpisode(epMeta,newOptions );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
|
||||
public HistoryPageViewModel(){
|
||||
ProgramManager = ProgramManager.Instance;
|
||||
|
||||
|
||||
_storageProvider = ProgramManager.StorageProvider ?? throw new ArgumentNullException(nameof(ProgramManager.StorageProvider));
|
||||
|
||||
if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null){
|
||||
|
|
@ -177,8 +177,10 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.ScaleValue = ScaleValue;
|
||||
CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.SelectedView = currentViewType;
|
||||
CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.SelectedSorting = currentSortingType;
|
||||
CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.Ascending = SortDir;
|
||||
} else{
|
||||
CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties = new HistoryPageProperties(){ ScaleValue = ScaleValue, SelectedView = currentViewType, SelectedSorting = currentSortingType };
|
||||
CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties = new HistoryPageProperties()
|
||||
{ ScaleValue = ScaleValue, SelectedView = currentViewType, SelectedSorting = currentSortingType, Ascending = SortDir };
|
||||
}
|
||||
|
||||
CfgManager.WriteSettingsToFile();
|
||||
|
|
@ -503,6 +505,12 @@ public class HistoryPageProperties(){
|
|||
public bool Ascending{ get; set; }
|
||||
}
|
||||
|
||||
public class SeasonsPageProperties(){
|
||||
public SortingType? SelectedSorting{ get; set; }
|
||||
|
||||
public bool Ascending{ get; set; }
|
||||
}
|
||||
|
||||
public class SortingListElement(){
|
||||
public SortingType SelectedSorting{ get; set; }
|
||||
public string? SortingTitle{ get; set; }
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
|||
|
||||
await Task.WhenAll(downloadTasks);
|
||||
}
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
public async Task UpdateData(string? season){
|
||||
await SelectedSeries.FetchData(season);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.ObjectModel;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using CRD.Downloader.Crunchyroll.ViewModels;
|
||||
using CRD.Downloader.Crunchyroll.Views;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ using System.Text.Json;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CRD.Downloader;
|
||||
|
|
@ -154,6 +155,19 @@ public partial class UpcomingPageViewModel : ViewModelBase{
|
|||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
[ObservableProperty]
|
||||
private SortingListElement? _selectedSorting;
|
||||
|
||||
[ObservableProperty]
|
||||
private static bool _sortingSelectionOpen;
|
||||
|
||||
private SortingType currentSortingType;
|
||||
|
||||
[ObservableProperty]
|
||||
private static bool _sortDir = false;
|
||||
|
||||
public ObservableCollection<SortingListElement> SortingList{ get; } =[];
|
||||
|
||||
public ObservableCollection<SeasonViewModel> Seasons{ get; set; } =[];
|
||||
|
||||
public ObservableCollection<AnilistSeries> SelectedSeason{ get; set; } =[];
|
||||
|
|
@ -165,6 +179,23 @@ public partial class UpcomingPageViewModel : ViewModelBase{
|
|||
}
|
||||
|
||||
private async void LoadSeasons(){
|
||||
SeasonsPageProperties? properties = CrunchyrollManager.Instance.CrunOptions.SeasonsPageProperties;
|
||||
|
||||
currentSortingType = properties?.SelectedSorting ?? SortingType.SeriesTitle;
|
||||
SortDir = properties?.Ascending ?? false;
|
||||
|
||||
foreach (SortingType sortingType in Enum.GetValues(typeof(SortingType))){
|
||||
if (sortingType == SortingType.HistorySeriesAddDate){
|
||||
continue;
|
||||
}
|
||||
|
||||
var combobox = new SortingListElement(){ SortingTitle = sortingType.GetEnumMemberValue(), SelectedSorting = sortingType };
|
||||
SortingList.Add(combobox);
|
||||
if (sortingType == currentSortingType){
|
||||
SelectedSorting = combobox;
|
||||
}
|
||||
}
|
||||
|
||||
Seasons = GetTargetSeasonsAndYears();
|
||||
|
||||
currentSelection = Seasons.Last();
|
||||
|
|
@ -175,6 +206,8 @@ public partial class UpcomingPageViewModel : ViewModelBase{
|
|||
foreach (var anilistSeries in list){
|
||||
SelectedSeason.Add(anilistSeries);
|
||||
}
|
||||
|
||||
SortItems();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
|
@ -188,6 +221,7 @@ public partial class UpcomingPageViewModel : ViewModelBase{
|
|||
foreach (var anilistSeries in list){
|
||||
SelectedSeason.Add(anilistSeries);
|
||||
}
|
||||
SortItems();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
|
@ -304,7 +338,6 @@ public partial class UpcomingPageViewModel : ViewModelBase{
|
|||
HttpResponseMessage getUrlResponse = await HttpClientReq.Instance.GetHttpClient().SendAsync(getUrlRequest);
|
||||
|
||||
finalUrl = getUrlResponse.RequestMessage?.RequestUri?.ToString();
|
||||
|
||||
} catch (Exception ex){
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
|
|
@ -391,4 +424,73 @@ public partial class UpcomingPageViewModel : ViewModelBase{
|
|||
partial void OnSelectedSeriesChanged(AnilistSeries? value){
|
||||
SelectionChangedOfSeries(value);
|
||||
}
|
||||
|
||||
#region Sorting
|
||||
|
||||
private void UpdateSettings(){
|
||||
if (CrunchyrollManager.Instance.CrunOptions.SeasonsPageProperties != null){
|
||||
CrunchyrollManager.Instance.CrunOptions.SeasonsPageProperties.SelectedSorting = currentSortingType;
|
||||
CrunchyrollManager.Instance.CrunOptions.SeasonsPageProperties.Ascending = SortDir;
|
||||
} else{
|
||||
CrunchyrollManager.Instance.CrunOptions.SeasonsPageProperties = new SeasonsPageProperties(){ SelectedSorting = currentSortingType, Ascending = SortDir };
|
||||
}
|
||||
|
||||
CfgManager.WriteSettingsToFile();
|
||||
}
|
||||
|
||||
partial void OnSelectedSortingChanged(SortingListElement? oldValue, SortingListElement? newValue){
|
||||
if (newValue == null){
|
||||
if (CrunchyrollManager.Instance.CrunOptions.SeasonsPageProperties != null){
|
||||
CrunchyrollManager.Instance.CrunOptions.SeasonsPageProperties.Ascending = !CrunchyrollManager.Instance.CrunOptions.SeasonsPageProperties.Ascending;
|
||||
SortDir = CrunchyrollManager.Instance.CrunOptions.SeasonsPageProperties.Ascending;
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() => {
|
||||
SelectedSorting = oldValue ?? SortingList.First();
|
||||
RaisePropertyChanged(nameof(SelectedSorting));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (newValue.SelectedSorting != null){
|
||||
currentSortingType = newValue.SelectedSorting;
|
||||
if (CrunchyrollManager.Instance.CrunOptions.SeasonsPageProperties != null) CrunchyrollManager.Instance.CrunOptions.SeasonsPageProperties.SelectedSorting = currentSortingType;
|
||||
SortItems();
|
||||
}
|
||||
|
||||
SortingSelectionOpen = false;
|
||||
UpdateSettings();
|
||||
}
|
||||
|
||||
private void SortItems(){
|
||||
var sortingDir = CrunchyrollManager.Instance.CrunOptions.SeasonsPageProperties != null && CrunchyrollManager.Instance.CrunOptions.SeasonsPageProperties.Ascending;
|
||||
|
||||
var sortedList = currentSortingType switch{
|
||||
SortingType.SeriesTitle => sortingDir
|
||||
? SelectedSeason
|
||||
.OrderByDescending(item => item.Title.English)
|
||||
.ToList()
|
||||
: SelectedSeason
|
||||
.OrderBy(item => item.Title.English)
|
||||
.ToList(),
|
||||
SortingType.NextAirDate => sortingDir
|
||||
? SelectedSeason
|
||||
.OrderByDescending(item => item.StartDate?.ToDateTime() ?? DateTime.MinValue)
|
||||
.ThenByDescending(item => item.Title.English)
|
||||
.ToList()
|
||||
: SelectedSeason
|
||||
.OrderBy(item => item.StartDate?.ToDateTime() ?? DateTime.MinValue)
|
||||
.ThenBy(item => item.Title.English)
|
||||
.ToList(),
|
||||
_ => SelectedSeason.ToList()
|
||||
};
|
||||
|
||||
|
||||
SelectedSeason.Clear();
|
||||
foreach (var item in sortedList){
|
||||
SelectedSeason.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -87,10 +87,11 @@
|
|||
</controls:SettingsExpander>
|
||||
|
||||
|
||||
<controls:SettingsExpander IsVisible="{Binding CustomCalendar}" Header="Custom Calendar Dub Filter">
|
||||
<controls:SettingsExpander IsVisible="{Binding CustomCalendar}" Header="Custom Calendar">
|
||||
|
||||
<controls:SettingsExpander.Footer>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock Text="Dub Filter"></TextBlock>
|
||||
<ComboBox HorizontalAlignment="Center" Margin="5 0 0 5" MinWidth="200"
|
||||
SelectedItem="{Binding CurrentCalendarDubFilter}"
|
||||
ItemsSource="{Binding CalendarDubFilter}">
|
||||
|
|
@ -98,6 +99,9 @@
|
|||
<CheckBox IsChecked="{Binding FilterByAirDate}"
|
||||
Content="Filter by episode air date" Margin="5 5 0 0">
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding ShowUpcomingEpisodes}"
|
||||
Content="Show Upcoming episodes" Margin="5 5 0 0">
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
|
||||
</controls:SettingsExpander.Footer>
|
||||
|
|
|
|||
|
|
@ -104,14 +104,22 @@ public partial class MainWindow : AppWindow{
|
|||
.Subscribe(message => ShowToast(message.Message, message.Type, message.Seconds));
|
||||
}
|
||||
|
||||
public async void ShowError(string message){
|
||||
public async void ShowError(string message,bool githubWikiButton = false){
|
||||
var dialog = new ContentDialog(){
|
||||
Title = "Error",
|
||||
Content = message,
|
||||
CloseButtonText = "Close"
|
||||
};
|
||||
|
||||
_ = await dialog.ShowAsync();
|
||||
if (githubWikiButton){
|
||||
dialog.PrimaryButtonText = "Github Wiki";
|
||||
}
|
||||
|
||||
var result = await dialog.ShowAsync();
|
||||
|
||||
if (result == ContentDialogResult.Primary){
|
||||
Helpers.OpenUrl($"https://github.com/Crunchy-DL/Crunchy-Downloader/wiki");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -191,7 +199,7 @@ public partial class MainWindow : AppWindow{
|
|||
var screen = screens[settings.ScreenIndex];
|
||||
|
||||
// Restore the position first
|
||||
Position = new PixelPoint(settings.PosX, settings.PosY + TitleBarHeightAdjustment);
|
||||
Position = new PixelPoint(settings.PosX, settings.PosY);
|
||||
|
||||
// Restore the size
|
||||
Width = settings.Width;
|
||||
|
|
@ -199,10 +207,10 @@ public partial class MainWindow : AppWindow{
|
|||
|
||||
// Set restore size and position for non-maximized state
|
||||
_restoreSize = new Size(settings.Width, settings.Height);
|
||||
_restorePosition = new PixelPoint(settings.PosX, settings.PosY + TitleBarHeightAdjustment);
|
||||
_restorePosition = new PixelPoint(settings.PosX, settings.PosY);
|
||||
|
||||
// Ensure the window is on the correct screen before maximizing
|
||||
Position = new PixelPoint(settings.PosX, settings.PosY + TitleBarHeightAdjustment);
|
||||
Position = new PixelPoint(settings.PosX, settings.PosY );
|
||||
}
|
||||
|
||||
if (settings.IsMaximized){
|
||||
|
|
|
|||
|
|
@ -138,51 +138,51 @@
|
|||
|
||||
<controls:SettingsExpander.Footer>
|
||||
<StackPanel>
|
||||
|
||||
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 0 0 10">
|
||||
<TextBlock Text="Video Quality" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0 0 10 0"></TextBlock>
|
||||
<!-- <ComboBox HorizontalContentAlignment="Center" MinWidth="210" MaxDropDownHeight="400" -->
|
||||
<!-- ItemsSource="{Binding CrunchyrollManager.VideoQualityList}" -->
|
||||
<!-- SelectedItem="{Binding SelectedSeries.SelectedVideoQuality}"> -->
|
||||
<!-- </ComboBox> -->
|
||||
|
||||
|
||||
<StackPanel>
|
||||
<ToggleButton x:Name="OverrideDropdownButtonQuality" Width="210" HorizontalContentAlignment="Stretch">
|
||||
<ToggleButton.Content>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding SelectedSeries.SelectedVideoQualityItem.stringValue, FallbackValue=''}"
|
||||
VerticalAlignment="Center" />
|
||||
<Path Grid.Column="1" Data="M 0,1 L 4,4 L 8,1" Stroke="White" StrokeThickness="1"
|
||||
VerticalAlignment="Center" Margin="5,0,5,0" Stretch="Uniform" Width="8" />
|
||||
</Grid>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
<Popup IsLightDismissEnabled="True"
|
||||
IsOpen="{Binding IsChecked, ElementName=OverrideDropdownButtonQuality, Mode=TwoWay}"
|
||||
Placement="Bottom"
|
||||
PlacementTarget="{Binding ElementName=OverrideDropdownButtonQuality}">
|
||||
<Border BorderThickness="1" Background="{DynamicResource ComboBoxDropDownBackground}">
|
||||
<ListBox x:Name="ListBoxQulitiesSelection" SelectionMode="Single,Toggle" Width="210"
|
||||
MaxHeight="400"
|
||||
ItemsSource="{Binding SelectedSeries.VideoQualityList , Mode=OneWay}"
|
||||
SelectedItem="{Binding SelectedSeries.SelectedVideoQualityItem}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type structs:StringItem}">
|
||||
<TextBlock Text="{Binding stringValue}"></TextBlock>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Border>
|
||||
</Popup>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
<ToggleButton x:Name="OverrideDropdownButtonQuality" Width="210" HorizontalContentAlignment="Stretch">
|
||||
<ToggleButton.Content>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding SelectedSeries.SelectedVideoQualityItem.stringValue, FallbackValue=''}"
|
||||
VerticalAlignment="Center" />
|
||||
<Path Grid.Column="1" Data="M 0,1 L 4,4 L 8,1" Stroke="White" StrokeThickness="1"
|
||||
VerticalAlignment="Center" Margin="5,0,5,0" Stretch="Uniform" Width="8" />
|
||||
</Grid>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
<Popup IsLightDismissEnabled="True"
|
||||
IsOpen="{Binding IsChecked, ElementName=OverrideDropdownButtonQuality, Mode=TwoWay}"
|
||||
Placement="Bottom"
|
||||
PlacementTarget="{Binding ElementName=OverrideDropdownButtonQuality}">
|
||||
<Border BorderThickness="1" Background="{DynamicResource ComboBoxDropDownBackground}">
|
||||
<ListBox x:Name="ListBoxQulitiesSelection" SelectionMode="Single,Toggle" Width="210"
|
||||
MaxHeight="400"
|
||||
ItemsSource="{Binding SelectedSeries.VideoQualityList , Mode=OneWay}"
|
||||
SelectedItem="{Binding SelectedSeries.SelectedVideoQualityItem}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type structs:StringItem}">
|
||||
<TextBlock Text="{Binding stringValue}"></TextBlock>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Border>
|
||||
</Popup>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
</StackPanel>
|
||||
|
||||
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 0 0 10">
|
||||
<TextBlock Text="Dub" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0 0 10 0"></TextBlock>
|
||||
<StackPanel>
|
||||
|
|
@ -342,6 +342,7 @@
|
|||
|
||||
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
|
||||
<StackPanel VerticalAlignment="Center" Margin="0 0 5 0"
|
||||
IsVisible="{Binding $parent[UserControl].((vm:SeriesPageViewModel)DataContext).SonarrAvailable}">
|
||||
|
||||
|
|
@ -385,7 +386,25 @@
|
|||
<StackPanel Orientation="Horizontal">
|
||||
<controls:SymbolIcon Symbol="Download" FontSize="18" />
|
||||
</StackPanel>
|
||||
<ToolTip.Tip>
|
||||
<TextBlock Text="Download Episode" FontSize="15" />
|
||||
</ToolTip.Tip>
|
||||
</Button>
|
||||
|
||||
<Button Margin="0 0 5 0" FontStyle="Italic" HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding DownloadEpisode}"
|
||||
CommandParameter="true">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<controls:SymbolIcon Symbol="ClosedCaption" FontSize="18" />
|
||||
</StackPanel>
|
||||
<ToolTip.Tip>
|
||||
<TextBlock Text="Download Subs" FontSize="15" />
|
||||
</ToolTip.Tip>
|
||||
</Button>
|
||||
|
||||
|
||||
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
|
@ -488,10 +507,10 @@
|
|||
|
||||
<controls:SettingsExpander.Footer>
|
||||
<StackPanel>
|
||||
|
||||
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 0 0 10">
|
||||
<TextBlock Text="Video Quality" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0 0 10 0"></TextBlock>
|
||||
|
||||
|
||||
<StackPanel>
|
||||
<ToggleButton x:Name="SeasonOverrideDropdownButtonQuality" Width="210" HorizontalContentAlignment="Stretch">
|
||||
<ToggleButton.Content>
|
||||
|
|
@ -525,10 +544,10 @@
|
|||
</Border>
|
||||
</Popup>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
|
||||
|
||||
</StackPanel>
|
||||
|
||||
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 0 0 10">
|
||||
<TextBlock Text="Dub" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0 0 10 0"></TextBlock>
|
||||
<StackPanel>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
|
||||
namespace CRD.Views;
|
||||
|
||||
|
|
@ -6,4 +7,6 @@ public partial class SeriesPageView : UserControl{
|
|||
public SeriesPageView(){
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -12,11 +12,26 @@
|
|||
<Design.DataContext>
|
||||
<vm:SettingsPageViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
|
||||
<controls:TabView TabItems="{Binding Tabs}"
|
||||
AllowDropTabs="False" IsAddTabButtonVisible="False"
|
||||
Background="Transparent" CanDragTabs="False" CanReorderTabs="False"
|
||||
CanDragTabs="False" CanReorderTabs="False"
|
||||
VerticalAlignment="Stretch">
|
||||
<controls:TabView.Styles>
|
||||
<Style Selector="controls|TabViewItem:selected /template/ Border#TabContainerBorder">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
<Style Selector="controls|TabViewItem:selected /template/ Path#SelectedBackgroundPath">
|
||||
<Setter Property="Fill" Value="#10FFFFFF" />
|
||||
</Style>
|
||||
<Style Selector="controls|TabView:not(:selected) /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
</controls:TabView.Styles>
|
||||
|
||||
|
||||
</controls:TabView>
|
||||
|
||||
|
||||
|
||||
</UserControl>
|
||||
|
|
@ -16,8 +16,13 @@
|
|||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="10">
|
||||
<ItemsControl ItemsSource="{Binding Seasons}">
|
||||
<Grid Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10">
|
||||
<!-- Define columns: one for centering and one for the right-aligned content -->
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" /> <!-- For centering the ItemsControl -->
|
||||
<ColumnDefinition Width="Auto" /> <!-- For the settings StackPanel -->
|
||||
</Grid.ColumnDefinitions>
|
||||
<ItemsControl ItemsSource="{Binding Seasons}" HorizontalAlignment="Center">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" />
|
||||
|
|
@ -45,7 +50,65 @@
|
|||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1" Margin="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
|
||||
<StackPanel>
|
||||
<ToggleButton x:Name="DropdownButtonSorting" Width="50" Height="50"
|
||||
BorderThickness="0" VerticalAlignment="Center"
|
||||
IsEnabled="{Binding !IsLoading}"
|
||||
IsChecked="{Binding SortingSelectionOpen}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:SymbolIcon Symbol="Sort" FontSize="25" />
|
||||
<TextBlock Text="Sort" HorizontalAlignment="Center" 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,Toggle" Width="210"
|
||||
MaxHeight="400"
|
||||
ItemsSource="{Binding SortingList}" SelectedItem="{Binding SelectedSorting}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<controls:SymbolIcon IsVisible="{Binding !$parent[UserControl].((vm:UpcomingPageViewModel)DataContext).SortDir}" Symbol="ChevronUp" FontSize="12" Margin="0 0 10 0" />
|
||||
<controls:SymbolIcon IsVisible="{Binding $parent[UserControl].((vm:UpcomingPageViewModel)DataContext).SortDir}" Symbol="ChevronDown" FontSize="12" Margin="0 0 10 0" />
|
||||
<TextBlock Text="{Binding SortingTitle}"></TextBlock>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Border>
|
||||
</Popup>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
<!-- <ToggleButton x:Name="CalendarSettings" HorizontalContentAlignment="Stretch"> -->
|
||||
<!-- <StackPanel Orientation="Horizontal"> -->
|
||||
<!-- <controls:SymbolIcon Symbol="Settings" FontSize="18" /> -->
|
||||
<!-- </StackPanel> -->
|
||||
<!-- </ToggleButton> -->
|
||||
<!-- <Popup IsLightDismissEnabled="True" -->
|
||||
<!-- MaxWidth="400" -->
|
||||
<!-- MaxHeight="600" -->
|
||||
<!-- IsOpen="{Binding IsChecked, ElementName=CalendarSettings, Mode=TwoWay}" -->
|
||||
<!-- Placement="Bottom" -->
|
||||
<!-- PlacementTarget="{Binding ElementName=CalendarSettings}"> -->
|
||||
<!-- <Border BorderThickness="1" Background="{DynamicResource ComboBoxDropDownBackground}"> -->
|
||||
<!-- -->
|
||||
<!-- <StackPanel> -->
|
||||
<!-- -->
|
||||
<!-- </StackPanel> -->
|
||||
<!-- -->
|
||||
<!-- </Border> -->
|
||||
<!-- </Popup> -->
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
|
||||
<ListBox Grid.Row="1" IsVisible="{Binding !IsLoading}" ItemsSource="{Binding SelectedSeason}"
|
||||
|
|
@ -88,15 +151,15 @@
|
|||
</Image>
|
||||
|
||||
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Right"
|
||||
IsVisible="{Binding IsInHistory}" Margin="0 5 5 5" >
|
||||
IsVisible="{Binding IsInHistory}" Margin="0 5 5 5">
|
||||
<Border Background="DarkGray" CornerRadius="50">
|
||||
<controls:SymbolIcon Symbol="Library" Foreground="Black" FontSize="22" Margin="2" />
|
||||
<controls:SymbolIcon Symbol="Library" Foreground="Black" FontSize="22" Margin="2" />
|
||||
<ToolTip.Tip>
|
||||
<TextBlock Text="Series is in History" FontSize="15" />
|
||||
</ToolTip.Tip>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
</Grid>
|
||||
|
||||
<TextBlock HorizontalAlignment="Center" TextAlignment="Center"
|
||||
|
|
@ -125,14 +188,14 @@
|
|||
|
||||
</StackPanel>
|
||||
|
||||
<Expander Grid.Column="0" Grid.ColumnSpan="2" ExpandDirection="Right" >
|
||||
<Expander Grid.Column="0" Grid.ColumnSpan="2" ExpandDirection="Right">
|
||||
<Expander.Styles>
|
||||
<Style Selector="Expander:not(:expanded) /template/ ToggleButton#ExpanderHeader">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
</Style>
|
||||
<Style Selector="Expander:expanded /template/ ToggleButton#ExpanderHeader">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
</Style>
|
||||
<Style Selector="ToggleButton:pointerover /template/ Border#ExpandCollapseChevronBorder">
|
||||
|
|
@ -146,12 +209,12 @@
|
|||
</Style>
|
||||
</Expander.Styles>
|
||||
<Expander.Header>
|
||||
<Border Width="117" Height="315" />
|
||||
<Border Width="117" Height="315" />
|
||||
</Expander.Header>
|
||||
<Expander.Content>
|
||||
|
||||
|
||||
<StackPanel>
|
||||
|
||||
|
||||
<ScrollViewer MaxHeight="265" MinHeight="265" PointerWheelChanged="ScrollViewer_PointerWheelChanged" Margin="0 0 0 5">
|
||||
<TextBlock HorizontalAlignment="Center" TextAlignment="Center"
|
||||
Text="{Binding Description}"
|
||||
|
|
@ -162,22 +225,23 @@
|
|||
</TextBlock>
|
||||
</ScrollViewer>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Bottom">
|
||||
<Button HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="Trailer" Margin=" 0 0 5 0"
|
||||
Command="{Binding $parent[UserControl].((vm:UpcomingPageViewModel)DataContext).OpenTrailer}"
|
||||
CommandParameter="{Binding}"></Button>
|
||||
<Button HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="Trailer" Margin=" 0 0 5 0"
|
||||
Command="{Binding $parent[UserControl].((vm:UpcomingPageViewModel)DataContext).OpenTrailer}"
|
||||
CommandParameter="{Binding}">
|
||||
</Button>
|
||||
<StackPanel IsVisible="{Binding HasCrID}">
|
||||
<Button HorizontalAlignment="Right" VerticalAlignment="Bottom"
|
||||
IsVisible="{Binding !IsInHistory}"
|
||||
Command="{Binding $parent[UserControl].((vm:UpcomingPageViewModel)DataContext).AddToHistory}"
|
||||
Command="{Binding $parent[UserControl].((vm:UpcomingPageViewModel)DataContext).AddToHistory}"
|
||||
CommandParameter="{Binding}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<controls:SymbolIcon Symbol="Library" FontSize="20" />
|
||||
<controls:SymbolIcon Symbol="Add" FontSize="20" />
|
||||
<controls:SymbolIcon Symbol="Add" FontSize="20" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
|
||||
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Expander.Content>
|
||||
|
|
@ -190,6 +254,6 @@
|
|||
</ListBox>
|
||||
|
||||
<controls:ProgressRing Grid.Row="1" IsVisible="{Binding IsLoading}" VerticalAlignment="Center" HorizontalAlignment="Center" Width="100" Height="100"></controls:ProgressRing>
|
||||
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
</Design.DataContext>
|
||||
|
||||
|
||||
<ScrollViewer Padding="20 20 20 0">
|
||||
<ScrollViewer Padding="20 20 20 0" >
|
||||
<StackPanel Spacing="8">
|
||||
|
||||
<controls:SettingsExpander Header="History"
|
||||
|
|
|
|||
Loading…
Reference in a new issue