Add - Temp folder option to the settings - episodes are downloaded and processed in this folder an moved afterwards https://github.com/Crunchy-DL/Crunchy-Downloader/issues/62

Chg - Renamed {showTitle} to {seasonTitle} make sure to change it if you have it in the file name
Fix - Height of window increased each about 31px  https://github.com/Crunchy-DL/Crunchy-Downloader/issues/89
Fix - History bar now has a different color for dark mode and light mode https://github.com/Crunchy-DL/Crunchy-Downloader/issues/85
Fix - FFMPEG/Mkvmerge/Mp4decrypt errors are now correctly logged
Fix - Crash with separate video files option
This commit is contained in:
Elwador 2024-08-20 18:40:45 +02:00
parent c7a2ef5af7
commit 3a28c0fbd1
14 changed files with 431 additions and 114 deletions

View File

@ -98,12 +98,12 @@ public class CrunchyrollManager{
options.Force = "Y"; options.Force = "Y";
options.FileName = "${seriesTitle} - S${season}E${episode} [${height}p]"; options.FileName = "${seriesTitle} - S${season}E${episode} [${height}p]";
options.Partsize = 10; options.Partsize = 10;
options.DlSubs = new List<string>{ "de-DE" }; options.DlSubs = new List<string>{ "en-US" };
options.Skipmux = false; options.Skipmux = false;
options.MkvmergeOptions = new List<string>{ "--no-date", "--disable-track-statistics-tags", "--engage no_variable_data" }; options.MkvmergeOptions = new List<string>{ "--no-date", "--disable-track-statistics-tags", "--engage no_variable_data" };
options.FfmpegOptions = new(); options.FfmpegOptions = new();
options.DefaultAudio = "ja-JP"; options.DefaultAudio = "ja-JP";
options.DefaultSub = "de-DE"; options.DefaultSub = "en-US";
options.CcTag = "CC"; options.CcTag = "CC";
options.FsRetryTime = 5; options.FsRetryTime = 5;
options.Numbers = 2; options.Numbers = 2;
@ -223,15 +223,16 @@ public class CrunchyrollManager{
QueueManager.Instance.Queue.Refresh(); QueueManager.Instance.Queue.Refresh();
var fileNameAndPath = CrunOptions.DownloadToTempFolder ? Path.Combine(res.TempFolderPath, res.FileName) : Path.Combine(res.FolderPath, res.FileName);
if (CrunOptions is{ DlVideoOnce: false, KeepDubsSeperate: true }){ if (CrunOptions is{ DlVideoOnce: false, KeepDubsSeperate: true }){
var groupByDub = Helpers.GroupByLanguageWithSubtitles(res.Data); var groupByDub = Helpers.GroupByLanguageWithSubtitles(res.Data);
var mergers = new List<Merger>();
foreach (var keyValue in groupByDub){ foreach (var keyValue in groupByDub){
await MuxStreams(keyValue.Value, var result = await MuxStreams(keyValue.Value,
new CrunchyMuxOptions{ new CrunchyMuxOptions{
FfmpegOptions = options.FfmpegOptions, FfmpegOptions = options.FfmpegOptions,
SkipSubMux = options.SkipSubsMux, SkipSubMux = options.SkipSubsMux,
Output = res.FileName, Output = fileNameAndPath,
Mp4 = options.Mp4, Mp4 = options.Mp4,
VideoTitle = res.VideoTitle, VideoTitle = res.VideoTitle,
Novids = options.Novids, Novids = options.Novids,
@ -245,14 +246,23 @@ public class CrunchyrollManager{
KeepAllVideos = true, KeepAllVideos = true,
MuxDescription = options.IncludeVideoDescription MuxDescription = options.IncludeVideoDescription
}, },
res.FileName); fileNameAndPath);
if (result is{ merger: not null, isMuxed: true }){
mergers.Add(result.merger);
}
}
foreach (var merger in mergers){
merger.CleanUp();
await MoveFromTempFolder(merger, data, res.TempFolderPath, res.Data.Where(e => e.Type == DownloadMediaType.Subtitle));
} }
} else{ } else{
await MuxStreams(res.Data, var result = await MuxStreams(res.Data,
new CrunchyMuxOptions{ new CrunchyMuxOptions{
FfmpegOptions = options.FfmpegOptions, FfmpegOptions = options.FfmpegOptions,
SkipSubMux = options.SkipSubsMux, SkipSubMux = options.SkipSubsMux,
Output = res.FileName, Output = fileNameAndPath,
Mp4 = options.Mp4, Mp4 = options.Mp4,
VideoTitle = res.VideoTitle, VideoTitle = res.VideoTitle,
Novids = options.Novids, Novids = options.Novids,
@ -266,9 +276,16 @@ public class CrunchyrollManager{
KeepAllVideos = true, KeepAllVideos = true,
MuxDescription = options.IncludeVideoDescription MuxDescription = options.IncludeVideoDescription
}, },
res.FileName); fileNameAndPath);
if (result is{ merger: not null, isMuxed: true }){
result.merger.CleanUp();
await MoveFromTempFolder(result.merger, data, res.TempFolderPath, res.Data.Where(e => e.Type == DownloadMediaType.Subtitle));
}
} }
data.DownloadProgress = new DownloadProgress(){ data.DownloadProgress = new DownloadProgress(){
IsDownloading = true, IsDownloading = true,
Done = true, Done = true,
@ -296,7 +313,85 @@ public class CrunchyrollManager{
return true; return true;
} }
private async Task MuxStreams(List<DownloadedMedia> data, CrunchyMuxOptions options, string filename){ #region Temp Files Move
private async Task MoveFromTempFolder(Merger merger, CrunchyEpMeta data, string tempFolderPath, IEnumerable<DownloadedMedia> subtitles){
if (!CrunOptions.DownloadToTempFolder) return;
data.DownloadProgress = new DownloadProgress{
IsDownloading = true,
Done = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
Doing = "Moving Files"
};
QueueManager.Instance.Queue.Refresh();
if (string.IsNullOrEmpty(tempFolderPath) || !Directory.Exists(tempFolderPath)){
Console.WriteLine("Invalid or non-existent temp folder path.");
return;
}
// Move the main output file
await MoveFile(merger.options.Output, tempFolderPath, data.DownloadPath);
// Move the subtitle files
if (CrunOptions.SkipSubsMux){
foreach (var downloadedMedia in subtitles){
await MoveFile(downloadedMedia.Path ?? string.Empty, tempFolderPath, data.DownloadPath);
}
}
}
private async Task MoveFile(string sourcePath, string tempFolderPath, string downloadPath){
if (string.IsNullOrEmpty(sourcePath) || !File.Exists(sourcePath)){
Console.Error.WriteLine("Source file does not exist or path is invalid.");
return;
}
if (!sourcePath.StartsWith(tempFolderPath)){
Console.Error.WriteLine("Source file is not located in the temp folder.");
return;
}
try{
var fileName = sourcePath[tempFolderPath.Length..].TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
var destinationFolder = !string.IsNullOrEmpty(downloadPath)
? downloadPath
: !string.IsNullOrEmpty(CrunOptions.DownloadDirPath)
? CrunOptions.DownloadDirPath
: CfgManager.PathVIDEOS_DIR;
var destinationPath = Path.Combine(destinationFolder, fileName);
string destinationDirectory = Path.GetDirectoryName(destinationPath);
if (string.IsNullOrEmpty(destinationDirectory)){
Console.WriteLine("Invalid destination directory path.");
return;
}
await Task.Run(() => {
if (!Directory.Exists(destinationDirectory)){
Directory.CreateDirectory(destinationDirectory);
}
});
await Task.Run(() => File.Move(sourcePath, destinationPath));
Console.WriteLine($"File moved to {destinationPath}");
} catch (IOException ex){
Console.Error.WriteLine($"An error occurred while moving the file: {ex.Message}");
} catch (UnauthorizedAccessException ex){
Console.Error.WriteLine($"Access denied while moving the file: {ex.Message}");
} catch (Exception ex){
Console.Error.WriteLine($"An unexpected error occurred: {ex.Message}");
}
}
#endregion
private async Task<(Merger? merger, bool isMuxed)> MuxStreams(List<DownloadedMedia> data, CrunchyMuxOptions options, string filename){
var muxToMp3 = false; var muxToMp3 = false;
if (options.Novids == true || data.FindAll(a => a.Type == DownloadMediaType.Video).Count == 0){ if (options.Novids == true || data.FindAll(a => a.Type == DownloadMediaType.Video).Count == 0){
@ -305,7 +400,7 @@ public class CrunchyrollManager{
muxToMp3 = true; muxToMp3 = true;
} else{ } else{
Console.WriteLine("Skip muxing since no videos are downloaded"); Console.WriteLine("Skip muxing since no videos are downloaded");
return; return (null, false);
} }
} }
@ -333,7 +428,8 @@ public class CrunchyrollManager{
bool muxDesc = false; bool muxDesc = false;
if (options.MuxDescription){ if (options.MuxDescription){
if (File.Exists($"{filename}.xml")){ var descriptionPath = data.Where(a => a.Type == DownloadMediaType.Description).First().Path;
if (File.Exists(descriptionPath)){
muxDesc = true; muxDesc = true;
} else{ } else{
Console.Error.WriteLine("No xml description file found to mux description"); Console.Error.WriteLine("No xml description file found to mux description");
@ -409,9 +505,7 @@ public class CrunchyrollManager{
isMuxed = true; isMuxed = true;
} }
if (isMuxed && options.NoCleanup == false){ return (merger, isMuxed);
merger.CleanUp();
}
} }
private async Task<DownloadResponse> DownloadMediaList(CrunchyEpMeta data, CrDownloadOptions options){ private async Task<DownloadResponse> DownloadMediaList(CrunchyEpMeta data, CrDownloadOptions options){
@ -506,13 +600,18 @@ public class CrunchyrollManager{
foreach (CrunchyEpMetaData epMeta in data.Data){ foreach (CrunchyEpMetaData epMeta in data.Data){
Console.WriteLine($"Requesting: [{epMeta.MediaId}] {mediaName}"); Console.WriteLine($"Requesting: [{epMeta.MediaId}] {mediaName}");
fileDir = !string.IsNullOrEmpty(data.DownloadPath) ? data.DownloadPath : !string.IsNullOrEmpty(options.DownloadDirPath) ? options.DownloadDirPath : CfgManager.PathVIDEOS_DIR; string currentMediaId = (epMeta.MediaId.Contains(':') ? epMeta.MediaId.Split(':')[1] : epMeta.MediaId);
fileDir = CrunOptions.DownloadToTempFolder ? !string.IsNullOrEmpty(CrunOptions.DownloadTempDirPath)
? Path.Combine(CrunOptions.DownloadTempDirPath, Helpers.GetValidFolderName(currentMediaId))
: Path.Combine(CfgManager.PathTEMP_DIR, Helpers.GetValidFolderName(currentMediaId)) :
!string.IsNullOrEmpty(data.DownloadPath) ? data.DownloadPath :
!string.IsNullOrEmpty(options.DownloadDirPath) ? options.DownloadDirPath : CfgManager.PathVIDEOS_DIR;
if (!Helpers.IsValidPath(fileDir)){ if (!Helpers.IsValidPath(fileDir)){
fileDir = CfgManager.PathVIDEOS_DIR; fileDir = CfgManager.PathVIDEOS_DIR;
} }
string currentMediaId = (epMeta.MediaId.Contains(':') ? epMeta.MediaId.Split(':')[1] : epMeta.MediaId);
await CrAuth.RefreshToken(true); await CrAuth.RefreshToken(true);
@ -607,7 +706,7 @@ public class CrunchyrollManager{
variables.Add(new Variable("episode", variables.Add(new Variable("episode",
(double.TryParse(data.EpisodeNumber, NumberStyles.Any, CultureInfo.InvariantCulture, out double episodeNum) ? (object)Math.Round(episodeNum, 1) : data.AbsolutEpisodeNumberE) ?? string.Empty, false)); (double.TryParse(data.EpisodeNumber, NumberStyles.Any, CultureInfo.InvariantCulture, out double episodeNum) ? (object)Math.Round(episodeNum, 1) : data.AbsolutEpisodeNumberE) ?? string.Empty, false));
variables.Add(new Variable("seriesTitle", data.SeriesTitle ?? string.Empty, true)); variables.Add(new Variable("seriesTitle", data.SeriesTitle ?? string.Empty, true));
variables.Add(new Variable("showTitle", data.SeasonTitle ?? string.Empty, true)); variables.Add(new Variable("seasonTitle", data.SeasonTitle ?? string.Empty, true));
variables.Add(new Variable("season", !string.IsNullOrEmpty(data.Season) ? Math.Round(double.Parse(data.Season, CultureInfo.InvariantCulture), 1) : 0, false)); variables.Add(new Variable("season", !string.IsNullOrEmpty(data.Season) ? Math.Round(double.Parse(data.Season, CultureInfo.InvariantCulture), 1) : 0, false));
if (pbStreams?.Keys != null){ if (pbStreams?.Keys != null){
@ -1275,18 +1374,34 @@ public class CrunchyrollManager{
Type = DownloadMediaType.Description, Type = DownloadMediaType.Description,
Path = fullPath, Path = fullPath,
}); });
} else{
if (files.All(e => e.Type != DownloadMediaType.Description)){
files.Add(new DownloadedMedia{
Type = DownloadMediaType.Description,
Path = fullPath,
});
}
} }
Console.WriteLine($"{fileName} has been created with the description."); Console.WriteLine($"{fileName}.xml has been created with the description.");
}
var tempFolderPath = "";
if (CrunOptions.DownloadToTempFolder){
tempFolderPath = fileDir;
fileDir = !string.IsNullOrEmpty(data.DownloadPath) ? data.DownloadPath :
!string.IsNullOrEmpty(options.DownloadDirPath) ? options.DownloadDirPath : CfgManager.PathVIDEOS_DIR;
} }
return new DownloadResponse{ return new DownloadResponse{
Data = files, Data = files,
Error = dlFailed, Error = dlFailed,
FileName = fileName.Length > 0 ? (Path.IsPathRooted(fileName) ? fileName : Path.Combine(fileDir, fileName)) : "./unknown", FileName = fileName.Length > 0 ? fileName : "unknown - " + Guid.NewGuid(),
ErrorText = "", ErrorText = "",
VideoTitle = FileNameManager.ParseFileName(options.VideoTitle ?? "", variables, options.Numbers, options.Override).Last() VideoTitle = FileNameManager.ParseFileName(options.VideoTitle ?? "", variables, options.Numbers, options.Override).Last(),
FolderPath = fileDir,
TempFolderPath = tempFolderPath
}; };
} }

View File

@ -19,21 +19,22 @@ namespace CRD.Utils;
public class CfgManager{ public class CfgManager{
private static string WorkingDirectory = Directory.GetCurrentDirectory(); private static string WorkingDirectory = Directory.GetCurrentDirectory();
public static readonly string PathCrToken = WorkingDirectory + "/config/cr_token.yml"; public static readonly string PathCrToken = Path.Combine(WorkingDirectory, "config", "cr_token.yml");
public static readonly string PathCrDownloadOptions = WorkingDirectory + "/config/settings.yml"; public static readonly string PathCrDownloadOptions = Path.Combine(WorkingDirectory, "config", "settings.yml");
public static readonly string PathCrHistory = WorkingDirectory + "/config/history.json"; public static readonly string PathCrHistory = Path.Combine(WorkingDirectory, "config", "history.json");
public static readonly string PathWindowSettings= WorkingDirectory + "/config/windowSettings.json"; public static readonly string PathWindowSettings = Path.Combine(WorkingDirectory, "config", "windowSettings.json");
public static readonly string PathFFMPEG = WorkingDirectory + "/lib/ffmpeg.exe"; public static readonly string PathFFMPEG = Path.Combine(WorkingDirectory, "lib", "ffmpeg.exe");
public static readonly string PathMKVMERGE = WorkingDirectory + "/lib/mkvmerge.exe"; public static readonly string PathMKVMERGE = Path.Combine(WorkingDirectory, "lib", "mkvmerge.exe");
public static readonly string PathMP4Decrypt = WorkingDirectory + "/lib/mp4decrypt.exe"; public static readonly string PathMP4Decrypt = Path.Combine(WorkingDirectory, "lib", "mp4decrypt.exe");
public static readonly string PathWIDEVINE_DIR = WorkingDirectory + "/widevine/"; public static readonly string PathWIDEVINE_DIR = Path.Combine(WorkingDirectory, "widevine");
public static readonly string PathVIDEOS_DIR = WorkingDirectory + "/video/"; public static readonly string PathVIDEOS_DIR = Path.Combine(WorkingDirectory, "video");
public static readonly string PathFONTS_DIR = WorkingDirectory + "/video/"; public static readonly string PathTEMP_DIR = Path.Combine(WorkingDirectory, "temp");
public static readonly string PathFONTS_DIR = Path.Combine(WorkingDirectory, "video");
public static readonly string PathLogFile = WorkingDirectory + "/logfile.txt"; public static readonly string PathLogFile = Path.Combine(WorkingDirectory, "logfile.txt");
private static StreamWriter logFile; private static StreamWriter logFile;
private static bool isLogModeEnabled = false; private static bool isLogModeEnabled = false;
@ -210,6 +211,10 @@ public class CfgManager{
} }
public static void UpdateHistoryFile(){ public static void UpdateHistoryFile(){
if (!CrunchyrollManager.Instance.CrunOptions.History){
return;
}
WriteJsonToFile(PathCrHistory, CrunchyrollManager.Instance.HistoryList); WriteJsonToFile(PathCrHistory, CrunchyrollManager.Instance.HistoryList);
} }

View File

@ -49,6 +49,7 @@ public class FileNameManager{
if (overrides == null){ if (overrides == null){
return variables; return variables;
} }
foreach (var item in overrides){ foreach (var item in overrides){
int index = item.IndexOf('='); int index = item.IndexOf('=');
if (index == -1){ if (index == -1){
@ -107,4 +108,39 @@ public class FileNameManager{
return filename; return filename;
} }
public static void DeleteEmptyFolders(string rootFolderPath){
if (string.IsNullOrEmpty(rootFolderPath) || !Directory.Exists(rootFolderPath)){
Console.WriteLine("Invalid directory path.");
return;
}
DeleteEmptyFoldersRecursive(rootFolderPath, isRoot: true);
}
private static bool DeleteEmptyFoldersRecursive(string folderPath, bool isRoot = false){
bool isFolderEmpty = true;
try{
foreach (var directory in Directory.GetDirectories(folderPath)){
// Recursively delete empty subfolders
if (!DeleteEmptyFoldersRecursive(directory)){
isFolderEmpty = false;
}
}
// Check if the current folder is empty (no files and no non-deleted subfolders)
if (!isRoot && isFolderEmpty && Directory.GetFiles(folderPath).Length == 0){
Directory.Delete(folderPath);
Console.WriteLine($"Deleted empty folder: {folderPath}");
return true;
}
return false;
} catch (Exception ex){
Console.WriteLine($"An error occurred while deleting folder {folderPath}: {ex.Message}");
return false;
}
}
} }

View File

@ -9,6 +9,7 @@ using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using CRD.Downloader.Crunchyroll;
using CRD.Utils.JsonConv; using CRD.Utils.JsonConv;
using CRD.Utils.Structs; using CRD.Utils.Structs;
using CRD.Utils.Structs.Crunchyroll.Music; using CRD.Utils.Structs.Crunchyroll.Music;
@ -212,7 +213,7 @@ public class Helpers{
process.ErrorDataReceived += (sender, e) => { process.ErrorDataReceived += (sender, e) => {
if (!string.IsNullOrEmpty(e.Data)){ if (!string.IsNullOrEmpty(e.Data)){
Console.WriteLine($"{e.Data}"); Console.Error.WriteLine($"{e.Data}");
} }
}; };
@ -223,7 +224,6 @@ public class Helpers{
await process.WaitForExitAsync(); await process.WaitForExitAsync();
// Define success condition more appropriately based on the application
bool isSuccess = process.ExitCode == 0; bool isSuccess = process.ExitCode == 0;
return (IsOk: isSuccess, ErrorCode: process.ExitCode); return (IsOk: isSuccess, ErrorCode: process.ExitCode);
@ -245,7 +245,6 @@ public class Helpers{
} }
} catch (Exception ex){ } catch (Exception ex){
Console.Error.WriteLine($"Failed to delete file {filePath}. Error: {ex.Message}"); Console.Error.WriteLine($"Failed to delete file {filePath}. Error: {ex.Message}");
// Handle exceptions if you need to log them or throw
} }
} }
@ -268,7 +267,7 @@ public class Helpers{
process.ErrorDataReceived += (sender, e) => { process.ErrorDataReceived += (sender, e) => {
if (!string.IsNullOrEmpty(e.Data)){ if (!string.IsNullOrEmpty(e.Data)){
Console.WriteLine($"{e.Data}"); Console.Error.WriteLine($"{e.Data}");
} }
}; };
@ -279,7 +278,6 @@ public class Helpers{
await process.WaitForExitAsync(); await process.WaitForExitAsync();
// Define success condition more appropriately based on the application
bool isSuccess = process.ExitCode == 0; bool isSuccess = process.ExitCode == 0;
return (IsOk: isSuccess, ErrorCode: process.ExitCode); return (IsOk: isSuccess, ErrorCode: process.ExitCode);
@ -351,7 +349,7 @@ public class Helpers{
} }
public static string? ExtractNumberAfterS(string input){ public static string? ExtractNumberAfterS(string input){
// Define the 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 followed by another number
string pattern = @"\|S(\d+)(?:C(\d+))?"; string pattern = @"\|S(\d+)(?:C(\d+))?";
Match match = Regex.Match(input, pattern); Match match = Regex.Match(input, pattern);
@ -380,7 +378,6 @@ public class Helpers{
} }
} }
} catch (Exception ex){ } catch (Exception ex){
// Handle exceptions
Console.Error.WriteLine("Failed to load image: " + ex.Message); Console.Error.WriteLine("Failed to load image: " + ex.Message);
} }
@ -388,7 +385,13 @@ public class Helpers{
} }
public static Dictionary<string, List<DownloadedMedia>> GroupByLanguageWithSubtitles(List<DownloadedMedia> allMedia){ public static Dictionary<string, List<DownloadedMedia>> GroupByLanguageWithSubtitles(List<DownloadedMedia> allMedia){
//Group by language
var languageGroups = allMedia var languageGroups = allMedia
.Where(media =>
!string.IsNullOrEmpty(media.Lang.CrLocale) ||
(media.Type == DownloadMediaType.Subtitle && media.RelatedVideoDownloadMedia != null &&
!string.IsNullOrEmpty(media.RelatedVideoDownloadMedia.Lang.CrLocale))
)
.GroupBy(media => { .GroupBy(media => {
if (media.Type == DownloadMediaType.Subtitle && media.RelatedVideoDownloadMedia != null){ if (media.Type == DownloadMediaType.Subtitle && media.RelatedVideoDownloadMedia != null){
return media.RelatedVideoDownloadMedia.Lang.CrLocale; return media.RelatedVideoDownloadMedia.Lang.CrLocale;
@ -398,7 +401,34 @@ public class Helpers{
}) })
.ToDictionary(group => group.Key, group => group.ToList()); .ToDictionary(group => group.Key, group => group.ToList());
//Find and add Description media to each group
var descriptionMedia = allMedia.Where(media => media.Type == DownloadMediaType.Description).ToList();
foreach (var description in descriptionMedia){
foreach (var group in languageGroups.Values){
group.Add(description);
}
}
return languageGroups; return languageGroups;
} }
public static string GetValidFolderName(string folderName){
// Get the invalid characters for a folder name
char[] invalidChars = Path.GetInvalidFileNameChars();
// Check if the folder name contains any invalid characters
bool isValid = !folderName.Any(c => invalidChars.Contains(c));
// Check for reserved names on Windows
string[] reservedNames =["CON", "PRN", "AUX", "NUL", "COM1", "LPT1"];
bool isReservedName = reservedNames.Contains(folderName.ToUpperInvariant());
if (isValid && !isReservedName && folderName.Length <= 255){
return folderName; // Return the original folder name if it's valid
}
string uuid = Guid.NewGuid().ToString();
return uuid;
}
} }

View File

@ -36,6 +36,8 @@ public class Merger{
var audioIndex = 0; var audioIndex = 0;
var hasVideo = false; var hasVideo = false;
args.Add("-loglevel warning");
if (!options.mp3){ if (!options.mp3){
foreach (var vid in options.OnlyVid){ foreach (var vid in options.OnlyVid){
if (!hasVideo || options.KeepAllVideos == true){ if (!hasVideo || options.KeepAllVideos == true){
@ -325,7 +327,7 @@ public class Merger{
allMediaFiles.ForEach(file => Helpers.DeleteFile(file.Path)); allMediaFiles.ForEach(file => Helpers.DeleteFile(file.Path));
allMediaFiles.ForEach(file => Helpers.DeleteFile(file.Path + ".resume")); allMediaFiles.ForEach(file => Helpers.DeleteFile(file.Path + ".resume"));
options.Description?.ForEach(chapter => Helpers.DeleteFile(chapter.Path)); options.Description?.ForEach(description => Helpers.DeleteFile(description.Path));
// Delete chapter files if any // Delete chapter files if any
options.Chapters?.ForEach(chapter => Helpers.DeleteFile(chapter.Path)); options.Chapters?.ForEach(chapter => Helpers.DeleteFile(chapter.Path));

View File

@ -67,6 +67,14 @@ public class SonarrClient{
} }
} }
public async Task RefreshSonarrLite(){
await CheckSonarrSettings();
if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties is{ SonarrEnabled: true }){
SonarrSeries = await GetSeries();
CrunchyrollManager.Instance.History.MatchHistorySeriesWithSonarr(true);
}
}
public void SetApiUrl(){ public void SetApiUrl(){
if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null) properties = CrunchyrollManager.Instance.CrunOptions.SonarrProperties; if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null) properties = CrunchyrollManager.Instance.CrunOptions.SonarrProperties;

View File

@ -180,6 +180,12 @@ public class CrDownloadOptions{
[YamlMember(Alias = "download_dir_path", ApplyNamingConventions = false)] [YamlMember(Alias = "download_dir_path", ApplyNamingConventions = false)]
public string? DownloadDirPath{ get; set; } public string? DownloadDirPath{ get; set; }
[YamlMember(Alias = "download_temp_dir_path", ApplyNamingConventions = false)]
public string? DownloadTempDirPath{ get; set; }
[YamlMember(Alias = "download_to_temp_folder", ApplyNamingConventions = false)]
public bool DownloadToTempFolder{ get; set; }
[YamlMember(Alias = "history_page_properties", ApplyNamingConventions = false)] [YamlMember(Alias = "history_page_properties", ApplyNamingConventions = false)]
public HistoryPageProperties? HistoryPageProperties{ get; set; } public HistoryPageProperties? HistoryPageProperties{ get; set; }

View File

@ -68,6 +68,9 @@ public struct DownloadResponse{
public List<DownloadedMedia> Data{ get; set; } public List<DownloadedMedia> Data{ get; set; }
public string FileName{ get; set; } public string FileName{ get; set; }
public string FolderPath{ get; set; }
public string TempFolderPath{ get; set; }
public string VideoTitle{ get; set; } public string VideoTitle{ get; set; }
public bool Error{ get; set; } public bool Error{ get; set; }
public string ErrorText{ get; set; } public string ErrorText{ get; set; }
@ -101,12 +104,14 @@ public class StringItem{
public string stringValue{ get; set; } public string stringValue{ get; set; }
} }
public class WindowSettings{ public class WindowSettings
public double Width{ get; set; } {
public double Height{ get; set; } public double Width { get; set; }
public int ScreenIndex{ get; set; } public double Height { get; set; }
public int PosX{ get; set; } public int ScreenIndex { get; set; }
public int PosY{ get; set; } public int PosX { get; set; }
public int PosY { get; set; }
public bool IsMaximized { get; set; }
} }
public class ToastMessage(string message, ToastType type, int i){ public class ToastMessage(string message, ToastType type, int i){

View File

@ -4,6 +4,7 @@ using System.Net.Http;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia; using Avalonia;
using Avalonia.Controls.Chrome;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
@ -56,7 +57,7 @@ public partial class MainWindowViewModel : ViewModelBase{
if (CrunchyrollManager.Instance.CrunOptions.AccentColor != null){ if (CrunchyrollManager.Instance.CrunOptions.AccentColor != null){
_faTheme.CustomAccentColor = Color.Parse(CrunchyrollManager.Instance.CrunOptions.AccentColor); _faTheme.CustomAccentColor = Color.Parse(CrunchyrollManager.Instance.CrunOptions.AccentColor);
} }
if (CrunchyrollManager.Instance.CrunOptions.Theme == "System"){ if (CrunchyrollManager.Instance.CrunOptions.Theme == "System"){
_faTheme.PreferSystemTheme = true; _faTheme.PreferSystemTheme = true;
} else if (CrunchyrollManager.Instance.CrunOptions.Theme == "Dark"){ } else if (CrunchyrollManager.Instance.CrunOptions.Theme == "Dark"){

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.IO;
using System.Linq; using System.Linq;
using System.Net.Mime; using System.Net.Mime;
using System.Reflection; using System.Reflection;
@ -20,6 +21,7 @@ using CRD.Utils;
using CRD.Utils.CustomList; using CRD.Utils.CustomList;
using CRD.Utils.Sonarr; using CRD.Utils.Sonarr;
using CRD.Utils.Structs; using CRD.Utils.Structs;
using CRD.Utils.Structs.History;
using FluentAvalonia.Styling; using FluentAvalonia.Styling;
namespace CRD.ViewModels; namespace CRD.ViewModels;
@ -42,10 +44,12 @@ public partial class SettingsPageViewModel : ViewModelBase{
[ObservableProperty] [ObservableProperty]
private bool _includeSignSubs; private bool _includeSignSubs;
[ObservableProperty] [ObservableProperty]
private bool _includeCcSubs; private bool _includeCcSubs;
[ObservableProperty]
private bool _downloadToTempFolder;
[ObservableProperty] [ObservableProperty]
private ComboBoxItem _selectedScaledBorderAndShadow; private ComboBoxItem _selectedScaledBorderAndShadow;
@ -57,13 +61,13 @@ public partial class SettingsPageViewModel : ViewModelBase{
[ObservableProperty] [ObservableProperty]
private bool _muxToMp4; private bool _muxToMp4;
[ObservableProperty] [ObservableProperty]
private bool _syncTimings; private bool _syncTimings;
[ObservableProperty] [ObservableProperty]
private bool _defaultSubSigns; private bool _defaultSubSigns;
[ObservableProperty] [ObservableProperty]
private bool _defaultSubForcedDisplay; private bool _defaultSubForcedDisplay;
@ -72,7 +76,7 @@ public partial class SettingsPageViewModel : ViewModelBase{
[ObservableProperty] [ObservableProperty]
private bool _downloadVideoForEveryDub; private bool _downloadVideoForEveryDub;
[ObservableProperty] [ObservableProperty]
private bool _keepDubsSeparate; private bool _keepDubsSeparate;
@ -81,7 +85,7 @@ public partial class SettingsPageViewModel : ViewModelBase{
[ObservableProperty] [ObservableProperty]
private bool _history; private bool _history;
[ObservableProperty] [ObservableProperty]
private bool _historyAddSpecials; private bool _historyAddSpecials;
@ -126,7 +130,7 @@ public partial class SettingsPageViewModel : ViewModelBase{
[ObservableProperty] [ObservableProperty]
private ComboBoxItem _selectedDescriptionLang; private ComboBoxItem _selectedDescriptionLang;
[ObservableProperty] [ObservableProperty]
private string _selectedDubs = "ja-JP"; private string _selectedDubs = "ja-JP";
@ -326,6 +330,9 @@ public partial class SettingsPageViewModel : ViewModelBase{
[ObservableProperty] [ObservableProperty]
private string _downloadDirPath; private string _downloadDirPath;
[ObservableProperty]
private string _tempDownloadDirPath;
private readonly FluentAvaloniaTheme _faTheme; private readonly FluentAvaloniaTheme _faTheme;
private bool settingsLoaded; private bool settingsLoaded;
@ -349,6 +356,7 @@ public partial class SettingsPageViewModel : ViewModelBase{
CrDownloadOptions options = CrunchyrollManager.Instance.CrunOptions; CrDownloadOptions options = CrunchyrollManager.Instance.CrunOptions;
DownloadDirPath = string.IsNullOrEmpty(options.DownloadDirPath) ? CfgManager.PathVIDEOS_DIR : options.DownloadDirPath; DownloadDirPath = string.IsNullOrEmpty(options.DownloadDirPath) ? CfgManager.PathVIDEOS_DIR : options.DownloadDirPath;
TempDownloadDirPath = string.IsNullOrEmpty(options.DownloadTempDirPath) ? CfgManager.PathTEMP_DIR : options.DownloadTempDirPath;
ComboBoxItem? descriptionLang = DescriptionLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == options.DescriptionLang) ?? null; ComboBoxItem? descriptionLang = DescriptionLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == options.DescriptionLang) ?? null;
SelectedDescriptionLang = descriptionLang ?? DescriptionLangList[0]; SelectedDescriptionLang = descriptionLang ?? DescriptionLangList[0];
@ -381,7 +389,7 @@ public partial class SettingsPageViewModel : ViewModelBase{
foreach (var listBoxItem in dubLang){ foreach (var listBoxItem in dubLang){
SelectedDubLang.Add(listBoxItem); SelectedDubLang.Add(listBoxItem);
} }
var props = options.SonarrProperties; var props = options.SonarrProperties;
if (props != null){ if (props != null){
@ -407,6 +415,7 @@ public partial class SettingsPageViewModel : ViewModelBase{
DownloadVideo = !options.Novids; DownloadVideo = !options.Novids;
DownloadAudio = !options.Noaudio; DownloadAudio = !options.Noaudio;
DownloadVideoForEveryDub = !options.DlVideoOnce; DownloadVideoForEveryDub = !options.DlVideoOnce;
DownloadToTempFolder = options.DownloadToTempFolder;
KeepDubsSeparate = options.KeepDubsSeperate; KeepDubsSeparate = options.KeepDubsSeperate;
DownloadChapters = options.Chapters; DownloadChapters = options.Chapters;
MuxToMp4 = options.Mp4; MuxToMp4 = options.Mp4;
@ -445,10 +454,10 @@ public partial class SettingsPageViewModel : ViewModelBase{
FfmpegOptions.Add(new MuxingParam(){ ParamValue = ffmpegParam }); FfmpegOptions.Add(new MuxingParam(){ ParamValue = ffmpegParam });
} }
} }
var dubs = SelectedDubLang.Select(item => item.Content?.ToString()); var dubs = SelectedDubLang.Select(item => item.Content?.ToString());
SelectedDubs = string.Join(", ", dubs) ?? ""; SelectedDubs = string.Join(", ", dubs) ?? "";
var subs = SelectedSubLang.Select(item => item.Content?.ToString()); var subs = SelectedSubLang.Select(item => item.Content?.ToString());
SelectedSubs = string.Join(", ", subs) ?? ""; SelectedSubs = string.Join(", ", subs) ?? "";
@ -466,6 +475,7 @@ public partial class SettingsPageViewModel : ViewModelBase{
return; return;
} }
CrunchyrollManager.Instance.CrunOptions.DownloadToTempFolder = DownloadToTempFolder;
CrunchyrollManager.Instance.CrunOptions.DefaultSubSigns = DefaultSubSigns; CrunchyrollManager.Instance.CrunOptions.DefaultSubSigns = DefaultSubSigns;
CrunchyrollManager.Instance.CrunOptions.DefaultSubForcedDisplay = DefaultSubForcedDisplay; CrunchyrollManager.Instance.CrunOptions.DefaultSubForcedDisplay = DefaultSubForcedDisplay;
CrunchyrollManager.Instance.CrunOptions.IncludeVideoDescription = IncludeEpisodeDescription; CrunchyrollManager.Instance.CrunOptions.IncludeVideoDescription = IncludeEpisodeDescription;
@ -480,12 +490,12 @@ public partial class SettingsPageViewModel : ViewModelBase{
CrunchyrollManager.Instance.CrunOptions.Mp4 = MuxToMp4; CrunchyrollManager.Instance.CrunOptions.Mp4 = MuxToMp4;
CrunchyrollManager.Instance.CrunOptions.SyncTiming = SyncTimings; CrunchyrollManager.Instance.CrunOptions.SyncTiming = SyncTimings;
CrunchyrollManager.Instance.CrunOptions.SkipSubsMux = SkipSubMux; CrunchyrollManager.Instance.CrunOptions.SkipSubsMux = SkipSubMux;
CrunchyrollManager.Instance.CrunOptions.Numbers = Math.Clamp((int)(LeadingNumbers ?? 0),0,10); CrunchyrollManager.Instance.CrunOptions.Numbers = Math.Clamp((int)(LeadingNumbers ?? 0), 0, 10);
CrunchyrollManager.Instance.CrunOptions.FileName = FileName; CrunchyrollManager.Instance.CrunOptions.FileName = FileName;
CrunchyrollManager.Instance.CrunOptions.IncludeSignsSubs = IncludeSignSubs; CrunchyrollManager.Instance.CrunOptions.IncludeSignsSubs = IncludeSignSubs;
CrunchyrollManager.Instance.CrunOptions.IncludeCcSubs = IncludeCcSubs; CrunchyrollManager.Instance.CrunOptions.IncludeCcSubs = IncludeCcSubs;
CrunchyrollManager.Instance.CrunOptions.DownloadSpeedLimit = Math.Clamp((int)(DownloadSpeed ?? 0),0,1000000000); CrunchyrollManager.Instance.CrunOptions.DownloadSpeedLimit = Math.Clamp((int)(DownloadSpeed ?? 0), 0, 1000000000);
CrunchyrollManager.Instance.CrunOptions.SimultaneousDownloads = Math.Clamp((int)(SimultaneousDownloads ?? 0),1,10); CrunchyrollManager.Instance.CrunOptions.SimultaneousDownloads = Math.Clamp((int)(SimultaneousDownloads ?? 0), 1, 10);
CrunchyrollManager.Instance.CrunOptions.SubsAddScaledBorder = GetScaledBorderAndShadowSelection(); CrunchyrollManager.Instance.CrunOptions.SubsAddScaledBorder = GetScaledBorderAndShadowSelection();
@ -520,7 +530,7 @@ public partial class SettingsPageViewModel : ViewModelBase{
} }
CrunchyrollManager.Instance.CrunOptions.DubLang = dubLangs; CrunchyrollManager.Instance.CrunOptions.DubLang = dubLangs;
CrunchyrollManager.Instance.CrunOptions.QualityAudio = SelectedAudioQuality?.Content + ""; CrunchyrollManager.Instance.CrunOptions.QualityAudio = SelectedAudioQuality?.Content + "";
CrunchyrollManager.Instance.CrunOptions.QualityVideo = SelectedVideoQuality?.Content + ""; CrunchyrollManager.Instance.CrunOptions.QualityVideo = SelectedVideoQuality?.Content + "";
CrunchyrollManager.Instance.CrunOptions.Theme = CurrentAppTheme?.Content + ""; CrunchyrollManager.Instance.CrunOptions.Theme = CurrentAppTheme?.Content + "";
@ -592,7 +602,7 @@ public partial class SettingsPageViewModel : ViewModelBase{
return ScaledBorderAndShadow[0]; return ScaledBorderAndShadow[0];
} }
} }
[RelayCommand] [RelayCommand]
public void AddMkvMergeParam(){ public void AddMkvMergeParam(){
MkvMergeOptions.Add(new MuxingParam(){ ParamValue = MkvMergeOption }); MkvMergeOptions.Add(new MuxingParam(){ ParamValue = MkvMergeOption });
@ -621,22 +631,38 @@ public partial class SettingsPageViewModel : ViewModelBase{
[RelayCommand] [RelayCommand]
public async Task OpenFolderDialogAsync(){ public async Task OpenFolderDialogAsync(){
await OpenFolderDialogAsyncInternal(
pathSetter: (path) => CrunchyrollManager.Instance.CrunOptions.DownloadDirPath = path,
pathGetter: () => CrunchyrollManager.Instance.CrunOptions.DownloadDirPath,
defaultPath: CfgManager.PathVIDEOS_DIR
);
}
[RelayCommand]
public async Task OpenFolderDialogTempFolderAsync(){
await OpenFolderDialogAsyncInternal(
pathSetter: (path) => CrunchyrollManager.Instance.CrunOptions.DownloadTempDirPath = path,
pathGetter: () => CrunchyrollManager.Instance.CrunOptions.DownloadTempDirPath,
defaultPath: CfgManager.PathTEMP_DIR
);
}
private async Task OpenFolderDialogAsyncInternal(Action<string> pathSetter, Func<string> pathGetter, string defaultPath){
if (_storageProvider == null){ if (_storageProvider == null){
Console.Error.WriteLine("StorageProvider must be set before using the dialog."); Console.Error.WriteLine("StorageProvider must be set before using the dialog.");
throw new InvalidOperationException("StorageProvider must be set before using the dialog."); throw new InvalidOperationException("StorageProvider must be set before using the dialog.");
} }
var result = await _storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions{ var result = await _storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions{
Title = "Select Folder" Title = "Select Folder"
}); });
if (result.Count > 0){ if (result.Count > 0){
var selectedFolder = result[0]; var selectedFolder = result[0];
// Do something with the selected folder path
Console.WriteLine($"Selected folder: {selectedFolder.Path.LocalPath}"); Console.WriteLine($"Selected folder: {selectedFolder.Path.LocalPath}");
CrunchyrollManager.Instance.CrunOptions.DownloadDirPath = selectedFolder.Path.LocalPath; pathSetter(selectedFolder.Path.LocalPath);
DownloadDirPath = string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.DownloadDirPath) ? CfgManager.PathVIDEOS_DIR : CrunchyrollManager.Instance.CrunOptions.DownloadDirPath; var finalPath = string.IsNullOrEmpty(pathGetter()) ? defaultPath : pathGetter();
pathSetter(finalPath);
CfgManager.WriteSettingsToFile(); CfgManager.WriteSettingsToFile();
} }
} }
@ -696,27 +722,50 @@ public partial class SettingsPageViewModel : ViewModelBase{
} }
private void Changes(object? sender, NotifyCollectionChangedEventArgs e){ private void Changes(object? sender, NotifyCollectionChangedEventArgs e){
UpdateSettings(); UpdateSettings();
var dubs = SelectedDubLang.Select(item => item.Content?.ToString()); var dubs = SelectedDubLang.Select(item => item.Content?.ToString());
SelectedDubs = string.Join(", ", dubs) ?? ""; SelectedDubs = string.Join(", ", dubs) ?? "";
var subs = SelectedSubLang.Select(item => item.Content?.ToString()); var subs = SelectedSubLang.Select(item => item.Content?.ToString());
SelectedSubs = string.Join(", ", subs) ?? ""; SelectedSubs = string.Join(", ", subs) ?? "";
} }
protected override void OnPropertyChanged(PropertyChangedEventArgs e){ protected override void OnPropertyChanged(PropertyChangedEventArgs e){
base.OnPropertyChanged(e); base.OnPropertyChanged(e);
if (e.PropertyName is nameof(SelectedDubs) or nameof(SelectedSubs) or nameof(CustomAccentColor) or nameof(ListBoxColor) or nameof(CurrentAppTheme) or nameof(UseCustomAccent) or nameof(LogMode)){ if (e.PropertyName is nameof(SelectedDubs) or nameof(SelectedSubs) or nameof(CustomAccentColor) or nameof(ListBoxColor) or nameof(CurrentAppTheme) or nameof(UseCustomAccent) or nameof(LogMode)){
return; return;
} }
UpdateSettings(); UpdateSettings();
if (e.PropertyName is nameof(History)){
if (CrunchyrollManager.Instance.CrunOptions.History){
if (File.Exists(CfgManager.PathCrHistory)){
var decompressedJson = CfgManager.DecompressJsonFile(CfgManager.PathCrHistory);
if (!string.IsNullOrEmpty(decompressedJson)){
CrunchyrollManager.Instance.HistoryList = Helpers.Deserialize<ObservableCollection<HistorySeries>>(decompressedJson, CrunchyrollManager.Instance.SettingsJsonSerializerSettings) ??
new ObservableCollection<HistorySeries>();
foreach (var historySeries in CrunchyrollManager.Instance.HistoryList){
historySeries.Init();
foreach (var historySeriesSeason in historySeries.Seasons){
historySeriesSeason.Init();
}
}
} else{
CrunchyrollManager.Instance.HistoryList =[];
}
}
_ = SonarrClient.Instance.RefreshSonarrLite();
} else{
CrunchyrollManager.Instance.HistoryList =[];
}
}
} }
partial void OnLogModeChanged(bool value){ partial void OnLogModeChanged(bool value){
UpdateSettings(); UpdateSettings();
if (value){ if (value){
@ -725,7 +774,6 @@ public partial class SettingsPageViewModel : ViewModelBase{
CfgManager.DisableLogMode(); CfgManager.DisableLogMode();
} }
} }
} }
public class MuxingParam{ public class MuxingParam{

View File

@ -30,7 +30,7 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Background="#2a2a2a"></StackPanel> <StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Background="{DynamicResource ControlAltFillColorQuarternary}"></StackPanel>
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal"> <StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal">

View File

@ -14,7 +14,7 @@
<Design.DataContext> <Design.DataContext>
<vm:MainWindowViewModel /> <vm:MainWindowViewModel />
</Design.DataContext> </Design.DataContext>
<Grid Name="mainGrid"> <Grid Name="MainGrid">
<ContentControl x:Name="MainContent"> <ContentControl x:Name="MainContent">
<Grid RowDefinitions="Auto, *"> <Grid RowDefinitions="Auto, *">

View File

@ -3,9 +3,12 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using CRD.Utils; using CRD.Utils;
using CRD.Utils.Files;
using CRD.Utils.Structs; using CRD.Utils.Structs;
using CRD.Utils.Updater; using CRD.Utils.Updater;
using CRD.ViewModels; using CRD.ViewModels;
@ -48,17 +51,29 @@ public partial class MainWindow : AppWindow{
private object selectedNavVieItem; private object selectedNavVieItem;
private const int TitleBarHeightAdjustment = 31;
private PixelPoint _restorePosition;
private Size _restoreSize;
public MainWindow(){ public MainWindow(){
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
InitializeComponent(); InitializeComponent();
ExtendClientAreaTitleBarHeightHint = TitleBarHeightAdjustment;
TitleBar.Height = TitleBarHeightAdjustment;
TitleBar.ExtendsContentIntoTitleBar = true;
TitleBar.TitleBarHitTestType = TitleBarHitTestType.Complex;
Opened += OnOpened; Opened += OnOpened;
Closing += OnClosing; Closing += OnClosing;
TitleBar.ExtendsContentIntoTitleBar = true; PropertyChanged += OnWindowStateChanged;
TitleBar.TitleBarHitTestType = TitleBarHitTestType.Complex;
PositionChanged += OnPositionChanged;
SizeChanged += OnSizeChanged;
//select first element as default //select first element as default
var nv = this.FindControl<NavigationView>("NavView"); var nv = this.FindControl<NavigationView>("NavView");
nv.SelectedItem = nv.MenuItems.ElementAt(0); nv.SelectedItem = nv.MenuItems.ElementAt(0);
@ -177,26 +192,29 @@ public partial class MainWindow : AppWindow{
if (File.Exists(CfgManager.PathWindowSettings)){ if (File.Exists(CfgManager.PathWindowSettings)){
var settings = JsonConvert.DeserializeObject<WindowSettings>(File.ReadAllText(CfgManager.PathWindowSettings)); var settings = JsonConvert.DeserializeObject<WindowSettings>(File.ReadAllText(CfgManager.PathWindowSettings));
if (settings != null){ if (settings != null){
Width = settings.Width;
Height = settings.Height;
var screens = Screens.All; var screens = Screens.All;
if (settings.ScreenIndex >= 0 && settings.ScreenIndex < screens.Count){ if (settings.ScreenIndex >= 0 && settings.ScreenIndex < screens.Count){
var screen = screens[settings.ScreenIndex]; var screen = screens[settings.ScreenIndex];
var screenBounds = screen.Bounds; var screenBounds = screen.Bounds;
var topLeft = screenBounds.TopLeft; // Restore the position first
var bottomRight = screenBounds.BottomRight; Position = new PixelPoint(settings.PosX, settings.PosY + TitleBarHeightAdjustment);
if (settings.PosX >= topLeft.X && settings.PosX <= bottomRight.X - Width && // Restore the size
settings.PosY >= topLeft.Y && settings.PosY <= bottomRight.Y - Height){ Width = settings.Width;
Position = new PixelPoint(settings.PosX, settings.PosY); Height = settings.Height - TitleBarHeightAdjustment;
} else{
Position = new PixelPoint(topLeft.X, topLeft.Y + 31); // Set restore size and position for non-maximized state
} _restoreSize = new Size(settings.Width, settings.Height);
} else{ _restorePosition = new PixelPoint(settings.PosX, settings.PosY + TitleBarHeightAdjustment);
var primaryScreen = screens?[0].Bounds ?? new PixelRect(0, 0, 1000, 600); // Default size if no screens
Position = new PixelPoint(primaryScreen.TopLeft.X, primaryScreen.TopLeft.Y + 31); // Ensure the window is on the correct screen before maximizing
Position = new PixelPoint(settings.PosX, settings.PosY+ TitleBarHeightAdjustment);
}
if (settings.IsMaximized){
// Maximize the window after setting its position on the correct screen
WindowState = WindowState.Maximized;
} }
} }
} }
@ -214,13 +232,37 @@ public partial class MainWindow : AppWindow{
} }
var settings = new WindowSettings{ var settings = new WindowSettings{
Width = Width, Width = this.WindowState == WindowState.Maximized ? _restoreSize.Width : Width,
Height = Height, Height = this.WindowState == WindowState.Maximized ? _restoreSize.Height : Height,
ScreenIndex = screenIndex, ScreenIndex = screenIndex,
PosX = Position.X, PosX = this.WindowState == WindowState.Maximized ? _restorePosition.X : Position.X,
PosY = Position.Y PosY = this.WindowState == WindowState.Maximized ? _restorePosition.Y : Position.Y,
IsMaximized = this.WindowState == WindowState.Maximized
}; };
File.WriteAllText(CfgManager.PathWindowSettings, JsonConvert.SerializeObject(settings, Formatting.Indented)); File.WriteAllText(CfgManager.PathWindowSettings, JsonConvert.SerializeObject(settings, Formatting.Indented));
} }
private void OnWindowStateChanged(object sender, AvaloniaPropertyChangedEventArgs e){
if (e.Property == Window.WindowStateProperty){
if (WindowState == WindowState.Normal){
// When the window is restored to normal, use the stored restore size and position
Width = _restoreSize.Width;
Height = _restoreSize.Height;
Position = _restorePosition;
}
}
}
private void OnPositionChanged(object sender, PixelPointEventArgs e){
if (WindowState == WindowState.Normal){
_restorePosition = e.Point;
}
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e){
if (WindowState == WindowState.Normal){
_restoreSize = e.NewSize;
}
}
} }

View File

@ -12,7 +12,7 @@
<Design.DataContext> <Design.DataContext>
<vm:SettingsPageViewModel /> <vm:SettingsPageViewModel />
</Design.DataContext> </Design.DataContext>
<ScrollViewer Padding="20 20 20 0"> <ScrollViewer Padding="20 20 20 0">
<StackPanel Spacing="8"> <StackPanel Spacing="8">
@ -50,7 +50,7 @@
<Border BorderThickness="1" Background="{DynamicResource ComboBoxDropDownBackground}"> <Border BorderThickness="1" Background="{DynamicResource ComboBoxDropDownBackground}">
<ListBox x:Name="ListBoxDubsSelection" SelectionMode="Multiple,Toggle" Width="210" <ListBox x:Name="ListBoxDubsSelection" SelectionMode="Multiple,Toggle" Width="210"
MaxHeight="400" MaxHeight="400"
ItemsSource="{Binding DubLangList}" ItemsSource="{Binding DubLangList}"
SelectedItems="{Binding SelectedDubLang}"> SelectedItems="{Binding SelectedDubLang}">
</ListBox> </ListBox>
</Border> </Border>
@ -125,7 +125,7 @@
<CheckBox IsChecked="{Binding IncludeSignSubs}"> </CheckBox> <CheckBox IsChecked="{Binding IncludeSignSubs}"> </CheckBox>
</controls:SettingsExpanderItem.Footer> </controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem> </controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Include CC Subtitles "> <controls:SettingsExpanderItem Content="Include CC Subtitles ">
<controls:SettingsExpanderItem.Footer> <controls:SettingsExpanderItem.Footer>
<CheckBox IsChecked="{Binding IncludeCcSubs}"> </CheckBox> <CheckBox IsChecked="{Binding IncludeCcSubs}"> </CheckBox>
@ -149,13 +149,13 @@
</ComboBox> </ComboBox>
</controls:SettingsExpanderItem.Footer> </controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem> </controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="History Add Specials" Description="Add specials to the queue if they weren't downloaded before"> <controls:SettingsExpanderItem Content="History Add Specials" Description="Add specials to the queue if they weren't downloaded before">
<controls:SettingsExpanderItem.Footer> <controls:SettingsExpanderItem.Footer>
<CheckBox IsChecked="{Binding HistoryAddSpecials}"> </CheckBox> <CheckBox IsChecked="{Binding HistoryAddSpecials}"> </CheckBox>
</controls:SettingsExpanderItem.Footer> </controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem> </controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="History Missing/New Count from Sonarr" Description="The missing count (number in the orange corner) will count the episodes missing from sonarr"> <controls:SettingsExpanderItem Content="History Missing/New Count from Sonarr" Description="The missing count (number in the orange corner) will count the episodes missing from sonarr">
<controls:SettingsExpanderItem.Footer> <controls:SettingsExpanderItem.Footer>
<CheckBox IsChecked="{Binding HistoryCountSonarr}"> </CheckBox> <CheckBox IsChecked="{Binding HistoryCountSonarr}"> </CheckBox>
@ -168,7 +168,7 @@
IconSource="Download" IconSource="Download"
Description="Adjust download settings" Description="Adjust download settings"
IsExpanded="False"> IsExpanded="False">
<controls:SettingsExpanderItem Content="Max Download Speed" <controls:SettingsExpanderItem Content="Max Download Speed"
Description="Download in Kb/s - 0 is full speed"> Description="Download in Kb/s - 0 is full speed">
<controls:SettingsExpanderItem.Footer> <controls:SettingsExpanderItem.Footer>
@ -178,7 +178,7 @@
HorizontalAlignment="Stretch" /> HorizontalAlignment="Stretch" />
</controls:SettingsExpanderItem.Footer> </controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem> </controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Stream Endpoint "> <controls:SettingsExpanderItem Content="Stream Endpoint ">
<controls:SettingsExpanderItem.Footer> <controls:SettingsExpanderItem.Footer>
@ -189,6 +189,25 @@
</controls:SettingsExpanderItem.Footer> </controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem> </controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Use Temp Download Folder">
<controls:SettingsExpanderItem.Footer>
<StackPanel Orientation="Horizontal">
<TextBlock IsVisible="{Binding DownloadToTempFolder}" FontSize="15" Opacity="0.8" TextWrapping="NoWrap" Text="{Binding TempDownloadDirPath, Mode=OneWay}" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center" />
<Button IsVisible="{Binding DownloadToTempFolder}" Margin="10 0 10 0" FontStyle="Italic"
VerticalAlignment="Center"
Command="{Binding OpenFolderDialogTempFolderAsync}">
<ToolTip.Tip>
<TextBlock Text="Set Download Directory" FontSize="15" />
</ToolTip.Tip>
<StackPanel Orientation="Horizontal">
<controls:SymbolIcon Symbol="Folder" FontSize="18" />
</StackPanel>
</Button>
<CheckBox IsChecked="{Binding DownloadToTempFolder}"> </CheckBox>
</StackPanel>
</controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Download Folder"> <controls:SettingsExpanderItem Content="Download Folder">
<controls:SettingsExpanderItem.Footer> <controls:SettingsExpanderItem.Footer>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
@ -229,7 +248,7 @@
<CheckBox IsChecked="{Binding DownloadVideoForEveryDub}"> </CheckBox> <CheckBox IsChecked="{Binding DownloadVideoForEveryDub}"> </CheckBox>
<CheckBox IsVisible="{Binding DownloadVideoForEveryDub}" Content="Keep files separate" IsChecked="{Binding KeepDubsSeparate}"> </CheckBox> <CheckBox IsVisible="{Binding DownloadVideoForEveryDub}" Content="Keep files separate" IsChecked="{Binding KeepDubsSeparate}"> </CheckBox>
</StackPanel> </StackPanel>
</controls:SettingsExpanderItem.Footer> </controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem> </controls:SettingsExpanderItem>
@ -283,7 +302,7 @@
</controls:SettingsExpanderItem> </controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Filename" <controls:SettingsExpanderItem Content="Filename"
Description="${showTitle} ${seriesTitle} ${title} ${season} ${episode} ${height} ${width}"> Description="${seriesTitle} ${seasonTitle} ${title} ${season} ${episode} ${height} ${width}">
<controls:SettingsExpanderItem.Footer> <controls:SettingsExpanderItem.Footer>
<TextBox Name="FileNameTextBox" HorizontalAlignment="Left" MinWidth="250" <TextBox Name="FileNameTextBox" HorizontalAlignment="Left" MinWidth="250"
Text="{Binding FileName}" /> Text="{Binding FileName}" />
@ -332,10 +351,10 @@
</ComboBox> </ComboBox>
<CheckBox Content="Forced Display" IsChecked="{Binding DefaultSubForcedDisplay}"> </CheckBox> <CheckBox Content="Forced Display" IsChecked="{Binding DefaultSubForcedDisplay}"> </CheckBox>
</StackPanel> </StackPanel>
</controls:SettingsExpanderItem.Footer> </controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem> </controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Default Subtitle Signs" Description="Will set the signs subtitle as default instead"> <controls:SettingsExpanderItem Content="Default Subtitle Signs" Description="Will set the signs subtitle as default instead">
<controls:SettingsExpanderItem.Footer> <controls:SettingsExpanderItem.Footer>
<CheckBox IsChecked="{Binding DefaultSubSigns}"> </CheckBox> <CheckBox IsChecked="{Binding DefaultSubSigns}"> </CheckBox>
@ -343,7 +362,7 @@
</controls:SettingsExpanderItem> </controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="File title" <controls:SettingsExpanderItem Content="File title"
Description="${showTitle} ${seriesTitle} ${title} ${season} ${episode} ${height} ${width}"> Description="${seriesTitle} ${seasonTitle} ${title} ${season} ${episode} ${height} ${width}">
<controls:SettingsExpanderItem.Footer> <controls:SettingsExpanderItem.Footer>
<TextBox HorizontalAlignment="Left" MinWidth="250" <TextBox HorizontalAlignment="Left" MinWidth="250"
Text="{Binding FileTitle}" /> Text="{Binding FileTitle}" />
@ -355,8 +374,8 @@
<CheckBox IsChecked="{Binding IncludeEpisodeDescription}"> </CheckBox> <CheckBox IsChecked="{Binding IncludeEpisodeDescription}"> </CheckBox>
</controls:SettingsExpanderItem.Footer> </controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem> </controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Episode description Language" > <controls:SettingsExpanderItem Content="Episode description Language">
<controls:SettingsExpanderItem.Footer> <controls:SettingsExpanderItem.Footer>
<ComboBox HorizontalContentAlignment="Center" MinWidth="210" MaxDropDownHeight="400" <ComboBox HorizontalContentAlignment="Center" MinWidth="210" MaxDropDownHeight="400"
ItemsSource="{Binding DescriptionLangList}" ItemsSource="{Binding DescriptionLangList}"