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