- Added option to **allow early start of the next download**

- Updated some images
- Fixed **audio not always being 48 kHz** when the second endpoint is used
This commit is contained in:
Elwador 2025-09-11 22:30:13 +02:00
parent 15c62193ca
commit 62583953cc
14 changed files with 90 additions and 44 deletions

View file

@ -197,8 +197,8 @@ public class CrunchyrollManager{
CrunOptions.StreamEndpoint = "tv/android_tv";
CrAuthEndpoint1.AuthSettings = new CrAuthSettings(){
Endpoint = "tv/android_tv",
Authorization = "Basic Ym1icmt4eXgzZDd1NmpzZnlsYTQ6QUlONEQ1VkVfY3Awd1Z6Zk5vUDBZcUhVcllGcDloU2c=",
UserAgent = "ANDROIDTV/3.42.1_22267 Android/16",
Authorization = "Basic Y2I5bnpybWh0MzJ2Z3RleHlna286S1V3bU1qSlh4eHVyc0hJVGQxenZsMkMyeVFhUW84TjQ=",
UserAgent = "ANDROIDTV/3.42.1_22273 Android/16",
Device_name = "Android TV",
Device_type = "Android TV"
};
@ -213,14 +213,14 @@ public class CrunchyrollManager{
Device_type = "OnePlus CPH2449"
};
}
CrAuthEndpoint2.AuthSettings = CrunOptions.StreamEndpointSecondSettings;
await CrAuthEndpoint1.Auth();
if (!string.IsNullOrEmpty(CrAuthEndpoint2.AuthSettings.Endpoint)){
await CrAuthEndpoint2.Auth();
}
CfgManager.WriteCrSettings();
// var token = await GetBase64EncodedTokenAsync();
@ -281,17 +281,11 @@ public class CrunchyrollManager{
await SonarrClient.Instance.RefreshSonarr();
}
//Fix hslang - can be removed in a future version
var lang = Languages.Locale2language(CrunOptions.Hslang);
if (lang != Languages.DEFAULT_lang){
CrunOptions.Hslang = lang.CrLocale;
}
}
public async Task<bool> DownloadEpisode(CrunchyEpMeta data, CrDownloadOptions options){
QueueManager.Instance.ActiveDownloads++;
QueueManager.Instance.IncrementDownloads();
data.DownloadProgress = new DownloadProgress(){
IsDownloading = true,
@ -305,7 +299,7 @@ public class CrunchyrollManager{
var res = await DownloadMediaList(data, options);
if (res.Error){
QueueManager.Instance.ActiveDownloads--;
QueueManager.Instance.DecrementDownloads();
data.DownloadProgress = new DownloadProgress(){
IsDownloading = false,
Error = true,
@ -318,6 +312,10 @@ public class CrunchyrollManager{
return false;
}
if (options.DownloadAllowEarlyStart){
QueueManager.Instance.DecrementDownloads();
}
if (options.SkipMuxing == false){
bool syncError = false;
bool muxError = false;
@ -511,7 +509,10 @@ public class CrunchyrollManager{
}
QueueManager.Instance.ActiveDownloads--;
if (!options.DownloadAllowEarlyStart){
QueueManager.Instance.IncrementDownloads();
}
QueueManager.Instance.Queue.Refresh();
if (options.History && data.Data is{ Count: > 0 } && (options.HistoryIncludeCrArtists && data.Music || !data.Music)){
@ -523,7 +524,7 @@ public class CrunchyrollManager{
_ = CrEpisode.MarkAsWatched(data.Data.First().MediaId);
}
if (QueueManager.Instance.Queue.Count == 0){
if (QueueManager.Instance.Queue.Count == 0 || QueueManager.Instance.Queue.All(e => e.DownloadProgress.Done)){
try{
var audioPath = CrunOptions.DownloadFinishedSoundPath;
if (!string.IsNullOrEmpty(audioPath)){
@ -1192,7 +1193,7 @@ public class CrunchyrollManager{
Console.WriteLine("Downloading video...");
curStream = streams[options.Kstream - 1];
Console.WriteLine($"Playlists URL: {string.Join(", ", curStream.Url)} ({curStream.Type})");
Console.WriteLine($"Playlists URL: {string.Join(", ", curStream.Url.Select(u => u.Url))} ({curStream.Type})");
}
string tsFile = "";
@ -1321,8 +1322,8 @@ public class CrunchyrollManager{
// Audio: Remove duplicates, then sort by bandwidth
audios = audios
.GroupBy(a => new{ a.bandwidth, a.language }) // Add more properties if needed
.Select(g => g.First())
.GroupBy(a => new{ a.bandwidth, a.language })
.Select(g => g.OrderByDescending(x => x.audioSamplingRate).First())
.OrderBy(a => a.bandwidth)
.ThenBy(a => a.audioSamplingRate)
.ToList();
@ -1385,20 +1386,22 @@ public class CrunchyrollManager{
foreach (var server in streamServers){
Console.WriteLine($"\t{server}");
}
Console.WriteLine("Available Video Qualities:");
var sb = new StringBuilder();
sb.AppendLine("Available Video Qualities:");
for (int i = 0; i < videos.Count; i++){
Console.WriteLine($"\t[{i + 1}] {videos[i].resolutionText}");
sb.AppendLine($"\t- {videos[i].resolutionText}");
}
Console.WriteLine("Available Audio Qualities:");
sb.AppendLine("Available Audio Qualities:");
for (int i = 0; i < audios.Count; i++){
Console.WriteLine($"\t[{i + 1}] {audios[i].resolutionText} / {audios[i].audioSamplingRate}");
sb.AppendLine($"\t- {audios[i].resolutionText} / {audios[i].audioSamplingRate}");
}
variables.Add(new Variable("height", chosenVideoSegments.quality.height, false));
variables.Add(new Variable("width", chosenVideoSegments.quality.width, false));
if (string.IsNullOrEmpty(data.Resolution)) data.Resolution = chosenVideoSegments.quality.height + "p";
LanguageItem? lang = Languages.languages.FirstOrDefault(a => a.CrLocale == curStream.AudioLang.CrLocale);
if (lang == null){
@ -1412,11 +1415,18 @@ public class CrunchyrollManager{
};
}
Console.WriteLine($"Selected quality:");
Console.WriteLine($"\tVideo: {chosenVideoSegments.resolutionText}");
Console.WriteLine($"\tAudio: {chosenAudioSegments.resolutionText} / {chosenAudioSegments.audioSamplingRate}");
Console.WriteLine($"\tServer: {selectedServer}");
sb.AppendLine($"Selected quality:");
sb.AppendLine($"\tVideo: {chosenVideoSegments.resolutionText}");
sb.AppendLine($"\tAudio: {chosenAudioSegments.resolutionText} / {chosenAudioSegments.audioSamplingRate}");
sb.AppendLine($"\tServer: {selectedServer}");
string qualityConsoleLog = sb.ToString();
Console.WriteLine(qualityConsoleLog);
data.AvailableQualities = qualityConsoleLog;
Console.WriteLine("Stream URL:" + chosenVideoSegments.segments[0].uri.Split(new[]{ ",.urlset" }, StringSplitOptions.None)[0]);
fileName = Path.Combine(FileNameManager.ParseFileName(options.FileName, variables, options.Numbers, options.FileNameWhitespaceSubstitute, options.Override).ToArray());
@ -2302,7 +2312,7 @@ public class CrunchyrollManager{
private async Task<(bool IsOk, string ResponseContent, string error)> SendPlaybackRequestAsync(string endpoint, CrAuth authEndpoint){
var request = HttpClientReq.CreateRequestMessage(endpoint, HttpMethod.Get, true, authEndpoint.Token?.access_token, null);
request.Headers.UserAgent.ParseAdd(authEndpoint.AuthSettings.UserAgent);
return await HttpClientReq.Instance.SendHttpRequest(request,false,authEndpoint.cookieStore);
return await HttpClientReq.Instance.SendHttpRequest(request, false, authEndpoint.cookieStore);
}
private async Task<(bool IsOk, string ResponseContent, string error)> HandleStreamErrorsAsync((bool IsOk, string ResponseContent, string error) response, string endpoint, CrAuth authEndpoint){

View file

@ -691,9 +691,7 @@ public partial class CrunchyrollSettingsViewModel : ViewModelBase{
_ = await dialog.ShowAsync();
if (CrunchyrollManager.Instance.CrAuthEndpoint2.Profile.Username == "???"){
EndpointNotSignedWarning = true;
}
EndpointNotSignedWarning = CrunchyrollManager.Instance.CrAuthEndpoint2.Profile.Username == "???";
}

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CRD.Downloader.Crunchyroll;
@ -21,7 +22,9 @@ public partial class QueueManager : ObservableObject{
public RefreshableObservableCollection<CrunchyEpMeta> Queue = new RefreshableObservableCollection<CrunchyEpMeta>();
public ObservableCollection<DownloadItemModel> DownloadItemModels = new ObservableCollection<DownloadItemModel>();
public int ActiveDownloads;
private int activeDownloads;
public int ActiveDownloads => Volatile.Read(ref activeDownloads);
#endregion
@ -53,6 +56,20 @@ public partial class QueueManager : ObservableObject{
Queue.CollectionChanged += UpdateItemListOnRemove;
}
public void IncrementDownloads(){
Interlocked.Increment(ref activeDownloads);
}
public void DecrementDownloads(){
while (true){
int current = Volatile.Read(ref activeDownloads);
if (current == 0) return;
if (Interlocked.CompareExchange(ref activeDownloads, current - 1, current) == current)
return;
}
}
private void UpdateItemListOnRemove(object? sender, NotifyCollectionChangedEventArgs e){
if (e.Action == NotifyCollectionChangedAction.Remove){

View file

@ -70,8 +70,6 @@ public class HttpClientReq{
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");
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36");
// client.DefaultRequestHeaders.UserAgent.ParseAdd("Crunchyroll/1.9.0 Nintendo Switch/18.1.0.0 UE4/4.27");

View file

@ -31,6 +31,9 @@ public class CrDownloadOptions{
[JsonProperty("download_methode_new")]
public bool DownloadMethodeNew{ get; set; }
[JsonProperty("download_allow_early_start")]
public bool DownloadAllowEarlyStart{ get; set; }
[JsonProperty("simultaneous_downloads")]
public int SimultaneousDownloads{ get; set; }

View file

@ -384,6 +384,8 @@ public class CrunchyEpMeta{
public bool Music{ get; set; }
public string Resolution{ get; set; }
public string AvailableQualities{ get; set; }
public List<string> downloadedFiles{ get; set; } =[];

View file

@ -109,6 +109,7 @@ public partial class DownloadItemModel : INotifyPropertyChanged{
public string DoingWhat{ get; set; }
public string DownloadSpeed{ get; set; }
public string InfoText{ get; set; }
public string InfoTextHover{ get; set; }
public CrunchyEpMeta epMeta{ get; set; }
@ -137,6 +138,7 @@ public partial class DownloadItemModel : INotifyPropertyChanged{
GetSubtitleString(),
epMeta.Resolution
);
InfoTextHover = epMeta.AvailableQualities;
Error = epMeta.DownloadProgress.Error;
}
@ -202,7 +204,7 @@ public partial class DownloadItemModel : INotifyPropertyChanged{
GetSubtitleString(),
epMeta.Resolution
);
InfoTextHover = epMeta.AvailableQualities;
Error = epMeta.DownloadProgress.Error;
@ -214,6 +216,7 @@ public partial class DownloadItemModel : INotifyPropertyChanged{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DoingWhat)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Error)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(InfoText)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(InfoTextHover)));
}
}

View file

@ -56,6 +56,9 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
[ObservableProperty]
private bool _downloadMethodeNew;
[ObservableProperty]
private bool _downloadAllowEarlyStart;
[ObservableProperty]
private double? _downloadSpeed;
@ -272,6 +275,7 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
HistoryCountSonarr = options.HistoryCountSonarr;
DownloadSpeed = options.DownloadSpeedLimit;
DownloadMethodeNew = options.DownloadMethodeNew;
DownloadAllowEarlyStart = options.DownloadAllowEarlyStart;
RetryAttempts = Math.Clamp((options.RetryAttempts), 1, 10);
RetryDelay = Math.Clamp((options.RetryDelay), 1, 30);
DownloadToTempFolder = options.DownloadToTempFolder;
@ -300,6 +304,7 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
settings.DownloadFinishedPlaySound = DownloadFinishedPlaySound;
settings.DownloadMethodeNew = DownloadMethodeNew;
settings.DownloadAllowEarlyStart = DownloadAllowEarlyStart;
settings.BackgroundImageBlurRadius = Math.Clamp((BackgroundImageBlurRadius ?? 0), 0, 40);
settings.BackgroundImageOpacity = Math.Clamp((BackgroundImageOpacity ?? 0), 0, 1);

View file

@ -1,4 +1,4 @@
<UserControl xmlns="https://github.com/avaloniaui"
<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"
@ -98,7 +98,11 @@
TextWrapping="Wrap" VerticalAlignment="Top" />
<TextBlock Grid.Row="1" Grid.Column="0" MaxHeight="117" Text="{Binding InfoText}" Opacity="0.8"
TextWrapping="Wrap" VerticalAlignment="Center" />
TextWrapping="Wrap" VerticalAlignment="Center">
<ToolTip.Tip>
<TextBlock Text="{Binding InfoTextHover}" FontSize="14"></TextBlock>
</ToolTip.Tip>
</TextBlock>
<Button Grid.Row="0" Grid.Column="1" Margin="0 0 5 0" IsVisible="{Binding !Error}" Command="{Binding ToggleIsDownloading}" FontStyle="Italic"
HorizontalAlignment="Right" VerticalAlignment="Top">

View file

@ -75,6 +75,12 @@
<CheckBox IsChecked="{Binding DownloadMethodeNew}"> </CheckBox>
</controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Allow early start of next download" Description="When enabled, the next download starts as soon as the previous file has finished downloading, even if it is still being finalized (muxed/moved).">
<controls:SettingsExpanderItem.Footer>
<CheckBox IsChecked="{Binding DownloadAllowEarlyStart}"> </CheckBox>
</controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Max Download Speed"
Description="Download in Kb/s - 0 is full speed">

BIN
images/Download_Queue.png (Stored with Git LFS)

Binary file not shown.

BIN
images/History_Series_Overview.png (Stored with Git LFS)

Binary file not shown.

BIN
images/Settings.png (Stored with Git LFS)

Binary file not shown.

BIN
images/Settings_Download.png (Stored with Git LFS)

Binary file not shown.