Chg - Muxing now doesn't overwrite existing files

Chg - A bit more information why the download failed

Fix - Hotfix DRM downloads
This commit is contained in:
Elwador 2024-06-06 03:08:13 +02:00
parent 4cb55acd0f
commit 9e975062dc
4 changed files with 142 additions and 59 deletions

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized; using System.Collections.Specialized;
@ -346,7 +346,7 @@ public class Crunchyroll{
Percent = 100, Percent = 100,
Time = 0, Time = 0,
DownloadSpeed = 0, DownloadSpeed = 0,
Doing = "Download Error" Doing = "Download Error" + (!string.IsNullOrEmpty(res.ErrorText) ? " - " + res.ErrorText : ""),
}; };
Queue.Refresh(); Queue.Refresh();
return false; return false;
@ -435,6 +435,19 @@ public class Crunchyroll{
subsList.Add(subt); subsList.Add(subt);
} }
if (File.Exists($"{filename}.{(muxToMp3 ? "mp3" : options.Mp4 ? "mp4" : "mkv")}") && !string.IsNullOrEmpty(filename)){
string newFilePath = filename;
int counter = 1;
while (File.Exists($"{newFilePath}.{(muxToMp3 ? "mp3" : options.Mp4 ? "mp4" : "mkv")}")){
newFilePath = filename + $"({counter})";
counter++;
}
filename = newFilePath;
}
var merger = new Merger(new MergerOptions{ var merger = new Merger(new MergerOptions{
OnlyVid = hasAudioStreams ? data.Where(a => a.Type == DownloadMediaType.Video).Select(a => new MergerInput{ Language = a.Lang, Path = a.Path ?? string.Empty }).ToList() : new List<MergerInput>(), OnlyVid = hasAudioStreams ? data.Where(a => a.Type == DownloadMediaType.Video).Select(a => new MergerInput{ Language = a.Lang, Path = a.Path ?? string.Empty }).ToList() : new List<MergerInput>(),
SkipSubMux = options.SkipSubMux, SkipSubMux = options.SkipSubMux,
@ -493,7 +506,8 @@ public class Crunchyroll{
return new DownloadResponse{ return new DownloadResponse{
Data = new List<DownloadedMedia>(), Data = new List<DownloadedMedia>(),
Error = true, Error = true,
FileName = "./unknown" FileName = "./unknown",
ErrorText = "Login problem"
}; };
} }
@ -502,7 +516,8 @@ public class Crunchyroll{
return new DownloadResponse{ return new DownloadResponse{
Data = new List<DownloadedMedia>(), Data = new List<DownloadedMedia>(),
Error = true, Error = true,
FileName = "./unknown" FileName = "./unknown",
ErrorText = "Login problem"
}; };
} }
@ -512,7 +527,8 @@ public class Crunchyroll{
return new DownloadResponse{ return new DownloadResponse{
Data = new List<DownloadedMedia>(), Data = new List<DownloadedMedia>(),
Error = true, Error = true,
FileName = "./unknown" FileName = "./unknown",
ErrorText = "Missing ffmpeg"
}; };
} }
@ -538,7 +554,8 @@ public class Crunchyroll{
return new DownloadResponse{ return new DownloadResponse{
Data = files, Data = files,
Error = true, Error = true,
FileName = fileName.Length > 0 ? (Path.IsPathRooted(fileName) ? fileName : Path.Combine(CfgManager.PathVIDEOS_DIR, fileName)) : "./unknown" FileName = fileName.Length > 0 ? (Path.IsPathRooted(fileName) ? fileName : Path.Combine(CfgManager.PathVIDEOS_DIR, fileName)) : "./unknown",
ErrorText = "Video Data not found"
}; };
} }
@ -608,14 +625,15 @@ public class Crunchyroll{
#endregion #endregion
var fetchPlaybackData = await FetchPlaybackData(mediaId, epMeta); var fetchPlaybackData = await FetchPlaybackData(mediaId, mediaGuid, epMeta);
if (!fetchPlaybackData.IsOk){ if (!fetchPlaybackData.IsOk){
MainWindow.Instance.ShowError("Couldn't get Playback Data"); MainWindow.Instance.ShowError("Couldn't get Playback Data");
return new DownloadResponse{ return new DownloadResponse{
Data = new List<DownloadedMedia>(), Data = new List<DownloadedMedia>(),
Error = true, Error = true,
FileName = "./unknown" FileName = "./unknown",
ErrorText = "Playback data not found"
}; };
} }
@ -675,7 +693,8 @@ public class Crunchyroll{
return new DownloadResponse{ return new DownloadResponse{
Data = new List<DownloadedMedia>(), Data = new List<DownloadedMedia>(),
Error = true, Error = true,
FileName = "./unknown" FileName = "./unknown",
ErrorText = "Streams not found"
}; };
} }
@ -754,6 +773,12 @@ public class Crunchyroll{
if (!streamPlaylistsReqResponse.IsOk){ if (!streamPlaylistsReqResponse.IsOk){
dlFailed = true; dlFailed = true;
return new DownloadResponse{
Data = new List<DownloadedMedia>(),
Error = true,
FileName = "./unknown",
ErrorText = "Playlist fetch problem"
};
} }
if (dlFailed){ if (dlFailed){
@ -777,7 +802,8 @@ public class Crunchyroll{
return new DownloadResponse{ return new DownloadResponse{
Data = new List<DownloadedMedia>(), Data = new List<DownloadedMedia>(),
Error = true, Error = true,
FileName = "./unknown" FileName = "./unknown",
ErrorText = "No stream servers found"
}; };
} }
@ -880,7 +906,8 @@ public class Crunchyroll{
return new DownloadResponse{ return new DownloadResponse{
Data = new List<DownloadedMedia>(), Data = new List<DownloadedMedia>(),
Error = true, Error = true,
FileName = "./unknown" FileName = "./unknown",
ErrorText = "Language not found"
}; };
} }
@ -934,7 +961,8 @@ public class Crunchyroll{
return new DownloadResponse{ return new DownloadResponse{
Data = files, Data = files,
Error = dlFailed, Error = dlFailed,
FileName = fileName.Length > 0 ? (Path.IsPathRooted(fileName) ? fileName : Path.Combine(CfgManager.PathVIDEOS_DIR, fileName)) : "./unknown" FileName = fileName.Length > 0 ? (Path.IsPathRooted(fileName) ? fileName : Path.Combine(CfgManager.PathVIDEOS_DIR, fileName)) : "./unknown",
ErrorText = ""
}; };
} }
@ -974,7 +1002,8 @@ public class Crunchyroll{
return new DownloadResponse{ return new DownloadResponse{
Data = files, Data = files,
Error = dlFailed, Error = dlFailed,
FileName = fileName.Length > 0 ? (Path.IsPathRooted(fileName) ? fileName : Path.Combine(CfgManager.PathVIDEOS_DIR, fileName)) : "./unknown" FileName = fileName.Length > 0 ? (Path.IsPathRooted(fileName) ? fileName : Path.Combine(CfgManager.PathVIDEOS_DIR, fileName)) : "./unknown",
ErrorText = "DRM Authentication failed"
}; };
} }
@ -991,7 +1020,8 @@ public class Crunchyroll{
return new DownloadResponse{ return new DownloadResponse{
Data = files, Data = files,
Error = dlFailed, Error = dlFailed,
FileName = fileName.Length > 0 ? (Path.IsPathRooted(fileName) ? fileName : Path.Combine(CfgManager.PathVIDEOS_DIR, fileName)) : "./unknown" FileName = fileName.Length > 0 ? (Path.IsPathRooted(fileName) ? fileName : Path.Combine(CfgManager.PathVIDEOS_DIR, fileName)) : "./unknown",
ErrorText = "Couldn't get DRM encryption keys"
}; };
} }
@ -1029,8 +1059,12 @@ public class Crunchyroll{
Console.WriteLine($"Failed to delete file {tempTsFile}.video.enc.m4s. Error: {ex.Message}"); Console.WriteLine($"Failed to delete file {tempTsFile}.video.enc.m4s. Error: {ex.Message}");
// Handle exceptions if you need to log them or throw // Handle exceptions if you need to log them or throw
} }
}
try{ try{
if (File.Exists($"{tsFile}.video.m4s")){
File.Delete($"{tsFile}.video.m4s");
}
File.Move($"{tempTsFile}.video.m4s", $"{tsFile}.video.m4s"); File.Move($"{tempTsFile}.video.m4s", $"{tsFile}.video.m4s");
} catch (IOException ex){ } catch (IOException ex){
Console.WriteLine($"An error occurred: {ex.Message}"); Console.WriteLine($"An error occurred: {ex.Message}");
@ -1044,7 +1078,6 @@ public class Crunchyroll{
}); });
} }
} }
}
if (audioDownloaded){ if (audioDownloaded){
Console.WriteLine("Started decrypting audio"); Console.WriteLine("Started decrypting audio");
@ -1072,8 +1105,12 @@ public class Crunchyroll{
Console.WriteLine($"Failed to delete file {tempTsFile}.audio.enc.m4s. Error: {ex.Message}"); Console.WriteLine($"Failed to delete file {tempTsFile}.audio.enc.m4s. Error: {ex.Message}");
// Handle exceptions if you need to log them or throw // Handle exceptions if you need to log them or throw
} }
}
try{ try{
if (File.Exists($"{tsFile}.audio.m4s")){
File.Delete($"{tsFile}.audio.m4s");
}
File.Move($"{tempTsFile}.audio.m4s", $"{tsFile}.audio.m4s"); File.Move($"{tempTsFile}.audio.m4s", $"{tsFile}.audio.m4s");
} catch (IOException ex){ } catch (IOException ex){
Console.WriteLine($"An error occurred: {ex.Message}"); Console.WriteLine($"An error occurred: {ex.Message}");
@ -1087,7 +1124,6 @@ public class Crunchyroll{
}); });
} }
} }
}
} else{ } else{
Console.WriteLine("mp4decrypt not found, files need decryption. Decryption Keys: "); Console.WriteLine("mp4decrypt not found, files need decryption. Decryption Keys: ");
MainWindow.Instance.ShowError($"mp4decrypt not found, files need decryption"); MainWindow.Instance.ShowError($"mp4decrypt not found, files need decryption");
@ -1198,7 +1234,8 @@ public class Crunchyroll{
return new DownloadResponse{ return new DownloadResponse{
Data = files, Data = files,
Error = dlFailed, Error = dlFailed,
FileName = fileName.Length > 0 ? (Path.IsPathRooted(fileName) ? fileName : Path.Combine(CfgManager.PathVIDEOS_DIR, fileName)) : "./unknown" FileName = fileName.Length > 0 ? (Path.IsPathRooted(fileName) ? fileName : Path.Combine(CfgManager.PathVIDEOS_DIR, fileName)) : "./unknown",
ErrorText = ""
}; };
} }
@ -1428,7 +1465,7 @@ public class Crunchyroll{
var playbackRequestNonDrmResponse = await HttpClientReq.Instance.SendHttpRequest(playbackRequestNonDrm); var playbackRequestNonDrmResponse = await HttpClientReq.Instance.SendHttpRequest(playbackRequestNonDrm);
if (playbackRequestNonDrmResponse.IsOk && playbackRequestNonDrmResponse.ResponseContent != string.Empty){ if (playbackRequestNonDrmResponse.IsOk && playbackRequestNonDrmResponse.ResponseContent != string.Empty){
CrunchyNoDrmStream? playStream = JsonConvert.DeserializeObject<CrunchyNoDrmStream>(playbackRequestNonDrmResponse.ResponseContent, SettingsJsonSerializerSettings); CrunchyStreamData? playStream = JsonConvert.DeserializeObject<CrunchyStreamData>(playbackRequestNonDrmResponse.ResponseContent, SettingsJsonSerializerSettings);
CrunchyStreams derivedPlayCrunchyStreams = new CrunchyStreams(); CrunchyStreams derivedPlayCrunchyStreams = new CrunchyStreams();
if (playStream != null){ if (playStream != null){
var deauthVideoToken = HttpClientReq.CreateRequestMessage($"https://cr-play-service.prd.crunchyrollsvc.com/v1/token/{currentMediaId}/{playStream.Token}/inactive", HttpMethod.Patch, true, false, null); var deauthVideoToken = HttpClientReq.CreateRequestMessage($"https://cr-play-service.prd.crunchyrollsvc.com/v1/token/{currentMediaId}/{playStream.Token}/inactive", HttpMethod.Patch, true, false, null);
@ -1455,7 +1492,7 @@ public class Crunchyroll{
} }
} }
private async Task<(bool IsOk, PlaybackData pbData)> FetchPlaybackData(string mediaId, CrunchyEpMetaData epMeta){ private async Task<(bool IsOk, PlaybackData pbData)> FetchPlaybackData(string mediaId, string mediaGuidId, CrunchyEpMetaData epMeta){
PlaybackData temppbData = new PlaybackData{ Total = 0, Data = new List<Dictionary<string, Dictionary<string, StreamDetails>>>() }; PlaybackData temppbData = new PlaybackData{ Total = 0, Data = new List<Dictionary<string, Dictionary<string, StreamDetails>>>() };
bool ok = true; bool ok = true;
@ -1506,13 +1543,50 @@ public class Crunchyroll{
} }
} }
} else{ } else{
playbackRequest = HttpClientReq.CreateRequestMessage($"{Api.Cms}/videos/{mediaId}/streams", HttpMethod.Get, true, false, null); // var playbackRequest22 = HttpClientReq.CreateRequestMessage($"{Api.Cms}/videos/{mediaId}/streams", HttpMethod.Get, true, false, null);
//
// var playbackRequestResponse22 = await HttpClientReq.Instance.SendHttpRequest(playbackRequest22);
playbackRequest = HttpClientReq.CreateRequestMessage($"https://cr-play-service.prd.crunchyrollsvc.com/v1/{mediaGuidId}/web/firefox/play", HttpMethod.Get, true, false, null);
playbackRequestResponse = await HttpClientReq.Instance.SendHttpRequest(playbackRequest); playbackRequestResponse = await HttpClientReq.Instance.SendHttpRequest(playbackRequest);
if (playbackRequestResponse.IsOk){ if (playbackRequestResponse.IsOk){
temppbData = Helpers.Deserialize<PlaybackData>(playbackRequestResponse.ResponseContent, SettingsJsonSerializerSettings) ?? // temppbData = Helpers.Deserialize<PlaybackData>(playbackRequestResponse22.ResponseContent, SettingsJsonSerializerSettings) ??
new PlaybackData{ Total = 0, Data = new List<Dictionary<string, Dictionary<string, StreamDetails>>>() }; // new PlaybackData{ Total = 0, Data = new List<Dictionary<string, Dictionary<string, StreamDetails>>>() };
temppbData = new PlaybackData{ Total = 0, Data = new List<Dictionary<string, Dictionary<string, StreamDetails>>>() };
temppbData.Data.Add(new Dictionary<string, Dictionary<string, StreamDetails>>());
CrunchyStreamData? playStream = JsonConvert.DeserializeObject<CrunchyStreamData>(playbackRequestResponse.ResponseContent, SettingsJsonSerializerSettings);
CrunchyStreams derivedPlayCrunchyStreams = new CrunchyStreams();
if (playStream != null){
var deauthVideoToken = HttpClientReq.CreateRequestMessage($"https://cr-play-service.prd.crunchyrollsvc.com/v1/token/{mediaGuidId}/{playStream.Token}/inactive", HttpMethod.Patch, true, false, null);
var deauthVideoTokenResponse = await HttpClientReq.Instance.SendHttpRequest(deauthVideoToken);
if (playStream.HardSubs != null)
foreach (var hardsub in playStream.HardSubs){
var stream = hardsub.Value;
derivedPlayCrunchyStreams[hardsub.Key] = new StreamDetails{
Url = stream.Url,
HardsubLocale = Helpers.ConvertStringToLocale(stream.Hlang)
};
}
derivedPlayCrunchyStreams[""] = new StreamDetails{
Url = playStream.Url,
HardsubLocale = Locale.DefaulT
};
if (temppbData.Data != null) temppbData.Data[0]["drm_adaptive_dash"] = derivedPlayCrunchyStreams;
temppbData.Meta = new PlaybackMeta(){ AudioLocale = playStream.AudioLocale, Versions = playStream.Versions, Bifs = new List<string>{ playStream.Bifs }, MediaId = mediaId };
temppbData.Meta.Subtitles = new Subtitles();
foreach (var playStreamSubtitle in playStream.Subtitles){
Subtitle sub = playStreamSubtitle.Value;
temppbData.Meta.Subtitles.Add(playStreamSubtitle.Key, new SubtitleInfo(){ Format = sub.Format, Locale = sub.Locale, Url = sub.Url });
}
}
} else{ } else{
Console.WriteLine("Request Stream URLs FAILED! Attempting fallback"); Console.WriteLine("Request Stream URLs FAILED! Attempting fallback");

View File

@ -44,7 +44,8 @@ public class HttpClientReq{
// Initialize the HttpClient with the handler // Initialize the HttpClient with the handler
client = new HttpClient(handler); client = new HttpClient(handler);
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0"); // 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("Crunchyroll/1.8.0 Nintendo Switch/12.3.12.0 UE4/4.27");
// // Set Accept headers // // Set Accept headers
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/html")); // client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/html"));
@ -85,16 +86,19 @@ public class HttpClientReq{
} }
public async Task<(bool IsOk, string ResponseContent)> SendHttpRequest(HttpRequestMessage request){ public async Task<(bool IsOk, string ResponseContent)> SendHttpRequest(HttpRequestMessage request){
string content = string.Empty;
try{ try{
HttpResponseMessage response = await client.SendAsync(request); HttpResponseMessage response = await client.SendAsync(request);
content = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
string content = await response.Content.ReadAsStringAsync();
return (IsOk: true, ResponseContent: content); return (IsOk: true, ResponseContent: content);
} catch (Exception e){ } catch (Exception e){
Console.WriteLine(e); Console.WriteLine(e);
return (IsOk: false, ResponseContent: String.Empty); return (IsOk: false, ResponseContent: content);
} }
} }
@ -113,6 +117,7 @@ public class HttpClientReq{
if (disableDrmHeader){ if (disableDrmHeader){
request.Headers.Add("X-Cr-Disable-Drm", "true"); request.Headers.Add("X-Cr-Disable-Drm", "true");
request.Headers.Add("x-cr-stream-limits", "false");
} }

View File

@ -1,10 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json;
namespace CRD.Utils.Structs; namespace CRD.Utils.Structs;
public class CrunchyNoDrmStream{ public class CrunchyStreamData{
public string? AssetId{ get; set; } public string? AssetId{ get; set; }
public string? AudioLocale{ get; set; } public Locale? AudioLocale{ get; set; }
public string? Bifs{ get; set; } public string? Bifs{ get; set; }
public string? BurnedInLocale{ get; set; } public string? BurnedInLocale{ get; set; }
public Dictionary<string, Caption>? Captions{ get; set; } public Dictionary<string, Caption>? Captions{ get; set; }
@ -14,7 +15,7 @@ public class CrunchyNoDrmStream{
public Dictionary<string, Subtitle>? Subtitles{ get; set; } public Dictionary<string, Subtitle>? Subtitles{ get; set; }
public string? Token{ get; set; } public string? Token{ get; set; }
public string? Url{ get; set; } public string? Url{ get; set; }
public List<object>? Versions{ get; set; } // Use a more specific type if known public List<PlaybackVersion>? Versions{ get; set; }
} }
public class Caption{ public class Caption{
@ -41,6 +42,8 @@ public class Session{
public class Subtitle{ public class Subtitle{
public string? Format{ get; set; } public string? Format{ get; set; }
public string? Language{ get; set; } [JsonProperty("language")]
public Locale? Locale{ get; set; }
public string? Url{ get; set; } public string? Url{ get; set; }
} }

View File

@ -63,6 +63,7 @@ public struct DownloadResponse{
public List<DownloadedMedia> Data{ get; set; } public List<DownloadedMedia> Data{ get; set; }
public string FileName{ get; set; } public string FileName{ get; set; }
public bool Error{ get; set; } public bool Error{ get; set; }
public string ErrorText{ get; set; }
} }
public class DownloadedMedia : SxItem{ public class DownloadedMedia : SxItem{