Add - Added stream endpoint selection to settings

Chg - Removed non drm stream requests (RIP)
This commit is contained in:
Elwador 2024-06-19 03:50:10 +02:00
parent 272d59a03b
commit 0cd056c8e4
5 changed files with 130 additions and 206 deletions

View File

@ -33,7 +33,6 @@ namespace CRD.Downloader;
public class Crunchyroll{
public CrToken? Token;
public CrCmsToken? CmsToken;
private readonly string _api = "web"; //web | android
public CrProfile Profile = new();
public CrDownloadOptions CrunOptions;
@ -151,7 +150,7 @@ public class Crunchyroll{
CrunOptions.Theme = "System";
CrunOptions.SelectedCalendarLanguage = "de";
CrunOptions.DlVideoOnce = true;
CrunOptions.UseNonDrmStreams = true;
CrunOptions.StreamEndpoint = "web/firefox";
CrunOptions.History = true;
@ -551,15 +550,16 @@ public class Crunchyroll{
};
}
// if (!options.UseNonDrmStreams && !_widevine.canDecrypt){
// Console.Error.WriteLine("Only searching for drm streams but widevine can't decrypt");
// MainWindow.Instance.ShowError("Settings set to not search for DRM streams - but can't find CDM files in widevine folder ");
// return new DownloadResponse{
// Data = new List<DownloadedMedia>(),
// Error = true,
// FileName = "./unknown"
// };
// }
if (!_widevine.canDecrypt){
Console.Error.WriteLine("L3 key files missing");
MainWindow.Instance.ShowError("Can't find CDM files in widevine folder ");
return new DownloadResponse{
Data = new List<DownloadedMedia>(),
Error = true,
FileName = "./unknown",
ErrorText = "Missing L3 Key"
};
}
string mediaName = $"{data.SeasonTitle} - {data.EpisodeNumber} - {data.EpisodeTitle}";
string fileName = "";
@ -673,16 +673,6 @@ public class Crunchyroll{
var pbData = fetchPlaybackData.pbData;
#region NonDrmRequest
if (options.UseNonDrmStreams || !_widevine.canDecrypt){
await FetchNoDrmPlaybackData(mediaGuid, pbData);
}
#endregion
List<string> hsLangs = new List<string>();
var pbStreams = pbData.Data?[0];
var streams = new List<StreamDetailsPop>();
@ -809,7 +799,7 @@ public class Crunchyroll{
dlFailed = true;
return new DownloadResponse{
Data = new List<DownloadedMedia>(),
Error = true,
Error = dlFailed,
FileName = "./unknown",
ErrorText = "Playlist fetch problem"
};
@ -1500,31 +1490,39 @@ public class Crunchyroll{
return (audioDownloadResult.Ok, audioDownloadResult.Parts, tsFile);
}
private async Task FetchNoDrmPlaybackData(string currentMediaId, PlaybackData pbData){
var playbackRequestNonDrm = HttpClientReq.CreateRequestMessage($"https://cr-play-service.prd.crunchyrollsvc.com/v1/{currentMediaId}/console/switch/play", HttpMethod.Get, true, true, null);
playbackRequestNonDrm.Headers.UserAgent.ParseAdd("Crunchyroll/1.8.0 Nintendo Switch/12.3.12.0 UE4/4.27");
private async Task<(bool IsOk, PlaybackData pbData, string error)> FetchPlaybackData(string mediaId, string mediaGuidId, CrunchyEpMetaData epMeta){
PlaybackData temppbData = new PlaybackData{ Total = 0, Data = new List<Dictionary<string, Dictionary<string, StreamDetails>>>() };
bool ok = true;
var playbackRequestNonDrmResponse = await HttpClientReq.Instance.SendHttpRequest(playbackRequestNonDrm);
HttpRequestMessage playbackRequest;
(bool IsOk, string ResponseContent) playbackRequestResponse;
if (!playbackRequestNonDrmResponse.IsOk && playbackRequestNonDrmResponse.ResponseContent != string.Empty){
var s = playbackRequestNonDrmResponse.ResponseContent;
playbackRequest = HttpClientReq.CreateRequestMessage($"https://cr-play-service.prd.crunchyrollsvc.com/v1/{mediaGuidId}/{CrunOptions.StreamEndpoint}/play", HttpMethod.Get, true, false, null);
playbackRequestResponse = await HttpClientReq.Instance.SendHttpRequest(playbackRequest);
if (!playbackRequestResponse.IsOk && playbackRequestResponse.ResponseContent != string.Empty){
var s = playbackRequestResponse.ResponseContent;
var error = StreamError.FromJson(s);
if (error != null && error.IsTooManyActiveStreamsError()){
foreach (var errorActiveStream in error.ActiveStreams){
await HttpClientReq.DeAuthVideo(errorActiveStream.ContentId, errorActiveStream.Token);
}
playbackRequestNonDrm = HttpClientReq.CreateRequestMessage($"https://cr-play-service.prd.crunchyrollsvc.com/v1/{currentMediaId}/console/switch/play", HttpMethod.Get, true, true, null);
playbackRequestNonDrm.Headers.UserAgent.ParseAdd("Crunchyroll/1.8.0 Nintendo Switch/12.3.12.0 UE4/4.27");
playbackRequestNonDrmResponse = await HttpClientReq.Instance.SendHttpRequest(playbackRequestNonDrm);
playbackRequest = HttpClientReq.CreateRequestMessage($"https://cr-play-service.prd.crunchyrollsvc.com/v1/{mediaGuidId}/{CrunOptions.StreamEndpoint}/play", HttpMethod.Get, true, false, null);
playbackRequestResponse = await HttpClientReq.Instance.SendHttpRequest(playbackRequest);
}
}
if (playbackRequestNonDrmResponse.IsOk && playbackRequestNonDrmResponse.ResponseContent != string.Empty){
CrunchyStreamData? playStream = JsonConvert.DeserializeObject<CrunchyStreamData>(playbackRequestNonDrmResponse.ResponseContent, SettingsJsonSerializerSettings);
if (playbackRequestResponse.IsOk){
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){
if (playStream.Token != null) await HttpClientReq.DeAuthVideo(currentMediaId, playStream.Token);
if (playStream.Token != null) await HttpClientReq.DeAuthVideo(mediaGuidId, playStream.Token);
if (playStream.HardSubs != null)
foreach (var hardsub in playStream.HardSubs){
@ -1540,88 +1538,18 @@ public class Crunchyroll{
HardsubLocale = Locale.DefaulT
};
if (pbData.Data != null) pbData.Data[0]["adaptive_switch_dash"] = derivedPlayCrunchyStreams;
}
} else{
Console.WriteLine("Non-DRM Request Stream URLs FAILED!");
}
if (temppbData.Data != null){
temppbData.Data[0]["drm_adaptive_dash"] = derivedPlayCrunchyStreams;
temppbData.Total = 1;
}
private async Task<(bool IsOk, PlaybackData pbData, string error)> FetchPlaybackData(string mediaId, string mediaGuidId, CrunchyEpMetaData epMeta){
PlaybackData temppbData = new PlaybackData{ Total = 0, Data = new List<Dictionary<string, Dictionary<string, StreamDetails>>>() };
bool ok = true;
HttpRequestMessage playbackRequest;
(bool IsOk, string ResponseContent) playbackRequestResponse;
if (_api == "android"){
NameValueCollection query = HttpUtility.ParseQueryString(new UriBuilder().Query);
query["force_locale"] = "";
query["preferred_audio_language"] = "ja-JP";
query["Policy"] = CmsToken?.Cms.Policy;
query["Signature"] = CmsToken?.Cms.Signature;
query["Key-Pair-Id"] = CmsToken?.Cms.KeyPairId;
playbackRequest = HttpClientReq.CreateRequestMessage($"{Api.BetaCms}{CmsToken?.Cms.Bucket}/videos/{mediaId}/streams?", HttpMethod.Get, true, true, query);
playbackRequestResponse = await HttpClientReq.Instance.SendHttpRequest(playbackRequest);
if (playbackRequestResponse.IsOk){
var androidTempData = Helpers.Deserialize<PlaybackDataAndroid>(playbackRequestResponse.ResponseContent, SettingsJsonSerializerSettings);
temppbData = new PlaybackData(){
Data = androidTempData.streams, Total = androidTempData.streams.Count,
Meta = new PlaybackMeta(){
MediaId = androidTempData.media_id, Subtitles = androidTempData.subtitles, Bifs = androidTempData.bifs, Versions = androidTempData.versions, AudioLocale = androidTempData.audio_locale,
ClosedCaptions = androidTempData.closed_captions, Captions = androidTempData.captions
}
};
} else{
NameValueCollection query2 = HttpUtility.ParseQueryString(new UriBuilder().Query);
query2["preferred_audio_language"] = "ja-JP";
query2["Policy"] = CmsToken?.Cms.Policy;
query2["Signature"] = CmsToken?.Cms.Signature;
query2["Key-Pair-Id"] = CmsToken?.Cms.KeyPairId;
playbackRequest = HttpClientReq.CreateRequestMessage($"{Api.ApiBeta}{epMeta.Playback}?", HttpMethod.Get, true, true, query2);
playbackRequestResponse = await HttpClientReq.Instance.SendHttpRequest(playbackRequest);
if (playbackRequestResponse.IsOk){
temppbData = Helpers.Deserialize<PlaybackData>(playbackRequestResponse.ResponseContent, SettingsJsonSerializerSettings) ??
new PlaybackData{ Total = 0, Data = new List<Dictionary<string, Dictionary<string, StreamDetails>>>() };
} else{
Console.WriteLine("'Fallback Request Stream URLs FAILED!'");
ok = playbackRequestResponse.IsOk;
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{
// var playbackRequest22 = HttpClientReq.CreateRequestMessage($"{Api.Cms}/videos/{mediaId}/streams", HttpMethod.Get, true, false, null);
//
// var playbackRequestResponse22 = await HttpClientReq.Instance.SendHttpRequest(playbackRequest22);
playbackRequest = HttpClientReq.CreateRequestMessage($"{Api.Cms}/videos/{mediaId}/streams", HttpMethod.Get, true, false, null);
playbackRequestResponse = await HttpClientReq.Instance.SendHttpRequest(playbackRequest);
if (!playbackRequestResponse.IsOk && playbackRequestResponse.ResponseContent != string.Empty){
var s = playbackRequestResponse.ResponseContent;
var error = StreamError.FromJson(s);
if (error != null && error.IsTooManyActiveStreamsError()){
foreach (var errorActiveStream in error.ActiveStreams){
await HttpClientReq.DeAuthVideo(errorActiveStream.ContentId, errorActiveStream.Token);
}
playbackRequest = HttpClientReq.CreateRequestMessage($"{Api.Cms}/videos/{mediaId}/streams", HttpMethod.Get, true, false, null);
playbackRequestResponse = await HttpClientReq.Instance.SendHttpRequest(playbackRequest);
}
}
if (!playbackRequestResponse.IsOk){
temppbData = Helpers.Deserialize<PlaybackData>(playbackRequestResponse.ResponseContent, SettingsJsonSerializerSettings) ??
new PlaybackData{ Total = 0, Data = new List<Dictionary<string, Dictionary<string, StreamDetails>>>() };
} else{
Console.WriteLine("Request Stream URLs FAILED! Attempting fallback");
@ -1665,7 +1593,10 @@ public class Crunchyroll{
HardsubLocale = Locale.DefaulT
};
if (temppbData.Data != null) temppbData.Data[0]["drm_adaptive_switch_dash"] = derivedPlayCrunchyStreams;
if (temppbData.Data != null){
temppbData.Data[0]["drm_adaptive_dash"] = derivedPlayCrunchyStreams;
temppbData.Total = 1;
}
temppbData.Meta = new PlaybackMeta(){ AudioLocale = playStream.AudioLocale, Versions = playStream.Versions, Bifs = new List<string>{ playStream.Bifs }, MediaId = mediaId };
temppbData.Meta.Subtitles = new Subtitles();
@ -1679,7 +1610,7 @@ public class Crunchyroll{
ok = playbackRequestResponse.IsOk;
}
}
}
return (IsOk: ok, pbData: temppbData, error: ok ? "" : playbackRequestResponse.ResponseContent);
}

View File

@ -47,33 +47,6 @@ public class HttpClientReq{
// 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
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/html"));
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xhtml+xml"));
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml", 0.9));
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("image/avif"));
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("image/webp"));
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("image/apng"));
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*", 0.8));
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/signed-exchange", 0.7));
//
// // Set Accept-Language
// client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("en-US"));
// client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("en", 0.9));
//
// // Set Cache-Control and Pragma for no caching
// client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue{ NoCache = true };
// client.DefaultRequestHeaders.Pragma.Add(new NameValueHeaderValue("no-cache"));
//
// // Set other headers
// client.DefaultRequestHeaders.Add("sec-ch-ua", "\"Google Chrome\";v=\"123\", \"Not:A-Brand\";v=\"8\", \"Chromium\";v=\"123\"");
// client.DefaultRequestHeaders.Add("sec-ch-ua-mobile", "?0");
// client.DefaultRequestHeaders.Add("sec-ch-ua-platform", "\"Windows\"");
// client.DefaultRequestHeaders.Add("sec-fetch-dest", "document");
// client.DefaultRequestHeaders.Add("sec-fetch-mode", "navigate");
// client.DefaultRequestHeaders.Add("sec-fetch-site", "none");
// client.DefaultRequestHeaders.Add("sec-fetch-user", "?1");
// client.DefaultRequestHeaders.Add("upgrade-insecure-requests", "1");
}
public void SetETPCookie(string refresh_token){

View File

@ -113,13 +113,13 @@ public class CrDownloadOptions{
[YamlMember(Alias = "history", ApplyNamingConventions = false)]
public bool History{ get; set; }
[YamlMember(Alias = "user_non_drm_streams", ApplyNamingConventions = false)]
public bool UseNonDrmStreams{ get; set; }
[YamlMember(Alias = "sonarr_properties", ApplyNamingConventions = false)]
public SonarrProperties? SonarrProperties{ get; set; }
[YamlMember(Alias = "log_mode", ApplyNamingConventions = false)]
public bool LogMode{ get; set; }
[YamlMember(Alias = "stream_endpoint", ApplyNamingConventions = false)]
public string? StreamEndpoint{ get; set; }
}

View File

@ -45,9 +45,6 @@ public partial class SettingsPageViewModel : ViewModelBase{
[ObservableProperty]
private bool _history;
[ObservableProperty]
private bool _useNonDrmEndpoint = true;
[ObservableProperty]
private int _leadingNumbers;
@ -81,6 +78,9 @@ public partial class SettingsPageViewModel : ViewModelBase{
[ObservableProperty]
private ObservableCollection<ListBoxItem> _selectedDubLang = new();
[ObservableProperty]
private ComboBoxItem _selectedStreamEndpoint;
[ObservableProperty]
private ComboBoxItem _selectedDefaultDubLang;
@ -222,6 +222,22 @@ public partial class SettingsPageViewModel : ViewModelBase{
new ListBoxItem(){ Content = "none" },
};
public ObservableCollection<ComboBoxItem> StreamEndpoints{ get; } = new(){
new ComboBoxItem(){ Content = "web/firefox" },
new ComboBoxItem(){ Content = "console/switch" },
new ComboBoxItem(){ Content = "console/ps4" },
new ComboBoxItem(){ Content = "console/ps5" },
new ComboBoxItem(){ Content = "console/xbox_one" },
new ComboBoxItem(){ Content = "web/edge" },
// new ComboBoxItem(){ Content = "web/safari" },
new ComboBoxItem(){ Content = "web/chrome" },
new ComboBoxItem(){ Content = "web/fallback" },
// new ComboBoxItem(){ Content = "ios/iphone" },
// new ComboBoxItem(){ Content = "ios/ipad" },
new ComboBoxItem(){ Content = "android/phone" },
new ComboBoxItem(){ Content = "tv/samsung" },
};
private readonly FluentAvaloniaTheme _faTheme;
private bool settingsLoaded;
@ -252,6 +268,9 @@ public partial class SettingsPageViewModel : ViewModelBase{
ComboBoxItem? defaultSubLang = DefaultSubLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == (options.DefaultSub ?? "")) ?? null;
SelectedDefaultSubLang = defaultSubLang ?? DefaultSubLangList[0];
ComboBoxItem? streamEndpoint = StreamEndpoints.FirstOrDefault(a => a.Content != null && (string)a.Content == (options.StreamEndpoint ?? "")) ?? null;
SelectedStreamEndpoint = streamEndpoint ?? StreamEndpoints[0];
var softSubLang = SubLangList.Where(a => options.DlSubs.Contains(a.Content)).ToList();
SelectedSubLang.Clear();
@ -278,7 +297,6 @@ public partial class SettingsPageViewModel : ViewModelBase{
SonarrApiKey = props.ApiKey + "";
}
UseNonDrmEndpoint = options.UseNonDrmStreams;
DownloadVideo = !options.Novids;
DownloadAudio = !options.Noaudio;
DownloadVideoForEveryDub = !options.DlVideoOnce;
@ -359,6 +377,9 @@ public partial class SettingsPageViewModel : ViewModelBase{
Crunchyroll.Instance.CrunOptions.DefaultAudio = SelectedDefaultDubLang.Content + "";
Crunchyroll.Instance.CrunOptions.DefaultSub = SelectedDefaultSubLang.Content + "";
Crunchyroll.Instance.CrunOptions.StreamEndpoint = SelectedStreamEndpoint.Content + "";
List<string> dubLangs = new List<string>();
foreach (var listBoxItem in SelectedDubLang){
dubLangs.Add(listBoxItem.Content + "");
@ -369,7 +390,6 @@ public partial class SettingsPageViewModel : ViewModelBase{
Crunchyroll.Instance.CrunOptions.SimultaneousDownloads = SimultaneousDownloads;
Crunchyroll.Instance.CrunOptions.UseNonDrmStreams = UseNonDrmEndpoint;
Crunchyroll.Instance.CrunOptions.QualityAudio = SelectedAudioQuality?.Content + "";
Crunchyroll.Instance.CrunOptions.QualityVideo = SelectedVideoQuality?.Content + "";
Crunchyroll.Instance.CrunOptions.Theme = CurrentAppTheme?.Content + "";
@ -412,8 +432,6 @@ public partial class SettingsPageViewModel : ViewModelBase{
Crunchyroll.Instance.CrunOptions.FfmpegOptions = ffmpegParams;
CfgManager.WriteSettingsToFile();
// Console.WriteLine("Updated Settings");
}
private void UpdateSubAndDubString(){
@ -556,10 +574,6 @@ public partial class SettingsPageViewModel : ViewModelBase{
UpdateSettings();
}
partial void OnUseNonDrmEndpointChanged(bool value){
UpdateSettings();
}
partial void OnHistoryChanged(bool value){
UpdateSettings();
}
@ -608,6 +622,10 @@ public partial class SettingsPageViewModel : ViewModelBase{
partial void OnDownloadVideoForEveryDubChanged(bool value){
UpdateSettings();
}
partial void OnSelectedStreamEndpointChanged(ComboBoxItem value){
UpdateSettings();
}
}
public class MuxingParam{

View File

@ -123,6 +123,15 @@
Description="Adjust download settings"
IsExpanded="False">
<controls:SettingsExpanderItem Content="Stream Endpoint ">
<controls:SettingsExpanderItem.Footer>
<ComboBox HorizontalContentAlignment="Center" MinWidth="210" MaxDropDownHeight="400"
ItemsSource="{Binding StreamEndpoints}"
SelectedItem="{Binding SelectedStreamEndpoint}">
</ComboBox>
</controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Simultaneous Downloads">
<controls:SettingsExpanderItem.Footer>
<controls:NumberBox Minimum="0" Maximum="5"
@ -174,14 +183,7 @@
</controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem>
<controls:SettingsExpanderItem Content="Check for Non-DRM streams">
<controls:SettingsExpanderItem.Footer>
<CheckBox IsChecked="{Binding UseNonDrmEndpoint}"> </CheckBox>
</controls:SettingsExpanderItem.Footer>
</controls:SettingsExpanderItem>
<controls:SettingsExpander.Footer>
</controls:SettingsExpander.Footer>
</controls:SettingsExpander>