mirror of
https://github.com/Crunchy-DL/Crunchy-Downloader.git
synced 2026-01-11 20:10:26 +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();
|
||||
}
|
||||
|
||||
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") :[];
|
||||
|
||||
foreach (var file in jsonFiles){
|
||||
try{
|
||||
// Read the content of the JSON file
|
||||
var jsonContent = File.ReadAllText(file);
|
||||
|
||||
// Deserialize the JSON content into a MyClass object
|
||||
var obj = Helpers.Deserialize<VideoPreset>(jsonContent, null);
|
||||
|
||||
if (obj != null){
|
||||
|
|
@ -210,6 +181,48 @@ public class CrunchyrollManager{
|
|||
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)){
|
||||
Console.Error.WriteLine("mp4decrypt not found");
|
||||
MainWindow.Instance.ShowError($"Can't find mp4decrypt in lib folder at: {CfgManager.PathMP4Decrypt}");
|
||||
if (!File.Exists(CfgManager.PathMP4Decrypt) && !File.Exists(CfgManager.PathShakaPackager)){
|
||||
Console.Error.WriteLine("mp4decrypt or shaka-packager not found");
|
||||
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{
|
||||
Data = new List<DownloadedMedia>(),
|
||||
Error = true,
|
||||
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 key = BitConverter.ToString(encryptionKeys[0].Bytes).Replace("-", "").ToLower();
|
||||
|
||||
//mp4decrypt
|
||||
var commandBase = $"--show-progress --key {keyId}:{key}";
|
||||
var tempTsFileName = Path.GetFileName(tempTsFile);
|
||||
var tempTsFileWorkDir = Path.GetDirectoryName(tempTsFile) ?? CfgManager.PathVIDEOS_DIR;
|
||||
var commandVideo = commandBase + $" \"{tempTsFileName}.video.enc.m4s\" \"{tempTsFileName}.video.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){
|
||||
Console.WriteLine("Started decrypting video");
|
||||
data.DownloadProgress = new DownloadProgress(){
|
||||
|
|
@ -1326,7 +1353,8 @@ public class CrunchyrollManager{
|
|||
Doing = "Decrypting video"
|
||||
};
|
||||
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){
|
||||
Console.Error.WriteLine($"Decryption failed with exit code {decryptVideo.ErrorCode}");
|
||||
|
|
@ -1394,7 +1422,8 @@ public class CrunchyrollManager{
|
|||
Doing = "Decrypting audio"
|
||||
};
|
||||
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){
|
||||
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 }){
|
||||
|
||||
variables.Add(new Variable("height", 360, false));
|
||||
variables.Add(new Variable("width", 640, false));
|
||||
|
||||
|
||||
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers, options.Override).ToArray());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -417,7 +417,7 @@
|
|||
</controls:SettingsExpanderItem.Footer>
|
||||
</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>
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
|
|
|||
|
|
@ -678,7 +678,7 @@ public class History(){
|
|||
}
|
||||
});
|
||||
|
||||
CfgManager.UpdateHistoryFile();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ public partial class ProgramManager : ObservableObject{
|
|||
|
||||
[ObservableProperty]
|
||||
private bool _finishedLoading = false;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _navigationLock = false;
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
@ -115,7 +118,7 @@ public partial class ProgramManager : ObservableObject{
|
|||
}
|
||||
|
||||
|
||||
private async void Init(){
|
||||
private async Task Init(){
|
||||
CrunchyrollManager.Instance.InitOptions();
|
||||
|
||||
UpdateAvailable = await Updater.Instance.CheckForUpdatesAsync();
|
||||
|
|
|
|||
|
|
@ -7,25 +7,25 @@ namespace CRD.Utils.Ffmpeg_Encoding;
|
|||
public class FfmpegEncoding{
|
||||
public static readonly List<VideoPreset> presets = new List<VideoPreset>{
|
||||
// AV1 Software
|
||||
new(){PresetName = "AV1 1080p24",Codec = "libaom-av1", Resolution = "1920:1080", FrameRate = "24", Crf = 30 } ,
|
||||
new(){PresetName = "AV1 720p24", Codec = "libaom-av1", Resolution = "1280:720", FrameRate = "24", Crf = 30 } ,
|
||||
new(){PresetName = "AV1 480p24", Codec = "libaom-av1", Resolution = "854:480", FrameRate = "24", Crf = 30 } ,
|
||||
new(){PresetName = "AV1 360p24", Codec = "libaom-av1", Resolution = "640:360", FrameRate = "24", Crf = 30 } ,
|
||||
new(){PresetName = "AV1 240p24", Codec = "libaom-av1", Resolution = "426:240", 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 = "24000/1001", Crf = 30, AdditionalParameters ={ "-map 0" } },
|
||||
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 = "24000/1001", Crf = 30, AdditionalParameters ={ "-map 0" } },
|
||||
new(){ PresetName = "AV1 240p24", Codec = "libaom-av1", Resolution = "426:240", FrameRate = "24000/1001", Crf = 30, AdditionalParameters ={ "-map 0" } },
|
||||
|
||||
// H.265 Software
|
||||
new(){PresetName = "H.265 1080p24", Codec = "libx265", Resolution = "1920:1080", FrameRate = "24", Crf = 28 } ,
|
||||
new(){PresetName = "H.265 720p24", Codec = "libx265", Resolution = "1280:720", FrameRate = "24", Crf = 28 } ,
|
||||
new(){PresetName = "H.265 480p24", Codec = "libx265", Resolution = "854:480", FrameRate = "24", Crf = 28 } ,
|
||||
new(){PresetName = "H.265 360p24", Codec = "libx265", Resolution = "640:360", FrameRate = "24", Crf = 28 } ,
|
||||
new(){PresetName = "H.265 240p24", Codec = "libx265", Resolution = "426:240", 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 = "24000/1001", Crf = 28, AdditionalParameters ={ "-map 0" } },
|
||||
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 = "24000/1001", Crf = 28, AdditionalParameters ={ "-map 0" } },
|
||||
new(){ PresetName = "H.265 240p24", Codec = "libx265", Resolution = "426:240", FrameRate = "24000/1001", Crf = 28, AdditionalParameters ={ "-map 0" } },
|
||||
|
||||
// H.264 Software
|
||||
new(){ PresetName = "H.264 1080p24",Codec = "libx264", Resolution = "1920:1080", FrameRate = "24", Crf = 23 } ,
|
||||
new(){PresetName = "H.264 720p24", Codec = "libx264", Resolution = "1280:720", FrameRate = "24", Crf = 23 } ,
|
||||
new(){PresetName = "H.264 480p24", Codec = "libx264", Resolution = "854:480", FrameRate = "24", Crf = 23 },
|
||||
new(){PresetName = "H.264 360p24", Codec = "libx264", Resolution = "640:360", FrameRate = "24", Crf = 23 } ,
|
||||
new(){PresetName = "H.264 240p24", Codec = "libx264", Resolution = "426:240", 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 = "24000/1001", Crf = 23, AdditionalParameters ={ "-map 0" } },
|
||||
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 = "24000/1001", Crf = 23, AdditionalParameters ={ "-map 0" } },
|
||||
new(){ PresetName = "H.264 240p24", Codec = "libx264", Resolution = "426:240", FrameRate = "24000/1001", Crf = 23, AdditionalParameters ={ "-map 0" } },
|
||||
};
|
||||
|
||||
public static VideoPreset? GetPreset(string presetName){
|
||||
|
|
@ -49,13 +49,11 @@ public class FfmpegEncoding{
|
|||
}
|
||||
|
||||
public class VideoPreset{
|
||||
|
||||
public string? PresetName{ get; set; }
|
||||
public string? Codec{ get; set; }
|
||||
public string? Resolution{ get; set; }
|
||||
public string? FrameRate{ 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";
|
||||
|
||||
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");
|
||||
|
||||
|
|
|
|||
|
|
@ -322,7 +322,22 @@ public class Helpers{
|
|||
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(inputFilePath);
|
||||
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);
|
||||
|
||||
TimeSpan? totalDuration = await GetMediaDurationAsync(CfgManager.PathFFMPEG, inputFilePath);
|
||||
|
|
|
|||
|
|
@ -48,8 +48,10 @@ public class HttpClientReq{
|
|||
HttpClientHandler handler = new HttpClientHandler();
|
||||
|
||||
if (CrunchyrollManager.Instance.CrunOptions.ProxyEnabled && !string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.ProxyHost)){
|
||||
handler = CreateHandler(true, CrunchyrollManager.Instance.CrunOptions.ProxyHost, CrunchyrollManager.Instance.CrunOptions.ProxyPort);
|
||||
Console.Error.WriteLine($"Proxy is set: http://{CrunchyrollManager.Instance.CrunOptions.ProxyHost}:{CrunchyrollManager.Instance.CrunOptions.ProxyPort}");
|
||||
handler = CreateHandler(true, CrunchyrollManager.Instance.CrunOptions.ProxySocks, 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);
|
||||
} else if (systemProxy != null){
|
||||
Uri testUri = new Uri("https://icanhazip.com");
|
||||
|
|
@ -73,7 +75,7 @@ public class HttpClientReq{
|
|||
Console.Error.WriteLine("No proxy is being used.");
|
||||
client = new HttpClient(CreateHttpClientHandler());
|
||||
}
|
||||
|
||||
|
||||
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");
|
||||
|
|
@ -85,7 +87,6 @@ public class HttpClientReq{
|
|||
client.DefaultRequestHeaders.AcceptEncoding.ParseAdd("gzip, deflate, br");
|
||||
client.DefaultRequestHeaders.AcceptLanguage.ParseAdd("en-US,en;q=0.5");
|
||||
client.DefaultRequestHeaders.Connection.ParseAdd("keep-alive");
|
||||
|
||||
}
|
||||
|
||||
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{
|
||||
CookieContainer = new CookieContainer(),
|
||||
UseCookies = true,
|
||||
|
|
@ -119,7 +120,11 @@ public class HttpClientReq{
|
|||
};
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -89,13 +89,22 @@ public class CrDownloadOptions{
|
|||
|
||||
[YamlMember(Alias = "proxy_enabled", ApplyNamingConventions = false)]
|
||||
public bool ProxyEnabled{ get; set; }
|
||||
|
||||
[YamlMember(Alias = "proxy_socks", ApplyNamingConventions = false)]
|
||||
public bool ProxySocks{ get; set; }
|
||||
|
||||
[YamlMember(Alias = "proxy_host", ApplyNamingConventions = false)]
|
||||
public string? ProxyHost{ get; set; }
|
||||
|
||||
[YamlMember(Alias = "proxy_port", ApplyNamingConventions = false)]
|
||||
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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@ public class HistorySeason : INotifyPropertyChanged{
|
|||
[JsonIgnore]
|
||||
public StringItem? _selectedVideoQualityItem;
|
||||
|
||||
[JsonIgnore]
|
||||
private bool Loading = false;
|
||||
|
||||
[JsonIgnore]
|
||||
public StringItem? SelectedVideoQualityItem{
|
||||
get => _selectedVideoQualityItem;
|
||||
|
|
@ -59,7 +62,9 @@ public class HistorySeason : INotifyPropertyChanged{
|
|||
|
||||
HistorySeasonVideoQualityOverride = value?.stringValue ?? "";
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedVideoQualityItem)));
|
||||
CfgManager.UpdateHistoryFile();
|
||||
if (!Loading){
|
||||
CfgManager.UpdateHistoryFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -127,6 +132,7 @@ public class HistorySeason : INotifyPropertyChanged{
|
|||
}
|
||||
|
||||
public void Init(){
|
||||
Loading = true;
|
||||
if (!(SubLangList.Count > 2 || DubLangList.Count > 0)){
|
||||
foreach (var languageItem in Languages.languages){
|
||||
SubLangList.Add(new StringItem{ stringValue = languageItem.CrLocale });
|
||||
|
|
@ -154,6 +160,7 @@ public class HistorySeason : INotifyPropertyChanged{
|
|||
|
||||
SelectedSubLang.CollectionChanged += Changes;
|
||||
SelectedDubLang.CollectionChanged += Changes;
|
||||
Loading = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -94,6 +94,9 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
|
||||
#region Settings Override
|
||||
|
||||
[JsonIgnore]
|
||||
private bool Loading = false;
|
||||
|
||||
[JsonIgnore]
|
||||
public StringItem? _selectedVideoQualityItem;
|
||||
|
||||
|
|
@ -105,7 +108,10 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
|
||||
HistorySeriesVideoQualityOverride = value?.stringValue ?? "";
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedVideoQualityItem)));
|
||||
CfgManager.UpdateHistoryFile();
|
||||
if (!Loading){
|
||||
CfgManager.UpdateHistoryFile();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,6 +179,7 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
}
|
||||
|
||||
public void Init(){
|
||||
Loading = true;
|
||||
if (!(SubLangList.Count > 2 || DubLangList.Count > 0)){
|
||||
foreach (var languageItem in Languages.languages){
|
||||
SubLangList.Add(new StringItem{ stringValue = languageItem.CrLocale });
|
||||
|
|
@ -200,6 +207,7 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
|
||||
SelectedSubLang.CollectionChanged += Changes;
|
||||
SelectedDubLang.CollectionChanged += Changes;
|
||||
Loading = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -43,12 +43,20 @@ public class Updater : INotifyPropertyChanged{
|
|||
}
|
||||
|
||||
private string downloadUrl = "";
|
||||
private readonly string tempPath = Path.Combine(Path.GetTempPath(), "Update.zip");
|
||||
private readonly string extractPath = Path.Combine(Path.GetTempPath(), "ExtractedUpdate");
|
||||
private readonly string tempPath = Path.Combine(CfgManager.PathTEMP_DIR, "Update.zip");
|
||||
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";
|
||||
|
||||
public async Task<bool> CheckForUpdatesAsync(){
|
||||
if (Directory.Exists(tempPath)){
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
|
||||
if (Directory.Exists(extractPath)){
|
||||
Directory.Delete(extractPath, true);
|
||||
}
|
||||
|
||||
try{
|
||||
var platformAssetMapping = new Dictionary<OSPlatform, string>{
|
||||
{ OSPlatform.Windows, "windows" },
|
||||
|
|
@ -114,6 +122,8 @@ public class Updater : INotifyPropertyChanged{
|
|||
|
||||
public async Task DownloadAndUpdateAsync(){
|
||||
try{
|
||||
Helpers.EnsureDirectoriesExist(tempPath);
|
||||
|
||||
// Download the zip file
|
||||
var response = await HttpClientReq.Instance.GetHttpClient().GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
|
||||
|
||||
|
|
@ -160,8 +170,9 @@ public class Updater : INotifyPropertyChanged{
|
|||
}
|
||||
|
||||
private void ApplyUpdate(string updateFolder){
|
||||
var ExecutableExtension = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty;
|
||||
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}\"";
|
||||
|
||||
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 }){
|
||||
CrunchyrollManager.Instance.History.MatchHistoryEpisodesWithSonarr(true, SelectedSeries);
|
||||
CfgManager.UpdateHistoryFile();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -370,6 +371,7 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
SonarrOptionsOpen = false;
|
||||
AddingMissingSonarrSeries = true;
|
||||
ProgramManager.FetchingData = true;
|
||||
ProgramManager.NavigationLock = true;
|
||||
|
||||
var crInstance = CrunchyrollManager.Instance;
|
||||
|
||||
|
|
@ -407,6 +409,7 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
|
||||
// Await the CRUpdateSeries task for each seriesId
|
||||
await crInstance.History.CRUpdateSeries(seriesIds[count], "");
|
||||
RaisePropertyChanged(nameof(ProgressText));
|
||||
}
|
||||
|
||||
// var updateTasks = seriesIds.Select(seriesId => crInstance.History.CRUpdateSeries(seriesId, ""));
|
||||
|
|
@ -416,6 +419,7 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
ProgressText = "";
|
||||
AddingMissingSonarrSeries = false;
|
||||
ProgramManager.FetchingData = false;
|
||||
ProgramManager.NavigationLock = false;
|
||||
if (SelectedFilter != null){
|
||||
OnSelectedFilterChanged(SelectedFilter);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,5 +11,4 @@ public partial class MainWindowViewModel : ViewModelBase{
|
|||
public MainWindowViewModel(ProgramManager manager){
|
||||
ProgramManager = manager;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -48,9 +48,8 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
|||
public bool _seriesFolderPathExists;
|
||||
|
||||
public SeriesPageViewModel(){
|
||||
|
||||
_storageProvider = ProgramManager.Instance.StorageProvider ?? throw new ArgumentNullException(nameof(ProgramManager.Instance.StorageProvider));
|
||||
|
||||
|
||||
_selectedSeries = CrunchyrollManager.Instance.SelectedSeries;
|
||||
|
||||
if (_selectedSeries.ThumbnailImage == null){
|
||||
|
|
@ -153,7 +152,7 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
|||
|
||||
UpdateSeriesFolderPath();
|
||||
}
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
public async Task MatchSonarrSeries_Button(){
|
||||
var dialog = new ContentDialog(){
|
||||
|
|
@ -221,7 +220,18 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
|||
|
||||
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]
|
||||
public async Task UpdateData(string? season){
|
||||
await SelectedSeries.FetchData(season);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public partial class ContentDialogEncodingPresetViewModel : ViewModelBase{
|
|||
private double? _crf = 23;
|
||||
|
||||
[ObservableProperty]
|
||||
private double? _frameRate = 30;
|
||||
private string _frameRate = "";
|
||||
|
||||
[ObservableProperty]
|
||||
private string _additionalParametersString = "";
|
||||
|
|
@ -82,6 +82,8 @@ public partial class ContentDialogEncodingPresetViewModel : ViewModelBase{
|
|||
if (dialog is null){
|
||||
throw new ArgumentNullException(nameof(dialog));
|
||||
}
|
||||
|
||||
AdditionalParameters.Add(new StringItem(){ stringValue = "-map 0" });
|
||||
|
||||
if (editMode){
|
||||
EditMode = true;
|
||||
|
|
@ -106,7 +108,7 @@ public partial class ContentDialogEncodingPresetViewModel : ViewModelBase{
|
|||
PresetName = value.PresetName ?? "";
|
||||
Codec = value.Codec ?? "";
|
||||
Crf = value.Crf;
|
||||
FrameRate = double.Parse(value.FrameRate ?? "0");
|
||||
FrameRate = value.FrameRate ?? "24";
|
||||
|
||||
SelectedResolution = ResolutionList.FirstOrDefault(e => e.Content?.ToString() == value.Resolution) ?? ResolutionList.First();
|
||||
AdditionalParameters.Clear();
|
||||
|
|
@ -149,7 +151,7 @@ public partial class ContentDialogEncodingPresetViewModel : ViewModelBase{
|
|||
|
||||
SelectedCustomPreset.PresetName = PresetName;
|
||||
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.Resolution = SelectedResolution.Content?.ToString() ?? "1920:1080";
|
||||
SelectedCustomPreset.AdditionalParameters = AdditionalParameters.Select(additionalParameter => additionalParameter.stringValue).ToList();
|
||||
|
|
@ -171,7 +173,7 @@ public partial class ContentDialogEncodingPresetViewModel : ViewModelBase{
|
|||
VideoPreset newPreset = new VideoPreset(){
|
||||
PresetName = PresetName,
|
||||
Codec = Codec,
|
||||
FrameRate = Math.Clamp((int)(FrameRate ?? 1), 1, 999).ToString(),
|
||||
FrameRate = FrameRate,
|
||||
Crf = Math.Clamp((int)(Crf ?? 0), 0, 51),
|
||||
Resolution = SelectedResolution.Content?.ToString() ?? "1920:1080",
|
||||
AdditionalParameters = AdditionalParameters.Select(additionalParameter => additionalParameter.stringValue).ToList()
|
||||
|
|
|
|||
|
|
@ -168,12 +168,20 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
|
|||
[ObservableProperty]
|
||||
private bool _proxyEnabled;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _proxySocks;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _proxyHost;
|
||||
|
||||
[ObservableProperty]
|
||||
private double? _proxyPort;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _proxyUsername;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _proxyPassword;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _tempDownloadDirPath;
|
||||
|
|
@ -223,7 +231,10 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
|
|||
}
|
||||
|
||||
ProxyEnabled = options.ProxyEnabled;
|
||||
ProxySocks = options.ProxySocks;
|
||||
ProxyHost = options.ProxyHost ?? "";
|
||||
ProxyUsername = options.ProxyUsername ?? "";
|
||||
ProxyPassword = options.ProxyPassword ?? "";
|
||||
ProxyPort = options.ProxyPort;
|
||||
HistoryAddSpecials = options.HistoryAddSpecials;
|
||||
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.ProxyEnabled = ProxyEnabled;
|
||||
CrunchyrollManager.Instance.CrunOptions.ProxySocks = ProxySocks;
|
||||
CrunchyrollManager.Instance.CrunOptions.ProxyHost = ProxyHost;
|
||||
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 + "";
|
||||
|
||||
|
|
@ -470,24 +484,33 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
|
|||
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){
|
||||
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();
|
||||
|
||||
foreach (var historySeriesSeason in historySeries.Seasons){
|
||||
historySeriesSeason.Init();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} 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{
|
||||
CrunchyrollManager.Instance.HistoryList =[];
|
||||
CrunchyrollManager.Instance.HistoryList = new ObservableCollection<HistorySeries>();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -669,7 +669,8 @@
|
|||
<Button Margin="0 0 5 0" FontStyle="Italic"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding DownloadEpisode}">
|
||||
Command="{Binding DownloadEpisode}"
|
||||
CommandParameter="false">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<controls:SymbolIcon Symbol="Download"
|
||||
FontSize="18" />
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
<ui:NavigationView Grid.Row="1"
|
||||
IsPaneOpen="False"
|
||||
IsPaneToggleButtonVisible="False"
|
||||
IsEnabled="{Binding !ProgramManager.NavigationLock}"
|
||||
IsSettingsVisible="False"
|
||||
CompactPaneLength="72"
|
||||
Name="NavView"
|
||||
|
|
|
|||
|
|
@ -45,8 +45,7 @@ public partial class MainWindow : AppWindow{
|
|||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
private object selectedNavVieItem;
|
||||
|
||||
private const int TitleBarHeightAdjustment = 31;
|
||||
|
|
|
|||
|
|
@ -382,7 +382,9 @@
|
|||
</Button>
|
||||
|
||||
<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">
|
||||
<controls:SymbolIcon Symbol="Download" FontSize="18" />
|
||||
</StackPanel>
|
||||
|
|
@ -467,6 +469,13 @@
|
|||
Command="{Binding $parent[UserControl].((vm:SeriesPageViewModel)DataContext).DownloadSeasonMissingSonarr}"
|
||||
CommandParameter="{Binding }">
|
||||
</Button>
|
||||
<Button Margin="10 5"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
Content="Toggle Downloaded"
|
||||
Command="{Binding $parent[UserControl].((vm:SeriesPageViewModel)DataContext).ToggleDownloadedMark}"
|
||||
CommandParameter="{Binding }">
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
</Border>
|
||||
|
|
|
|||
|
|
@ -23,85 +23,87 @@
|
|||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</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>
|
||||
|
||||
<ItemsControl ItemsSource="{Binding AdditionalParameters}" Margin="0,10,0,0" MaxWidth="350">
|
||||
<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="10" VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center" Width="15" Height="15" Padding="0"
|
||||
Command="{Binding $parent[ItemsControl].((utils:ContentDialogEncodingPresetViewModel)DataContext).RemoveAdditionalParam}"
|
||||
CommandParameter="{Binding .}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</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" /> -->
|
||||
<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>
|
||||
|
||||
|
|
|
|||
|
|
@ -189,6 +189,13 @@
|
|||
<CheckBox IsChecked="{Binding ProxyEnabled}"> </CheckBox>
|
||||
</controls:SettingsExpanderItem.Footer>
|
||||
</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.Footer>
|
||||
|
|
@ -206,6 +213,22 @@
|
|||
|
||||
</controls:SettingsExpanderItem.Footer>
|
||||
</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>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue