mirror of
https://github.com/Crunchy-DL/Crunchy-Downloader.git
synced 2026-04-21 08:51:59 +00:00
Add - Added **Shaka Packager support**, which can be used as an alternative to MP4Decrypt
Add - Added a **toggle for Downloaded Mark** in the history Add - Proxy username/password and socks Chg - Changed the folder where the **auto-update** is downloaded to Chg - Changed **Encoding presets FPS** to default to 23.976 FPS Chg - Changed the **FPS input field** in Encoding presets to a text field, allowing formats like `24000/1001` for precise 23.976 FPS Fix - Fixed **Encoding presets** not including all available dubs and subs after encoding Fix - Fixed **Encoding presets additional parameters** with spaces, which now work correctly without needing escaped quotes (`\"`) Fix - Fixed **crash ** when adding episode to queue from history
This commit is contained in:
parent
51e3102503
commit
26d54ceb75
23 changed files with 329 additions and 171 deletions
|
|
@ -164,41 +164,12 @@ public class CrunchyrollManager{
|
||||||
CfgManager.DisableLogMode();
|
CfgManager.DisableLogMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CfgManager.CheckIfFileExists(CfgManager.PathCrToken)){
|
|
||||||
Token = CfgManager.DeserializeFromFile<CrToken>(CfgManager.PathCrToken);
|
|
||||||
await CrAuth.LoginWithToken();
|
|
||||||
} else{
|
|
||||||
await CrAuth.AuthAnonymous();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CrunOptions.History){
|
|
||||||
if (File.Exists(CfgManager.PathCrHistory)){
|
|
||||||
var decompressedJson = CfgManager.DecompressJsonFile(CfgManager.PathCrHistory);
|
|
||||||
if (!string.IsNullOrEmpty(decompressedJson)){
|
|
||||||
HistoryList = Helpers.Deserialize<ObservableCollection<HistorySeries>>(decompressedJson, CrunchyrollManager.Instance.SettingsJsonSerializerSettings) ?? new ObservableCollection<HistorySeries>();
|
|
||||||
|
|
||||||
foreach (var historySeries in HistoryList){
|
|
||||||
historySeries.Init();
|
|
||||||
foreach (var historySeriesSeason in historySeries.Seasons){
|
|
||||||
historySeriesSeason.Init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else{
|
|
||||||
HistoryList =[];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await SonarrClient.Instance.RefreshSonarr();
|
|
||||||
}
|
|
||||||
|
|
||||||
var jsonFiles = Directory.Exists(CfgManager.PathENCODING_PRESETS_DIR) ? Directory.GetFiles(CfgManager.PathENCODING_PRESETS_DIR, "*.json") :[];
|
var jsonFiles = Directory.Exists(CfgManager.PathENCODING_PRESETS_DIR) ? Directory.GetFiles(CfgManager.PathENCODING_PRESETS_DIR, "*.json") :[];
|
||||||
|
|
||||||
foreach (var file in jsonFiles){
|
foreach (var file in jsonFiles){
|
||||||
try{
|
try{
|
||||||
// Read the content of the JSON file
|
|
||||||
var jsonContent = File.ReadAllText(file);
|
var jsonContent = File.ReadAllText(file);
|
||||||
|
|
||||||
// Deserialize the JSON content into a MyClass object
|
|
||||||
var obj = Helpers.Deserialize<VideoPreset>(jsonContent, null);
|
var obj = Helpers.Deserialize<VideoPreset>(jsonContent, null);
|
||||||
|
|
||||||
if (obj != null){
|
if (obj != null){
|
||||||
|
|
@ -210,6 +181,48 @@ public class CrunchyrollManager{
|
||||||
Console.Error.WriteLine($"Failed to deserialize file {file}: {ex.Message}");
|
Console.Error.WriteLine($"Failed to deserialize file {file}: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CfgManager.CheckIfFileExists(CfgManager.PathCrToken)){
|
||||||
|
Token = CfgManager.DeserializeFromFile<CrToken>(CfgManager.PathCrToken);
|
||||||
|
await CrAuth.LoginWithToken();
|
||||||
|
} else{
|
||||||
|
await CrAuth.AuthAnonymous();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CrunOptions.History){
|
||||||
|
if (File.Exists(CfgManager.PathCrHistory)){
|
||||||
|
var decompressedJson = CfgManager.DecompressJsonFile(CfgManager.PathCrHistory);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(decompressedJson)){
|
||||||
|
var historyList = Helpers.Deserialize<ObservableCollection<HistorySeries>>(
|
||||||
|
decompressedJson,
|
||||||
|
SettingsJsonSerializerSettings
|
||||||
|
);
|
||||||
|
|
||||||
|
if (historyList != null){
|
||||||
|
|
||||||
|
HistoryList = historyList;
|
||||||
|
|
||||||
|
Parallel.ForEach(historyList, historySeries => {
|
||||||
|
historySeries.Init();
|
||||||
|
|
||||||
|
foreach (var historySeriesSeason in historySeries.Seasons){
|
||||||
|
historySeriesSeason.Init();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else{
|
||||||
|
HistoryList =[];
|
||||||
|
}
|
||||||
|
} else{
|
||||||
|
HistoryList =[];
|
||||||
|
}
|
||||||
|
} else{
|
||||||
|
HistoryList =[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await SonarrClient.Instance.RefreshSonarr();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -678,14 +691,15 @@ public class CrunchyrollManager{
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!File.Exists(CfgManager.PathMP4Decrypt)){
|
if (!File.Exists(CfgManager.PathMP4Decrypt) && !File.Exists(CfgManager.PathShakaPackager)){
|
||||||
Console.Error.WriteLine("mp4decrypt not found");
|
Console.Error.WriteLine("mp4decrypt or shaka-packager not found");
|
||||||
MainWindow.Instance.ShowError($"Can't find mp4decrypt in lib folder at: {CfgManager.PathMP4Decrypt}");
|
MainWindow.Instance.ShowError($"Either mp4decrypt (expected in lib folder at: {CfgManager.PathMP4Decrypt}) " +
|
||||||
|
$"or shaka-packager (expected in lib folder at: {CfgManager.PathShakaPackager}) must be available.");
|
||||||
return new DownloadResponse{
|
return new DownloadResponse{
|
||||||
Data = new List<DownloadedMedia>(),
|
Data = new List<DownloadedMedia>(),
|
||||||
Error = true,
|
Error = true,
|
||||||
FileName = "./unknown",
|
FileName = "./unknown",
|
||||||
ErrorText = "Missing mp4decrypt"
|
ErrorText = "Requires either mp4decrypt or shaka-packager"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1308,14 +1322,27 @@ public class CrunchyrollManager{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (Path.Exists(CfgManager.PathMP4Decrypt)){
|
if (Path.Exists(CfgManager.PathMP4Decrypt) || Path.Exists(CfgManager.PathShakaPackager)){
|
||||||
var keyId = BitConverter.ToString(encryptionKeys[0].KeyID).Replace("-", "").ToLower();
|
var keyId = BitConverter.ToString(encryptionKeys[0].KeyID).Replace("-", "").ToLower();
|
||||||
var key = BitConverter.ToString(encryptionKeys[0].Bytes).Replace("-", "").ToLower();
|
var key = BitConverter.ToString(encryptionKeys[0].Bytes).Replace("-", "").ToLower();
|
||||||
|
|
||||||
|
//mp4decrypt
|
||||||
var commandBase = $"--show-progress --key {keyId}:{key}";
|
var commandBase = $"--show-progress --key {keyId}:{key}";
|
||||||
var tempTsFileName = Path.GetFileName(tempTsFile);
|
var tempTsFileName = Path.GetFileName(tempTsFile);
|
||||||
var tempTsFileWorkDir = Path.GetDirectoryName(tempTsFile) ?? CfgManager.PathVIDEOS_DIR;
|
var tempTsFileWorkDir = Path.GetDirectoryName(tempTsFile) ?? CfgManager.PathVIDEOS_DIR;
|
||||||
var commandVideo = commandBase + $" \"{tempTsFileName}.video.enc.m4s\" \"{tempTsFileName}.video.m4s\"";
|
var commandVideo = commandBase + $" \"{tempTsFileName}.video.enc.m4s\" \"{tempTsFileName}.video.m4s\"";
|
||||||
var commandAudio = commandBase + $" \"{tempTsFileName}.audio.enc.m4s\" \"{tempTsFileName}.audio.m4s\"";
|
var commandAudio = commandBase + $" \"{tempTsFileName}.audio.enc.m4s\" \"{tempTsFileName}.audio.m4s\"";
|
||||||
|
|
||||||
|
bool shaka = Path.Exists(CfgManager.PathShakaPackager);
|
||||||
|
if (shaka){
|
||||||
|
commandBase = " --enable_raw_key_decryption " +
|
||||||
|
string.Join(" ",
|
||||||
|
encryptionKeys.Select(kb =>
|
||||||
|
$"--keys key_id={BitConverter.ToString(kb.KeyID).Replace("-", "").ToLower()}:key={BitConverter.ToString(kb.Bytes).Replace("-", "").ToLower()}"));
|
||||||
|
commandVideo = $"input=\"{tempTsFileName}.video.enc.m4s\",stream=video,output=\"{tempTsFileName}.video.m4s\"" + commandBase;
|
||||||
|
commandAudio = $"input=\"{tempTsFileName}.audio.enc.m4s\",stream=audio,output=\"{tempTsFileName}.audio.m4s\"" + commandBase;
|
||||||
|
}
|
||||||
|
|
||||||
if (videoDownloaded){
|
if (videoDownloaded){
|
||||||
Console.WriteLine("Started decrypting video");
|
Console.WriteLine("Started decrypting video");
|
||||||
data.DownloadProgress = new DownloadProgress(){
|
data.DownloadProgress = new DownloadProgress(){
|
||||||
|
|
@ -1326,7 +1353,8 @@ public class CrunchyrollManager{
|
||||||
Doing = "Decrypting video"
|
Doing = "Decrypting video"
|
||||||
};
|
};
|
||||||
QueueManager.Instance.Queue.Refresh();
|
QueueManager.Instance.Queue.Refresh();
|
||||||
var decryptVideo = await Helpers.ExecuteCommandAsyncWorkDir("mp4decrypt", CfgManager.PathMP4Decrypt, commandVideo, tempTsFileWorkDir);
|
var decryptVideo = await Helpers.ExecuteCommandAsyncWorkDir(shaka ? "shaka-packager" : "mp4decrypt", shaka ? CfgManager.PathShakaPackager : CfgManager.PathMP4Decrypt,
|
||||||
|
commandVideo, tempTsFileWorkDir);
|
||||||
|
|
||||||
if (!decryptVideo.IsOk){
|
if (!decryptVideo.IsOk){
|
||||||
Console.Error.WriteLine($"Decryption failed with exit code {decryptVideo.ErrorCode}");
|
Console.Error.WriteLine($"Decryption failed with exit code {decryptVideo.ErrorCode}");
|
||||||
|
|
@ -1394,7 +1422,8 @@ public class CrunchyrollManager{
|
||||||
Doing = "Decrypting audio"
|
Doing = "Decrypting audio"
|
||||||
};
|
};
|
||||||
QueueManager.Instance.Queue.Refresh();
|
QueueManager.Instance.Queue.Refresh();
|
||||||
var decryptAudio = await Helpers.ExecuteCommandAsyncWorkDir("mp4decrypt", CfgManager.PathMP4Decrypt, commandAudio, tempTsFileWorkDir);
|
var decryptAudio = await Helpers.ExecuteCommandAsyncWorkDir(shaka ? "shaka-packager" : "mp4decrypt", shaka ? CfgManager.PathShakaPackager : CfgManager.PathMP4Decrypt,
|
||||||
|
commandAudio, tempTsFileWorkDir);
|
||||||
|
|
||||||
if (!decryptAudio.IsOk){
|
if (!decryptAudio.IsOk){
|
||||||
Console.Error.WriteLine($"Decryption failed with exit code {decryptAudio.ErrorCode}");
|
Console.Error.WriteLine($"Decryption failed with exit code {decryptAudio.ErrorCode}");
|
||||||
|
|
@ -1481,10 +1510,9 @@ public class CrunchyrollManager{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (options is{ Novids: true, Noaudio: true }){
|
} else if (options is{ Novids: true, Noaudio: true }){
|
||||||
|
|
||||||
variables.Add(new Variable("height", 360, false));
|
variables.Add(new Variable("height", 360, false));
|
||||||
variables.Add(new Variable("width", 640, 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -417,7 +417,7 @@
|
||||||
</controls:SettingsExpanderItem.Footer>
|
</controls:SettingsExpanderItem.Footer>
|
||||||
</controls:SettingsExpanderItem>
|
</controls:SettingsExpanderItem>
|
||||||
|
|
||||||
<controls:SettingsExpanderItem IsVisible="{Binding !SkipMuxing}" Content="Additional FFMpeg Options">
|
<controls:SettingsExpanderItem IsVisible="{Binding !SkipMuxing}" Content="Additional FFMpeg Options" Description="Only used for MP4">
|
||||||
<controls:SettingsExpanderItem.Footer>
|
<controls:SettingsExpanderItem.Footer>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
|
|
|
||||||
|
|
@ -678,7 +678,7 @@ public class History(){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
CfgManager.UpdateHistoryFile();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,9 @@ public partial class ProgramManager : ObservableObject{
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _finishedLoading = false;
|
private bool _finishedLoading = false;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _navigationLock = false;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
@ -115,7 +118,7 @@ public partial class ProgramManager : ObservableObject{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async void Init(){
|
private async Task Init(){
|
||||||
CrunchyrollManager.Instance.InitOptions();
|
CrunchyrollManager.Instance.InitOptions();
|
||||||
|
|
||||||
UpdateAvailable = await Updater.Instance.CheckForUpdatesAsync();
|
UpdateAvailable = await Updater.Instance.CheckForUpdatesAsync();
|
||||||
|
|
|
||||||
|
|
@ -7,25 +7,25 @@ namespace CRD.Utils.Ffmpeg_Encoding;
|
||||||
public class FfmpegEncoding{
|
public class FfmpegEncoding{
|
||||||
public static readonly List<VideoPreset> presets = new List<VideoPreset>{
|
public static readonly List<VideoPreset> presets = new List<VideoPreset>{
|
||||||
// AV1 Software
|
// AV1 Software
|
||||||
new(){PresetName = "AV1 1080p24",Codec = "libaom-av1", Resolution = "1920:1080", FrameRate = "24", Crf = 30 } ,
|
new(){ PresetName = "AV1 1080p24", Codec = "libaom-av1", Resolution = "1920:1080", FrameRate = "24000/1001", Crf = 30, AdditionalParameters ={ "-map 0" } },
|
||||||
new(){PresetName = "AV1 720p24", Codec = "libaom-av1", Resolution = "1280:720", FrameRate = "24", Crf = 30 } ,
|
new(){ PresetName = "AV1 720p24", Codec = "libaom-av1", Resolution = "1280:720", FrameRate = "24000/1001", Crf = 30, AdditionalParameters ={ "-map 0" } },
|
||||||
new(){PresetName = "AV1 480p24", Codec = "libaom-av1", Resolution = "854:480", FrameRate = "24", Crf = 30 } ,
|
new(){ PresetName = "AV1 480p24", Codec = "libaom-av1", Resolution = "854:480", FrameRate = "24000/1001", Crf = 30, AdditionalParameters ={ "-map 0" } },
|
||||||
new(){PresetName = "AV1 360p24", Codec = "libaom-av1", Resolution = "640:360", FrameRate = "24", Crf = 30 } ,
|
new(){ PresetName = "AV1 360p24", Codec = "libaom-av1", Resolution = "640:360", FrameRate = "24000/1001", Crf = 30, AdditionalParameters ={ "-map 0" } },
|
||||||
new(){PresetName = "AV1 240p24", Codec = "libaom-av1", Resolution = "426:240", FrameRate = "24", Crf = 30 } ,
|
new(){ PresetName = "AV1 240p24", Codec = "libaom-av1", Resolution = "426:240", FrameRate = "24000/1001", Crf = 30, AdditionalParameters ={ "-map 0" } },
|
||||||
|
|
||||||
// H.265 Software
|
// H.265 Software
|
||||||
new(){PresetName = "H.265 1080p24", Codec = "libx265", Resolution = "1920:1080", FrameRate = "24", Crf = 28 } ,
|
new(){ PresetName = "H.265 1080p24", Codec = "libx265", Resolution = "1920:1080", FrameRate = "24000/1001", Crf = 28, AdditionalParameters ={ "-map 0" } },
|
||||||
new(){PresetName = "H.265 720p24", Codec = "libx265", Resolution = "1280:720", FrameRate = "24", Crf = 28 } ,
|
new(){ PresetName = "H.265 720p24", Codec = "libx265", Resolution = "1280:720", FrameRate = "24000/1001", Crf = 28, AdditionalParameters ={ "-map 0" } },
|
||||||
new(){PresetName = "H.265 480p24", Codec = "libx265", Resolution = "854:480", FrameRate = "24", Crf = 28 } ,
|
new(){ PresetName = "H.265 480p24", Codec = "libx265", Resolution = "854:480", FrameRate = "24000/1001", Crf = 28, AdditionalParameters ={ "-map 0" } },
|
||||||
new(){PresetName = "H.265 360p24", Codec = "libx265", Resolution = "640:360", FrameRate = "24", Crf = 28 } ,
|
new(){ PresetName = "H.265 360p24", Codec = "libx265", Resolution = "640:360", FrameRate = "24000/1001", Crf = 28, AdditionalParameters ={ "-map 0" } },
|
||||||
new(){PresetName = "H.265 240p24", Codec = "libx265", Resolution = "426:240", FrameRate = "24", Crf = 28 } ,
|
new(){ PresetName = "H.265 240p24", Codec = "libx265", Resolution = "426:240", FrameRate = "24000/1001", Crf = 28, AdditionalParameters ={ "-map 0" } },
|
||||||
|
|
||||||
// H.264 Software
|
// H.264 Software
|
||||||
new(){ PresetName = "H.264 1080p24",Codec = "libx264", Resolution = "1920:1080", FrameRate = "24", Crf = 23 } ,
|
new(){ PresetName = "H.264 1080p24", Codec = "libx264", Resolution = "1920:1080", FrameRate = "24000/1001", Crf = 23, AdditionalParameters ={ "-map 0" } },
|
||||||
new(){PresetName = "H.264 720p24", Codec = "libx264", Resolution = "1280:720", FrameRate = "24", Crf = 23 } ,
|
new(){ PresetName = "H.264 720p24", Codec = "libx264", Resolution = "1280:720", FrameRate = "24000/1001", Crf = 23, AdditionalParameters ={ "-map 0" } },
|
||||||
new(){PresetName = "H.264 480p24", Codec = "libx264", Resolution = "854:480", FrameRate = "24", Crf = 23 },
|
new(){ PresetName = "H.264 480p24", Codec = "libx264", Resolution = "854:480", FrameRate = "24000/1001", Crf = 23, AdditionalParameters ={ "-map 0" } },
|
||||||
new(){PresetName = "H.264 360p24", Codec = "libx264", Resolution = "640:360", FrameRate = "24", Crf = 23 } ,
|
new(){ PresetName = "H.264 360p24", Codec = "libx264", Resolution = "640:360", FrameRate = "24000/1001", Crf = 23, AdditionalParameters ={ "-map 0" } },
|
||||||
new(){PresetName = "H.264 240p24", Codec = "libx264", Resolution = "426:240", FrameRate = "24", Crf = 23 } ,
|
new(){ PresetName = "H.264 240p24", Codec = "libx264", Resolution = "426:240", FrameRate = "24000/1001", Crf = 23, AdditionalParameters ={ "-map 0" } },
|
||||||
};
|
};
|
||||||
|
|
||||||
public static VideoPreset? GetPreset(string presetName){
|
public static VideoPreset? GetPreset(string presetName){
|
||||||
|
|
@ -49,13 +49,11 @@ public class FfmpegEncoding{
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VideoPreset{
|
public class VideoPreset{
|
||||||
|
|
||||||
public string? PresetName{ get; set; }
|
public string? PresetName{ get; set; }
|
||||||
public string? Codec{ get; set; }
|
public string? Codec{ get; set; }
|
||||||
public string? Resolution{ get; set; }
|
public string? Resolution{ get; set; }
|
||||||
public string? FrameRate{ get; set; }
|
public string? FrameRate{ get; set; }
|
||||||
public int Crf{ get; set; }
|
public int Crf{ get; set; }
|
||||||
|
|
||||||
public List<string> AdditionalParameters { get; set; } = new List<string>();
|
public List<string> AdditionalParameters{ get; set; } = new List<string>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -31,6 +31,7 @@ public class CfgManager{
|
||||||
File.Exists(Path.Combine(WorkingDirectory, "lib", "mkvmerge")) ? Path.Combine(WorkingDirectory, "lib", "mkvmerge") : "mkvmerge";
|
File.Exists(Path.Combine(WorkingDirectory, "lib", "mkvmerge")) ? Path.Combine(WorkingDirectory, "lib", "mkvmerge") : "mkvmerge";
|
||||||
|
|
||||||
public static readonly string PathMP4Decrypt = Path.Combine(WorkingDirectory, "lib", "mp4decrypt" + ExecutableExtension);
|
public static readonly string PathMP4Decrypt = Path.Combine(WorkingDirectory, "lib", "mp4decrypt" + ExecutableExtension);
|
||||||
|
public static readonly string PathShakaPackager = Path.Combine(WorkingDirectory, "lib", "shaka-packager" + ExecutableExtension);
|
||||||
|
|
||||||
public static readonly string PathWIDEVINE_DIR = Path.Combine(WorkingDirectory, "widevine");
|
public static readonly string PathWIDEVINE_DIR = Path.Combine(WorkingDirectory, "widevine");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -322,7 +322,22 @@ public class Helpers{
|
||||||
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(inputFilePath);
|
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(inputFilePath);
|
||||||
string tempOutputFilePath = Path.Combine(directory, $"{fileNameWithoutExtension}_output{outputExtension}");
|
string tempOutputFilePath = Path.Combine(directory, $"{fileNameWithoutExtension}_output{outputExtension}");
|
||||||
|
|
||||||
string additionalParams = string.Join(" ", preset.AdditionalParameters);
|
string additionalParams = string.Join(" ", preset.AdditionalParameters.Select(param => {
|
||||||
|
var splitIndex = param.IndexOf(' ');
|
||||||
|
if (splitIndex > 0){
|
||||||
|
var prefix = param[..splitIndex];
|
||||||
|
var value = param[(splitIndex + 1)..];
|
||||||
|
|
||||||
|
if (value.Contains(' ') && !(value.StartsWith("\"") && value.EndsWith("\""))){
|
||||||
|
value = $"\"{value}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{prefix} {value}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return param;
|
||||||
|
}));
|
||||||
|
|
||||||
string qualityOption = GetQualityOption(preset);
|
string qualityOption = GetQualityOption(preset);
|
||||||
|
|
||||||
TimeSpan? totalDuration = await GetMediaDurationAsync(CfgManager.PathFFMPEG, inputFilePath);
|
TimeSpan? totalDuration = await GetMediaDurationAsync(CfgManager.PathFFMPEG, inputFilePath);
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,10 @@ public class HttpClientReq{
|
||||||
HttpClientHandler handler = new HttpClientHandler();
|
HttpClientHandler handler = new HttpClientHandler();
|
||||||
|
|
||||||
if (CrunchyrollManager.Instance.CrunOptions.ProxyEnabled && !string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.ProxyHost)){
|
if (CrunchyrollManager.Instance.CrunOptions.ProxyEnabled && !string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.ProxyHost)){
|
||||||
handler = CreateHandler(true, CrunchyrollManager.Instance.CrunOptions.ProxyHost, CrunchyrollManager.Instance.CrunOptions.ProxyPort);
|
handler = CreateHandler(true, CrunchyrollManager.Instance.CrunOptions.ProxySocks, CrunchyrollManager.Instance.CrunOptions.ProxyHost, CrunchyrollManager.Instance.CrunOptions.ProxyPort,
|
||||||
Console.Error.WriteLine($"Proxy is set: http://{CrunchyrollManager.Instance.CrunOptions.ProxyHost}:{CrunchyrollManager.Instance.CrunOptions.ProxyPort}");
|
CrunchyrollManager.Instance.CrunOptions.ProxyUsername, CrunchyrollManager.Instance.CrunOptions.ProxyPassword);
|
||||||
|
string scheme = CrunchyrollManager.Instance.CrunOptions.ProxySocks ? "socks5" : "http";
|
||||||
|
Console.Error.WriteLine($"Proxy is set: {scheme}://{CrunchyrollManager.Instance.CrunOptions.ProxyHost}:{CrunchyrollManager.Instance.CrunOptions.ProxyPort}");
|
||||||
client = new HttpClient(handler);
|
client = new HttpClient(handler);
|
||||||
} else if (systemProxy != null){
|
} else if (systemProxy != null){
|
||||||
Uri testUri = new Uri("https://icanhazip.com");
|
Uri testUri = new Uri("https://icanhazip.com");
|
||||||
|
|
@ -73,7 +75,7 @@ public class HttpClientReq{
|
||||||
Console.Error.WriteLine("No proxy is being used.");
|
Console.Error.WriteLine("No proxy is being used.");
|
||||||
client = new HttpClient(CreateHttpClientHandler());
|
client = new HttpClient(CreateHttpClientHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
client.Timeout = TimeSpan.FromSeconds(100);
|
client.Timeout = TimeSpan.FromSeconds(100);
|
||||||
|
|
||||||
// client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0");
|
// client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0");
|
||||||
|
|
@ -85,7 +87,6 @@ public class HttpClientReq{
|
||||||
client.DefaultRequestHeaders.AcceptEncoding.ParseAdd("gzip, deflate, br");
|
client.DefaultRequestHeaders.AcceptEncoding.ParseAdd("gzip, deflate, br");
|
||||||
client.DefaultRequestHeaders.AcceptLanguage.ParseAdd("en-US,en;q=0.5");
|
client.DefaultRequestHeaders.AcceptLanguage.ParseAdd("en-US,en;q=0.5");
|
||||||
client.DefaultRequestHeaders.Connection.ParseAdd("keep-alive");
|
client.DefaultRequestHeaders.Connection.ParseAdd("keep-alive");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpMessageHandler CreateHttpClientHandler(){
|
private HttpMessageHandler CreateHttpClientHandler(){
|
||||||
|
|
@ -110,7 +111,7 @@ public class HttpClientReq{
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpClientHandler CreateHandler(bool useProxy, string? proxyHost = null, int proxyPort = 0){
|
private HttpClientHandler CreateHandler(bool useProxy, bool useSocks = false, string? proxyHost = null, int proxyPort = 0, string? proxyUsername = "", string? proxyPassword = ""){
|
||||||
var handler = new HttpClientHandler{
|
var handler = new HttpClientHandler{
|
||||||
CookieContainer = new CookieContainer(),
|
CookieContainer = new CookieContainer(),
|
||||||
UseCookies = true,
|
UseCookies = true,
|
||||||
|
|
@ -119,7 +120,11 @@ public class HttpClientReq{
|
||||||
};
|
};
|
||||||
|
|
||||||
if (useProxy && proxyHost != null){
|
if (useProxy && proxyHost != null){
|
||||||
handler.Proxy = new WebProxy($"http://{proxyHost}:{proxyPort}");
|
string scheme = useSocks ? "socks5" : "http";
|
||||||
|
handler.Proxy = new WebProxy($"{scheme}://{proxyHost}:{proxyPort}");
|
||||||
|
if (!string.IsNullOrEmpty(proxyUsername) && !string.IsNullOrEmpty(proxyPassword)){
|
||||||
|
handler.Proxy.Credentials = new NetworkCredential(proxyUsername, proxyPassword);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler;
|
return handler;
|
||||||
|
|
|
||||||
|
|
@ -89,13 +89,22 @@ public class CrDownloadOptions{
|
||||||
|
|
||||||
[YamlMember(Alias = "proxy_enabled", ApplyNamingConventions = false)]
|
[YamlMember(Alias = "proxy_enabled", ApplyNamingConventions = false)]
|
||||||
public bool ProxyEnabled{ get; set; }
|
public bool ProxyEnabled{ get; set; }
|
||||||
|
|
||||||
|
[YamlMember(Alias = "proxy_socks", ApplyNamingConventions = false)]
|
||||||
|
public bool ProxySocks{ get; set; }
|
||||||
|
|
||||||
[YamlMember(Alias = "proxy_host", ApplyNamingConventions = false)]
|
[YamlMember(Alias = "proxy_host", ApplyNamingConventions = false)]
|
||||||
public string? ProxyHost{ get; set; }
|
public string? ProxyHost{ get; set; }
|
||||||
|
|
||||||
[YamlMember(Alias = "proxy_port", ApplyNamingConventions = false)]
|
[YamlMember(Alias = "proxy_port", ApplyNamingConventions = false)]
|
||||||
public int ProxyPort{ get; set; }
|
public int ProxyPort{ get; set; }
|
||||||
|
|
||||||
|
[YamlMember(Alias = "proxy_username", ApplyNamingConventions = false)]
|
||||||
|
public string? ProxyUsername{ get; set; }
|
||||||
|
|
||||||
|
[YamlMember(Alias = "proxy_password", ApplyNamingConventions = false)]
|
||||||
|
public string? ProxyPassword{ get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,9 @@ public class HistorySeason : INotifyPropertyChanged{
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public StringItem? _selectedVideoQualityItem;
|
public StringItem? _selectedVideoQualityItem;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
private bool Loading = false;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public StringItem? SelectedVideoQualityItem{
|
public StringItem? SelectedVideoQualityItem{
|
||||||
get => _selectedVideoQualityItem;
|
get => _selectedVideoQualityItem;
|
||||||
|
|
@ -59,7 +62,9 @@ public class HistorySeason : INotifyPropertyChanged{
|
||||||
|
|
||||||
HistorySeasonVideoQualityOverride = value?.stringValue ?? "";
|
HistorySeasonVideoQualityOverride = value?.stringValue ?? "";
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedVideoQualityItem)));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedVideoQualityItem)));
|
||||||
CfgManager.UpdateHistoryFile();
|
if (!Loading){
|
||||||
|
CfgManager.UpdateHistoryFile();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,6 +132,7 @@ public class HistorySeason : INotifyPropertyChanged{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Init(){
|
public void Init(){
|
||||||
|
Loading = true;
|
||||||
if (!(SubLangList.Count > 2 || DubLangList.Count > 0)){
|
if (!(SubLangList.Count > 2 || DubLangList.Count > 0)){
|
||||||
foreach (var languageItem in Languages.languages){
|
foreach (var languageItem in Languages.languages){
|
||||||
SubLangList.Add(new StringItem{ stringValue = languageItem.CrLocale });
|
SubLangList.Add(new StringItem{ stringValue = languageItem.CrLocale });
|
||||||
|
|
@ -154,6 +160,7 @@ public class HistorySeason : INotifyPropertyChanged{
|
||||||
|
|
||||||
SelectedSubLang.CollectionChanged += Changes;
|
SelectedSubLang.CollectionChanged += Changes;
|
||||||
SelectedDubLang.CollectionChanged += Changes;
|
SelectedDubLang.CollectionChanged += Changes;
|
||||||
|
Loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,9 @@ public class HistorySeries : INotifyPropertyChanged{
|
||||||
|
|
||||||
#region Settings Override
|
#region Settings Override
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
private bool Loading = false;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public StringItem? _selectedVideoQualityItem;
|
public StringItem? _selectedVideoQualityItem;
|
||||||
|
|
||||||
|
|
@ -105,7 +108,10 @@ public class HistorySeries : INotifyPropertyChanged{
|
||||||
|
|
||||||
HistorySeriesVideoQualityOverride = value?.stringValue ?? "";
|
HistorySeriesVideoQualityOverride = value?.stringValue ?? "";
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedVideoQualityItem)));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedVideoQualityItem)));
|
||||||
CfgManager.UpdateHistoryFile();
|
if (!Loading){
|
||||||
|
CfgManager.UpdateHistoryFile();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,6 +179,7 @@ public class HistorySeries : INotifyPropertyChanged{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Init(){
|
public void Init(){
|
||||||
|
Loading = true;
|
||||||
if (!(SubLangList.Count > 2 || DubLangList.Count > 0)){
|
if (!(SubLangList.Count > 2 || DubLangList.Count > 0)){
|
||||||
foreach (var languageItem in Languages.languages){
|
foreach (var languageItem in Languages.languages){
|
||||||
SubLangList.Add(new StringItem{ stringValue = languageItem.CrLocale });
|
SubLangList.Add(new StringItem{ stringValue = languageItem.CrLocale });
|
||||||
|
|
@ -200,6 +207,7 @@ public class HistorySeries : INotifyPropertyChanged{
|
||||||
|
|
||||||
SelectedSubLang.CollectionChanged += Changes;
|
SelectedSubLang.CollectionChanged += Changes;
|
||||||
SelectedDubLang.CollectionChanged += Changes;
|
SelectedDubLang.CollectionChanged += Changes;
|
||||||
|
Loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
|
||||||
|
|
@ -43,12 +43,20 @@ public class Updater : INotifyPropertyChanged{
|
||||||
}
|
}
|
||||||
|
|
||||||
private string downloadUrl = "";
|
private string downloadUrl = "";
|
||||||
private readonly string tempPath = Path.Combine(Path.GetTempPath(), "Update.zip");
|
private readonly string tempPath = Path.Combine(CfgManager.PathTEMP_DIR, "Update.zip");
|
||||||
private readonly string extractPath = Path.Combine(Path.GetTempPath(), "ExtractedUpdate");
|
private readonly string extractPath = Path.Combine(CfgManager.PathTEMP_DIR, "ExtractedUpdate");
|
||||||
|
|
||||||
private readonly string apiEndpoint = "https://api.github.com/repos/Crunchy-DL/Crunchy-Downloader/releases/latest";
|
private readonly string apiEndpoint = "https://api.github.com/repos/Crunchy-DL/Crunchy-Downloader/releases/latest";
|
||||||
|
|
||||||
public async Task<bool> CheckForUpdatesAsync(){
|
public async Task<bool> CheckForUpdatesAsync(){
|
||||||
|
if (Directory.Exists(tempPath)){
|
||||||
|
Directory.Delete(tempPath, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Directory.Exists(extractPath)){
|
||||||
|
Directory.Delete(extractPath, true);
|
||||||
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
var platformAssetMapping = new Dictionary<OSPlatform, string>{
|
var platformAssetMapping = new Dictionary<OSPlatform, string>{
|
||||||
{ OSPlatform.Windows, "windows" },
|
{ OSPlatform.Windows, "windows" },
|
||||||
|
|
@ -114,6 +122,8 @@ public class Updater : INotifyPropertyChanged{
|
||||||
|
|
||||||
public async Task DownloadAndUpdateAsync(){
|
public async Task DownloadAndUpdateAsync(){
|
||||||
try{
|
try{
|
||||||
|
Helpers.EnsureDirectoriesExist(tempPath);
|
||||||
|
|
||||||
// Download the zip file
|
// Download the zip file
|
||||||
var response = await HttpClientReq.Instance.GetHttpClient().GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
|
var response = await HttpClientReq.Instance.GetHttpClient().GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
|
||||||
|
|
@ -160,8 +170,9 @@ public class Updater : INotifyPropertyChanged{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyUpdate(string updateFolder){
|
private void ApplyUpdate(string updateFolder){
|
||||||
|
var ExecutableExtension = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty;
|
||||||
var currentPath = AppDomain.CurrentDomain.BaseDirectory;
|
var currentPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
var updaterPath = Path.Combine(currentPath, "Updater.exe");
|
var updaterPath = Path.Combine(currentPath, "Updater" + ExecutableExtension);
|
||||||
var arguments = $"\"{currentPath.Substring(0, currentPath.Length - 1)}\" \"{updateFolder}\"";
|
var arguments = $"\"{currentPath.Substring(0, currentPath.Length - 1)}\" \"{updateFolder}\"";
|
||||||
|
|
||||||
System.Diagnostics.Process.Start(updaterPath, arguments);
|
System.Diagnostics.Process.Start(updaterPath, arguments);
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,7 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(value.SonarrSeriesId) && CrunchyrollManager.Instance.CrunOptions.SonarrProperties is{ SonarrEnabled: true }){
|
if (!string.IsNullOrEmpty(value.SonarrSeriesId) && CrunchyrollManager.Instance.CrunOptions.SonarrProperties is{ SonarrEnabled: true }){
|
||||||
CrunchyrollManager.Instance.History.MatchHistoryEpisodesWithSonarr(true, SelectedSeries);
|
CrunchyrollManager.Instance.History.MatchHistoryEpisodesWithSonarr(true, SelectedSeries);
|
||||||
|
CfgManager.UpdateHistoryFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -370,6 +371,7 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
||||||
SonarrOptionsOpen = false;
|
SonarrOptionsOpen = false;
|
||||||
AddingMissingSonarrSeries = true;
|
AddingMissingSonarrSeries = true;
|
||||||
ProgramManager.FetchingData = true;
|
ProgramManager.FetchingData = true;
|
||||||
|
ProgramManager.NavigationLock = true;
|
||||||
|
|
||||||
var crInstance = CrunchyrollManager.Instance;
|
var crInstance = CrunchyrollManager.Instance;
|
||||||
|
|
||||||
|
|
@ -407,6 +409,7 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
||||||
|
|
||||||
// Await the CRUpdateSeries task for each seriesId
|
// Await the CRUpdateSeries task for each seriesId
|
||||||
await crInstance.History.CRUpdateSeries(seriesIds[count], "");
|
await crInstance.History.CRUpdateSeries(seriesIds[count], "");
|
||||||
|
RaisePropertyChanged(nameof(ProgressText));
|
||||||
}
|
}
|
||||||
|
|
||||||
// var updateTasks = seriesIds.Select(seriesId => crInstance.History.CRUpdateSeries(seriesId, ""));
|
// var updateTasks = seriesIds.Select(seriesId => crInstance.History.CRUpdateSeries(seriesId, ""));
|
||||||
|
|
@ -416,6 +419,7 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
||||||
ProgressText = "";
|
ProgressText = "";
|
||||||
AddingMissingSonarrSeries = false;
|
AddingMissingSonarrSeries = false;
|
||||||
ProgramManager.FetchingData = false;
|
ProgramManager.FetchingData = false;
|
||||||
|
ProgramManager.NavigationLock = false;
|
||||||
if (SelectedFilter != null){
|
if (SelectedFilter != null){
|
||||||
OnSelectedFilterChanged(SelectedFilter);
|
OnSelectedFilterChanged(SelectedFilter);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,4 @@ public partial class MainWindowViewModel : ViewModelBase{
|
||||||
public MainWindowViewModel(ProgramManager manager){
|
public MainWindowViewModel(ProgramManager manager){
|
||||||
ProgramManager = manager;
|
ProgramManager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -48,9 +48,8 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
||||||
public bool _seriesFolderPathExists;
|
public bool _seriesFolderPathExists;
|
||||||
|
|
||||||
public SeriesPageViewModel(){
|
public SeriesPageViewModel(){
|
||||||
|
|
||||||
_storageProvider = ProgramManager.Instance.StorageProvider ?? throw new ArgumentNullException(nameof(ProgramManager.Instance.StorageProvider));
|
_storageProvider = ProgramManager.Instance.StorageProvider ?? throw new ArgumentNullException(nameof(ProgramManager.Instance.StorageProvider));
|
||||||
|
|
||||||
_selectedSeries = CrunchyrollManager.Instance.SelectedSeries;
|
_selectedSeries = CrunchyrollManager.Instance.SelectedSeries;
|
||||||
|
|
||||||
if (_selectedSeries.ThumbnailImage == null){
|
if (_selectedSeries.ThumbnailImage == null){
|
||||||
|
|
@ -153,7 +152,7 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
||||||
|
|
||||||
UpdateSeriesFolderPath();
|
UpdateSeriesFolderPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
public async Task MatchSonarrSeries_Button(){
|
public async Task MatchSonarrSeries_Button(){
|
||||||
var dialog = new ContentDialog(){
|
var dialog = new ContentDialog(){
|
||||||
|
|
@ -221,7 +220,18 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
||||||
|
|
||||||
await Task.WhenAll(downloadTasks);
|
await Task.WhenAll(downloadTasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
public void ToggleDownloadedMark(HistorySeason season){
|
||||||
|
bool allDownloaded = season.EpisodesList.All(ep => ep.WasDownloaded);
|
||||||
|
|
||||||
|
foreach (var historyEpisode in season.EpisodesList){
|
||||||
|
if (historyEpisode.WasDownloaded == allDownloaded){
|
||||||
|
season.UpdateDownloaded(historyEpisode.EpisodeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
public async Task UpdateData(string? season){
|
public async Task UpdateData(string? season){
|
||||||
await SelectedSeries.FetchData(season);
|
await SelectedSeries.FetchData(season);
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ public partial class ContentDialogEncodingPresetViewModel : ViewModelBase{
|
||||||
private double? _crf = 23;
|
private double? _crf = 23;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private double? _frameRate = 30;
|
private string _frameRate = "";
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _additionalParametersString = "";
|
private string _additionalParametersString = "";
|
||||||
|
|
@ -82,6 +82,8 @@ public partial class ContentDialogEncodingPresetViewModel : ViewModelBase{
|
||||||
if (dialog is null){
|
if (dialog is null){
|
||||||
throw new ArgumentNullException(nameof(dialog));
|
throw new ArgumentNullException(nameof(dialog));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AdditionalParameters.Add(new StringItem(){ stringValue = "-map 0" });
|
||||||
|
|
||||||
if (editMode){
|
if (editMode){
|
||||||
EditMode = true;
|
EditMode = true;
|
||||||
|
|
@ -106,7 +108,7 @@ public partial class ContentDialogEncodingPresetViewModel : ViewModelBase{
|
||||||
PresetName = value.PresetName ?? "";
|
PresetName = value.PresetName ?? "";
|
||||||
Codec = value.Codec ?? "";
|
Codec = value.Codec ?? "";
|
||||||
Crf = value.Crf;
|
Crf = value.Crf;
|
||||||
FrameRate = double.Parse(value.FrameRate ?? "0");
|
FrameRate = value.FrameRate ?? "24";
|
||||||
|
|
||||||
SelectedResolution = ResolutionList.FirstOrDefault(e => e.Content?.ToString() == value.Resolution) ?? ResolutionList.First();
|
SelectedResolution = ResolutionList.FirstOrDefault(e => e.Content?.ToString() == value.Resolution) ?? ResolutionList.First();
|
||||||
AdditionalParameters.Clear();
|
AdditionalParameters.Clear();
|
||||||
|
|
@ -149,7 +151,7 @@ public partial class ContentDialogEncodingPresetViewModel : ViewModelBase{
|
||||||
|
|
||||||
SelectedCustomPreset.PresetName = PresetName;
|
SelectedCustomPreset.PresetName = PresetName;
|
||||||
SelectedCustomPreset.Codec = Codec;
|
SelectedCustomPreset.Codec = Codec;
|
||||||
SelectedCustomPreset.FrameRate = Math.Clamp((int)(FrameRate ?? 1), 1, 999).ToString();
|
SelectedCustomPreset.FrameRate = FrameRate;
|
||||||
SelectedCustomPreset.Crf = Math.Clamp((int)(Crf ?? 0), 0, 51);
|
SelectedCustomPreset.Crf = Math.Clamp((int)(Crf ?? 0), 0, 51);
|
||||||
SelectedCustomPreset.Resolution = SelectedResolution.Content?.ToString() ?? "1920:1080";
|
SelectedCustomPreset.Resolution = SelectedResolution.Content?.ToString() ?? "1920:1080";
|
||||||
SelectedCustomPreset.AdditionalParameters = AdditionalParameters.Select(additionalParameter => additionalParameter.stringValue).ToList();
|
SelectedCustomPreset.AdditionalParameters = AdditionalParameters.Select(additionalParameter => additionalParameter.stringValue).ToList();
|
||||||
|
|
@ -171,7 +173,7 @@ public partial class ContentDialogEncodingPresetViewModel : ViewModelBase{
|
||||||
VideoPreset newPreset = new VideoPreset(){
|
VideoPreset newPreset = new VideoPreset(){
|
||||||
PresetName = PresetName,
|
PresetName = PresetName,
|
||||||
Codec = Codec,
|
Codec = Codec,
|
||||||
FrameRate = Math.Clamp((int)(FrameRate ?? 1), 1, 999).ToString(),
|
FrameRate = FrameRate,
|
||||||
Crf = Math.Clamp((int)(Crf ?? 0), 0, 51),
|
Crf = Math.Clamp((int)(Crf ?? 0), 0, 51),
|
||||||
Resolution = SelectedResolution.Content?.ToString() ?? "1920:1080",
|
Resolution = SelectedResolution.Content?.ToString() ?? "1920:1080",
|
||||||
AdditionalParameters = AdditionalParameters.Select(additionalParameter => additionalParameter.stringValue).ToList()
|
AdditionalParameters = AdditionalParameters.Select(additionalParameter => additionalParameter.stringValue).ToList()
|
||||||
|
|
|
||||||
|
|
@ -168,12 +168,20 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _proxyEnabled;
|
private bool _proxyEnabled;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _proxySocks;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _proxyHost;
|
private string _proxyHost;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private double? _proxyPort;
|
private double? _proxyPort;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _proxyUsername;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _proxyPassword;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _tempDownloadDirPath;
|
private string _tempDownloadDirPath;
|
||||||
|
|
@ -223,7 +231,10 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
|
||||||
}
|
}
|
||||||
|
|
||||||
ProxyEnabled = options.ProxyEnabled;
|
ProxyEnabled = options.ProxyEnabled;
|
||||||
|
ProxySocks = options.ProxySocks;
|
||||||
ProxyHost = options.ProxyHost ?? "";
|
ProxyHost = options.ProxyHost ?? "";
|
||||||
|
ProxyUsername = options.ProxyUsername ?? "";
|
||||||
|
ProxyPassword = options.ProxyPassword ?? "";
|
||||||
ProxyPort = options.ProxyPort;
|
ProxyPort = options.ProxyPort;
|
||||||
HistoryAddSpecials = options.HistoryAddSpecials;
|
HistoryAddSpecials = options.HistoryAddSpecials;
|
||||||
HistoryCountSonarr = options.HistoryCountSonarr;
|
HistoryCountSonarr = options.HistoryCountSonarr;
|
||||||
|
|
@ -259,8 +270,11 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
|
||||||
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.ProxyEnabled = ProxyEnabled;
|
CrunchyrollManager.Instance.CrunOptions.ProxyEnabled = ProxyEnabled;
|
||||||
|
CrunchyrollManager.Instance.CrunOptions.ProxySocks = ProxySocks;
|
||||||
CrunchyrollManager.Instance.CrunOptions.ProxyHost = ProxyHost;
|
CrunchyrollManager.Instance.CrunOptions.ProxyHost = ProxyHost;
|
||||||
CrunchyrollManager.Instance.CrunOptions.ProxyPort = Math.Clamp((int)(ProxyPort ?? 0), 0, 65535);
|
CrunchyrollManager.Instance.CrunOptions.ProxyPort = Math.Clamp((int)(ProxyPort ?? 0), 0, 65535);
|
||||||
|
CrunchyrollManager.Instance.CrunOptions.ProxyUsername = ProxyUsername;
|
||||||
|
CrunchyrollManager.Instance.CrunOptions.ProxyPassword = ProxyPassword;
|
||||||
|
|
||||||
string historyLang = SelectedHistoryLang.Content + "";
|
string historyLang = SelectedHistoryLang.Content + "";
|
||||||
|
|
||||||
|
|
@ -470,24 +484,33 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
|
||||||
if (CrunchyrollManager.Instance.CrunOptions.History){
|
if (CrunchyrollManager.Instance.CrunOptions.History){
|
||||||
if (File.Exists(CfgManager.PathCrHistory)){
|
if (File.Exists(CfgManager.PathCrHistory)){
|
||||||
var decompressedJson = CfgManager.DecompressJsonFile(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){
|
if (!string.IsNullOrEmpty(decompressedJson)){
|
||||||
|
var historyList = Helpers.Deserialize<ObservableCollection<HistorySeries>>(
|
||||||
|
decompressedJson,
|
||||||
|
CrunchyrollManager.Instance.SettingsJsonSerializerSettings
|
||||||
|
) ?? new ObservableCollection<HistorySeries>();
|
||||||
|
|
||||||
|
CrunchyrollManager.Instance.HistoryList = historyList;
|
||||||
|
|
||||||
|
Parallel.ForEach(historyList, historySeries => {
|
||||||
historySeries.Init();
|
historySeries.Init();
|
||||||
|
|
||||||
foreach (var historySeriesSeason in historySeries.Seasons){
|
foreach (var historySeriesSeason in historySeries.Seasons){
|
||||||
historySeriesSeason.Init();
|
historySeriesSeason.Init();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
} else{
|
} else{
|
||||||
CrunchyrollManager.Instance.HistoryList =[];
|
CrunchyrollManager.Instance.HistoryList = new ObservableCollection<HistorySeries>();
|
||||||
}
|
}
|
||||||
|
} else{
|
||||||
|
CrunchyrollManager.Instance.HistoryList = new ObservableCollection<HistorySeries>();
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = SonarrClient.Instance.RefreshSonarrLite();
|
_ = Task.Run(() => SonarrClient.Instance.RefreshSonarrLite());
|
||||||
} else{
|
} else{
|
||||||
CrunchyrollManager.Instance.HistoryList =[];
|
CrunchyrollManager.Instance.HistoryList = new ObservableCollection<HistorySeries>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -669,7 +669,8 @@
|
||||||
<Button Margin="0 0 5 0" FontStyle="Italic"
|
<Button Margin="0 0 5 0" FontStyle="Italic"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Command="{Binding DownloadEpisode}">
|
Command="{Binding DownloadEpisode}"
|
||||||
|
CommandParameter="false">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<controls:SymbolIcon Symbol="Download"
|
<controls:SymbolIcon Symbol="Download"
|
||||||
FontSize="18" />
|
FontSize="18" />
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@
|
||||||
<ui:NavigationView Grid.Row="1"
|
<ui:NavigationView Grid.Row="1"
|
||||||
IsPaneOpen="False"
|
IsPaneOpen="False"
|
||||||
IsPaneToggleButtonVisible="False"
|
IsPaneToggleButtonVisible="False"
|
||||||
|
IsEnabled="{Binding !ProgramManager.NavigationLock}"
|
||||||
IsSettingsVisible="False"
|
IsSettingsVisible="False"
|
||||||
CompactPaneLength="72"
|
CompactPaneLength="72"
|
||||||
Name="NavView"
|
Name="NavView"
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,7 @@ public partial class MainWindow : AppWindow{
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
private object selectedNavVieItem;
|
private object selectedNavVieItem;
|
||||||
|
|
||||||
private const int TitleBarHeightAdjustment = 31;
|
private const int TitleBarHeightAdjustment = 31;
|
||||||
|
|
|
||||||
|
|
@ -382,7 +382,9 @@
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button Margin="0 0 5 0" FontStyle="Italic" HorizontalAlignment="Right"
|
<Button Margin="0 0 5 0" FontStyle="Italic" HorizontalAlignment="Right"
|
||||||
VerticalAlignment="Center" Command="{Binding DownloadEpisode}">
|
VerticalAlignment="Center"
|
||||||
|
Command="{Binding DownloadEpisode}"
|
||||||
|
CommandParameter="false">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<controls:SymbolIcon Symbol="Download" FontSize="18" />
|
<controls:SymbolIcon Symbol="Download" FontSize="18" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
@ -467,6 +469,13 @@
|
||||||
Command="{Binding $parent[UserControl].((vm:SeriesPageViewModel)DataContext).DownloadSeasonMissingSonarr}"
|
Command="{Binding $parent[UserControl].((vm:SeriesPageViewModel)DataContext).DownloadSeasonMissingSonarr}"
|
||||||
CommandParameter="{Binding }">
|
CommandParameter="{Binding }">
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button Margin="10 5"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Content="Toggle Downloaded"
|
||||||
|
Command="{Binding $parent[UserControl].((vm:SeriesPageViewModel)DataContext).ToggleDownloadedMark}"
|
||||||
|
CommandParameter="{Binding }">
|
||||||
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
</Border>
|
</Border>
|
||||||
|
|
|
||||||
|
|
@ -23,85 +23,87 @@
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ComboBox.ItemTemplate>
|
</ComboBox.ItemTemplate>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Preset Name -->
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="Enter Preset Name" Margin="0,0,0,5" />
|
|
||||||
<TextBox Watermark="H.265 1080p" Text="{Binding PresetName}" />
|
|
||||||
<TextBlock Text="Preset name already used" FontSize="12" Foreground="{DynamicResource SystemFillColorCaution}"
|
|
||||||
IsVisible="{Binding FileExists}" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Codec -->
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="Enter Codec" Margin="0,10,0,5" />
|
|
||||||
<TextBox Watermark="libx265" Text="{Binding Codec}" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Resolution ComboBox -->
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="Select Resolution" Margin="0,10,0,5" />
|
|
||||||
<ComboBox HorizontalContentAlignment="Center" MinWidth="210" MaxDropDownHeight="400"
|
|
||||||
ItemsSource="{Binding ResolutionList}"
|
|
||||||
SelectedItem="{Binding SelectedResolution}">
|
|
||||||
</ComboBox>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Frame Rate NumberBox -->
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="Enter Frame Rate" Margin="0,10,0,5" />
|
|
||||||
<controls:NumberBox Minimum="1" Maximum="999"
|
|
||||||
Value="{Binding FrameRate}"
|
|
||||||
SpinButtonPlacementMode="Inline"
|
|
||||||
HorizontalAlignment="Stretch" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- CRF NumberBox -->
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="Enter CRF (0-51) - (cq,global_quality,qp)" Margin="0,10,0,5" />
|
|
||||||
<controls:NumberBox Minimum="0" Maximum="51"
|
|
||||||
Value="{Binding Crf}"
|
|
||||||
SpinButtonPlacementMode="Inline"
|
|
||||||
HorizontalAlignment="Stretch" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Additional Parameters -->
|
|
||||||
<StackPanel Margin="0,20,0,0">
|
|
||||||
<TextBlock Text="Additional Parameters" Margin="0,0,0,5" />
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
|
||||||
<TextBox HorizontalAlignment="Left" MinWidth="250"
|
|
||||||
Text="{Binding AdditionalParametersString}" />
|
|
||||||
<Button HorizontalAlignment="Center" Command="{Binding AddAdditionalParam}">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<controls:SymbolIcon Symbol="Add" FontSize="18" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<ItemsControl ItemsSource="{Binding AdditionalParameters}" Margin="0,10,0,0" MaxWidth="350">
|
|
||||||
<ItemsControl.ItemsPanel>
|
<!-- Preset Name -->
|
||||||
<ItemsPanelTemplate>
|
<StackPanel>
|
||||||
<WrapPanel />
|
<TextBlock Text="Enter Preset Name" Margin="0,0,0,5" />
|
||||||
</ItemsPanelTemplate>
|
<TextBox Watermark="H.265 1080p" Text="{Binding PresetName}" />
|
||||||
</ItemsControl.ItemsPanel>
|
<TextBlock Text="Preset name already used" FontSize="12" Foreground="{DynamicResource SystemFillColorCaution}"
|
||||||
<ItemsControl.ItemTemplate>
|
IsVisible="{Binding FileExists}" />
|
||||||
<DataTemplate>
|
</StackPanel>
|
||||||
<Border BorderBrush="#4a4a4a" Background="{DynamicResource ControlAltFillColorQuarternary}" BorderThickness="1"
|
|
||||||
CornerRadius="10" Margin="2">
|
<!-- Codec -->
|
||||||
<StackPanel Orientation="Horizontal" Margin="5">
|
<StackPanel>
|
||||||
<TextBlock Text="{Binding stringValue}" Margin="5,0" />
|
<TextBlock Text="Enter Codec" Margin="0,10,0,5" />
|
||||||
<Button Content="X" FontSize="10" VerticalAlignment="Center"
|
<TextBox Watermark="libx265" Text="{Binding Codec}" />
|
||||||
HorizontalAlignment="Center" Width="15" Height="15" Padding="0"
|
</StackPanel>
|
||||||
Command="{Binding $parent[ItemsControl].((utils:ContentDialogEncodingPresetViewModel)DataContext).RemoveAdditionalParam}"
|
|
||||||
CommandParameter="{Binding .}" />
|
<!-- Resolution ComboBox -->
|
||||||
</StackPanel>
|
<StackPanel>
|
||||||
</Border>
|
<TextBlock Text="Select Resolution" Margin="0,10,0,5" />
|
||||||
</DataTemplate>
|
<ComboBox HorizontalContentAlignment="Center" MinWidth="210" MaxDropDownHeight="400"
|
||||||
</ItemsControl.ItemTemplate>
|
ItemsSource="{Binding ResolutionList}"
|
||||||
</ItemsControl>
|
SelectedItem="{Binding SelectedResolution}">
|
||||||
</StackPanel>
|
</ComboBox>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Frame Rate NumberBox -->
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="Enter Frame Rate" Margin="0,10,0,5" />
|
||||||
|
<!-- <controls:NumberBox Minimum="1" Maximum="999" -->
|
||||||
|
<!-- Value="{Binding FrameRate}" -->
|
||||||
|
<!-- SpinButtonPlacementMode="Inline" -->
|
||||||
|
<!-- HorizontalAlignment="Stretch" /> -->
|
||||||
|
<TextBox Watermark="24" Text="{Binding FrameRate}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- CRF NumberBox -->
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="Enter CRF (0-51) - (cq,global_quality,qp)" Margin="0,10,0,5" />
|
||||||
|
<controls:NumberBox Minimum="0" Maximum="51"
|
||||||
|
Value="{Binding Crf}"
|
||||||
|
SpinButtonPlacementMode="Inline"
|
||||||
|
HorizontalAlignment="Stretch" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Additional Parameters -->
|
||||||
|
<StackPanel Margin="0,20,0,0">
|
||||||
|
<TextBlock Text="Additional Parameters" Margin="0,0,0,5" />
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||||
|
<TextBox HorizontalAlignment="Left" MinWidth="250"
|
||||||
|
Text="{Binding AdditionalParametersString}" />
|
||||||
|
<Button HorizontalAlignment="Center" Command="{Binding AddAdditionalParam}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<controls:SymbolIcon Symbol="Add" FontSize="18" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<ItemsControl ItemsSource="{Binding AdditionalParameters}" Margin="0,10,0,0" MaxWidth="350" HorizontalAlignment="Left">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<WrapPanel />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Border BorderBrush="#4a4a4a" Background="{DynamicResource ControlAltFillColorQuarternary}" BorderThickness="1"
|
||||||
|
CornerRadius="10" Margin="2">
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="5">
|
||||||
|
<TextBlock Text="{Binding stringValue}" Margin="5,0" />
|
||||||
|
<Button Content="X" FontSize="8" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||||
|
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
|
||||||
|
Width="15" Height="15" Padding="0"
|
||||||
|
Command="{Binding $parent[ItemsControl].((utils:ContentDialogEncodingPresetViewModel)DataContext).RemoveAdditionalParam}"
|
||||||
|
CommandParameter="{Binding .}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,13 @@
|
||||||
<CheckBox IsChecked="{Binding ProxyEnabled}"> </CheckBox>
|
<CheckBox IsChecked="{Binding ProxyEnabled}"> </CheckBox>
|
||||||
</controls:SettingsExpanderItem.Footer>
|
</controls:SettingsExpanderItem.Footer>
|
||||||
</controls:SettingsExpanderItem>
|
</controls:SettingsExpanderItem>
|
||||||
|
|
||||||
|
<controls:SettingsExpanderItem Content="Use Socks5 Proxy">
|
||||||
|
<controls:SettingsExpanderItem.Footer>
|
||||||
|
<CheckBox IsChecked="{Binding ProxySocks}"> </CheckBox>
|
||||||
|
</controls:SettingsExpanderItem.Footer>
|
||||||
|
</controls:SettingsExpanderItem>
|
||||||
|
|
||||||
|
|
||||||
<controls:SettingsExpanderItem Content="Host">
|
<controls:SettingsExpanderItem Content="Host">
|
||||||
<controls:SettingsExpanderItem.Footer>
|
<controls:SettingsExpanderItem.Footer>
|
||||||
|
|
@ -206,6 +213,22 @@
|
||||||
|
|
||||||
</controls:SettingsExpanderItem.Footer>
|
</controls:SettingsExpanderItem.Footer>
|
||||||
</controls:SettingsExpanderItem>
|
</controls:SettingsExpanderItem>
|
||||||
|
|
||||||
|
<controls:SettingsExpanderItem Content="Username">
|
||||||
|
<controls:SettingsExpanderItem.Footer>
|
||||||
|
<TextBox HorizontalAlignment="Left" MinWidth="250"
|
||||||
|
Text="{Binding ProxyUsername}" />
|
||||||
|
|
||||||
|
</controls:SettingsExpanderItem.Footer>
|
||||||
|
</controls:SettingsExpanderItem>
|
||||||
|
|
||||||
|
<controls:SettingsExpanderItem Content="Password">
|
||||||
|
<controls:SettingsExpanderItem.Footer>
|
||||||
|
<TextBox HorizontalAlignment="Left" MinWidth="250"
|
||||||
|
Text="{Binding ProxyPassword}" />
|
||||||
|
|
||||||
|
</controls:SettingsExpanderItem.Footer>
|
||||||
|
</controls:SettingsExpanderItem>
|
||||||
|
|
||||||
</controls:SettingsExpander>
|
</controls:SettingsExpander>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue