Add: Added checkbox to disable non-drm streams
Chg: Show additional info for downloads (slected dubs and subs) Chg: When pressing update it now shows the progress Chg: Changed how the error window looks Chg: Additional checks for premium episodes to make sure it is possible the logged in user can download the episode
This commit is contained in:
parent
d769e5e75e
commit
f0346fd4c8
|
@ -219,6 +219,12 @@ public class CrEpisode(Crunchyroll crunInstance){
|
|||
Time = 0,
|
||||
DownloadSpeed = 0
|
||||
};
|
||||
epMeta.AvailableSubs = item.SubtitleLocales;
|
||||
if (episode.Langs.Count > 0){
|
||||
epMeta.SelectedDubs = dubLang
|
||||
.Where(language => episode.Langs.Any(epLang => epLang.CrLocale == language))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
var epMetaData = epMeta.Data[0];
|
||||
if (!string.IsNullOrEmpty(item.StreamsLink)){
|
||||
|
|
|
@ -83,7 +83,12 @@ public class CrSeries(Crunchyroll crunInstance){
|
|||
Time = 0,
|
||||
DownloadSpeed = 0
|
||||
};
|
||||
|
||||
epMeta.AvailableSubs = item.SubtitleLocales;
|
||||
if (episode.Langs.Count > 0){
|
||||
epMeta.SelectedDubs = dubLang
|
||||
.Where(language => episode.Langs.Any(epLang => epLang.CrLocale == language))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
var epMetaData = epMeta.Data[0];
|
||||
|
|
|
@ -150,7 +150,7 @@ public class Crunchyroll{
|
|||
CrunOptions.History = true;
|
||||
|
||||
CfgManager.UpdateSettingsFromFile();
|
||||
|
||||
|
||||
if (CrunOptions.History){
|
||||
if (File.Exists(CfgManager.PathCrHistory)){
|
||||
HistoryList = JsonConvert.DeserializeObject<ObservableCollection<HistorySeries>>(File.ReadAllText(CfgManager.PathCrHistory)) ??[];
|
||||
|
@ -281,8 +281,8 @@ public class Crunchyroll{
|
|||
|
||||
|
||||
if (episodeL != null){
|
||||
if (episodeL.Value.Data != null && episodeL.Value.Data.First().IsPremiumOnly && Profile.Username == "???"){
|
||||
MessageBus.Current.SendMessage(new ToastMessage($"Episode is a premium episode - try to login first", ToastType.Error, 3));
|
||||
if (episodeL.Value.Data != null && episodeL.Value.Data.First().IsPremiumOnly && (episodeL.Value.Data.First().StreamsLink == null || Profile.Username == "???")){
|
||||
MessageBus.Current.SendMessage(new ToastMessage($"Episode is a premium episode – make sure that you are signed in with an account that has an active premium subscription", ToastType.Error, 3));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -292,6 +292,7 @@ public class Crunchyroll{
|
|||
foreach (var crunchyEpMeta in metas){
|
||||
Queue.Add(crunchyEpMeta);
|
||||
}
|
||||
|
||||
Console.WriteLine("Added Episode to Queue");
|
||||
MessageBus.Current.SendMessage(new ToastMessage($"Added episode to the queue", ToastType.Information, 1));
|
||||
}
|
||||
|
@ -300,8 +301,20 @@ public class Crunchyroll{
|
|||
public void AddSeriesToQueue(CrunchySeriesList list, CrunchyMultiDownload data){
|
||||
var selected = CrSeries.ItemSelectMultiDub(list.Data, data.DubLang, data.But, data.AllEpisodes, data.E);
|
||||
|
||||
bool failed = false;
|
||||
|
||||
foreach (var crunchyEpMeta in selected.Values.ToList()){
|
||||
Queue.Add(crunchyEpMeta);
|
||||
if (crunchyEpMeta.Data?.First().Playback != null){
|
||||
Queue.Add(crunchyEpMeta);
|
||||
} else{
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed){
|
||||
MainWindow.Instance.ShowError("Not all episodes could be added – make sure that you are signed in with an account that has an active premium subscription?");
|
||||
} else{
|
||||
MessageBus.Current.SendMessage(new ToastMessage($"Added episodes to the queue", ToastType.Information, 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -471,7 +484,7 @@ public class Crunchyroll{
|
|||
private async Task<DownloadResponse> DownloadMediaList(CrunchyEpMeta data, CrDownloadOptions options){
|
||||
if (CmsToken?.Cms == null){
|
||||
Console.WriteLine("Missing CMS Token");
|
||||
MainWindow.ShowError("Missing CMS Token - are you signed in?");
|
||||
MainWindow.Instance.ShowError("Missing CMS Token - are you signed in?");
|
||||
return new DownloadResponse{
|
||||
Data = new List<DownloadedMedia>(),
|
||||
Error = true,
|
||||
|
@ -480,7 +493,7 @@ public class Crunchyroll{
|
|||
}
|
||||
|
||||
if (Profile.Username == "???"){
|
||||
MainWindow.ShowError("User Account not recognized - are you signed in?");
|
||||
MainWindow.Instance.ShowError("User Account not recognized - are you signed in?");
|
||||
return new DownloadResponse{
|
||||
Data = new List<DownloadedMedia>(),
|
||||
Error = true,
|
||||
|
@ -490,7 +503,17 @@ public class Crunchyroll{
|
|||
|
||||
if (!File.Exists(CfgManager.PathFFMPEG)){
|
||||
Console.Error.WriteLine("Missing ffmpeg");
|
||||
MainWindow.ShowError("FFmpeg not found");
|
||||
MainWindow.Instance.ShowError("FFmpeg not found");
|
||||
return new DownloadResponse{
|
||||
Data = new List<DownloadedMedia>(),
|
||||
Error = true,
|
||||
FileName = "./unknown"
|
||||
};
|
||||
}
|
||||
|
||||
if (!options.UseNonDrmStreams && !_widevine.canDecrypt){
|
||||
Console.Error.WriteLine("Only searching for drm streams but widevine can't decrypt");
|
||||
MainWindow.Instance.ShowError("Settings set to not search for DRM streams - but can't find CDM files in widevine folder ");
|
||||
return new DownloadResponse{
|
||||
Data = new List<DownloadedMedia>(),
|
||||
Error = true,
|
||||
|
@ -506,7 +529,7 @@ public class Crunchyroll{
|
|||
|
||||
if (data.Data != null && data.Data.All(a => a.Playback == null)){
|
||||
Console.WriteLine("Video not available!");
|
||||
MainWindow.ShowError("No Video Data found");
|
||||
MainWindow.Instance.ShowError("No Video Data found - Are you trying to download a premium episode without havíng a premium account?");
|
||||
return new DownloadResponse{
|
||||
Data = files,
|
||||
Error = true,
|
||||
|
@ -544,7 +567,7 @@ public class Crunchyroll{
|
|||
|
||||
if (currentVersion.MediaGuid == null){
|
||||
Console.WriteLine("Selected language not found in versions.");
|
||||
MainWindow.ShowError("Selected language not found");
|
||||
MainWindow.Instance.ShowError("Selected language not found");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -583,7 +606,7 @@ public class Crunchyroll{
|
|||
var fetchPlaybackData = await FetchPlaybackData(mediaId, epMeta);
|
||||
|
||||
if (!fetchPlaybackData.IsOk){
|
||||
MainWindow.ShowError("Couldn't get Playback Data");
|
||||
MainWindow.Instance.ShowError("Couldn't get Playback Data");
|
||||
return new DownloadResponse{
|
||||
Data = new List<DownloadedMedia>(),
|
||||
Error = true,
|
||||
|
@ -641,7 +664,7 @@ public class Crunchyroll{
|
|||
|
||||
if (streams.Count < 1){
|
||||
Console.WriteLine("No full streams found!");
|
||||
MainWindow.ShowError("No streams found");
|
||||
MainWindow.Instance.ShowError("No streams found");
|
||||
return new DownloadResponse{
|
||||
Data = new List<DownloadedMedia>(),
|
||||
Error = true,
|
||||
|
@ -700,7 +723,6 @@ public class Crunchyroll{
|
|||
|
||||
StreamDetailsPop? curStream = null;
|
||||
if (!dlFailed){
|
||||
|
||||
options.Kstream = options.Kstream >= 1 && options.Kstream <= streams.Count
|
||||
? options.Kstream
|
||||
: 1;
|
||||
|
@ -726,7 +748,7 @@ public class Crunchyroll{
|
|||
if (!streamPlaylistsReqResponse.IsOk){
|
||||
dlFailed = true;
|
||||
}
|
||||
|
||||
|
||||
if (dlFailed){
|
||||
Console.WriteLine($"CAN\'T FETCH VIDEO PLAYLISTS!");
|
||||
} else{
|
||||
|
@ -847,7 +869,7 @@ public class Crunchyroll{
|
|||
LanguageItem? lang = Languages.languages.FirstOrDefault(a => a.Code == curStream.AudioLang);
|
||||
if (lang == null){
|
||||
Console.Error.WriteLine($"Unable to find language for code {curStream.AudioLang}");
|
||||
MainWindow.ShowError($"Unable to find language for code {curStream.AudioLang}");
|
||||
MainWindow.Instance.ShowError($"Unable to find language for code {curStream.AudioLang}");
|
||||
return new DownloadResponse{
|
||||
Data = new List<DownloadedMedia>(),
|
||||
Error = true,
|
||||
|
@ -869,7 +891,7 @@ public class Crunchyroll{
|
|||
|
||||
if (options.DlVideoOnce && dlVideoOnce){
|
||||
Console.WriteLine("Already downloaded video, skipping video download...");
|
||||
}else if (options.Novids){
|
||||
} else if (options.Novids){
|
||||
Console.WriteLine("Skipping video download...");
|
||||
} else{
|
||||
var videoDownloadResult = await DownloadVideo(chosenVideoSegments, options, outFile, tsFile, tempTsFile, data);
|
||||
|
@ -1059,7 +1081,7 @@ public class Crunchyroll{
|
|||
}
|
||||
} else{
|
||||
Console.WriteLine("mp4decrypt not found, files need decryption. Decryption Keys: ");
|
||||
MainWindow.ShowError($"mp4decrypt not found, files need decryption");
|
||||
MainWindow.Instance.ShowError($"mp4decrypt not found, files need decryption");
|
||||
}
|
||||
} else{
|
||||
if (videoDownloaded){
|
||||
|
@ -1082,7 +1104,7 @@ public class Crunchyroll{
|
|||
}
|
||||
} else if (!options.Novids){
|
||||
//TODO
|
||||
MainWindow.ShowError("Requested Video with the current settings not implemented");
|
||||
MainWindow.Instance.ShowError("Requested Video with the current settings not implemented");
|
||||
} else if (options.Novids){
|
||||
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers, options.Override).ToArray());
|
||||
Console.WriteLine("Downloading skipped!");
|
||||
|
@ -1395,15 +1417,14 @@ public class Crunchyroll{
|
|||
playbackRequestNonDrm.Headers.UserAgent.ParseAdd("Crunchyroll/1.8.0 Nintendo Switch/12.3.12.0 UE4/4.27");
|
||||
|
||||
var playbackRequestNonDrmResponse = await HttpClientReq.Instance.SendHttpRequest(playbackRequestNonDrm);
|
||||
|
||||
|
||||
if (playbackRequestNonDrmResponse.IsOk && playbackRequestNonDrmResponse.ResponseContent != string.Empty){
|
||||
CrunchyNoDrmStream? playStream = JsonConvert.DeserializeObject<CrunchyNoDrmStream>(playbackRequestNonDrmResponse.ResponseContent, SettingsJsonSerializerSettings);
|
||||
CrunchyStreams derivedPlayCrunchyStreams = new CrunchyStreams();
|
||||
if (playStream != null){
|
||||
|
||||
var deauthVideoToken = HttpClientReq.CreateRequestMessage($"https://cr-play-service.prd.crunchyrollsvc.com/v1/token/{currentMediaId}/{playStream.Token}/inactive", HttpMethod.Patch, true, false, null);
|
||||
var deauthVideoTokenResponse = await HttpClientReq.Instance.SendHttpRequest(deauthVideoToken);
|
||||
|
||||
|
||||
if (playStream.HardSubs != null)
|
||||
foreach (var hardsub in playStream.HardSubs){
|
||||
var stream = hardsub.Value;
|
||||
|
|
|
@ -141,6 +141,7 @@ public class CfgManager{
|
|||
Crunchyroll.Instance.CrunOptions.Theme = loadedOptions.Theme;
|
||||
Crunchyroll.Instance.CrunOptions.AccentColor = loadedOptions.AccentColor;
|
||||
Crunchyroll.Instance.CrunOptions.History = loadedOptions.History;
|
||||
Crunchyroll.Instance.CrunOptions.UseNonDrmStreams = loadedOptions.UseNonDrmStreams;
|
||||
}
|
||||
|
||||
private static object fileLock = new object();
|
||||
|
|
|
@ -111,4 +111,8 @@ public class CrDownloadOptions{
|
|||
|
||||
[YamlMember(Alias = "history", ApplyNamingConventions = false)]
|
||||
public bool History{ get; set; }
|
||||
|
||||
[YamlMember(Alias = "user_non_drm_streams", ApplyNamingConventions = false)]
|
||||
public bool UseNonDrmStreams{ get; set; }
|
||||
|
||||
}
|
|
@ -187,6 +187,10 @@ public class CrunchyEpMeta{
|
|||
public string? Image{ get; set; }
|
||||
public bool Paused{ get; set; }
|
||||
public DownloadProgress? DownloadProgress{ get; set; }
|
||||
|
||||
public List<string>? SelectedDubs{ get; set; }
|
||||
|
||||
public List<string>? AvailableSubs{ get; set; }
|
||||
}
|
||||
|
||||
public class DownloadProgress{
|
||||
|
|
|
@ -1,20 +1,27 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using CRD.ViewModels;
|
||||
using CRD.Views.Utils;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace CRD.Utils.Updater;
|
||||
|
||||
public class Updater{
|
||||
public class Updater : INotifyPropertyChanged{
|
||||
#region Singelton
|
||||
|
||||
private static Updater? _instance;
|
||||
private static readonly object Padlock = new();
|
||||
|
||||
public double progress = 0;
|
||||
|
||||
public static Updater Instance{
|
||||
get{
|
||||
if (_instance == null){
|
||||
|
@ -31,6 +38,12 @@ public class Updater{
|
|||
|
||||
#endregion
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null){
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
private string downloadUrl = "";
|
||||
private readonly string tempPath = Path.Combine(Path.GetTempPath(), "Update.zip");
|
||||
private readonly string extractPath = Path.Combine(Path.GetTempPath(), "ExtractedUpdate");
|
||||
|
@ -65,22 +78,52 @@ public class Updater{
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task DownloadAndUpdateAsync(){
|
||||
|
||||
|
||||
|
||||
try{
|
||||
using (var client = new HttpClient()){
|
||||
// Download the zip file
|
||||
var response = await client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
|
||||
using (var stream = await response.Content.ReadAsStreamAsync())
|
||||
using (var fileStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.None)){
|
||||
await stream.CopyToAsync(fileStream);
|
||||
|
||||
if (response.IsSuccessStatusCode){
|
||||
var totalBytes = response.Content.Headers.ContentLength ?? -1L;
|
||||
var totalBytesRead = 0L;
|
||||
var buffer = new byte[8192];
|
||||
var isMoreToRead = true;
|
||||
|
||||
using (var stream = await response.Content.ReadAsStreamAsync())
|
||||
using (var fileStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.None)){
|
||||
do{
|
||||
var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
|
||||
if (bytesRead == 0){
|
||||
isMoreToRead = false;
|
||||
progress = 100;
|
||||
OnPropertyChanged(nameof(progress));
|
||||
continue;
|
||||
}
|
||||
|
||||
await fileStream.WriteAsync(buffer, 0, bytesRead);
|
||||
|
||||
totalBytesRead += bytesRead;
|
||||
if (totalBytes != -1){
|
||||
progress = (double)totalBytesRead / totalBytes * 100;
|
||||
OnPropertyChanged(nameof(progress));
|
||||
}
|
||||
} while (isMoreToRead);
|
||||
}
|
||||
|
||||
ZipFile.ExtractToDirectory(tempPath, extractPath, true);
|
||||
|
||||
ApplyUpdate(extractPath);
|
||||
} else{
|
||||
Console.WriteLine("Failed to get Update");
|
||||
}
|
||||
|
||||
ZipFile.ExtractToDirectory(tempPath, extractPath, true);
|
||||
|
||||
ApplyUpdate(extractPath);
|
||||
}
|
||||
} catch (Exception e){
|
||||
Console.WriteLine("Failed to get Update");
|
||||
Console.WriteLine($"Failed to get Update: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ public partial class AddDownloadPageViewModel : ViewModelBase{
|
|||
|
||||
if (currentSeriesList != null){
|
||||
Crunchyroll.Instance.AddSeriesToQueue(currentSeriesList.Value, new CrunchyMultiDownload(Crunchyroll.Instance.CrunOptions.DubLang, AddAllEpisodes, false, selectedEpisodes));
|
||||
MessageBus.Current.SendMessage(new ToastMessage($"Added episodes to the queue", ToastType.Information, 1));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CRD.Downloader;
|
||||
using CRD.Utils.Structs;
|
||||
using CRD.Utils.Updater;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
|
||||
namespace CRD.ViewModels;
|
||||
|
||||
public partial class ContentDialogUpdateViewModel : ViewModelBase{
|
||||
private readonly ContentDialog dialog;
|
||||
|
||||
[ObservableProperty]
|
||||
private double _progress;
|
||||
|
||||
|
||||
private AccountPageViewModel accountPageViewModel;
|
||||
|
||||
public ContentDialogUpdateViewModel(ContentDialog dialog){
|
||||
if (dialog is null){
|
||||
throw new ArgumentNullException(nameof(dialog));
|
||||
}
|
||||
|
||||
this.dialog = dialog;
|
||||
dialog.Closed += DialogOnClosed;
|
||||
Updater.Instance.PropertyChanged += Progress_PropertyChanged;
|
||||
}
|
||||
|
||||
private void Progress_PropertyChanged(object? sender, PropertyChangedEventArgs e){
|
||||
if (e.PropertyName == nameof(Updater.Instance.progress)){
|
||||
Progress = Updater.Instance.progress;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void DialogOnClosed(ContentDialog sender, ContentDialogClosedEventArgs args){
|
||||
dialog.Closed -= DialogOnClosed;
|
||||
}
|
||||
}
|
|
@ -116,10 +116,26 @@ public partial class DownloadItemModel : INotifyPropertyChanged{
|
|||
Paused = epMeta.Paused || !isDownloading && !epMeta.Paused;
|
||||
DoingWhat = epMeta.Paused ? "Paused" : Done ? "Done" : epMeta.DownloadProgress.Doing != string.Empty ? epMeta.DownloadProgress.Doing : "Waiting";
|
||||
|
||||
if (epMeta.Data != null) InfoText = "Dub: " + epMeta.Data.First().Lang?.CrLocale + " - " + GetSubtitleString();
|
||||
if (epMeta.Data != null) InfoText = GetDubString() + " - " + GetSubtitleString();
|
||||
|
||||
Error = epMeta.DownloadProgress.Error;
|
||||
}
|
||||
|
||||
private string GetDubString(){
|
||||
var hardSubs = Crunchyroll.Instance.CrunOptions.Hslang != "none" ? "Hardsub: " + Crunchyroll.Instance.CrunOptions.Hslang : "";
|
||||
if (hardSubs != string.Empty){
|
||||
return hardSubs;
|
||||
}
|
||||
|
||||
var dubs = "Dub: ";
|
||||
|
||||
if (epMeta.SelectedDubs != null)
|
||||
foreach (var crunOptionsDlDub in epMeta.SelectedDubs){
|
||||
dubs += crunOptionsDlDub + " ";
|
||||
}
|
||||
|
||||
return dubs;
|
||||
}
|
||||
|
||||
private string GetSubtitleString(){
|
||||
var hardSubs = Crunchyroll.Instance.CrunOptions.Hslang != "none" ? "Hardsub: " + Crunchyroll.Instance.CrunOptions.Hslang : "";
|
||||
|
@ -129,8 +145,11 @@ public partial class DownloadItemModel : INotifyPropertyChanged{
|
|||
|
||||
var softSubs = "Softsub: ";
|
||||
|
||||
|
||||
foreach (var crunOptionsDlSub in Crunchyroll.Instance.CrunOptions.DlSubs){
|
||||
softSubs += crunOptionsDlSub + " ";
|
||||
if (epMeta.AvailableSubs != null && epMeta.AvailableSubs.Contains(crunOptionsDlSub)){
|
||||
softSubs += crunOptionsDlSub + " ";
|
||||
}
|
||||
}
|
||||
|
||||
return softSubs;
|
||||
|
@ -146,6 +165,8 @@ public partial class DownloadItemModel : INotifyPropertyChanged{
|
|||
Paused = epMeta.Paused || !isDownloading && !epMeta.Paused;
|
||||
DoingWhat = epMeta.Paused ? "Paused" : Done ? "Done" : epMeta.DownloadProgress.Doing != string.Empty ? epMeta.DownloadProgress.Doing : "Waiting";
|
||||
|
||||
if (epMeta.Data != null) InfoText = GetDubString() + " - " + GetSubtitleString();
|
||||
|
||||
Error = epMeta.DownloadProgress.Error;
|
||||
|
||||
if (PropertyChanged != null){
|
||||
|
@ -155,6 +176,7 @@ public partial class DownloadItemModel : INotifyPropertyChanged{
|
|||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DownloadSpeed)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DoingWhat)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Error)));
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(InfoText)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,16 +31,19 @@ public partial class SettingsPageViewModel : ViewModelBase{
|
|||
private bool _downloadChapters = true;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _muxToMp4 = false;
|
||||
private bool _muxToMp4;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _history = false;
|
||||
private bool _history;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _useNonDrmEndpoint = true;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _leadingNumbers = 0;
|
||||
private int _leadingNumbers;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _simultaneousDownloads = 0;
|
||||
private int _simultaneousDownloads;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _fileName = "";
|
||||
|
@ -76,7 +79,7 @@ public partial class SettingsPageViewModel : ViewModelBase{
|
|||
private ObservableCollection<ListBoxItem> _selectedSubLang = new();
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _useCustomAccent = false;
|
||||
private bool _useCustomAccent;
|
||||
|
||||
[ObservableProperty]
|
||||
private Color _listBoxColor;
|
||||
|
@ -174,7 +177,7 @@ public partial class SettingsPageViewModel : ViewModelBase{
|
|||
|
||||
private readonly FluentAvaloniaTheme _faTheme;
|
||||
|
||||
private bool settingsLoaded = false;
|
||||
private bool settingsLoaded;
|
||||
|
||||
public SettingsPageViewModel(){
|
||||
var version = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
|
@ -190,7 +193,10 @@ public partial class SettingsPageViewModel : ViewModelBase{
|
|||
}
|
||||
|
||||
CrDownloadOptions options = Crunchyroll.Instance.CrunOptions;
|
||||
|
||||
|
||||
ComboBoxItem? hsLang = HardSubLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == options.Hslang) ?? null;
|
||||
SelectedHSLang = hsLang ?? HardSubLangList[0];
|
||||
|
||||
var softSubLang = SubLangList.Where(a => options.DlSubs.Contains(a.Content)).ToList();
|
||||
|
||||
SelectedSubLang.Clear();
|
||||
|
@ -198,21 +204,6 @@ public partial class SettingsPageViewModel : ViewModelBase{
|
|||
SelectedSubLang.Add(listBoxItem);
|
||||
}
|
||||
|
||||
if (SelectedSubLang.Count == 0){
|
||||
SelectedSubs = "none";
|
||||
} else{
|
||||
SelectedSubs = SelectedSubLang[0].Content.ToString();
|
||||
for (var i = 1; i < SelectedSubLang.Count; i++){
|
||||
SelectedSubs += "," + SelectedSubLang[i].Content;
|
||||
}
|
||||
}
|
||||
|
||||
ComboBoxItem? hsLang = HardSubLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == options.Hslang) ?? null;
|
||||
SelectedHSLang = hsLang ?? HardSubLangList[0];
|
||||
|
||||
// ComboBoxItem? dubLang = DubLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == options.DubLang[0]) ?? null;
|
||||
// SelectedDubLang = dubLang ?? DubLangList[0];
|
||||
|
||||
var dubLang = DubLangList.Where(a => options.DubLang.Contains(a.Content)).ToList();
|
||||
|
||||
SelectedDubLang.Clear();
|
||||
|
@ -220,16 +211,10 @@ public partial class SettingsPageViewModel : ViewModelBase{
|
|||
SelectedDubLang.Add(listBoxItem);
|
||||
}
|
||||
|
||||
if (SelectedDubLang.Count == 0){
|
||||
SelectedDubs = "none";
|
||||
} else{
|
||||
SelectedDubs = SelectedDubLang[0].Content.ToString();
|
||||
for (var i = 1; i < SelectedDubLang.Count; i++){
|
||||
SelectedDubs += "," + SelectedDubLang[i].Content;
|
||||
}
|
||||
}
|
||||
UpdateSubAndDubString();
|
||||
|
||||
|
||||
UseNonDrmEndpoint = options.UseNonDrmStreams;
|
||||
DownloadVideo = !options.Novids;
|
||||
DownloadAudio = !options.Noaudio;
|
||||
DownloadChapters = options.Chapters;
|
||||
|
@ -266,23 +251,7 @@ public partial class SettingsPageViewModel : ViewModelBase{
|
|||
return;
|
||||
}
|
||||
|
||||
if (SelectedSubLang.Count == 0){
|
||||
SelectedSubs = "none";
|
||||
} else{
|
||||
SelectedSubs = SelectedSubLang[0].Content.ToString();
|
||||
for (var i = 1; i < SelectedSubLang.Count; i++){
|
||||
SelectedSubs += "," + SelectedSubLang[i].Content;
|
||||
}
|
||||
}
|
||||
|
||||
if (SelectedDubLang.Count == 0){
|
||||
SelectedDubs = "none";
|
||||
} else{
|
||||
SelectedDubs = SelectedDubLang[0].Content.ToString();
|
||||
for (var i = 1; i < SelectedDubLang.Count; i++){
|
||||
SelectedDubs += "," + SelectedDubLang[i].Content;
|
||||
}
|
||||
}
|
||||
UpdateSubAndDubString();
|
||||
|
||||
Crunchyroll.Instance.CrunOptions.Novids = !DownloadVideo;
|
||||
Crunchyroll.Instance.CrunOptions.Noaudio = !DownloadAudio;
|
||||
|
@ -314,7 +283,7 @@ public partial class SettingsPageViewModel : ViewModelBase{
|
|||
|
||||
Crunchyroll.Instance.CrunOptions.SimultaneousDownloads = SimultaneousDownloads;
|
||||
|
||||
|
||||
Crunchyroll.Instance.CrunOptions.UseNonDrmStreams = UseNonDrmEndpoint;
|
||||
Crunchyroll.Instance.CrunOptions.QualityAudio = SelectedAudioQuality?.Content + "";
|
||||
Crunchyroll.Instance.CrunOptions.QualityVideo = SelectedVideoQuality?.Content + "";
|
||||
Crunchyroll.Instance.CrunOptions.Theme = CurrentAppTheme?.Content + "";
|
||||
|
@ -330,6 +299,26 @@ public partial class SettingsPageViewModel : ViewModelBase{
|
|||
// Console.WriteLine("Updated Settings");
|
||||
}
|
||||
|
||||
private void UpdateSubAndDubString(){
|
||||
if (SelectedSubLang.Count == 0){
|
||||
SelectedSubs = "none";
|
||||
} else{
|
||||
SelectedSubs = SelectedSubLang[0].Content.ToString();
|
||||
for (var i = 1; i < SelectedSubLang.Count; i++){
|
||||
SelectedSubs += "," + SelectedSubLang[i].Content;
|
||||
}
|
||||
}
|
||||
|
||||
if (SelectedDubLang.Count == 0){
|
||||
SelectedDubs = "none";
|
||||
} else{
|
||||
SelectedDubs = SelectedDubLang[0].Content.ToString();
|
||||
for (var i = 1; i < SelectedDubLang.Count; i++){
|
||||
SelectedDubs += "," + SelectedDubLang[i].Content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnCurrentAppThemeChanged(ComboBoxItem? value){
|
||||
if (value?.Content?.ToString() == "System"){
|
||||
_faTheme.PreferSystemTheme = true;
|
||||
|
|
|
@ -58,8 +58,8 @@
|
|||
<TextBlock Grid.Row="0" Grid.Column="0" MaxHeight="117" Text="{Binding Title}" FontWeight="Bold" FontSize="16"
|
||||
TextWrapping="Wrap" VerticalAlignment="Top" />
|
||||
|
||||
<!-- <TextBlock Grid.Row="1" Grid.Column="0" MaxHeight="117" Text="{Binding InfoText}" Opacity="0.8" -->
|
||||
<!-- TextWrapping="Wrap" VerticalAlignment="Center" /> -->
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" MaxHeight="117" Text="{Binding InfoText}" Opacity="0.8"
|
||||
TextWrapping="Wrap" VerticalAlignment="Center" />
|
||||
|
||||
<Button Grid.Row="0" Grid.Column="1" Margin="0 0 5 0" IsVisible="{Binding !Error}" Command="{Binding ToggleIsDownloading}" FontStyle="Italic"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Top">
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<Design.DataContext>
|
||||
<vm:MainWindowViewModel />
|
||||
</Design.DataContext>
|
||||
<Grid>
|
||||
<Grid Name="mainGrid">
|
||||
<ContentControl x:Name="MainContent">
|
||||
<Grid RowDefinitions="Auto, *">
|
||||
|
||||
|
@ -76,8 +76,9 @@
|
|||
</Grid>
|
||||
</ContentControl>
|
||||
|
||||
<!-- Your main window content -->
|
||||
<!-- Tost Message -->
|
||||
<views:ToastNotification x:Name="Toast" IsVisible="False" />
|
||||
|
||||
</Grid>
|
||||
|
||||
|
||||
|
|
|
@ -1,22 +1,13 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CRD.Downloader;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using CRD.Utils.Updater;
|
||||
using CRD.ViewModels;
|
||||
using CRD.Views.Utils;
|
||||
using FluentAvalonia.Core;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using FluentAvalonia.UI.Navigation;
|
||||
using FluentAvalonia.UI.Windowing;
|
||||
using Newtonsoft.Json;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace CRD.Views;
|
||||
|
@ -25,9 +16,32 @@ public partial class MainWindow : AppWindow{
|
|||
private Stack<object> navigationStack = new Stack<object>();
|
||||
|
||||
|
||||
#region Singelton
|
||||
|
||||
private static MainWindow? _instance;
|
||||
private static readonly object Padlock = new();
|
||||
|
||||
public static MainWindow Instance{
|
||||
get{
|
||||
if (_instance == null){
|
||||
lock (Padlock){
|
||||
if (_instance == null){
|
||||
_instance = new MainWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private object selectedNavVieItem;
|
||||
|
||||
|
||||
public MainWindow(){
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
InitializeComponent();
|
||||
|
||||
TitleBar.ExtendsContentIntoTitleBar = true;
|
||||
|
@ -38,7 +52,7 @@ public partial class MainWindow : AppWindow{
|
|||
var nv = this.FindControl<NavigationView>("NavView");
|
||||
nv.SelectedItem = nv.MenuItems.ElementAt(0);
|
||||
selectedNavVieItem = nv.SelectedItem;
|
||||
|
||||
|
||||
MessageBus.Current.Listen<NavigationMessage>()
|
||||
.Subscribe(message => {
|
||||
if (message.Refresh){
|
||||
|
@ -61,23 +75,26 @@ public partial class MainWindow : AppWindow{
|
|||
.Subscribe(message => ShowToast(message.Message, message.Type, message.Seconds));
|
||||
}
|
||||
|
||||
public async void ShowError(string message){
|
||||
var dialog = new ContentDialog(){
|
||||
Title = "Error",
|
||||
Content = message,
|
||||
CloseButtonText = "Close"
|
||||
};
|
||||
|
||||
public static void ShowError(string message){
|
||||
var window = new ErrorWindow();
|
||||
window.SetErrorMessage(message);
|
||||
window.Show(); // 'this' is a reference to the parent window, if applicable
|
||||
_ = await dialog.ShowAsync();
|
||||
}
|
||||
|
||||
|
||||
public void ShowToast(string message, ToastType type, int durationInSeconds = 5){
|
||||
this.FindControl<ToastNotification>("Toast").Show(message, type, durationInSeconds);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void NavView_SelectionChanged(object? sender, NavigationViewSelectionChangedEventArgs e){
|
||||
if (sender is NavigationView navView){
|
||||
var selectedItem = navView.SelectedItem as NavigationViewItem;
|
||||
if (selectedItem != null){
|
||||
|
||||
switch (selectedItem.Tag){
|
||||
case "DownloadQueue":
|
||||
navView.Content = Activator.CreateInstance(typeof(DownloadsPageViewModel));
|
||||
|
@ -107,6 +124,7 @@ public partial class MainWindow : AppWindow{
|
|||
break;
|
||||
case "UpdateAvailable":
|
||||
Updater.Instance.DownloadAndUpdateAsync();
|
||||
ShowUpdateDialog();
|
||||
break;
|
||||
default:
|
||||
// (sender as NavigationView).Content = Activator.CreateInstance(typeof(DownloadsPageViewModel));
|
||||
|
@ -116,7 +134,19 @@ public partial class MainWindow : AppWindow{
|
|||
}
|
||||
}
|
||||
|
||||
public async void ShowUpdateDialog(){
|
||||
var dialog = new ContentDialog(){
|
||||
Title = "Updating",
|
||||
// CloseButtonText = "Close"
|
||||
};
|
||||
|
||||
var viewModel = new ContentDialogUpdateViewModel(dialog);
|
||||
dialog.Content = new ContentDialogUpdateView(){
|
||||
DataContext = viewModel
|
||||
};
|
||||
|
||||
_ = await dialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public class ToastMessage(string message, ToastType type, int i){
|
||||
|
|
|
@ -166,6 +166,12 @@
|
|||
<CheckBox IsChecked="{Binding DownloadChapters}"> </CheckBox>
|
||||
</controls:SettingsExpanderItem.Footer>
|
||||
</controls:SettingsExpanderItem>
|
||||
|
||||
<controls:SettingsExpanderItem Content="Check for Non-DRM streams">
|
||||
<controls:SettingsExpanderItem.Footer>
|
||||
<CheckBox IsChecked="{Binding UseNonDrmEndpoint}"> </CheckBox>
|
||||
</controls:SettingsExpanderItem.Footer>
|
||||
</controls:SettingsExpanderItem>
|
||||
|
||||
<controls:SettingsExpander.Footer>
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="clr-namespace:CRD.ViewModels"
|
||||
x:DataType="vm:ContentDialogUpdateViewModel"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="CRD.Views.Utils.ContentDialogUpdateView">
|
||||
|
||||
<StackPanel Spacing="10" MinWidth="400">
|
||||
|
||||
<ProgressBar Minimum="0" Maximum="100" Value="{Binding Progress}" HorizontalAlignment="Center" VerticalAlignment="Center" Width="350"/>
|
||||
|
||||
</StackPanel>
|
||||
</UserControl>
|
|
@ -0,0 +1,11 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace CRD.Views.Utils;
|
||||
|
||||
public partial class ContentDialogUpdateView : UserControl{
|
||||
public ContentDialogUpdateView(){
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="200"
|
||||
x:Class="CRD.Views.Utils.ErrorWindow"
|
||||
Icon="/Assets/app_icon.ico"
|
||||
Title="Error" Width="300" Height="150">
|
||||
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock HorizontalAlignment="Center" TextAlignment="Center" Margin="10" x:Name="ErrorMessage" Text="Error goes here" TextWrapping="Wrap" />
|
||||
<Button Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Center" Content="Close" Click="Close_Click"/>
|
||||
</Grid>
|
||||
|
||||
|
||||
</Window>
|
|
@ -1,23 +0,0 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace CRD.Views.Utils;
|
||||
|
||||
public partial class ErrorWindow : Window{
|
||||
public ErrorWindow(){
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void Close_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e){
|
||||
Close();
|
||||
}
|
||||
|
||||
private void InitializeComponent(){
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void SetErrorMessage(string message){
|
||||
this.FindControl<TextBlock>("ErrorMessage").Text = message;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue