- Changed so that most requests now use a guest token

- Updated android tokens
- Fixed **episodes being hidden in the calendar when the history language was not `en-US`
- Fixed **download speed unit formatting** from `Mb/s` to `MB/s` [#385](https://github.com/Crunchy-DL/Crunchy-Downloader/pull/385)
This commit is contained in:
Elwador 2026-01-24 02:48:52 +01:00
parent c7687c80e8
commit 6abbc129b6
16 changed files with 133 additions and 57 deletions

View file

@ -293,7 +293,7 @@ public class CalendarManager{
string? seasonTitle = string.IsNullOrEmpty(crBrowseEpisode.EpisodeMetadata.SeasonTitle)
? crBrowseEpisode.EpisodeMetadata.SeriesTitle
: Regex.IsMatch(crBrowseEpisode.EpisodeMetadata.SeasonTitle, @"^Season\s+\d+$", RegexOptions.IgnoreCase)
: LooksLikeGenericSeasonLabel(crBrowseEpisode.EpisodeMetadata.SeasonTitle, crBrowseEpisode.EpisodeMetadata.SeasonNumber)
? $"{crBrowseEpisode.EpisodeMetadata.SeriesTitle} {crBrowseEpisode.EpisodeMetadata.SeasonTitle}"
: crBrowseEpisode.EpisodeMetadata.SeasonTitle;
@ -312,7 +312,7 @@ public class CalendarManager{
calEpisode.AudioLocale = crBrowseEpisode.EpisodeMetadata.AudioLocale;
var existingEpisode = calendarDay.CalendarEpisodes
.FirstOrDefault(e => e.SeasonName == calEpisode.SeasonName && e.AudioLocale == calEpisode.AudioLocale);
.FirstOrDefault(e => e.CrSeriesID == calEpisode.CrSeriesID && e.AudioLocale == calEpisode.AudioLocale);
if (existingEpisode != null){
if (!int.TryParse(existingEpisode.EpisodeNumber, out _)){
@ -569,6 +569,24 @@ public class CalendarManager{
}
}
private bool LooksLikeGenericSeasonLabel(string? seasonTitle, double? seasonNo){
if (string.IsNullOrWhiteSpace(seasonTitle)) return true;
var t = seasonTitle.Trim();
var m = Regex.Match(t, @"^(?<word>\p{L}+(?:[\p{L}\p{Mn}'\.\- ]*\p{L})?)\s+(?<n>\d+)$",
RegexOptions.CultureInvariant);
if (!m.Success) return false;
if (seasonNo.HasValue &&
double.TryParse(m.Groups["n"].Value, NumberStyles.None, CultureInfo.InvariantCulture, out var n)){
return n == seasonNo.Value;
}
return true;
}
#region Query
private string query = @"query ($weekStart: Int, $weekEnd: Int, $page: Int) {

View file

@ -16,6 +16,7 @@ public class CrEpisode(){
private readonly CrunchyrollManager crunInstance = CrunchyrollManager.Instance;
public async Task<CrunchyEpisode?> ParseEpisodeById(string id, string crLocale, bool forcedLang = false){
await crunInstance.CrAuthGuest.RefreshToken(true);
NameValueCollection query = HttpUtility.ParseQueryString(new UriBuilder().Query);
query["preferred_audio_language"] = "ja-JP";
@ -27,7 +28,7 @@ public class CrEpisode(){
}
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/episodes/{id}", HttpMethod.Get, true, crunInstance.CrAuthEndpoint1.Token?.access_token, query);
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/episodes/{id}", HttpMethod.Get, true, crunInstance.CrAuthGuest.Token?.access_token, query);
var response = await HttpClientReq.Instance.SendHttpRequest(request);
@ -51,7 +52,7 @@ public class CrEpisode(){
//guid for episode id
foreach (var episodeVersionse in list){
foreach (var version in episodeVersionse){
var checkRequest = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/episodes/{version.Guid}", HttpMethod.Get, true, crunInstance.CrAuthEndpoint1.Token?.access_token, query);
var checkRequest = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/episodes/{version.Guid}", HttpMethod.Get, true, crunInstance.CrAuthGuest.Token?.access_token, query);
var checkResponse = await HttpClientReq.Instance.SendHttpRequest(checkRequest, true);
if (!checkResponse.IsOk){
epsidoe.Data.First().Versions?.Remove(version);
@ -197,7 +198,7 @@ public class CrEpisode(){
Error = false,
Percent = 0,
Time = 0,
DownloadSpeed = 0
DownloadSpeedBytes = 0
};
epMeta.AvailableSubs = item.SubtitleLocales;
epMeta.Description = item.Description;
@ -236,7 +237,7 @@ public class CrEpisode(){
}
public async Task<CrBrowseEpisodeBase?> GetNewEpisodes(string? crLocale, int requestAmount, DateTime? firstWeekDay = null, bool forcedLang = false){
await crunInstance.CrAuthEndpoint1.RefreshToken(true);
await crunInstance.CrAuthGuest.RefreshToken(true);
if (string.IsNullOrEmpty(crLocale)){
@ -256,7 +257,7 @@ public class CrEpisode(){
query["sort_by"] = "newly_added";
query["type"] = "episode";
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Browse}", HttpMethod.Get, true, crunInstance.CrAuthEndpoint1.Token?.access_token, query);
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Browse}", HttpMethod.Get, true, crunInstance.CrAuthGuest.Token?.access_token, query);
var response = await HttpClientReq.Instance.SendHttpRequest(request);

View file

@ -14,6 +14,7 @@ public class CrMovies{
private readonly CrunchyrollManager crunInstance = CrunchyrollManager.Instance;
public async Task<CrunchyMovie?> ParseMovieById(string id, string crLocale, bool forcedLang = false){
await crunInstance.CrAuthGuest.RefreshToken(true);
NameValueCollection query = HttpUtility.ParseQueryString(new UriBuilder().Query);
query["preferred_audio_language"] = "ja-JP";
@ -25,7 +26,7 @@ public class CrMovies{
}
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/objects/{id}", HttpMethod.Get, true, crunInstance.CrAuthEndpoint1.Token?.access_token, query);
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/objects/{id}", HttpMethod.Get, true, crunInstance.CrAuthGuest.Token?.access_token, query);
var response = await HttpClientReq.Instance.SendHttpRequest(request);
@ -81,7 +82,7 @@ public class CrMovies{
Error = false,
Percent = 0,
Time = 0,
DownloadSpeed = 0
DownloadSpeedBytes = 0
};
epMeta.AvailableSubs = [];
epMeta.Description = episodeP.Description;

View file

@ -105,8 +105,9 @@ public class CrMusic{
}
public async Task<CrArtist> ParseArtistByIdAsync(string id, string crLocale, bool forcedLang = false){
await crunInstance.CrAuthGuest.RefreshToken(true);
var query = CreateQueryParameters(crLocale, forcedLang);
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Content}/music/artists/{id}", HttpMethod.Get, true, crunInstance.CrAuthEndpoint1.Token?.access_token, query);
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Content}/music/artists/{id}", HttpMethod.Get, true, crunInstance.CrAuthGuest.Token?.access_token, query);
var response = await HttpClientReq.Instance.SendHttpRequest(request);
@ -135,8 +136,9 @@ public class CrMusic{
}
private async Task<CrunchyMusicVideoList> FetchMediaListAsync(string url, string crLocale, bool forcedLang){
await crunInstance.CrAuthGuest.RefreshToken(true);
var query = CreateQueryParameters(crLocale, forcedLang);
var request = HttpClientReq.CreateRequestMessage(url, HttpMethod.Get, true, crunInstance.CrAuthEndpoint1.Token?.access_token, query);
var request = HttpClientReq.CreateRequestMessage(url, HttpMethod.Get, true, crunInstance.CrAuthGuest.Token?.access_token, query);
var response = await HttpClientReq.Instance.SendHttpRequest(request);
@ -186,7 +188,7 @@ public class CrMusic{
Error = false,
Percent = 0,
Time = 0,
DownloadSpeed = 0
DownloadSpeedBytes = 0
};
epMeta.AvailableSubs = new List<string>();
epMeta.Description = episodeP.Description;

View file

@ -77,7 +77,7 @@ public class CrSeries{
Done = false,
Percent = 0,
Time = 0,
DownloadSpeed = 0
DownloadSpeedBytes = 0
};
epMeta.Hslang = CrunchyrollManager.Instance.CrunOptions.Hslang;
epMeta.Description = item.Description;
@ -120,8 +120,6 @@ public class CrSeries{
public async Task<CrunchySeriesList?> ListSeriesId(string id, string crLocale, CrunchyMultiDownload? data, bool forcedLocale = false){
await crunInstance.CrAuthEndpoint1.RefreshToken(true);
bool serieshasversions = true;
CrSeriesSearch? parsedSeries = await ParseSeriesById(id, crLocale, forcedLocale);
@ -291,6 +289,7 @@ public class CrSeries{
}
public async Task<CrunchyEpisodeList> GetSeasonDataById(string seasonId, string? crLocale, bool forcedLang = false, bool log = false){
await crunInstance.CrAuthGuest.RefreshToken(true);
CrunchyEpisodeList episodeList = new CrunchyEpisodeList(){ Data = new List<CrunchyEpisode>(), Total = 0, Meta = new Meta() };
NameValueCollection query;
@ -305,7 +304,7 @@ public class CrSeries{
}
}
var showRequest = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/seasons/{seasonId}", HttpMethod.Get, true, crunInstance.CrAuthEndpoint1.Token?.access_token, query);
var showRequest = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/seasons/{seasonId}", HttpMethod.Get, true, crunInstance.CrAuthGuest.Token?.access_token, query);
var response = await HttpClientReq.Instance.SendHttpRequest(showRequest);
@ -326,7 +325,7 @@ public class CrSeries{
}
}
var episodeRequest = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/seasons/{seasonId}/episodes", HttpMethod.Get, true, crunInstance.CrAuthEndpoint1.Token?.access_token, query);
var episodeRequest = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/seasons/{seasonId}/episodes", HttpMethod.Get, true, crunInstance.CrAuthGuest.Token?.access_token, query);
var episodeRequestResponse = await HttpClientReq.Instance.SendHttpRequest(episodeRequest);
@ -345,7 +344,7 @@ public class CrSeries{
}
public async Task<CrSeriesSearch?> ParseSeriesById(string id, string? crLocale, bool forced = false){
await crunInstance.CrAuthEndpoint1.RefreshToken(true);
await crunInstance.CrAuthGuest.RefreshToken(true);
NameValueCollection query = HttpUtility.ParseQueryString(new UriBuilder().Query);
query["preferred_audio_language"] = "ja-JP";
@ -357,7 +356,7 @@ public class CrSeries{
}
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/series/{id}/seasons", HttpMethod.Get, true, crunInstance.CrAuthEndpoint1.Token?.access_token, query);
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/series/{id}/seasons", HttpMethod.Get, true, crunInstance.CrAuthGuest.Token?.access_token, query);
var response = await HttpClientReq.Instance.SendHttpRequest(request);
@ -377,7 +376,9 @@ public class CrSeries{
}
public async Task<CrSeriesBase?> SeriesById(string id, string? crLocale, bool forced = false){
await crunInstance.CrAuthEndpoint1.RefreshToken(true);
await crunInstance.CrAuthGuest.RefreshToken(true);
NameValueCollection query = HttpUtility.ParseQueryString(new UriBuilder().Query);
query["preferred_audio_language"] = "ja-JP";
@ -388,7 +389,7 @@ public class CrSeries{
}
}
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/series/{id}", HttpMethod.Get, true, crunInstance.CrAuthEndpoint1.Token?.access_token, query);
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Cms}/series/{id}", HttpMethod.Get, true, crunInstance.CrAuthGuest.Token?.access_token, query);
var response = await HttpClientReq.Instance.SendHttpRequest(request);
@ -409,7 +410,8 @@ public class CrSeries{
public async Task<CrSearchSeriesBase?> Search(string searchString, string? crLocale, bool forced = false){
await crunInstance.CrAuthEndpoint1.RefreshToken(true);
await crunInstance.CrAuthGuest.RefreshToken(true);
NameValueCollection query = HttpUtility.ParseQueryString(new UriBuilder().Query);
if (!string.IsNullOrEmpty(crLocale)){
@ -423,7 +425,7 @@ public class CrSeries{
query["n"] = "6";
query["type"] = "series";
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Search}", HttpMethod.Get, true, crunInstance.CrAuthEndpoint1.Token?.access_token, query);
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Search}", HttpMethod.Get, true, crunInstance.CrAuthGuest.Token?.access_token, query);
var response = await HttpClientReq.Instance.SendHttpRequest(request);
@ -452,6 +454,7 @@ public class CrSeries{
}
public async Task<CrBrowseSeriesBase?> GetAllSeries(string? crLocale){
await crunInstance.CrAuthGuest.RefreshToken(true);
CrBrowseSeriesBase complete = new CrBrowseSeriesBase();
complete.Data =[];
@ -468,7 +471,7 @@ public class CrSeries{
query["n"] = "50";
query["sort_by"] = "alphabetical";
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Browse}", HttpMethod.Get, true, crunInstance.CrAuthEndpoint1.Token?.access_token, query);
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Browse}", HttpMethod.Get, true, crunInstance.CrAuthGuest.Token?.access_token, query);
var response = await HttpClientReq.Instance.SendHttpRequest(request);
@ -494,6 +497,7 @@ public class CrSeries{
}
public async Task<CrBrowseSeriesBase?> GetSeasonalSeries(string season, string year, string? crLocale){
await crunInstance.CrAuthGuest.RefreshToken(true);
NameValueCollection query = HttpUtility.ParseQueryString(new UriBuilder().Query);
if (!string.IsNullOrEmpty(crLocale)){
@ -503,7 +507,7 @@ public class CrSeries{
query["seasonal_tag"] = season.ToLower() + "-" + year;
query["n"] = "100";
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Browse}", HttpMethod.Get, true, crunInstance.CrAuthEndpoint1.Token?.access_token, query);
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Browse}", HttpMethod.Get, true, crunInstance.CrAuthGuest.Token?.access_token, query);
var response = await HttpClientReq.Instance.SendHttpRequest(request);

View file

@ -52,6 +52,7 @@ public class CrunchyrollManager{
public string DefaultLocale = "en-US";
public CrAuthSettings DefaultAndroidAuthSettings = new CrAuthSettings();
public CrAuthSettings GuestAndroidAuthSettings = new CrAuthSettings();
public JsonSerializerSettings? SettingsJsonSerializerSettings = new(){
NullValueHandling = NullValueHandling.Ignore,
@ -61,6 +62,8 @@ public class CrunchyrollManager{
public CrAuth CrAuthEndpoint1;
public CrAuth CrAuthEndpoint2;
public CrAuth CrAuthGuest;
public CrEpisode CrEpisode;
public CrSeries CrSeries;
@ -161,6 +164,9 @@ public class CrunchyrollManager{
CrAuthEndpoint1.Init();
CrAuthEndpoint2 = new CrAuth(this, new CrAuthSettings());
CrAuthEndpoint2.Init();
CrAuthGuest = new CrAuth(this, new CrAuthSettings());
CrAuthGuest.Init();
CrEpisode = new CrEpisode();
CrSeries = new CrSeries();
@ -201,15 +207,14 @@ public class CrunchyrollManager{
DefaultAndroidAuthSettings = new CrAuthSettings(){
Endpoint = "android/phone",
Client_ID = "pd6uw3dfyhzghs0wxae3",
Authorization = "Basic cGQ2dXczZGZ5aHpnaHMwd3hhZTM6NXJ5SjJFQXR3TFc0UklIOEozaWk1anVqbnZrRWRfTkY=",
UserAgent = "Crunchyroll/3.95.2 Android/16 okhttp/4.12.0",
Authorization = "Basic bzJhNndsamdub3FtdjloMWJ5bHI6Ujk3S3ExZm5faExZVFk0bDJxTjJIT2lDQnpfYnpBSUU=",
UserAgent = "Crunchyroll/3.97.0 Android/16 okhttp/4.12.0",
Device_name = "CPH2449",
Device_type = "OnePlus CPH2449",
Audio = true,
Video = true,
};
CrunOptions.StreamEndpoint ??= new CrAuthSettings(){ Endpoint = "tv/android_tv", Audio = true, Video = true };
CrunOptions.StreamEndpoint.Endpoint = "tv/android_tv";
CrAuthEndpoint1.AuthSettings = new CrAuthSettings(){
@ -232,6 +237,10 @@ public class CrunchyrollManager{
await CrAuthEndpoint2.Auth();
}
CrAuthGuest.AuthSettings = GuestAndroidAuthSettings;
CrAuthGuest.EndpointEnum = CrunchyrollEndpoints.Guest;
await CrAuthGuest.AuthAnonymousFoxy();
CfgManager.WriteCrSettings();
// var token = await GetBase64EncodedTokenAsync();
@ -303,7 +312,7 @@ public class CrunchyrollManager{
Error = false,
Percent = 0,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Starting"
};
QueueManager.Instance.Queue.Refresh();
@ -323,7 +332,7 @@ public class CrunchyrollManager{
Error = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Download Error" + (!string.IsNullOrEmpty(res.ErrorText) ? " - " + res.ErrorText : ""),
};
QueueManager.Instance.Queue.Refresh();
@ -337,7 +346,7 @@ public class CrunchyrollManager{
IsDownloading = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Waiting for Muxing/Encoding"
};
QueueManager.Instance.Queue.Refresh();
@ -354,7 +363,7 @@ public class CrunchyrollManager{
IsDownloading = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Muxing"
};
@ -422,7 +431,7 @@ public class CrunchyrollManager{
IsDownloading = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Encoding"
};
@ -481,7 +490,7 @@ public class CrunchyrollManager{
IsDownloading = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Encoding"
};
@ -517,7 +526,7 @@ public class CrunchyrollManager{
Done = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = (muxError ? "Muxing Failed" : "Done") + (syncError ? $" - Couldn't sync dubs ({notSyncedDubs})" : "")
};
@ -543,7 +552,7 @@ public class CrunchyrollManager{
Done = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Done - Skipped muxing"
};
@ -602,7 +611,7 @@ public class CrunchyrollManager{
IsDownloading = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Moving Files"
};
@ -765,7 +774,7 @@ public class CrunchyrollManager{
IsDownloading = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Muxing Syncing Dub Timings"
};
@ -808,7 +817,7 @@ public class CrunchyrollManager{
IsDownloading = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Muxing"
};
@ -1675,7 +1684,7 @@ public class CrunchyrollManager{
IsDownloading = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Decrypting"
};
QueueManager.Instance.Queue.Refresh();
@ -1766,7 +1775,7 @@ public class CrunchyrollManager{
IsDownloading = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Decrypting video"
};
QueueManager.Instance.Queue.Refresh();
@ -1837,7 +1846,7 @@ public class CrunchyrollManager{
IsDownloading = true,
Percent = 100,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Decrypting audio"
};
QueueManager.Instance.Queue.Refresh();

View file

@ -487,12 +487,12 @@ public partial class QueueManager : ObservableObject{
activeProcessingJobs.Release(giveBack);
_borrowed -= giveBack;
}
int more = newLimit - _limit - giveBack;
if (more > 0) activeProcessingJobs.Release(more);
} else{
int toPark = _limit - newLimit;
for (int i = 0; i < toPark; i++){
_ = Task.Run(async () => {
await activeProcessingJobs.WaitAsync().ConfigureAwait(false);

View file

@ -14,6 +14,18 @@ public enum StreamingService{
Unknown
}
[JsonConverter(typeof(StringEnumConverter))]
public enum CrunchyrollEndpoints{
[EnumMember(Value = "android/phone")]
Android,
[EnumMember(Value = "tv/android_tv")]
AndroidTv,
[EnumMember(Value = "Guest")]
Guest,
[EnumMember(Value = "Unknown")]
Unknown
}
[JsonConverter(typeof(StringEnumConverter))]
public enum EpisodeType{
[EnumMember(Value = "MusicVideo")]

View file

@ -7,6 +7,7 @@ using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using CRD.Downloader;
using CRD.Downloader.Crunchyroll;
using CRD.Utils.Parser.Utils;
using CRD.Utils.Structs;
using Newtonsoft.Json;
@ -261,14 +262,18 @@ public class HlsDownloader{
string resumeDataJson = JsonConvert.SerializeObject(new{ _data.Parts.Completed, Total = totalSeg });
File.WriteAllText($"{fn}.resume", resumeDataJson);
var downloadSpeed = CrunchyrollManager.Instance.CrunOptions.DownloadSpeedInBits
? $"{dataLog.DownloadSpeedBytes * 8 / 1000000.0:F2} Mb/s"
: $"{dataLog.DownloadSpeedBytes / 1000000.0:F2} MB/s";
// Log progress
Console.WriteLine($"{_data.Parts.Completed} of {totalSeg} parts downloaded [{dataLog.Percent}%] ({FormatTime(dataLog.Time)} | {dataLog.DownloadSpeed / 1000000.0:F2}Mb/s)");
Console.WriteLine($"{_data.Parts.Completed} of {totalSeg} parts downloaded [{dataLog.Percent}%] ({FormatTime(dataLog.Time)} | {downloadSpeed})");
_currentEpMeta.DownloadProgress = new DownloadProgress(){
IsDownloading = true,
Percent = dataLog.Percent,
Time = dataLog.Time,
DownloadSpeed = dataLog.DownloadSpeed,
DownloadSpeedBytes = dataLog.DownloadSpeedBytes,
Doing = _isAudio ? "Downloading Audio" : (_isVideo ? "Downloading Video" : "")
};
@ -386,13 +391,17 @@ public class HlsDownloader{
_data.BytesDownloaded = 0;
_lastUiUpdate = DateTimeOffset.Now.ToUnixTimeMilliseconds();
Console.WriteLine($"{currentDownloaded}/{totalSeg} [{dataLog.Percent}%] Speed: {dataLog.DownloadSpeed / 1000000.0:F2} MB/s ETA: {FormatTime(dataLog.Time)}");
var downloadSpeed = CrunchyrollManager.Instance.CrunOptions.DownloadSpeedInBits
? $"{dataLog.DownloadSpeedBytes * 8 / 1000000.0:F2} Mb/s"
: $"{dataLog.DownloadSpeedBytes / 1000000.0:F2} MB/s";
Console.WriteLine($"{currentDownloaded}/{totalSeg} [{dataLog.Percent}%] Speed: {downloadSpeed} ETA: {FormatTime(dataLog.Time)}");
_currentEpMeta.DownloadProgress = new DownloadProgress{
IsDownloading = true,
Percent = dataLog.Percent,
Time = dataLog.Time,
DownloadSpeed = dataLog.DownloadSpeed,
DownloadSpeedBytes = dataLog.DownloadSpeedBytes,
Doing = _isAudio ? "Downloading Audio" : (_isVideo ? "Downloading Video" : "")
};
}
@ -468,7 +477,7 @@ public class HlsDownloader{
IsDownloading = true,
Percent = dataLog.Percent,
Time = dataLog.Time,
DownloadSpeed = dataLog.DownloadSpeed,
DownloadSpeedBytes = dataLog.DownloadSpeedBytes,
Doing = _isAudio ? "Merging Audio" : (_isVideo ? "Merging Video" : "")
};
@ -542,7 +551,7 @@ public class HlsDownloader{
return new Info{
Percent = percent,
Time = etaSec,
DownloadSpeed = speed
DownloadSpeedBytes = speed
};
}
@ -761,7 +770,7 @@ public static class HttpContentExtensions{
public class Info{
public int Percent{ get; set; }
public double Time{ get; set; } // Remaining time estimate
public double DownloadSpeed{ get; set; } // Bytes per second
public double DownloadSpeedBytes{ get; set; } // Bytes per second
}
public class ResumeData{

View file

@ -499,7 +499,7 @@ public class Helpers{
IsDownloading = true,
Percent = progress,
Time = 0,
DownloadSpeed = 0,
DownloadSpeedBytes = 0,
Doing = "Encoding"
};

View file

@ -32,7 +32,7 @@ public class FontsManager{
#endregion
public Dictionary<string, string> Fonts{ get; private set; } = new(){
public Dictionary<string, string> Fonts{ get; private set; } = new(StringComparer.OrdinalIgnoreCase){
{ "Adobe Arabic", "AdobeArabic-Bold.otf" },
{ "Andale Mono", "andalemo.ttf" },
{ "Arial", "arial.ttf" },

View file

@ -116,6 +116,9 @@ public class CrDownloadOptions{
[JsonProperty("download_speed_limit")]
public int DownloadSpeedLimit{ get; set; }
[JsonProperty("download_speed_bits")]
public bool DownloadSpeedInBits{ get; set; }
[JsonProperty("proxy_enabled")]
public bool ProxyEnabled{ get; set; }

View file

@ -409,7 +409,7 @@ public class DownloadProgress{
public int Percent{ get; set; }
public double Time{ get; set; }
public double DownloadSpeed{ get; set; }
public double DownloadSpeedBytes{ get; set; }
}
public class CrunchyEpMetaData{

View file

@ -127,7 +127,11 @@ public partial class DownloadItemModel : INotifyPropertyChanged{
Done = epMeta.DownloadProgress.Done;
Percent = epMeta.DownloadProgress.Percent;
Time = "Estimated Time: " + TimeSpan.FromSeconds(epMeta.DownloadProgress.Time).ToString(@"hh\:mm\:ss");
DownloadSpeed = $"{epMeta.DownloadProgress.DownloadSpeed / 1000000.0:F2}Mb/s";
DownloadSpeed = CrunchyrollManager.Instance.CrunOptions.DownloadSpeedInBits
? $"{epMeta.DownloadProgress.DownloadSpeedBytes * 8 / 1000000.0:F2} Mb/s"
: $"{epMeta.DownloadProgress.DownloadSpeedBytes / 1000000.0:F2} MB/s";
;
Paused = epMeta.Paused || !isDownloading && !epMeta.Paused;
DoingWhat = epMeta.Paused ? "Paused" :
Done ? (epMeta.DownloadProgress.Doing != string.Empty ? epMeta.DownloadProgress.Doing : "Done") :
@ -192,7 +196,9 @@ public partial class DownloadItemModel : INotifyPropertyChanged{
Done = epMeta.DownloadProgress.Done;
Percent = epMeta.DownloadProgress.Percent;
Time = "Estimated Time: " + TimeSpan.FromSeconds(epMeta.DownloadProgress.Time).ToString(@"hh\:mm\:ss");
DownloadSpeed = $"{epMeta.DownloadProgress.DownloadSpeed / 1000000.0:F2}Mb/s";
DownloadSpeed = CrunchyrollManager.Instance.CrunOptions.DownloadSpeedInBits
? $"{epMeta.DownloadProgress.DownloadSpeedBytes * 8 / 1000000.0:F2} Mb/s"
: $"{epMeta.DownloadProgress.DownloadSpeedBytes / 1000000.0:F2} MB/s";
Paused = epMeta.Paused || !isDownloading && !epMeta.Paused;
DoingWhat = epMeta.Paused ? "Paused" :

View file

@ -65,6 +65,9 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
[ObservableProperty]
private double? _downloadSpeed;
[ObservableProperty]
private bool _downloadSpeedInBits;
[ObservableProperty]
private double? _retryAttempts;
@ -298,6 +301,7 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
HistorySkipUnmonitored = options.HistorySkipUnmonitored;
HistoryCountSonarr = options.HistoryCountSonarr;
DownloadSpeed = options.DownloadSpeedLimit;
DownloadSpeedInBits = options.DownloadSpeedInBits;
DownloadMethodeNew = options.DownloadMethodeNew;
DownloadAllowEarlyStart = options.DownloadAllowEarlyStart;
RetryAttempts = Math.Clamp((options.RetryAttempts), 1, 10);
@ -344,6 +348,7 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
settings.HistorySkipUnmonitored = HistorySkipUnmonitored;
settings.HistoryCountSonarr = HistoryCountSonarr;
settings.DownloadSpeedLimit = Math.Clamp((int)(DownloadSpeed ?? 0), 0, 1000000000);
settings.DownloadSpeedInBits = DownloadSpeedInBits;
settings.SimultaneousDownloads = Math.Clamp((int)(SimultaneousDownloads ?? 0), 1, 10);
settings.SimultaneousProcessingJobs = Math.Clamp((int)(SimultaneousProcessingJobs ?? 0), 1, 10);

View file

@ -91,6 +91,12 @@
HorizontalAlignment="Stretch" />
</controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Show download speed in bits/s (Mbps)" Description="When enabled, shows Mbps instead of MB/s">
<controls:SettingsExpanderItem.Footer>
<CheckBox IsChecked="{Binding DownloadSpeedInBits}"> </CheckBox>
</controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Download Retry"
Description="Sett the number of retry attempts and the delay between each retry">