mirror of
https://github.com/Crunchy-DL/Crunchy-Downloader.git
synced 2026-01-11 20:10:26 +00:00
Fix - Fix download issues
This commit is contained in:
parent
e80568cbb0
commit
28c452d537
7 changed files with 112 additions and 43 deletions
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue