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

@ -189,8 +189,8 @@ public class CrEpisode(){
epMeta.Season = Helpers.ExtractNumberAfterS(item.Identifier) ?? item.SeasonNumber + ""; epMeta.Season = Helpers.ExtractNumberAfterS(item.Identifier) ?? item.SeasonNumber + "";
epMeta.SeriesId = item.SeriesId; epMeta.SeriesId = item.SeriesId;
epMeta.AbsolutEpisodeNumberE = epNum; epMeta.AbsolutEpisodeNumberE = epNum;
epMeta.Image = images[images.Count / 2].FirstOrDefault()?.Source; epMeta.Image = images.FirstOrDefault()?.FirstOrDefault()?.Source ?? string.Empty;
epMeta.ImageBig = images[images.Count / 2].LastOrDefault()?.Source; epMeta.ImageBig = images.FirstOrDefault()?.LastOrDefault()?.Source ?? string.Empty;
epMeta.DownloadProgress = new DownloadProgress(){ epMeta.DownloadProgress = new DownloadProgress(){
IsDownloading = false, IsDownloading = false,
Done = false, Done = false,
@ -290,7 +290,6 @@ public class CrEpisode(){
} }
public async Task MarkAsWatched(string episodeId){ 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); var response = await HttpClientReq.Instance.SendHttpRequest(request);
@ -298,8 +297,5 @@ public class CrEpisode(){
if (!response.IsOk){ if (!response.IsOk){
Console.Error.WriteLine($"Mark as watched for {episodeId} failed"); Console.Error.WriteLine($"Mark as watched for {episodeId} failed");
} }
} }
} }

View file

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

View file

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

View file

