Add - Added **Shutdown PC toggle** to the Download Queue page [#298](https://github.com/Crunchy-DL/Crunchy-Downloader/issues/298)

Add - Added **Download Duplicate Subs** option to the softsub settings [#294](https://github.com/Crunchy-DL/Crunchy-Downloader/issues/294)
Chg - Changed **ScaledBorderAndShadow** setting to now also apply to **CC subtitles** [#293](https://github.com/Crunchy-DL/Crunchy-Downloader/issues/293)
Fix - Fixed **crash with certain manifest versions** [#288](https://github.com/Crunchy-DL/Crunchy-Downloader/issues/288)
Fix - Fixed **infinite loading bug with Sonarr**
This commit is contained in:
Elwador 2025-07-13 17:45:47 +02:00
parent 1f96ab731e
commit 65200147a0
12 changed files with 184 additions and 50 deletions

View file

@ -516,6 +516,12 @@ public class CrunchyrollManager{
} catch (Exception exception){
Console.Error.WriteLine("Failed to play sound: " + exception);
}
if (CrunOptions.ShutdownWhenQueueEmpty){
Helpers.ShutdownComputer();
}
}
return true;
@ -603,7 +609,7 @@ public class CrunchyrollManager{
if (data.FindAll(a => a.Type == DownloadMediaType.Audio).Count > 0){
if (options.Mp3){
Console.WriteLine("Mux to MP3");
muxToMp3 = true;
muxToMp3 = true;
}
} else{
Console.WriteLine("Skip muxing since no videos are downloaded");
@ -647,9 +653,9 @@ public class CrunchyrollManager{
var merger = new Merger(new MergerOptions{
DubLangList = options.DubLangList,
SubLangList = options.SubLangList,
OnlyVid = data.Where(a => a.Type == DownloadMediaType.Video).Select(a => new MergerInput{ Language = a.Lang, Path = a.Path ?? string.Empty ,Bitrate = a.bitrate}).ToList(),
OnlyVid = data.Where(a => a.Type == DownloadMediaType.Video).Select(a => new MergerInput{ Language = a.Lang, Path = a.Path ?? string.Empty, Bitrate = a.bitrate }).ToList(),
SkipSubMux = options.SkipSubMux,
OnlyAudio = data.Where(a => a.Type == DownloadMediaType.Audio).Select(a => new MergerInput{ Language = a.Lang, Path = a.Path ?? string.Empty ,Bitrate = a.bitrate}).ToList(),
OnlyAudio = data.Where(a => a.Type == DownloadMediaType.Audio).Select(a => new MergerInput{ Language = a.Lang, Path = a.Path ?? string.Empty, Bitrate = a.bitrate }).ToList(),
Output = $"{filename}.{(muxToMp3 ? "mp3" : options.Mp4 ? "mp4" : "mkv")}",
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(),
@ -920,7 +926,7 @@ public class CrunchyrollManager{
if (mediaGuid.Contains(':')){
mediaGuid = mediaGuid.Split(':')[1];
}
Console.WriteLine("MediaGuid: " + mediaId);
#region Chapters
@ -937,7 +943,7 @@ public class CrunchyrollManager{
}
#endregion
var fetchPlaybackData = await FetchPlaybackData(options.StreamEndpoint ?? "web/firefox", mediaId, mediaGuid, data.Music);
(bool IsOk, PlaybackData pbData, string error) fetchPlaybackData2 = default;
if (!string.IsNullOrEmpty(options.StreamEndpointSecondary) && !(options.StreamEndpoint ?? "web/firefox").Equals(options.StreamEndpointSecondary)){
@ -989,18 +995,30 @@ public class CrunchyrollManager{
ErrorText = "Playback data not found"
};
}
if (fetchPlaybackData2.IsOk){
if (fetchPlaybackData.pbData.Data != null)
foreach (var keyValuePair in fetchPlaybackData.pbData.Data){
var value = fetchPlaybackData2.pbData?.Data?[keyValuePair.Key];
var url = value?.Url.First() ?? "";
if (fetchPlaybackData.pbData.Data != null && fetchPlaybackData2.pbData?.Data != null)
foreach (var keyValuePair in fetchPlaybackData2.pbData.Data){
var pbDataFirstEndpoint = fetchPlaybackData.pbData?.Data;
if (pbDataFirstEndpoint != null && pbDataFirstEndpoint.TryGetValue(keyValuePair.Key, out var value)){
var urlSecondEndpoint = keyValuePair.Value.Url.First() ?? "";
var match = Regex.Match(url, @"(.*\.urlset\/)");
var shortendUrl = match.Success ? match.Value : url;
var match = Regex.Match(urlSecondEndpoint, @"(https?:\/\/.*?\/(?:dash\/|\.urlset\/))");
var shortendUrl = match.Success ? match.Value : urlSecondEndpoint;
if (!keyValuePair.Value.Url.Any(arrayUrl => arrayUrl != null && arrayUrl.Contains(shortendUrl))){
keyValuePair.Value.Url.Add(url);
if (!value.Url.Any(arrayUrl => arrayUrl != null && arrayUrl.Contains(shortendUrl))){
value.Url.Add(urlSecondEndpoint);
}
} else{
if (pbDataFirstEndpoint != null){
pbDataFirstEndpoint[keyValuePair.Key] = keyValuePair.Value;
} else{
if (fetchPlaybackData.pbData != null){
fetchPlaybackData.pbData.Data = new Dictionary<string, StreamDetails>{
[keyValuePair.Key] = keyValuePair.Value
};
}
}
}
}
}
@ -1158,21 +1176,19 @@ public class CrunchyrollManager{
Console.WriteLine("Downloading video...");
curStream = streams[options.Kstream - 1];
Console.WriteLine($"Playlists URL: {string.Join(", ",curStream.Url)} ({curStream.Type})");
Console.WriteLine($"Playlists URL: {string.Join(", ", curStream.Url)} ({curStream.Type})");
}
string tsFile = "";
var videoDownloadMedia = new DownloadedMedia(){ Lang = Languages.DEFAULT_lang };
if (!dlFailed && curStream != null && options is not{ Novids: true, Noaudio: true }){
Dictionary<string, string> streamPlaylistsReqResponseList =[];
foreach (var streamUrl in curStream.Url){
var streamPlaylistsReq = HttpClientReq.CreateRequestMessage(streamUrl ?? string.Empty, HttpMethod.Get, true, true, null);
var streamPlaylistsReqResponse = await HttpClientReq.Instance.SendHttpRequest(streamPlaylistsReq);
if (!streamPlaylistsReqResponse.IsOk){
dlFailed = true;
return new DownloadResponse{
@ -1185,9 +1201,9 @@ public class CrunchyrollManager{
if (streamPlaylistsReqResponse.ResponseContent.Contains("MPD")){
streamPlaylistsReqResponseList[streamUrl ?? ""] = streamPlaylistsReqResponse.ResponseContent;
}
}
}
//Use again when cr has all endpoints with new encoding
// var streamPlaylistsReq = HttpClientReq.CreateRequestMessage(curStream.Url ?? string.Empty, HttpMethod.Get, true, true, null);
//
@ -1223,18 +1239,23 @@ public class CrunchyrollManager{
Dictionary<string, ServerData> playListData = new Dictionary<string, ServerData>();
foreach (var curStreams in streamPlaylistsReqResponseList){
var match = Regex.Match(curStreams.Key ?? string.Empty, @"(.*\.urlset\/)");
var match = Regex.Match(curStreams.Key ?? string.Empty, @"(https?:\/\/.*?\/(?:dash\/|\.urlset\/))");
var matchedUrl = match.Success ? match.Value : null;
//Parse MPD Playlists
var crLocal = "";
if (pbData.Meta != null){
crLocal = pbData.Meta.AudioLocale.CrLocale;
}
MPDParsed streamPlaylists = MPDParser.Parse(curStreams.Value, Languages.FindLang(crLocal), matchedUrl);
streamServers.UnionWith(streamPlaylists.Data.Keys);
Helpers.MergePlaylistData(playListData, streamPlaylists.Data);
try{
MPDParsed streamPlaylists = MPDParser.Parse(curStreams.Value, Languages.FindLang(crLocal), matchedUrl);
streamServers.UnionWith(streamPlaylists.Data.Keys);
Helpers.MergePlaylistData(playListData, streamPlaylists.Data);
} catch (Exception e){
Console.Error.WriteLine(e);
}
}
options.StreamServer = options.StreamServer > streamServers.Count ? 1 : options.StreamServer;
if (streamServers.Count == 0){
@ -1252,7 +1273,7 @@ public class CrunchyrollManager{
// string selectedServer = streamServers[options.StreamServer - 1];
// ServerData selectedList = streamPlaylists.Data[selectedServer];
string selectedServer = streamServers.ToList()[options.StreamServer - 1];
ServerData selectedList = playListData[selectedServer];
@ -1380,7 +1401,7 @@ public class CrunchyrollManager{
Console.WriteLine("Stream URL:" + chosenVideoSegments.segments[0].uri.Split(new[]{ ",.urlset" }, StringSplitOptions.None)[0]);
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers,options.FileNameWhitespaceSubstitute, options.Override).ToArray());
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers, options.FileNameWhitespaceSubstitute, options.Override).ToArray());
string onlyFileName = Path.GetFileName(fileName);
int maxLength = 220;
@ -1395,7 +1416,7 @@ public class CrunchyrollManager{
if (excessLength > 0 && ((string)titleVariable.ReplaceWith).Length > excessLength){
titleVariable.ReplaceWith = ((string)titleVariable.ReplaceWith).Substring(0, ((string)titleVariable.ReplaceWith).Length - excessLength);
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers,options.FileNameWhitespaceSubstitute, options.Override).ToArray());
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers, options.FileNameWhitespaceSubstitute, options.Override).ToArray());
onlyFileName = Path.GetFileName(fileName);
if (onlyFileName.Length > maxLength){
@ -1414,7 +1435,8 @@ public class CrunchyrollManager{
string outFile = fileName + "." + (epMeta.Lang?.CrLocale ?? lang.CrLocale);
string tempFile = Path.Combine(FileNameManager
.ParseFileName($"temp-{(!string.IsNullOrEmpty(currentVersion.Guid) ? currentVersion.Guid : currentMediaId)}", variables, options.Numbers,options.FileNameWhitespaceSubstitute, options.Override)
.ParseFileName($"temp-{(!string.IsNullOrEmpty(currentVersion.Guid) ? currentVersion.Guid : currentMediaId)}", variables, options.Numbers, options.FileNameWhitespaceSubstitute,
options.Override)
.ToArray());
string tempTsFile = Path.IsPathRooted(tempFile) ? tempFile : Path.Combine(fileDir, tempFile);
@ -1718,7 +1740,7 @@ public class CrunchyrollManager{
}
}
} else if (options.Novids){
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers,options.FileNameWhitespaceSubstitute, options.Override).ToArray());
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers, options.FileNameWhitespaceSubstitute, options.Override).ToArray());
Console.WriteLine("Downloading skipped!");
}
}
@ -1726,14 +1748,15 @@ public class CrunchyrollManager{
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.FileNameWhitespaceSubstitute, options.Override).ToArray());
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers, options.FileNameWhitespaceSubstitute, options.Override).ToArray());
}
if (compiledChapters.Count > 0 && options is not{ Novids: true, Noaudio: true }){
try{
// Parsing and constructing the file names
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers,options.FileNameWhitespaceSubstitute, options.Override).ToArray());
var outFile = Path.Combine(FileNameManager.ParseFileName(options.FileName + "." + (epMeta.Lang?.CrLocale), variables, options.Numbers,options.FileNameWhitespaceSubstitute, options.Override).ToArray());
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers, options.FileNameWhitespaceSubstitute, options.Override).ToArray());
var outFile = Path.Combine(FileNameManager.ParseFileName(options.FileName + "." + (epMeta.Lang?.CrLocale), variables, options.Numbers, options.FileNameWhitespaceSubstitute, options.Override)
.ToArray());
if (Path.IsPathRooted(outFile)){
tsFile = outFile;
} else{
@ -1855,7 +1878,7 @@ public class CrunchyrollManager{
Error = dlFailed,
FileName = fileName.Length > 0 ? fileName : "unknown - " + Guid.NewGuid(),
ErrorText = "",
VideoTitle = FileNameManager.ParseFileName(options.VideoTitle ?? "", variables, options.Numbers,options.FileNameWhitespaceSubstitute, options.Override).Last(),
VideoTitle = FileNameManager.ParseFileName(options.VideoTitle ?? "", variables, options.Numbers, options.FileNameWhitespaceSubstitute, options.Override).Last(),
FolderPath = fileDir,
TempFolderPath = tempFolderPath
};
@ -1864,6 +1887,10 @@ public class CrunchyrollManager{
private static async Task DownloadSubtitles(CrDownloadOptions options, PlaybackData pbData, LanguageItem audDub, string fileName, List<DownloadedMedia> files, string fileDir, CrunchyEpMeta data,
DownloadedMedia videoDownloadMedia){
if (pbData.Meta != null && (pbData.Meta.Subtitles is{ Count: > 0 } || pbData.Meta.Captions is{ Count: > 0 })){
if (videoDownloadMedia.Lang == Languages.DEFAULT_lang){
videoDownloadMedia.Lang = pbData.Meta.AudioLocale;
}
List<SubtitleInfo> subsData = pbData.Meta.Subtitles?.Values.ToList() ??[];
List<Caption> capsData = pbData.Meta.Captions?.Values.ToList() ??[];
var subsDataMapped = subsData.Select(s => {
@ -1899,20 +1926,31 @@ public class CrunchyrollManager{
sxData.Language = langItem;
var isSigns = langItem.CrLocale == audDub.CrLocale && !subsItem.isCC;
var isCc = subsItem.isCC;
var isDuplicate = false;
sxData.File = Languages.SubsFile(fileName, index + "", langItem, isCc, options.CcTag, isSigns, subsItem.format, !(data.DownloadSubs.Count == 1 && !data.DownloadSubs.Contains("all")));
if ((!options.IncludeSignsSubs && isSigns) || (!options.IncludeCcSubs && isCc)){
continue;
}
var matchingSubs = files.Where(a => a.Type == DownloadMediaType.Subtitle &&
(a.Language.CrLocale == langItem.CrLocale || a.Language.Locale == langItem.Locale) &&
a.Cc == isCc &&
a.Signs == isSigns).ToList();
if (matchingSubs.Count > 0){
isDuplicate = true;
if (!options.SubsDownloadDuplicate || matchingSubs.Any(a => a.RelatedVideoDownloadMedia?.Lang == videoDownloadMedia.Lang)){
continue;
}
}
sxData.File = Languages.SubsFile(fileName, index + "", langItem, isDuplicate ? videoDownloadMedia.Lang.CrLocale : "",isCc, options.CcTag, isSigns, subsItem.format, !(data.DownloadSubs.Count == 1 && !data.DownloadSubs.Contains("all")));
sxData.Path = Path.Combine(fileDir, sxData.File);
Helpers.EnsureDirectoriesExist(sxData.Path);
// Check if any file matches the specified conditions
if (files.Any(a => a.Type == DownloadMediaType.Subtitle &&
(a.Language.CrLocale == langItem.CrLocale || a.Language.Locale == langItem.Locale) &&
a.Cc == isCc &&
a.Signs == isSigns) || (!options.IncludeSignsSubs && isSigns) || (!options.IncludeCcSubs && isCc)){
continue;
}
if (data.DownloadSubs.Contains("all") || data.DownloadSubs.Contains(langItem.CrLocale)){
if (string.IsNullOrEmpty(subsItem.url)){
continue;
@ -1953,6 +1991,12 @@ public class CrunchyrollManager{
assBuilder.AppendLine("PlayResX: 640");
assBuilder.AppendLine("PlayResY: 360");
assBuilder.AppendLine("Timer: 0.0");
if (options.SubsAddScaledBorder == ScaledBorderAndShadowSelection.ScaledBorderAndShadowYes){
assBuilder.AppendLine("ScaledBorderAndShadow: yes");
} else if (options.SubsAddScaledBorder == ScaledBorderAndShadowSelection.ScaledBorderAndShadowNo){
assBuilder.AppendLine("ScaledBorderAndShadow: no");
}
assBuilder.AppendLine();
assBuilder.AppendLine("[V4+ Styles]");
assBuilder.AppendLine("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, "
@ -2197,7 +2241,7 @@ public class CrunchyrollManager{
foreach (var hardsub in playStream.HardSubs){
var stream = hardsub.Value;
derivedPlayCrunchyStreams[hardsub.Key] = new StreamDetails{
Url = [stream.Url],
Url =[stream.Url],
IsHardsubbed = true,
HardsubLocale = stream.Hlang,
HardsubLang = Languages.FixAndFindCrLc((stream.Hlang ?? Locale.DefaulT).GetEnumMemberValue())
@ -2206,7 +2250,7 @@ public class CrunchyrollManager{
}
derivedPlayCrunchyStreams[""] = new StreamDetails{
Url = [playStream.Url],
Url =[playStream.Url],
IsHardsubbed = false,
HardsubLocale = Locale.DefaulT,
HardsubLang = Languages.DEFAULT_lang

View file

@ -40,7 +40,10 @@ public partial class CrunchyrollSettingsViewModel : ViewModelBase{
[ObservableProperty]
private bool _addScaledBorderAndShadow;
[ObservableProperty]
private bool _subsDownloadDuplicate;
[ObservableProperty]
private bool _includeSignSubs;
@ -332,6 +335,7 @@ public partial class CrunchyrollSettingsViewModel : ViewModelBase{
AddScaledBorderAndShadow = options.SubsAddScaledBorder is ScaledBorderAndShadowSelection.ScaledBorderAndShadowNo or ScaledBorderAndShadowSelection.ScaledBorderAndShadowYes;
SelectedScaledBorderAndShadow = GetScaledBorderAndShadowFromOptions(options);
SubsDownloadDuplicate = options.SubsDownloadDuplicate;
MarkAsWatched = options.MarkAsWatched;
DownloadFirstAvailableDub = options.DownloadFirstAvailableDub;
UseCrBetaApi = options.UseCrBetaApi;
@ -402,6 +406,7 @@ public partial class CrunchyrollSettingsViewModel : ViewModelBase{
return;
}
CrunchyrollManager.Instance.CrunOptions.SubsDownloadDuplicate = SubsDownloadDuplicate;
CrunchyrollManager.Instance.CrunOptions.MarkAsWatched = MarkAsWatched;
CrunchyrollManager.Instance.CrunOptions.DownloadFirstAvailableDub = DownloadFirstAvailableDub;
CrunchyrollManager.Instance.CrunOptions.UseCrBetaApi = UseCrBetaApi;

View file

@ -105,6 +105,12 @@
</StackPanel>
</controls:SettingsExpander.Footer>
<controls:SettingsExpanderItem Content="Download Duplicate" Description="Download subtitles from all dubs where they're available">
<controls:SettingsExpanderItem.Footer>
<CheckBox IsChecked="{Binding SubsDownloadDuplicate}"> </CheckBox>
</controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Add ScaledBorderAndShadow ">
<controls:SettingsExpanderItem.Footer>
<StackPanel Orientation="Horizontal">

View file

@ -8,6 +8,7 @@ using System.Net.Http;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
@ -21,6 +22,7 @@ using CRD.Utils.HLS;
using CRD.Utils.JsonConv;
using CRD.Utils.Structs;
using CRD.Utils.Structs.Crunchyroll;
using FluentAvalonia.UI.Controls;
using Microsoft.Win32;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
@ -806,4 +808,56 @@ public class Helpers{
}
}
}
private static readonly SemaphoreSlim ShutdownLock = new(1, 1);
public static async Task ShutdownComputer(){
if (!await ShutdownLock.WaitAsync(0))
return;
try{
var timer = new System.Timers.Timer(30000); // 30 seconds
timer.Elapsed += (sender, e) => { PerformShutdown(); };
timer.AutoReset = false;
timer.Start();
var dialog = new ContentDialog{
Title = "Shutdown Pending",
Content = "The PC will shut down in 30 seconds.\nClick 'Cancel' to abort.",
PrimaryButtonText = "Cancel Shutdown",
CloseButtonText = "Close",
DefaultButton = ContentDialogButton.Primary
};
var result = await dialog.ShowAsync();
if (result == ContentDialogResult.Primary){
timer.Stop();
}
} finally{
ShutdownLock.Release();
}
}
private static void PerformShutdown(){
string shutdownCmd;
string shutdownArgs;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)){
shutdownCmd = "shutdown";
shutdownArgs = "/s /t 0";
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX)){
shutdownCmd = "shutdown";
shutdownArgs = "-h now";
} else{
throw new PlatformNotSupportedException();
}
Process.Start(new ProcessStartInfo{
FileName = shutdownCmd,
Arguments = shutdownArgs,
CreateNoWindow = true,
UseShellExecute = false
});
}
}

View file

@ -52,6 +52,7 @@ public class FontsManager{
{ "Times New Roman", new List<string>{ "times.ttf", "timesbd.ttf", "timesbi.ttf", "timesi.ttf" } },
{ "Trebuchet MS", new List<string>{ "trebuc.ttf", "trebucbd.ttf", "trebucbi.ttf", "trebucit.ttf" } },
{ "Verdana", new List<string>{ "verdana.ttf", "verdanab.ttf", "verdanai.ttf", "verdanaz.ttf" } },
{ "Vrinda", new List<string>{ "vrinda.ttf", "vrindab.ttf"} },
{ "Webdings", new List<string>{ "webdings.ttf" } },
};

View file

@ -378,7 +378,7 @@ public class ToM3u8Class{
var language = ObjectUtilities.GetMemberValue(playlist.attributes, "lang") ?? string.Empty;
var label = ObjectUtilities.GetMemberValue(playlist.attributes, "label") ?? "main";
if (!string.IsNullOrEmpty(language) && string.IsNullOrEmpty(playlist.attributes.label)){
if (!string.IsNullOrEmpty(language) && string.IsNullOrEmpty(label)){
var roleLabel = !string.IsNullOrEmpty(role) ? $" ({role})" : string.Empty;
label = $"{language}{roleLabel}";
}

View file

@ -87,7 +87,7 @@ public static class MPDParser{
throw new NotImplementedException();
}
var foundLanguage = Languages.FindLang(Languages.languages.FirstOrDefault(a => a.Code == item.language)?.CrLocale ?? "unknown");
var foundLanguage = Languages.FindLang(Languages.languages.FirstOrDefault(a => a.CrLocale == item.language)?.CrLocale ?? "unknown");
LanguageItem? audioLang = item.language != null ? foundLanguage : (language != null ? language : foundLanguage);
var pItem = new AudioPlaylist{

View file

@ -54,7 +54,7 @@ public class SonarrClient{
CrunchyrollManager.Instance.History.MatchHistorySeriesWithSonarr(true);
foreach (var historySeries in CrunchyrollManager.Instance.HistoryList){
if (historySeries.SonarrSeriesId != null){
if (!string.IsNullOrEmpty(historySeries.SonarrSeriesId)){
List<SonarrEpisode>? episodes = await GetEpisodes(int.Parse(historySeries.SonarrSeriesId));
historySeries.SonarrNextAirDate = CrunchyrollManager.Instance.History.GetNextAirDate(episodes);
}

View file

@ -9,6 +9,9 @@ namespace CRD.Utils.Structs.Crunchyroll;
public class CrDownloadOptions{
#region General Settings
[JsonProperty("shutdown_when_queue_empty")]
public bool ShutdownWhenQueueEmpty{ get; set; }
[JsonProperty("auto_download")]
public bool AutoDownload{ get; set; }
@ -179,6 +182,9 @@ public class CrDownloadOptions{
[JsonProperty("subs_add_scaled_border")]
public ScaledBorderAndShadowSelection SubsAddScaledBorder{ get; set; }
[JsonProperty("subs_download_duplicate")]
public bool SubsDownloadDuplicate{ get; set; }
[JsonProperty("include_signs_subs")]
public bool IncludeSignsSubs{ get; set; }

View file

@ -90,10 +90,14 @@ public class Languages{
return FindLang(str);
}
public static string SubsFile(string fnOutput, string subsIndex, LanguageItem langItem, bool isCC, string ccTag, bool? isSigns = false, string? format = "ass", bool addIndexAndLangCode = true){
public static string SubsFile(string fnOutput, string subsIndex, LanguageItem langItem,string insert, bool isCC, string ccTag, bool? isSigns = false, string? format = "ass", bool addIndexAndLangCode = true){
subsIndex = (int.Parse(subsIndex) + 1).ToString().PadLeft(2, '0');
string fileName = $"{fnOutput}";
if (!string.IsNullOrEmpty(insert)){
fileName += $".{insert}";
}
if (addIndexAndLangCode){
fileName += $".{langItem.Locale}"; //.{subsIndex}
}

View file

@ -19,6 +19,9 @@ namespace CRD.ViewModels;
public partial class DownloadsPageViewModel : ViewModelBase{
public ObservableCollection<DownloadItemModel> Items{ get; }
[ObservableProperty]
private bool _shutdownWhenQueueEmpty;
[ObservableProperty]
private bool _autoDownload;
@ -34,6 +37,7 @@ public partial class DownloadsPageViewModel : ViewModelBase{
Items = QueueManagerIns.DownloadItemModels;
AutoDownload = CrunchyrollManager.Instance.CrunOptions.AutoDownload;
RemoveFinished = CrunchyrollManager.Instance.CrunOptions.RemoveFinishedDownload;
ShutdownWhenQueueEmpty = CrunchyrollManager.Instance.CrunOptions.ShutdownWhenQueueEmpty;
}
@ -51,6 +55,11 @@ public partial class DownloadsPageViewModel : ViewModelBase{
CfgManager.WriteCrSettings();
}
partial void OnShutdownWhenQueueEmptyChanged(bool value){
CrunchyrollManager.Instance.CrunOptions.ShutdownWhenQueueEmpty = value;
CfgManager.WriteCrSettings();
}
[RelayCommand]
public void ClearQueue(){
var items = QueueManagerIns.Queue;

View file

@ -23,6 +23,11 @@
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
<ToggleSwitch HorizontalAlignment="Right" Margin="0 0 10 0 " IsChecked="{Binding RemoveFinished}" OffContent="Remove Finished" OnContent="Remove Finished"></ToggleSwitch>
<ToggleSwitch HorizontalAlignment="Right" Margin="0 0 10 0 " IsChecked="{Binding AutoDownload}" OffContent="Auto Download" OnContent="Auto Download"></ToggleSwitch>
<ToggleSwitch HorizontalAlignment="Right" Margin="0 0 10 0 " IsChecked="{Binding ShutdownWhenQueueEmpty}" OffContent="Shutdown PC" OnContent="Shutdown PC">
<ToolTip.Tip>
<TextBlock Text="Automatically shut down the PC when the queue is empty" FontSize="16" HorizontalAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap"></TextBlock>
</ToolTip.Tip>
</ToggleSwitch>
<Button BorderThickness="0"
HorizontalAlignment="Right"