Fix - Fix download issues

This commit is contained in:
Elwador 2025-08-12 22:30:31 +02:00
parent e80568cbb0
commit 28c452d537
7 changed files with 112 additions and 43 deletions

View file

@ -43,16 +43,16 @@ public class CrEpisode(){
}
if (epsidoe is{ Total: 1, Data: not null } &&
(epsidoe.Data.First().Versions ?? [])
(epsidoe.Data.First().Versions ??[])
.GroupBy(v => v.AudioLocale)
.Any(g => g.Count() > 1)){
Console.Error.WriteLine("Episode has Duplicate Audio Locales");
var list = (epsidoe.Data.First().Versions ?? []).GroupBy(v => v.AudioLocale).Where(g => g.Count() > 1).ToList();
var list = (epsidoe.Data.First().Versions ??[]).GroupBy(v => v.AudioLocale).Where(g => g.Count() > 1).ToList();
//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, true, query);
var checkResponse = await HttpClientReq.Instance.SendHttpRequest(checkRequest,true);
var checkResponse = await HttpClientReq.Instance.SendHttpRequest(checkRequest, true);
if (!checkResponse.IsOk){
epsidoe.Data.First().Versions?.Remove(version);
}
@ -189,8 +189,8 @@ public class CrEpisode(){
epMeta.Season = Helpers.ExtractNumberAfterS(item.Identifier) ?? item.SeasonNumber + "";
epMeta.SeriesId = item.SeriesId;
epMeta.AbsolutEpisodeNumberE = epNum;
epMeta.Image = images[images.Count / 2].FirstOrDefault()?.Source;
epMeta.ImageBig = images[images.Count / 2].LastOrDefault()?.Source;
epMeta.Image = images.FirstOrDefault()?.FirstOrDefault()?.Source ?? string.Empty;
epMeta.ImageBig = images.FirstOrDefault()?.LastOrDefault()?.Source ?? string.Empty;
epMeta.DownloadProgress = new DownloadProgress(){
IsDownloading = false,
Done = false,
@ -288,18 +288,14 @@ public class CrEpisode(){
return complete;
}
public async Task MarkAsWatched(string episodeId){
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Content}/discover/{crunInstance.Token?.account_id}/mark_as_watched/{episodeId}", HttpMethod.Post, true,false,null);
var request = HttpClientReq.CreateRequestMessage($"{ApiUrls.Content}/discover/{crunInstance.Token?.account_id}/mark_as_watched/{episodeId}", HttpMethod.Post, true, false, null);
var response = await HttpClientReq.Instance.SendHttpRequest(request);
if (!response.IsOk){
Console.Error.WriteLine($"Mark as watched for {episodeId} failed");
}
}
}

View file

@ -73,8 +73,8 @@ public class CrMovies{
epMeta.Season = "";
epMeta.SeriesId = "";
epMeta.AbsolutEpisodeNumberE = "";
epMeta.Image = images[images.Count / 2].FirstOrDefault()?.Source;
epMeta.ImageBig = images[images.Count / 2].LastOrDefault()?.Source;
epMeta.Image = images.FirstOrDefault()?.FirstOrDefault()?.Source;
epMeta.ImageBig = images.FirstOrDefault()?.LastOrDefault()?.Source;
epMeta.DownloadProgress = new DownloadProgress(){
IsDownloading = false,
Done = false,

View file

@ -178,8 +178,8 @@ public class CrMusic{
epMeta.Season = "";
epMeta.SeriesId = episodeP.GetSeriesId();
epMeta.AbsolutEpisodeNumberE = "";
epMeta.Image = images[images.Count / 2].Source;
epMeta.ImageBig = images[images.Count / 2].Source;
epMeta.Image = images.FirstOrDefault()?.Source ?? string.Empty;
epMeta.ImageBig = images.FirstOrDefault()?.Source ?? string.Empty;
epMeta.DownloadProgress = new DownloadProgress(){
IsDownloading = false,
Done = false,

View file

@ -70,8 +70,8 @@ public class CrSeries{
epMeta.Season = Helpers.ExtractNumberAfterS(item.Identifier) ?? item.SeasonNumber + "";
epMeta.SeriesId = item.SeriesId;
epMeta.AbsolutEpisodeNumberE = epNum;
epMeta.Image = images[images.Count / 2].FirstOrDefault()?.Source ?? "";
epMeta.ImageBig = images[images.Count / 2].LastOrDefault()?.Source;
epMeta.Image = images.FirstOrDefault()?.FirstOrDefault()?.Source ?? string.Empty;
epMeta.ImageBig = images.FirstOrDefault()?.LastOrDefault()?.Source ?? string.Empty;
epMeta.DownloadProgress = new DownloadProgress(){
IsDownloading = false,
Done = false,
@ -266,22 +266,22 @@ public class CrSeries{
crunchySeriesList.List = sortedEpisodes.Select(kvp => {
var key = kvp.Key;
var value = kvp.Value;
var images = (value.Items[0].Images?.Thumbnail ??[new List<Image>{ new(){ Source = "/notFound.jpg" } }]);
var seconds = (int)Math.Floor(value.Items[0].DurationMs / 1000.0);
var images = (value.Items.FirstOrDefault()?.Images?.Thumbnail ??[new List<Image>{ new(){ Source = "/notFound.jpg" } }]);
var seconds = (int)Math.Floor((value.Items.FirstOrDefault()?.DurationMs ?? 0) / 1000.0);
var langList = value.Langs.Select(a => a.CrLocale).ToList();
Languages.SortListByLangList(langList);
return new Episode{
E = key.StartsWith("E") ? key.Substring(1) : key,
Lang = langList,
Name = value.Items[0].Title,
Season = Helpers.ExtractNumberAfterS(value.Items[0].Identifier) ?? value.Items[0].SeasonNumber.ToString(),
SeriesTitle = Regex.Replace(value.Items[0].SeriesTitle, @"\(\w+ Dub\)", "").TrimEnd(),
SeasonTitle = Regex.Replace(value.Items[0].SeasonTitle, @"\(\w+ Dub\)", "").TrimEnd(),
EpisodeNum = key.StartsWith("SP") ? key : value.Items[0].EpisodeNumber?.ToString() ?? value.Items[0].Episode ?? "?",
Id = value.Items[0].SeasonId,
Img = images[images.Count / 2].FirstOrDefault()?.Source ?? "",
Description = value.Items[0].Description,
Name = value.Items.FirstOrDefault()?.Title ?? string.Empty,
Season = (Helpers.ExtractNumberAfterS(value.Items.FirstOrDefault()?.Identifier?? string.Empty) ?? value.Items.FirstOrDefault()?.SeasonNumber.ToString()) ?? string.Empty,
SeriesTitle = Regex.Replace(value.Items.FirstOrDefault()?.SeriesTitle?? string.Empty, @"\(\w+ Dub\)", "").TrimEnd(),
SeasonTitle = Regex.Replace(value.Items.FirstOrDefault()?.SeasonTitle?? string.Empty, @"\(\w+ Dub\)", "").TrimEnd(),
EpisodeNum = key.StartsWith("SP") ? key : value.Items.FirstOrDefault()?.EpisodeNumber?.ToString() ?? value.Items.FirstOrDefault()?.Episode ?? "?",
Id = value.Items.FirstOrDefault()?.SeasonId ?? string.Empty,
Img = images.FirstOrDefault()?.FirstOrDefault()?.Source ?? string.Empty,
Description = value.Items.FirstOrDefault()?.Description ?? string.Empty,
EpisodeType = EpisodeType.Episode,
Time = $"{seconds / 60}:{seconds % 60:D2}" // Ensures two digits for seconds.
};

View file

@ -333,15 +333,27 @@ public class HlsDownloader{
var _lastUiUpdate = DateTimeOffset.Now.ToUnixTimeMilliseconds();
var cts = new CancellationTokenSource();
var token = cts.Token;
for (int i = 0; i < segments.Count; i++){
if (File.Exists(Path.Combine(tempDir, $"part_{i:D6}.tmp")))
try{
await semaphore.WaitAsync(token);
} catch (OperationCanceledException){
break;
}
if (File.Exists(Path.Combine(tempDir, $"part_{i:D6}.tmp"))){
semaphore.Release();
continue;
}
int index = i;
await semaphore.WaitAsync();
downloadTasks.Add(Task.Run(async () => {
try{
token.ThrowIfCancellationRequested();
var segment = new Segment{
Uri = ObjectUtilities.GetMemberValue(segments[index], "uri"),
Key = ObjectUtilities.GetMemberValue(segments[index], "key"),
@ -385,32 +397,53 @@ public class HlsDownloader{
};
}
if (!QueueManager.Instance.Queue.Contains(_currentEpMeta))
if (!QueueManager.Instance.Queue.Contains(_currentEpMeta)){
cts.Cancel();
return;
}
QueueManager.Instance.Queue.Refresh();
while (_currentEpMeta.Paused){
await Task.Delay(500);
if (!QueueManager.Instance.Queue.Contains(_currentEpMeta))
if (!QueueManager.Instance.Queue.Contains(_currentEpMeta)){
cts.Cancel();
return;
}
}
} catch (Exception ex){
Console.Error.WriteLine($"Error downloading part {index}: {ex.Message}");
errorOccurred = true;
cts.Cancel();
} finally{
semaphore.Release();
}
}));
}, token));
}
await Task.WhenAll(downloadTasks);
try{
await Task.WhenAll(downloadTasks);
} catch (OperationCanceledException){
if (!QueueManager.Instance.Queue.Contains(_currentEpMeta)){
if (!_currentEpMeta.DownloadProgress.Done){
CleanupNewDownloadMethod(tempDir, resumeFile, true);
}
} else{
Console.Error.WriteLine("Download cancelled due to error.");
}
return (false, _data.Parts);
}
if (errorOccurred)
return (false, _data.Parts);
using (var output = new FileStream(fn, FileMode.Append, FileAccess.Write, FileShare.None)){
for (int i = mergedParts; i < segments.Count; i++){
if (token.IsCancellationRequested)
return (false, _data.Parts);
string tempFile = Path.Combine(tempDir, $"part_{i:D6}.tmp");
if (!File.Exists(tempFile)){
Console.Error.WriteLine($"Missing temp file for part {i}, aborting merge.");
@ -439,19 +472,51 @@ public class HlsDownloader{
Doing = _isAudio ? "Merging Audio" : (_isVideo ? "Merging Video" : "")
};
if (!QueueManager.Instance.Queue.Contains(_currentEpMeta))
if (!QueueManager.Instance.Queue.Contains(_currentEpMeta)){
if (!_currentEpMeta.DownloadProgress.Done){
CleanupNewDownloadMethod(tempDir, resumeFile, true);
}
return (false, _data.Parts);
}
}
}
// Cleanup temp files
Directory.Delete(tempDir, true);
File.Delete(resumeFile);
CleanupNewDownloadMethod(tempDir, resumeFile);
return (true, _data.Parts);
}
private void CleanupNewDownloadMethod(string tempDir, string resumeFile, bool cleanAll = false){
if (cleanAll){
// Delete downloaded files
foreach (var file in _currentEpMeta.downloadedFiles){
try{
File.Delete(file); // Safe: File.Delete does nothing if file doesn't exist
} catch (Exception ex){
Console.Error.WriteLine($"Failed to delete file '{file}': {ex.Message}");
}
}
}
// Delete temp directory
try{
if (Directory.Exists(tempDir))
Directory.Delete(tempDir, true);
} catch (Exception ex){
Console.Error.WriteLine($"Failed to delete temp dir '{tempDir}': {ex.Message}");
}
// Delete resume file
try{
if (File.Exists(resumeFile))
File.Delete(resumeFile);
} catch (Exception ex){
Console.Error.WriteLine($"Failed to delete resume file '{resumeFile}': {ex.Message}");
}
}
public static Info GetDownloadInfo(long dateStartUnix, int partsDownloaded, int partsTotal, long incrementalBytes, long totalDownloadedBytes){
DateTime lastStart = DateTimeOffset.FromUnixTimeMilliseconds(dateStartUnix).UtcDateTime;

View file

@ -623,7 +623,7 @@ public class Helpers{
public static Dictionary<string, List<DownloadedMedia>> GroupByLanguageWithSubtitles(List<DownloadedMedia> allMedia){
//Group by language
var languageGroups = allMedia
.Where(media => media.Type != DownloadMediaType.Description &&
.Where(media => media.Type != DownloadMediaType.Description && media.Type != DownloadMediaType.Cover &&
(!string.IsNullOrEmpty(media.Lang?.CrLocale) ||
(media is{ Type: DownloadMediaType.Subtitle, RelatedVideoDownloadMedia: not null } &&
!string.IsNullOrEmpty(media.RelatedVideoDownloadMedia.Lang?.CrLocale)))
@ -645,6 +645,15 @@ public class Helpers{
group.Add(descriptionMedia[0]);
}
}
//Find and add Cover media to each group
var coverMedia = allMedia.Where(media => media.Type == DownloadMediaType.Cover).ToList();
if (coverMedia.Count > 0){
foreach (var group in languageGroups.Values){
group.Add(coverMedia[0]);
}
}
return languageGroups;
}

View file

@ -291,11 +291,10 @@ public class CrunchyEpisode : IHistorySource{
}
public string GetImageUrl(){
if (Images != null){
return Images.Thumbnail?.First().First().Source ?? string.Empty;
}
return string.Empty;
return Images?.Thumbnail?
.FirstOrDefault()?
.FirstOrDefault()?
.Source ?? string.Empty;
}
#endregion