@ -70,8 +70,8 @@ public class CrSeries{
epMeta.Season = Helpers.ExtractNumberAfterS(item.Identifier) ?? item.SeasonNumber + ""; epMeta.Season = Helpers.ExtractNumberAfterS(item.Identifier) ?? item.SeasonNumber + "";
epMeta.SeriesId = item.SeriesId; epMeta.SeriesId = item.SeriesId;
epMeta.AbsolutEpisodeNumberE = epNum; epMeta.AbsolutEpisodeNumberE = epNum;
epMeta.Image = images[images.Count / 2].FirstOrDefault()?.Source ?? ""; epMeta.Image = images.FirstOrDefault()?.FirstOrDefault()?.Source ?? string.Empty;
epMeta.ImageBig = images[images.Count / 2].LastOrDefault()?.Source; epMeta.ImageBig = images.FirstOrDefault()?.LastOrDefault()?.Source ?? string.Empty;
epMeta.DownloadProgress = new DownloadProgress(){ epMeta.DownloadProgress = new DownloadProgress(){
IsDownloading = false, IsDownloading = false,
Done = false, Done = false,
@ -266,22 +266,22 @@ public class CrSeries{
crunchySeriesList.List = sortedEpisodes.Select(kvp => { crunchySeriesList.List = sortedEpisodes.Select(kvp => {
var key = kvp.Key; var key = kvp.Key;
var value = kvp.Value; var value = kvp.Value;
var images = (value.Items[0].Images?.Thumbnail ??[new List<Image>{ new(){ Source = "/notFound.jpg" } }]); var images = (value.Items.FirstOrDefault()?.Images?.Thumbnail ??[new List<Image>{ new(){ Source = "/notFound.jpg" } }]);
var seconds = (int)Math.Floor(value.Items[0].DurationMs / 1000.0); var seconds = (int)Math.Floor((value.Items.FirstOrDefault()?.DurationMs ?? 0) / 1000.0);
var langList = value.Langs.Select(a => a.CrLocale).ToList(); var langList = value.Langs.Select(a => a.CrLocale).ToList();
Languages.SortListByLangList(langList); Languages.SortListByLangList(langList);
return new Episode{ return new Episode{
E = key.StartsWith("E") ? key.Substring(1) : key, E = key.StartsWith("E") ? key.Substring(1) : key,
Lang = langList, Lang = langList,
Name = value.Items[0].Title, Name = value.Items.FirstOrDefault()?.Title ?? string.Empty,
Season = Helpers.ExtractNumberAfterS(value.Items[0].Identifier) ?? value.Items[0].SeasonNumber.ToString(), Season = (Helpers.ExtractNumberAfterS(value.Items.FirstOrDefault()?.Identifier?? string.Empty) ?? value.Items.FirstOrDefault()?.SeasonNumber.ToString()) ?? string.Empty,
SeriesTitle = Regex.Replace(value.Items[0].SeriesTitle, @"\(\w+ Dub\)", "").TrimEnd(), SeriesTitle = Regex.Replace(value.Items.FirstOrDefault()?.SeriesTitle?? string.Empty, @"\(\w+ Dub\)", "").TrimEnd(),
SeasonTitle = Regex.Replace(value.Items[0].SeasonTitle, @"\(\w+ Dub\)", "").TrimEnd(), SeasonTitle = Regex.Replace(value.Items.FirstOrDefault()?.SeasonTitle?? string.Empty, @"\(\w+ Dub\)", "").TrimEnd(),
EpisodeNum = key.StartsWith("SP") ? key : value.Items[0].EpisodeNumber?.ToString() ?? value.Items[0].Episode ?? "?", EpisodeNum = key.StartsWith("SP") ? key : value.Items.FirstOrDefault()?.EpisodeNumber?.ToString() ?? value.Items.FirstOrDefault()?.Episode ?? "?",
Id = value.Items[0].SeasonId, Id = value.Items.FirstOrDefault()?.SeasonId ?? string.Empty,
Img = images[images.Count / 2].FirstOrDefault()?.Source ?? "", Img = images.FirstOrDefault()?.FirstOrDefault()?.Source ?? string.Empty,
Description = value.Items[0].Description, Description = value.Items.FirstOrDefault()?.Description ?? string.Empty,
EpisodeType = EpisodeType.Episode, EpisodeType = EpisodeType.Episode,
Time = $"{seconds / 60}:{seconds % 60:D2}" // Ensures two digits for seconds. 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 _lastUiUpdate = DateTimeOffset.Now.ToUnixTimeMilliseconds();
var cts = new CancellationTokenSource();
var token = cts.Token;
for (int i = 0; i < segments.Count; i++){ 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; continue;
}
int index = i; int index = i;
await semaphore.WaitAsync();
downloadTasks.Add(Task.Run(async () => { downloadTasks.Add(Task.Run(async () => {
try{ try{
token.ThrowIfCancellationRequested();
var segment = new Segment{ var segment = new Segment{
Uri = ObjectUtilities.GetMemberValue(segments[index], "uri"), Uri = ObjectUtilities.GetMemberValue(segments[index], "uri"),
Key = ObjectUtilities.GetMemberValue(segments[index], "key"), 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; return;
}
QueueManager.Instance.Queue.Refresh(); QueueManager.Instance.Queue.Refresh();
while (_currentEpMeta.Paused){ while (_currentEpMeta.Paused){
await Task.Delay(500); await Task.Delay(500);
if (!QueueManager.Instance.Queue.Contains(_currentEpMeta)) if (!QueueManager.Instance.Queue.Contains(_currentEpMeta)){
cts.Cancel();
return; return;
} }
}
} catch (Exception ex){ } catch (Exception ex){
Console.Error.WriteLine($"Error downloading part {index}: {ex.Message}"); Console.Error.WriteLine($"Error downloading part {index}: {ex.Message}");
errorOccurred = true; errorOccurred = true;
cts.Cancel();
} finally{ } finally{
semaphore.Release(); semaphore.Release();
} }
})); }, token));
} }
try{
await Task.WhenAll(downloadTasks); 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) if (errorOccurred)
return (false, _data.Parts); return (false, _data.Parts);
using (var output = new FileStream(fn, FileMode.Append, FileAccess.Write, FileShare.None)){ using (var output = new FileStream(fn, FileMode.Append, FileAccess.Write, FileShare.None)){
for (int i = mergedParts; i < segments.Count; i++){ for (int i = mergedParts; i < segments.Count; i++){
if (token.IsCancellationRequested)
return (false, _data.Parts);
string tempFile = Path.Combine(tempDir, $"part_{i:D6}.tmp"); string tempFile = Path.Combine(tempDir, $"part_{i:D6}.tmp");
if (!File.Exists(tempFile)){ if (!File.Exists(tempFile)){
Console.Error.WriteLine($"Missing temp file for part {i}, aborting merge."); 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" : "") Doing = _isAudio ? "Merging Audio" : (_isVideo ? "Merging Video" : "")
}; };
if (!QueueManager.Instance.Queue.Contains(_currentEpMeta)) if (!QueueManager.Instance.Queue.Contains(_currentEpMeta)){
return (false, _data.Parts); if (!_currentEpMeta.DownloadProgress.Done){
CleanupNewDownloadMethod(tempDir, resumeFile, true);
}
return (false, _data.Parts);
}
} }
} }
// Cleanup temp files // Cleanup temp files
Directory.Delete(tempDir, true); CleanupNewDownloadMethod(tempDir, resumeFile);
File.Delete(resumeFile);
return (true, _data.Parts); 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){ public static Info GetDownloadInfo(long dateStartUnix, int partsDownloaded, int partsTotal, long incrementalBytes, long totalDownloadedBytes){
DateTime lastStart = DateTimeOffset.FromUnixTimeMilliseconds(dateStartUnix).UtcDateTime; 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){ public static Dictionary<string, List<DownloadedMedia>> GroupByLanguageWithSubtitles(List<DownloadedMedia> allMedia){
//Group by language //Group by language
var languageGroups = allMedia var languageGroups = allMedia
.Where(media => media.Type != DownloadMediaType.Description && .Where(media => media.Type != DownloadMediaType.Description && media.Type != DownloadMediaType.Cover &&
(!string.IsNullOrEmpty(media.Lang?.CrLocale) || (!string.IsNullOrEmpty(media.Lang?.CrLocale) ||
(media is{ Type: DownloadMediaType.Subtitle, RelatedVideoDownloadMedia: not null } && (media is{ Type: DownloadMediaType.Subtitle, RelatedVideoDownloadMedia: not null } &&
!string.IsNullOrEmpty(media.RelatedVideoDownloadMedia.Lang?.CrLocale))) !string.IsNullOrEmpty(media.RelatedVideoDownloadMedia.Lang?.CrLocale)))
@ -646,6 +646,15 @@ public class Helpers{
} }
} }
//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; return languageGroups;
} }

View file

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