mirror of
https://github.com/Crunchy-DL/Crunchy-Downloader.git
synced 2026-01-11 20:10:26 +00:00
Add - Added **toggle to count missing history episodes** instead of just new episodes
Add - Added **Fast Add button** to the Seasons tab Chg - Changed **"Couldn't sync dubs" message** to include the specific failed dubs and added more details to the log Chg - Changed **history episode addition** to maintain order (slightly slower when adding but prevents queue mixing) Chg - Changed **language sorting** for improved clarity Chg - Changed **subtitle locale handling** to use the actual locale instead of Crunchyroll's local language tag Chg - Changed **filename format** when downloading all dubs to separate files to include the locale in the filename Fix - Fixed **download retries not being logged** Fix - Fixed **dubbed episodes added from the calendar** not updating correctly in the history Fix - Fixed **DRM authentication issue**
This commit is contained in:
parent
4f6d0f2257
commit
ae0f936ff5
22 changed files with 415 additions and 448 deletions
|
|
@ -82,13 +82,7 @@ public class CrEpisode(){
|
|||
if (episode.EpisodeAndLanguages.Langs.All(a => a.CrLocale != version.AudioLocale)){
|
||||
// Push to arrays if there are no duplicates of the same language
|
||||
episode.EpisodeAndLanguages.Items.Add(dlEpisode);
|
||||
episode.EpisodeAndLanguages.Langs.Add(Array.Find(Languages.languages, a => a.CrLocale == version.AudioLocale) ?? new LanguageItem{
|
||||
CrLocale = "und",
|
||||
Locale = "un",
|
||||
Code = "und",
|
||||
Name = string.Empty,
|
||||
Language = string.Empty
|
||||
});
|
||||
episode.EpisodeAndLanguages.Langs.Add(Array.Find(Languages.languages, a => a.CrLocale == version.AudioLocale) ?? Languages.DEFAULT_lang);
|
||||
}
|
||||
}
|
||||
} else{
|
||||
|
|
@ -98,13 +92,7 @@ public class CrEpisode(){
|
|||
if (episode.EpisodeAndLanguages.Langs.All(a => a.CrLocale != dlEpisode.AudioLocale)){
|
||||
// Push to arrays if there are no duplicates of the same language
|
||||
episode.EpisodeAndLanguages.Items.Add(dlEpisode);
|
||||
episode.EpisodeAndLanguages.Langs.Add(Array.Find(Languages.languages, a => a.CrLocale == dlEpisode.AudioLocale) ?? new LanguageItem{
|
||||
CrLocale = "und",
|
||||
Locale = "un",
|
||||
Code = "und",
|
||||
Name = string.Empty,
|
||||
Language = string.Empty
|
||||
});
|
||||
episode.EpisodeAndLanguages.Langs.Add(Array.Find(Languages.languages, a => a.CrLocale == dlEpisode.AudioLocale) ?? Languages.DEFAULT_lang);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -343,40 +343,6 @@ public class CrSeries{
|
|||
return episodeList;
|
||||
}
|
||||
|
||||
public Dictionary<int, Dictionary<string, SeriesSearchItem>> ParseSeriesResult(CrSeriesSearch seasonsList){
|
||||
var ret = new Dictionary<int, Dictionary<string, SeriesSearchItem>>();
|
||||
int i = 0;
|
||||
|
||||
if (seasonsList.Data == null) return ret;
|
||||
|
||||
foreach (var item in seasonsList.Data){
|
||||
i++;
|
||||
foreach (var lang in Languages.languages){
|
||||
int seasonNumber = item.SeasonNumber;
|
||||
if (item.Versions != null){
|
||||
seasonNumber = i;
|
||||
}
|
||||
|
||||
if (!ret.ContainsKey(seasonNumber)){
|
||||
ret[seasonNumber] = new Dictionary<string, SeriesSearchItem>();
|
||||
}
|
||||
|
||||
if (item.Title.Contains($"({lang.Name} Dub)") || item.Title.Contains($"({lang.Name})")){
|
||||
ret[seasonNumber][lang.Code] = item;
|
||||
} else if (item.IsSubbed && !item.IsDubbed && lang.Code == "jpn"){
|
||||
ret[seasonNumber][lang.Code] = item;
|
||||
} else if (item.IsDubbed && lang.Code == "eng" && !Languages.languages.Any(a => (item.Title.Contains($"({a.Name})") || item.Title.Contains($"({a.Name} Dub)")))){
|
||||
// Dubbed with no more infos will be treated as eng dubs
|
||||
ret[seasonNumber][lang.Code] = item;
|
||||
} else if (item.AudioLocale == lang.CrLocale){
|
||||
ret[seasonNumber][lang.Code] = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public async Task<CrSeriesSearch?> ParseSeriesById(string id, string? crLocale, bool forced = false){
|
||||
await crunInstance.CrAuth.RefreshToken(true);
|
||||
NameValueCollection query = HttpUtility.ParseQueryString(new UriBuilder().Query);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
|
|||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.InteropServices;
|
||||
|
|
@ -296,6 +297,7 @@ public class CrunchyrollManager{
|
|||
await CrAuth.AuthAnonymous();
|
||||
}
|
||||
|
||||
|
||||
if (CrunOptions.History){
|
||||
if (File.Exists(CfgManager.PathCrHistory)){
|
||||
var decompressedJson = CfgManager.DecompressJsonFile(CfgManager.PathCrHistory);
|
||||
|
|
@ -329,6 +331,12 @@ public class CrunchyrollManager{
|
|||
|
||||
await SonarrClient.Instance.RefreshSonarr();
|
||||
}
|
||||
|
||||
//Fix hslang - can be removed in a future version
|
||||
var lang = Languages.Locale2language(CrunOptions.Hslang);
|
||||
if (lang != Languages.DEFAULT_lang){
|
||||
CrunOptions.Hslang = lang.CrLocale;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -363,6 +371,7 @@ public class CrunchyrollManager{
|
|||
if (options.SkipMuxing == false){
|
||||
bool syncError = false;
|
||||
bool muxError = false;
|
||||
var notSyncedDubs = "";
|
||||
|
||||
data.DownloadProgress = new DownloadProgress(){
|
||||
IsDownloading = true,
|
||||
|
|
@ -382,6 +391,7 @@ public class CrunchyrollManager{
|
|||
? Path.Combine(res.TempFolderPath ?? string.Empty, res.FileName ?? string.Empty)
|
||||
: Path.Combine(res.FolderPath ?? string.Empty, res.FileName ?? string.Empty);
|
||||
if (options is{ DlVideoOnce: false, KeepDubsSeperate: true }){
|
||||
|
||||
var groupByDub = Helpers.GroupByLanguageWithSubtitles(res.Data);
|
||||
var mergers = new List<Merger>();
|
||||
foreach (var keyValue in groupByDub){
|
||||
|
|
@ -391,7 +401,7 @@ public class CrunchyrollManager{
|
|||
SubLangList = options.DlSubs,
|
||||
FfmpegOptions = options.FfmpegOptions,
|
||||
SkipSubMux = options.SkipSubsMux,
|
||||
Output = fileNameAndPath,
|
||||
Output = fileNameAndPath + $".{keyValue.Value.First().Lang.Locale}",
|
||||
Mp4 = options.Mp4,
|
||||
MuxFonts = options.MuxFonts,
|
||||
VideoTitle = res.VideoTitle,
|
||||
|
|
@ -411,7 +421,7 @@ public class CrunchyrollManager{
|
|||
CcSubsMuxingFlag = options.CcSubsMuxingFlag,
|
||||
SignsSubsAsForced = options.SignsSubsAsForced,
|
||||
},
|
||||
fileNameAndPath, data);
|
||||
fileNameAndPath + $".{keyValue.Value.First().Lang.Locale}", data);
|
||||
|
||||
if (result is{ merger: not null, isMuxed: true }){
|
||||
mergers.Add(result.merger);
|
||||
|
|
@ -479,6 +489,7 @@ public class CrunchyrollManager{
|
|||
fileNameAndPath, data);
|
||||
|
||||
syncError = result.syncError;
|
||||
notSyncedDubs = result.notSyncedDubs;
|
||||
muxError = !result.isMuxed;
|
||||
|
||||
if (result is{ merger: not null, isMuxed: true }){
|
||||
|
|
@ -512,7 +523,7 @@ public class CrunchyrollManager{
|
|||
Percent = 100,
|
||||
Time = 0,
|
||||
DownloadSpeed = 0,
|
||||
Doing = (muxError ? "Muxing Failed" : "Done") + (syncError ? " - Couldn't sync dubs" : "")
|
||||
Doing = (muxError ? "Muxing Failed" : "Done") + (syncError ? $" - Couldn't sync dubs ({notSyncedDubs})" : "")
|
||||
};
|
||||
|
||||
if (CrunOptions.RemoveFinishedDownload && !syncError){
|
||||
|
|
@ -551,7 +562,8 @@ public class CrunchyrollManager{
|
|||
QueueManager.Instance.Queue.Refresh();
|
||||
|
||||
if (options.History && data.Data is{ Count: > 0 } && (options.HistoryIncludeCrArtists && data.Music || !data.Music)){
|
||||
History.SetAsDownloaded(data.SeriesId, data.SeasonId, data.Data.First().MediaId);
|
||||
var ids = data.Data.First().GetOriginalIds();
|
||||
History.SetAsDownloaded(data.SeriesId, ids.seasonID ?? data.SeasonId, ids.guid ?? data.Data.First().MediaId);
|
||||
}
|
||||
|
||||
if (options.MarkAsWatched && data.Data is{ Count: > 0 }){
|
||||
|
|
@ -648,7 +660,7 @@ public class CrunchyrollManager{
|
|||
|
||||
#endregion
|
||||
|
||||
private async Task<(Merger? merger, bool isMuxed, bool syncError)> MuxStreams(List<DownloadedMedia> data, CrunchyMuxOptions options, string filename, CrunchyEpMeta crunchyEpMeta){
|
||||
private async Task<(Merger? merger, bool isMuxed, bool syncError, string notSyncedDubs)> MuxStreams(List<DownloadedMedia> data, CrunchyMuxOptions options, string filename, CrunchyEpMeta crunchyEpMeta){
|
||||
var muxToMp3 = false;
|
||||
|
||||
if (options.Novids == true || data.FindAll(a => a.Type == DownloadMediaType.Video).Count == 0){
|
||||
|
|
@ -657,7 +669,7 @@ public class CrunchyrollManager{
|
|||
muxToMp3 = true;
|
||||
} else{
|
||||
Console.WriteLine("Skip muxing since no videos are downloaded");
|
||||
return (null, false, false);
|
||||
return (null, false, false, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -733,6 +745,8 @@ public class CrunchyrollManager{
|
|||
}
|
||||
|
||||
bool isMuxed, syncError = false;
|
||||
List<string> notSyncedDubs =[];
|
||||
|
||||
|
||||
if (options is{ SyncTiming: true, DlVideoOnce: true }){
|
||||
crunchyEpMeta.DownloadProgress = new DownloadProgress(){
|
||||
|
|
@ -755,6 +769,7 @@ public class CrunchyrollManager{
|
|||
|
||||
if (delay <= -100){
|
||||
syncError = true;
|
||||
notSyncedDubs.Add(syncVideo.Lang.CrLocale ?? syncVideo.Language.CrLocale);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -794,7 +809,7 @@ public class CrunchyrollManager{
|
|||
isMuxed = await merger.Merge("ffmpeg", CfgManager.PathFFMPEG);
|
||||
}
|
||||
|
||||
return (merger, isMuxed, syncError);
|
||||
return (merger, isMuxed, syncError, string.Join(", ", notSyncedDubs));
|
||||
}
|
||||
|
||||
private async Task<DownloadResponse> DownloadMediaList(CrunchyEpMeta data, CrDownloadOptions options){
|
||||
|
|
@ -974,7 +989,7 @@ public class CrunchyrollManager{
|
|||
|
||||
List<string> compiledChapters = new List<string>();
|
||||
|
||||
if (options.Chapters){
|
||||
if (options.Chapters && !data.OnlySubs){
|
||||
await ParseChapters(mediaGuid, compiledChapters);
|
||||
|
||||
if (compiledChapters.Count == 0 && primaryVersion.MediaGuid != null && mediaGuid != primaryVersion.MediaGuid){
|
||||
|
|
@ -1052,18 +1067,16 @@ public class CrunchyrollManager{
|
|||
|
||||
if (pbStreams?.Keys != null){
|
||||
var pb = pbStreams.Select(v => {
|
||||
v.Value.HardsubLang = v.Value.HardsubLocale != null
|
||||
? Languages.FixAndFindCrLc(v.Value.HardsubLocale.GetEnumMemberValue()).Locale
|
||||
: null;
|
||||
if (v.Value.HardsubLocale != null && v.Value.HardsubLang != null && !hsLangs.Contains(v.Value.HardsubLocale.GetEnumMemberValue())){
|
||||
hsLangs.Add(v.Value.HardsubLang);
|
||||
if (v.Value is{ IsHardsubbed: true, HardsubLocale: not null } && v.Value.HardsubLocale != Locale.DefaulT && !hsLangs.Contains(v.Value.HardsubLang.CrLocale)){
|
||||
hsLangs.Add(v.Value.HardsubLang.CrLocale);
|
||||
}
|
||||
|
||||
return new StreamDetailsPop{
|
||||
Url = v.Value.Url,
|
||||
IsHardsubbed = v.Value.IsHardsubbed,
|
||||
HardsubLocale = v.Value.HardsubLocale,
|
||||
HardsubLang = v.Value.HardsubLang,
|
||||
AudioLang = v.Value.AudioLang,
|
||||
AudioLang = pbData.Meta?.AudioLocale ?? Languages.DEFAULT_lang,
|
||||
Type = v.Value.Type,
|
||||
Format = "drm_adaptive_dash",
|
||||
};
|
||||
|
|
@ -1083,16 +1096,16 @@ public class CrunchyrollManager{
|
|||
};
|
||||
}
|
||||
|
||||
var audDub = "";
|
||||
var audDub = Languages.DEFAULT_lang;
|
||||
if (pbData.Meta != null){
|
||||
audDub = Languages.FindLang(Languages.FixLanguageTag((pbData.Meta.AudioLocale ?? Locale.DefaulT).GetEnumMemberValue())).Code;
|
||||
audDub = pbData.Meta.AudioLocale;
|
||||
}
|
||||
|
||||
hsLangs = Languages.SortTags(hsLangs);
|
||||
|
||||
streams = streams.Select(s => {
|
||||
s.AudioLang = audDub;
|
||||
s.HardsubLang = string.IsNullOrEmpty(s.HardsubLang) ? "-" : s.HardsubLang;
|
||||
s.HardsubLang = s.HardsubLang;
|
||||
s.Type = $"{s.Format}/{s.AudioLang}/{s.HardsubLang}";
|
||||
return s;
|
||||
}).ToList();
|
||||
|
|
@ -1102,21 +1115,15 @@ public class CrunchyrollManager{
|
|||
if (options.Hslang != "none"){
|
||||
if (hsLangs.IndexOf(options.Hslang) > -1){
|
||||
Console.WriteLine($"Selecting stream with {Languages.Locale2language(options.Hslang).Language} hardsubs");
|
||||
streams = streams.Where((s) => s.HardsubLang != "-" && s.HardsubLang == options.Hslang).ToList();
|
||||
streams = streams.Where((s) => s.IsHardsubbed && s.HardsubLang.CrLocale == options.Hslang).ToList();
|
||||
} else{
|
||||
Console.Error.WriteLine($"Selected stream with {Languages.Locale2language(options.Hslang).CrLocale} hardsubs not available");
|
||||
Console.Error.WriteLine($"Selected stream with {options.Hslang} hardsubs not available");
|
||||
if (hsLangs.Count > 0){
|
||||
Console.Error.WriteLine("Try hardsubs stream: " + string.Join(", ", hsLangs));
|
||||
}
|
||||
|
||||
if (dlVideoOnce && options.DlVideoOnce){
|
||||
streams = streams.Where((s) => {
|
||||
if (s.HardsubLang != "-"){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}).ToList();
|
||||
streams = streams.Where((s) => !s.IsHardsubbed).ToList();
|
||||
} else{
|
||||
if (hsLangs.Count > 0){
|
||||
var dialog = new ContentDialog(){
|
||||
|
|
@ -1141,7 +1148,7 @@ public class CrunchyrollManager{
|
|||
|
||||
if (hsLangs.IndexOf(selectedValue) > -1){
|
||||
Console.WriteLine($"Selecting stream with {Languages.Locale2language(selectedValue).Language} hardsubs");
|
||||
streams = streams.Where((s) => s.HardsubLang != "-" && s.HardsubLang == selectedValue).ToList();
|
||||
streams = streams.Where((s) => s.IsHardsubbed && s.HardsubLang?.CrLocale == selectedValue).ToList();
|
||||
data.Hslang = selectedValue;
|
||||
}
|
||||
} else{
|
||||
|
|
@ -1167,13 +1174,7 @@ public class CrunchyrollManager{
|
|||
}
|
||||
}
|
||||
} else{
|
||||
streams = streams.Where((s) => {
|
||||
if (s.HardsubLang != "-"){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}).ToList();
|
||||
streams = streams.Where((s) => !s.IsHardsubbed).ToList();
|
||||
|
||||
if (streams.Count < 1){
|
||||
Console.Error.WriteLine("Raw streams not available!");
|
||||
|
|
@ -1205,7 +1206,7 @@ public class CrunchyrollManager{
|
|||
}
|
||||
|
||||
string tsFile = "";
|
||||
var videoDownloadMedia = new DownloadedMedia();
|
||||
var videoDownloadMedia = new DownloadedMedia(){ Lang = Languages.DEFAULT_lang };
|
||||
|
||||
if (!dlFailed && curStream != null && options is not{ Novids: true, Noaudio: true }){
|
||||
var streamPlaylistsReq = HttpClientReq.CreateRequestMessage(curStream.Url ?? string.Empty, HttpMethod.Get, true, true, null);
|
||||
|
|
@ -1231,7 +1232,7 @@ public class CrunchyrollManager{
|
|||
//Parse MPD Playlists
|
||||
var crLocal = "";
|
||||
if (pbData.Meta != null){
|
||||
crLocal = Languages.FixLanguageTag((pbData.Meta.AudioLocale ?? Locale.DefaulT).GetEnumMemberValue());
|
||||
crLocal = pbData.Meta.AudioLocale.CrLocale;
|
||||
}
|
||||
|
||||
MPDParsed streamPlaylists = MPDParser.Parse(streamPlaylistsReqResponse.ResponseContent, Languages.FindLang(crLocal), matchedUrl);
|
||||
|
|
@ -1348,10 +1349,10 @@ public class CrunchyrollManager{
|
|||
variables.Add(new Variable("width", chosenVideoSegments.quality.width, false));
|
||||
if (string.IsNullOrEmpty(data.Resolution)) data.Resolution = chosenVideoSegments.quality.height + "p";
|
||||
|
||||
LanguageItem? lang = Languages.languages.FirstOrDefault(a => a.Code == curStream.AudioLang);
|
||||
LanguageItem? lang = Languages.languages.FirstOrDefault(a => a.CrLocale == curStream.AudioLang.CrLocale);
|
||||
if (lang == null){
|
||||
Console.Error.WriteLine($"Unable to find language for code {curStream.AudioLang}");
|
||||
MainWindow.Instance.ShowError($"Unable to find language for code {curStream.AudioLang}");
|
||||
Console.Error.WriteLine($"Unable to find language for code {curStream.AudioLang.CrLocale}");
|
||||
MainWindow.Instance.ShowError($"Unable to find language for code {curStream.AudioLang.CrLocale}");
|
||||
return new DownloadResponse{
|
||||
Data = new List<DownloadedMedia>(),
|
||||
Error = true,
|
||||
|
|
@ -1465,10 +1466,6 @@ public class CrunchyrollManager{
|
|||
};
|
||||
QueueManager.Instance.Queue.Refresh();
|
||||
|
||||
var assetIdRegexMatch = Regex.Match(chosenVideoSegments.segments[0].uri, @"/assets/(?:p/)?([^_,]+)");
|
||||
var assetId = assetIdRegexMatch.Success ? assetIdRegexMatch.Groups[1].Value : null;
|
||||
var sessionId = Helpers.GenerateSessionId();
|
||||
|
||||
Console.WriteLine("Decryption Needed, attempting to decrypt");
|
||||
|
||||
if (!_widevine.canDecrypt){
|
||||
|
|
@ -1481,40 +1478,10 @@ public class CrunchyrollManager{
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
var reqBodyData = new{
|
||||
accounting_id = "crunchyroll",
|
||||
asset_id = assetId,
|
||||
session_id = sessionId,
|
||||
user_id = Token?.account_id
|
||||
};
|
||||
|
||||
var json = JsonConvert.SerializeObject(reqBodyData);
|
||||
var reqBody = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
var decRequest = HttpClientReq.CreateRequestMessage($"{ApiUrls.DRM}", HttpMethod.Post, false, false, null);
|
||||
decRequest.Content = reqBody;
|
||||
|
||||
var decRequestResponse = await HttpClientReq.Instance.SendHttpRequest(decRequest);
|
||||
|
||||
if (!decRequestResponse.IsOk){
|
||||
Console.Error.WriteLine("Request to DRM Authentication failed: ");
|
||||
MainWindow.Instance.ShowError("Request to DRM Authentication failed");
|
||||
dlFailed = true;
|
||||
return new DownloadResponse{
|
||||
Data = files,
|
||||
Error = dlFailed,
|
||||
FileName = fileName.Length > 0 ? (Path.IsPathRooted(fileName) ? fileName : Path.Combine(fileDir, fileName)) : "./unknown",
|
||||
ErrorText = "DRM Authentication failed"
|
||||
};
|
||||
}
|
||||
|
||||
DrmAuthData authData = Helpers.Deserialize<DrmAuthData>(decRequestResponse.ResponseContent, SettingsJsonSerializerSettings) ?? new DrmAuthData();
|
||||
|
||||
Dictionary<string, string> authDataDict = new Dictionary<string, string>
|
||||
{ { "dt-custom-data", authData.CustomData ?? string.Empty },{ "x-dt-auth-token", authData.Token ?? string.Empty } };
|
||||
|
||||
var encryptionKeys = await _widevine.getKeys(chosenVideoSegments.pssh, "https://lic.drmtoday.com/license-proxy-widevine/cenc/", authDataDict);
|
||||
{ { "authorization", "Bearer " + Token?.access_token },{"x-cr-content-id", mediaGuid},{"x-cr-video-token", pbData.Meta?.Token ?? string.Empty} };
|
||||
|
||||
var encryptionKeys = await _widevine.getKeys(chosenVideoSegments.pssh, ApiUrls.WidevineLicenceUrl, authDataDict);
|
||||
|
||||
if (encryptionKeys.Count == 0){
|
||||
Console.Error.WriteLine("Failed to get encryption keys");
|
||||
|
|
@ -1527,7 +1494,6 @@ public class CrunchyrollManager{
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
if (Path.Exists(CfgManager.PathMP4Decrypt) || Path.Exists(CfgManager.PathShakaPackager)){
|
||||
var keyId = BitConverter.ToString(encryptionKeys[0].KeyID).Replace("-", "").ToLower();
|
||||
var key = BitConverter.ToString(encryptionKeys[0].Bytes).Replace("-", "").ToLower();
|
||||
|
|
@ -1610,6 +1576,7 @@ public class CrunchyrollManager{
|
|||
Type = syncTimingDownload ? DownloadMediaType.SyncVideo : DownloadMediaType.Video,
|
||||
Path = $"{tsFile}.video.m4s",
|
||||
Lang = lang,
|
||||
Language = lang,
|
||||
IsPrimary = isPrimary
|
||||
};
|
||||
files.Add(videoDownloadMedia);
|
||||
|
|
@ -1694,6 +1661,7 @@ public class CrunchyrollManager{
|
|||
Type = syncTimingDownload ? DownloadMediaType.SyncVideo : DownloadMediaType.Video,
|
||||
Path = $"{tsFile}.video.m4s",
|
||||
Lang = lang,
|
||||
Language = lang,
|
||||
IsPrimary = isPrimary
|
||||
};
|
||||
files.Add(videoDownloadMedia);
|
||||
|
|
@ -1753,13 +1721,7 @@ public class CrunchyrollManager{
|
|||
}
|
||||
|
||||
// Finding language by code
|
||||
var lang = Languages.languages.FirstOrDefault(l => l.Code == curStream?.AudioLang) ?? new LanguageItem{
|
||||
CrLocale = "und",
|
||||
Locale = "un",
|
||||
Code = "und",
|
||||
Name = string.Empty,
|
||||
Language = string.Empty
|
||||
};
|
||||
var lang = Languages.languages.FirstOrDefault(l => l == curStream?.AudioLang) ?? Languages.DEFAULT_lang;
|
||||
if (lang.Code == "und"){
|
||||
Console.Error.WriteLine($"Unable to find language for code {curStream?.AudioLang}");
|
||||
}
|
||||
|
|
@ -1793,7 +1755,7 @@ public class CrunchyrollManager{
|
|||
}
|
||||
}
|
||||
|
||||
if (options.IncludeVideoDescription){
|
||||
if (options.IncludeVideoDescription && !data.OnlySubs){
|
||||
string fullPath = (Path.IsPathRooted(fileName) ? fileName : Path.Combine(fileDir, fileName)) + ".xml";
|
||||
|
||||
if (!File.Exists(fullPath)){
|
||||
|
|
@ -1821,6 +1783,7 @@ public class CrunchyrollManager{
|
|||
files.Add(new DownloadedMedia{
|
||||
Type = DownloadMediaType.Description,
|
||||
Path = fullPath,
|
||||
Lang = Languages.DEFAULT_lang
|
||||
});
|
||||
data.downloadedFiles.Add(fullPath);
|
||||
} else{
|
||||
|
|
@ -1828,6 +1791,7 @@ public class CrunchyrollManager{
|
|||
files.Add(new DownloadedMedia{
|
||||
Type = DownloadMediaType.Description,
|
||||
Path = fullPath,
|
||||
Lang = Languages.DEFAULT_lang
|
||||
});
|
||||
data.downloadedFiles.Add(fullPath);
|
||||
}
|
||||
|
|
@ -1858,7 +1822,7 @@ public class CrunchyrollManager{
|
|||
};
|
||||
}
|
||||
|
||||
private static async Task DownloadSubtitles(CrDownloadOptions options, PlaybackData pbData, string audDub, string fileName, List<DownloadedMedia> files, string fileDir, CrunchyEpMeta data,
|
||||
private static async Task DownloadSubtitles(CrDownloadOptions options, PlaybackData pbData, LanguageItem audDub, string fileName, List<DownloadedMedia> files, string fileDir, CrunchyEpMeta data,
|
||||
DownloadedMedia videoDownloadMedia){
|
||||
if (pbData.Meta != null && (pbData.Meta.Subtitles is{ Count: > 0 } || pbData.Meta.Captions is{ Count: > 0 })){
|
||||
List<SubtitleInfo> subsData = pbData.Meta.Subtitles?.Values.ToList() ??[];
|
||||
|
|
@ -1894,7 +1858,7 @@ public class CrunchyrollManager{
|
|||
var langItem = subsItem.locale;
|
||||
var sxData = new SxItem();
|
||||
sxData.Language = langItem;
|
||||
var isSigns = langItem.Code == audDub && !subsItem.isCC;
|
||||
var isSigns = langItem.CrLocale == audDub.CrLocale && !subsItem.isCC;
|
||||
var isCc = subsItem.isCC;
|
||||
|
||||
sxData.File = Languages.SubsFile(fileName, index + "", langItem, isCc, options.CcTag, isSigns, subsItem.format, !(data.DownloadSubs.Count == 1 && !data.DownloadSubs.Contains("all")));
|
||||
|
|
@ -2127,7 +2091,7 @@ public class CrunchyrollManager{
|
|||
Data = new Dictionary<string, StreamDetails>()
|
||||
};
|
||||
|
||||
var playbackEndpoint = $"https://cr-play-service.prd.crunchyrollsvc.com/v2/{(music ? "music/" : "")}{mediaGuidId}/{options.StreamEndpoint}/play";
|
||||
var playbackEndpoint = $"{ApiUrls.Playback}/{(music ? "music/" : "")}{mediaGuidId}/{options.StreamEndpoint}/play";
|
||||
var playbackRequestResponse = await SendPlaybackRequestAsync(playbackEndpoint);
|
||||
|
||||
if (!playbackRequestResponse.IsOk){
|
||||
|
|
@ -2138,7 +2102,7 @@ public class CrunchyrollManager{
|
|||
temppbData = await ProcessPlaybackResponseAsync(playbackRequestResponse.ResponseContent, mediaId, mediaGuidId);
|
||||
} else{
|
||||
Console.WriteLine("Request Stream URLs FAILED! Attempting fallback");
|
||||
playbackEndpoint = $"https://cr-play-service.prd.crunchyrollsvc.com/v2/{(music ? "music/" : "")}{mediaGuidId}/web/firefox/play";
|
||||
playbackEndpoint = $"{ApiUrls.Playback}/{(music ? "music/" : "")}{mediaGuidId}/web/firefox/play";
|
||||
playbackRequestResponse = await SendPlaybackRequestAsync(playbackEndpoint);
|
||||
|
||||
if (!playbackRequestResponse.IsOk){
|
||||
|
|
@ -2190,30 +2154,36 @@ public class CrunchyrollManager{
|
|||
|
||||
var derivedPlayCrunchyStreams = new CrunchyStreams();
|
||||
if (playStream.HardSubs != null){
|
||||
//hlang "none" is no hardsube same url as the default url
|
||||
foreach (var hardsub in playStream.HardSubs){
|
||||
var stream = hardsub.Value;
|
||||
derivedPlayCrunchyStreams[hardsub.Key] = new StreamDetails{
|
||||
Url = stream.Url,
|
||||
HardsubLocale = stream.Hlang
|
||||
IsHardsubbed = true,
|
||||
HardsubLocale = stream.Hlang,
|
||||
HardsubLang = Languages.FixAndFindCrLc((stream.Hlang ?? Locale.DefaulT).GetEnumMemberValue())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
derivedPlayCrunchyStreams[""] = new StreamDetails{
|
||||
Url = playStream.Url,
|
||||
HardsubLocale = Locale.DefaulT
|
||||
IsHardsubbed = false,
|
||||
HardsubLocale = Locale.DefaulT,
|
||||
HardsubLang = Languages.DEFAULT_lang
|
||||
};
|
||||
|
||||
temppbData.Data = derivedPlayCrunchyStreams;
|
||||
temppbData.Total = 1;
|
||||
|
||||
temppbData.Meta = new PlaybackMeta{
|
||||
AudioLocale = playStream.AudioLocale,
|
||||
AudioLocale = Languages.FindLang(playStream.AudioLocale != null ? playStream.AudioLocale.GetEnumMemberValue() : ""),
|
||||
Versions = playStream.Versions,
|
||||
Bifs = new List<string>{ playStream.Bifs ?? "" },
|
||||
MediaId = mediaId,
|
||||
Captions = playStream.Captions,
|
||||
Subtitles = new Subtitles()
|
||||
Subtitles = new Subtitles(),
|
||||
Token = playStream.Token,
|
||||
};
|
||||
|
||||
if (playStream.Subtitles != null){
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ public partial class CrunchyrollSettingsViewModel : ViewModelBase{
|
|||
ComboBoxItem? descriptionLang = DescriptionLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == options.DescriptionLang) ?? null;
|
||||
SelectedDescriptionLang = descriptionLang ?? DescriptionLangList[0];
|
||||
|
||||
ComboBoxItem? hsLang = HardSubLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == Languages.Locale2language(options.Hslang).CrLocale) ?? null;
|
||||
ComboBoxItem? hsLang = HardSubLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == options.Hslang) ?? null;
|
||||
SelectedHSLang = hsLang ?? HardSubLangList[0];
|
||||
|
||||
ComboBoxItem? defaultDubLang = DefaultDubLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == (options.DefaultAudio ?? "")) ?? null;
|
||||
|
|
@ -416,10 +416,8 @@ public partial class CrunchyrollSettingsViewModel : ViewModelBase{
|
|||
string descLang = SelectedDescriptionLang.Content + "";
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.DescriptionLang = descLang != "default" ? descLang : CrunchyrollManager.Instance.DefaultLocale;
|
||||
|
||||
string hslang = SelectedHSLang.Content + "";
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.Hslang = hslang != "none" ? Languages.FindLang(hslang).Locale : hslang;
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.Hslang = SelectedHSLang.Content + "";
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.DefaultAudio = SelectedDefaultDubLang.Content + "";
|
||||
CrunchyrollManager.Instance.CrunOptions.DefaultSub = SelectedDefaultSubLang.Content + "";
|
||||
|
|
|
|||
|
|
@ -41,10 +41,10 @@ public class Widevine{
|
|||
if (Directory.Exists(CfgManager.PathWIDEVINE_DIR)){
|
||||
foreach (var file in Directory.EnumerateFiles(CfgManager.PathWIDEVINE_DIR)){
|
||||
var fileInfo = new FileInfo(file);
|
||||
|
||||
|
||||
if (fileInfo.Length >= 1024 * 8 || fileInfo.Attributes.HasFlag(FileAttributes.Directory))
|
||||
continue;
|
||||
|
||||
|
||||
string fileContents = File.ReadAllText(file, Encoding.UTF8);
|
||||
|
||||
if (IsPrivateKey(fileContents)){
|
||||
|
|
@ -107,7 +107,9 @@ public class Widevine{
|
|||
}
|
||||
|
||||
var licenceReq = ses.GetLicenseRequest();
|
||||
playbackRequest2.Content = new ByteArrayContent(licenceReq);
|
||||
var content = new ByteArrayContent(licenceReq);
|
||||
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
|
||||
playbackRequest2.Content = content;
|
||||
|
||||
var response = await HttpClientReq.Instance.SendHttpRequest(playbackRequest2);
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public enum SeriesType{
|
|||
public enum Locale{
|
||||
[EnumMember(Value = "")]
|
||||
DefaulT,
|
||||
|
||||
|
||||
[EnumMember(Value = "un")]
|
||||
Unknown,
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public class HlsDownloader{
|
|||
Offset = options.Offset ?? 0,
|
||||
BaseUrl = options.BaseUrl,
|
||||
SkipInit = options.SkipInit ?? false,
|
||||
Timeout = options.Timeout ?? 60 * 1000,
|
||||
Timeout = options.Timeout ?? 15 * 1000,
|
||||
CheckPartLength = true,
|
||||
IsResume = options.Offset.HasValue && options.Offset.Value > 0,
|
||||
BytesDownloaded = 0,
|
||||
|
|
@ -449,17 +449,17 @@ public class HlsDownloader{
|
|||
// Log retry attempts
|
||||
string partType = isKey ? "Key" : "Part";
|
||||
int partIndx = partIndex + 1 + segOffset;
|
||||
Console.WriteLine($"{partType} {partIndx}: Attempt {attempt + 1} to retrieve data failed.");
|
||||
Console.WriteLine($"\tError: {ex.Message}");
|
||||
Console.Error.WriteLine($"{partType} {partIndx}: Attempt {attempt + 1} to retrieve data failed.");
|
||||
Console.Error.WriteLine($"\tError: {ex.Message}");
|
||||
if (attempt == retryCount)
|
||||
throw; // rethrow after last retry
|
||||
|
||||
await Task.Delay(_data.WaitTime);
|
||||
}catch (Exception ex) {
|
||||
|
||||
Console.WriteLine($"Unexpected exception at part {partIndex + 1 + segOffset}:");
|
||||
Console.WriteLine($"\tType: {ex.GetType()}");
|
||||
Console.WriteLine($"\tMessage: {ex.Message}");
|
||||
Console.Error.WriteLine($"Unexpected exception at part {partIndex + 1 + segOffset}:");
|
||||
Console.Error.WriteLine($"\tType: {ex.GetType()}");
|
||||
Console.Error.WriteLine($"\tMessage: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ public class Helpers{
|
|||
if (e.Data.StartsWith("Error:")){
|
||||
Console.Error.WriteLine(e.Data);
|
||||
} else{
|
||||
Console.WriteLine(e.Data);
|
||||
Console.WriteLine(e.Data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -604,29 +604,29 @@ public class Helpers{
|
|||
public static Dictionary<string, List<DownloadedMedia>> GroupByLanguageWithSubtitles(List<DownloadedMedia> allMedia){
|
||||
//Group by language
|
||||
var languageGroups = allMedia
|
||||
.Where(media =>
|
||||
!string.IsNullOrEmpty(media.Lang.CrLocale) ||
|
||||
(media.Type == DownloadMediaType.Subtitle && media.RelatedVideoDownloadMedia != null &&
|
||||
!string.IsNullOrEmpty(media.RelatedVideoDownloadMedia.Lang.CrLocale))
|
||||
.Where(media => media.Type != DownloadMediaType.Description &&
|
||||
(!string.IsNullOrEmpty(media.Lang?.CrLocale) ||
|
||||
(media is{ Type: DownloadMediaType.Subtitle, RelatedVideoDownloadMedia: not null } &&
|
||||
!string.IsNullOrEmpty(media.RelatedVideoDownloadMedia.Lang?.CrLocale)))
|
||||
)
|
||||
.GroupBy(media => {
|
||||
if (media.Type == DownloadMediaType.Subtitle && media.RelatedVideoDownloadMedia != null){
|
||||
return media.RelatedVideoDownloadMedia.Lang.CrLocale;
|
||||
if (media is{ Type: DownloadMediaType.Subtitle, RelatedVideoDownloadMedia: not null }){
|
||||
return media.RelatedVideoDownloadMedia.Lang?.CrLocale ?? "und";
|
||||
}
|
||||
|
||||
return media.Lang.CrLocale;
|
||||
return media.Lang?.CrLocale ?? "und";
|
||||
})
|
||||
.ToDictionary(group => group.Key, group => group.ToList());
|
||||
|
||||
//Find and add Description media to each group
|
||||
var descriptionMedia = allMedia.Where(media => media.Type == DownloadMediaType.Description).ToList();
|
||||
|
||||
foreach (var description in descriptionMedia){
|
||||
if (descriptionMedia.Count > 0){
|
||||
foreach (var group in languageGroups.Values){
|
||||
group.Add(description);
|
||||
group.Add(descriptionMedia[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return languageGroups;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -263,14 +263,22 @@ public static class ApiUrls{
|
|||
public static string Cms => (CrunchyrollManager.Instance.CrunOptions.UseCrBetaApi ? ApiBeta : ApiN) + "/content/v2/cms";
|
||||
public static string Content => (CrunchyrollManager.Instance.CrunOptions.UseCrBetaApi ? ApiBeta : ApiN) + "/content/v2";
|
||||
|
||||
public static string Playback => "https://cr-play-service.prd.crunchyrollsvc.com/v2";
|
||||
//https://www.crunchyroll.com/playback/v2
|
||||
//https://cr-play-service.prd.crunchyrollsvc.com/v2
|
||||
|
||||
public static string Subscription => (CrunchyrollManager.Instance.CrunOptions.UseCrBetaApi ? ApiBeta : ApiN) + "/subs/v3/subscriptions/";
|
||||
|
||||
public static readonly string BetaBrowse = ApiBeta + "/content/v1/browse";
|
||||
public static readonly string BetaCms = ApiBeta + "/cms/v2";
|
||||
public static readonly string DRM = ApiBeta + "/drm/v1/auth";
|
||||
|
||||
public static readonly string WidevineLicenceUrl = "https://www.crunchyroll.com/license/v1/license/widevine";
|
||||
//https://lic.drmtoday.com/license-proxy-widevine/cenc/
|
||||
//https://lic.staging.drmtoday.com/license-proxy-widevine/cenc/
|
||||
|
||||
public static string authBasicMob = "Basic eHVuaWh2ZWRidDNtYmlzdWhldnQ6MWtJUzVkeVR2akUwX3JxYUEzWWVBaDBiVVhVbXhXMTE=";
|
||||
|
||||
public static readonly string MobileUserAgent = "Crunchyroll/3.79.0 Android/15 okhttp/4.12.0";
|
||||
public static readonly string MobileUserAgent = "Crunchyroll/3.81.8 Android/15 okhttp/4.12.0";
|
||||
public static readonly string FirefoxUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0";
|
||||
}
|
||||
|
|
@ -361,6 +361,10 @@ public class Merger{
|
|||
case < 0.1:
|
||||
return startOffset;
|
||||
case > 1:
|
||||
Console.Error.WriteLine($"Couldn't sync dub:");
|
||||
Console.Error.WriteLine($"\tStart offset: {startOffset} seconds");
|
||||
Console.Error.WriteLine($"\tEnd offset: {endOffset} seconds");
|
||||
Console.Error.WriteLine($"\tVideo length difference: {lengthDiff} seconds");
|
||||
return -100;
|
||||
default:
|
||||
return endOffset;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@ public class CrDownloadOptions{
|
|||
|
||||
[JsonProperty("history")]
|
||||
public bool History{ get; set; }
|
||||
|
||||
[JsonProperty("history_count_missing")]
|
||||
public bool HistoryCountMissing { get; set; }
|
||||
|
||||
[JsonProperty("history_include_cr_artists")]
|
||||
public bool HistoryIncludeCrArtists{ get; set; }
|
||||
|
|
|
|||
|
|
@ -402,6 +402,16 @@ public class CrunchyEpMetaData{
|
|||
public List<EpisodeVersion>? Versions{ get; set; }
|
||||
public bool IsSubbed{ get; set; }
|
||||
public bool IsDubbed{ get; set; }
|
||||
|
||||
public (string? seasonID, string? guid) GetOriginalIds(){
|
||||
var version = Versions?.FirstOrDefault(a => a.Original);
|
||||
if (version != null && !string.IsNullOrEmpty(version.Guid) && !string.IsNullOrEmpty(version.SeasonGuid)){
|
||||
return (version.SeasonGuid, version.Guid);
|
||||
}
|
||||
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class CrunchyRollEpisodeData{
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ public class StreamDetails{
|
|||
public string? Url{ get; set; }
|
||||
|
||||
[JsonProperty("hardsub_lang")]
|
||||
public string? HardsubLang{ get; set; }
|
||||
public required LanguageItem HardsubLang{ get; set; }
|
||||
|
||||
public bool IsHardsubbed{ get; set; }
|
||||
|
||||
[JsonProperty("audio_lang")]
|
||||
public string? AudioLang{ get; set; }
|
||||
|
|
@ -24,6 +26,18 @@ public class StreamDetails{
|
|||
public string? Type{ get; set; }
|
||||
}
|
||||
|
||||
public class StreamDetailsPop{
|
||||
public Locale? HardsubLocale{ get; set; }
|
||||
public string? Url{ get; set; }
|
||||
public required LanguageItem HardsubLang{ get; set; }
|
||||
|
||||
public bool IsHardsubbed{ get; set; }
|
||||
|
||||
public required LanguageItem AudioLang{ get; set; }
|
||||
public string? Type{ get; set; }
|
||||
public string? Format{ get; set; }
|
||||
}
|
||||
|
||||
public class PlaybackMeta{
|
||||
[JsonProperty("media_id")]
|
||||
public string? MediaId{ get; set; }
|
||||
|
|
@ -33,12 +47,14 @@ public class PlaybackMeta{
|
|||
public List<PlaybackVersion>? Versions{ get; set; }
|
||||
|
||||
[JsonProperty("audio_locale")]
|
||||
public Locale? AudioLocale{ get; set; }
|
||||
public LanguageItem AudioLocale{ get; set; }
|
||||
|
||||
[JsonProperty("closed_captions")]
|
||||
public Subtitles? ClosedCaptions{ get; set; }
|
||||
|
||||
public Dictionary<string, Caption>? Captions{ get; set; }
|
||||
|
||||
public string? Token{ get; set; }
|
||||
}
|
||||
|
||||
public class SubtitleInfo{
|
||||
|
|
@ -71,11 +87,3 @@ public class PlaybackVersion{
|
|||
public string? Variant{ get; set; }
|
||||
}
|
||||
|
||||
public class StreamDetailsPop{
|
||||
public Locale? HardsubLocale{ get; set; }
|
||||
public string? Url{ get; set; }
|
||||
public string? HardsubLang{ get; set; }
|
||||
public string? AudioLang{ get; set; }
|
||||
public string? Type{ get; set; }
|
||||
public string? Format{ get; set; }
|
||||
}
|
||||
|
|
@ -82,7 +82,7 @@ public class DownloadResponse{
|
|||
|
||||
public class DownloadedMedia : SxItem{
|
||||
public DownloadMediaType Type{ get; set; }
|
||||
public LanguageItem Lang{ get; set; }
|
||||
public required LanguageItem Lang{ get; set; }
|
||||
public bool IsPrimary{ get; set; }
|
||||
|
||||
public bool? Cc{ get; set; }
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
|
||||
[JsonProperty("series_type")]
|
||||
public SeriesType SeriesType{ get; set; } = SeriesType.Unknown;
|
||||
|
||||
|
||||
[JsonProperty("series_is_inactive")]
|
||||
public bool IsInactive{ get; set; }
|
||||
|
||||
|
|
@ -107,10 +107,10 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
|
||||
[JsonIgnore]
|
||||
public string SeriesFolderPath{ get; set; }
|
||||
|
||||
|
||||
[JsonIgnore]
|
||||
public bool SeriesFolderPathExists{ get; set; }
|
||||
|
||||
|
||||
#region Settings Override
|
||||
|
||||
[JsonIgnore]
|
||||
|
|
@ -225,9 +225,9 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
|
||||
SelectedSubLang.CollectionChanged += Changes;
|
||||
SelectedDubLang.CollectionChanged += Changes;
|
||||
|
||||
|
||||
UpdateSeriesFolderPath();
|
||||
|
||||
|
||||
Loading = false;
|
||||
}
|
||||
|
||||
|
|
@ -249,113 +249,83 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
|
||||
public void UpdateNewEpisodes(){
|
||||
int count = 0;
|
||||
|
||||
bool foundWatched = false;
|
||||
var historyAddSpecials = CrunchyrollManager.Instance.CrunOptions.HistoryAddSpecials;
|
||||
var sonarrEnabled = SeriesType != SeriesType.Artist && CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null && CrunchyrollManager.Instance.CrunOptions.SonarrProperties.SonarrEnabled &&
|
||||
!string.IsNullOrEmpty(SonarrSeriesId);
|
||||
var options = CrunchyrollManager.Instance.CrunOptions;
|
||||
|
||||
var sonarrSkipUnmonitored = CrunchyrollManager.Instance.CrunOptions.HistorySkipUnmonitored;
|
||||
bool historyAddSpecials = options.HistoryAddSpecials;
|
||||
bool sonarrEnabled = SeriesType != SeriesType.Artist &&
|
||||
options.SonarrProperties?.SonarrEnabled == true &&
|
||||
!string.IsNullOrEmpty(SonarrSeriesId);
|
||||
bool skipUnmonitored = options.HistorySkipUnmonitored;
|
||||
bool countMissing = options.HistoryCountMissing;
|
||||
bool useSonarrCounting = options.HistoryCountSonarr;
|
||||
|
||||
if (sonarrEnabled && CrunchyrollManager.Instance.CrunOptions.HistoryCountSonarr){
|
||||
for (int i = Seasons.Count - 1; i >= 0; i--){
|
||||
var season = Seasons[i];
|
||||
for (int i = Seasons.Count - 1; i >= 0; i--){
|
||||
var season = Seasons[i];
|
||||
var episodes = season.EpisodesList;
|
||||
|
||||
if (season.SpecialSeason == true){
|
||||
if (historyAddSpecials){
|
||||
var episodes = season.EpisodesList;
|
||||
for (int j = episodes.Count - 1; j >= 0; j--){
|
||||
if (sonarrSkipUnmonitored && !episodes[j].SonarrIsMonitored){
|
||||
continue;
|
||||
}
|
||||
if (season.SpecialSeason == true){
|
||||
if (historyAddSpecials){
|
||||
for (int j = episodes.Count - 1; j >= 0; j--){
|
||||
var ep = episodes[j];
|
||||
|
||||
if (!string.IsNullOrEmpty(episodes[j].SonarrEpisodeId) && !episodes[j].SonarrHasFile){
|
||||
count++;
|
||||
}
|
||||
if (skipUnmonitored && sonarrEnabled && !ep.SonarrIsMonitored){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ShouldCountEpisode(ep, sonarrEnabled && useSonarrCounting, countMissing, false)){
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
for (int j = episodes.Count - 1; j >= 0; j--){
|
||||
var ep = episodes[j];
|
||||
|
||||
if (skipUnmonitored && sonarrEnabled && !ep.SonarrIsMonitored){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ep.SpecialEpisode){
|
||||
if (historyAddSpecials && ShouldCountEpisode(ep, sonarrEnabled && useSonarrCounting, countMissing, false)){
|
||||
count++;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var episodesList = season.EpisodesList;
|
||||
for (int j = episodesList.Count - 1; j >= 0; j--){
|
||||
var episode = episodesList[j];
|
||||
|
||||
if (sonarrSkipUnmonitored && !episode.SonarrIsMonitored){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (episode.SpecialEpisode){
|
||||
if (historyAddSpecials && !episode.SonarrHasFile){
|
||||
count++;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(episode.SonarrEpisodeId) && !episode.SonarrHasFile){
|
||||
count++;
|
||||
if (ShouldCountEpisode(ep, sonarrEnabled && useSonarrCounting, countMissing, foundWatched)){
|
||||
count++;
|
||||
} else{
|
||||
foundWatched = true;
|
||||
//if not count specials break
|
||||
if (!historyAddSpecials && !countMissing){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else{
|
||||
for (int i = Seasons.Count - 1; i >= 0; i--){
|
||||
var season = Seasons[i];
|
||||
|
||||
if (season.SpecialSeason == true){
|
||||
if (historyAddSpecials){
|
||||
var episodes = season.EpisodesList;
|
||||
for (int j = episodes.Count - 1; j >= 0; j--){
|
||||
if (sonarrEnabled && sonarrSkipUnmonitored && !episodes[j].SonarrIsMonitored){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!episodes[j].WasDownloaded){
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var episodesList = season.EpisodesList;
|
||||
for (int j = episodesList.Count - 1; j >= 0; j--){
|
||||
var episode = episodesList[j];
|
||||
|
||||
if (sonarrEnabled && sonarrSkipUnmonitored && !episode.SonarrIsMonitored){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (episode.SpecialEpisode){
|
||||
if (historyAddSpecials && !episode.WasDownloaded){
|
||||
count++;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!episode.WasDownloaded && !foundWatched){
|
||||
count++;
|
||||
} else{
|
||||
foundWatched = true;
|
||||
if (!historyAddSpecials){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundWatched && !historyAddSpecials){
|
||||
break;
|
||||
}
|
||||
if (foundWatched && !historyAddSpecials && !countMissing){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NewEpisodes = count;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NewEpisodes)));
|
||||
}
|
||||
|
||||
private bool ShouldCountEpisode(HistoryEpisode episode, bool useSonarr, bool countMissing, bool foundWatched){
|
||||
if (useSonarr)
|
||||
return !string.IsNullOrEmpty(episode.SonarrEpisodeId) && !episode.SonarrHasFile;
|
||||
|
||||
return !episode.WasDownloaded && (!foundWatched || countMissing);
|
||||
}
|
||||
|
||||
public void SetFetchingData(){
|
||||
FetchingData = true;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData)));
|
||||
|
|
@ -363,104 +333,65 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
|
||||
public async Task AddNewMissingToDownloads(){
|
||||
bool foundWatched = false;
|
||||
var historyAddSpecials = CrunchyrollManager.Instance.CrunOptions.HistoryAddSpecials;
|
||||
var sonarrEnabled = SeriesType != SeriesType.Artist && CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null && CrunchyrollManager.Instance.CrunOptions.SonarrProperties.SonarrEnabled &&
|
||||
!string.IsNullOrEmpty(SonarrSeriesId);
|
||||
var options = CrunchyrollManager.Instance.CrunOptions;
|
||||
|
||||
var sonarrSkipUnmonitored = CrunchyrollManager.Instance.CrunOptions.HistorySkipUnmonitored;
|
||||
bool historyAddSpecials = options.HistoryAddSpecials;
|
||||
bool sonarrEnabled = SeriesType != SeriesType.Artist &&
|
||||
options.SonarrProperties?.SonarrEnabled == true &&
|
||||
!string.IsNullOrEmpty(SonarrSeriesId);
|
||||
bool skipUnmonitored = options.HistorySkipUnmonitored;
|
||||
bool countMissing = options.HistoryCountMissing;
|
||||
bool useSonarrCounting = options.HistoryCountSonarr;
|
||||
|
||||
if (sonarrEnabled && CrunchyrollManager.Instance.CrunOptions.HistoryCountSonarr){
|
||||
for (int i = Seasons.Count - 1; i >= 0; i--){
|
||||
var season = Seasons[i];
|
||||
for (int i = Seasons.Count - 1; i >= 0; i--){
|
||||
var season = Seasons[i];
|
||||
var episodes = season.EpisodesList;
|
||||
|
||||
if (season.SpecialSeason == true){
|
||||
if (historyAddSpecials){
|
||||
var episodes = season.EpisodesList;
|
||||
for (int j = episodes.Count - 1; j >= 0; j--){
|
||||
if (sonarrSkipUnmonitored && !episodes[j].SonarrIsMonitored){
|
||||
continue;
|
||||
}
|
||||
if (season.SpecialSeason == true){
|
||||
if (historyAddSpecials){
|
||||
for (int j = episodes.Count - 1; j >= 0; j--){
|
||||
var ep = episodes[j];
|
||||
|
||||
if (!string.IsNullOrEmpty(episodes[j].SonarrEpisodeId) && !episodes[j].SonarrHasFile){
|
||||
await Seasons[i].EpisodesList[j].DownloadEpisode();
|
||||
}
|
||||
if (skipUnmonitored && sonarrEnabled && !ep.SonarrIsMonitored){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ShouldCountEpisode(ep, sonarrEnabled && useSonarrCounting, countMissing, false)){
|
||||
await ep.DownloadEpisode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = episodes.Count - 1; j >= 0; j--){
|
||||
var ep = episodes[j];
|
||||
|
||||
if (skipUnmonitored && sonarrEnabled && !ep.SonarrIsMonitored){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ep.SpecialEpisode){
|
||||
if (historyAddSpecials && ShouldCountEpisode(ep, sonarrEnabled && useSonarrCounting, countMissing, false)){
|
||||
await ep.DownloadEpisode();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var episodesList = season.EpisodesList;
|
||||
for (int j = episodesList.Count - 1; j >= 0; j--){
|
||||
var episode = episodesList[j];
|
||||
|
||||
if (sonarrEnabled && sonarrSkipUnmonitored && !episode.SonarrIsMonitored){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (episode.SpecialEpisode){
|
||||
if (historyAddSpecials && !episode.SonarrHasFile){
|
||||
await Seasons[i].EpisodesList[j].DownloadEpisode();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(episode.SonarrEpisodeId) && !episode.SonarrHasFile){
|
||||
await Seasons[i].EpisodesList[j].DownloadEpisode();
|
||||
if (ShouldCountEpisode(ep, sonarrEnabled && useSonarrCounting, countMissing, foundWatched)){
|
||||
await ep.DownloadEpisode();
|
||||
} else{
|
||||
foundWatched = true;
|
||||
if (!historyAddSpecials && !countMissing){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else{
|
||||
for (int i = Seasons.Count - 1; i >= 0; i--){
|
||||
var season = Seasons[i];
|
||||
|
||||
if (season.SpecialSeason == true){
|
||||
if (historyAddSpecials){
|
||||
var episodes = season.EpisodesList;
|
||||
for (int j = episodes.Count - 1; j >= 0; j--){
|
||||
if (sonarrSkipUnmonitored && !episodes[j].SonarrIsMonitored){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!episodes[j].WasDownloaded){
|
||||
await Seasons[i].EpisodesList[j].DownloadEpisode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var episodesList = season.EpisodesList;
|
||||
for (int j = episodesList.Count - 1; j >= 0; j--){
|
||||
var episode = episodesList[j];
|
||||
|
||||
if (sonarrEnabled && sonarrSkipUnmonitored && !episode.SonarrIsMonitored){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (episode.SpecialEpisode){
|
||||
if (historyAddSpecials && !episode.WasDownloaded){
|
||||
await Seasons[i].EpisodesList[j].DownloadEpisode();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!episode.WasDownloaded && !foundWatched){
|
||||
await Seasons[i].EpisodesList[j].DownloadEpisode();
|
||||
} else{
|
||||
foundWatched = true;
|
||||
if (!historyAddSpecials){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundWatched && !historyAddSpecials){
|
||||
break;
|
||||
}
|
||||
if (foundWatched && !historyAddSpecials && !countMissing){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -470,7 +401,7 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
FetchingData = true;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData)));
|
||||
var isOk = true;
|
||||
|
||||
|
||||
switch (SeriesType){
|
||||
case SeriesType.Artist:
|
||||
try{
|
||||
|
|
@ -503,7 +434,7 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
UpdateNewEpisodes();
|
||||
FetchingData = false;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData)));
|
||||
|
||||
|
||||
return isOk;
|
||||
}
|
||||
|
||||
|
|
@ -536,7 +467,7 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void UpdateSeriesFolderPath(){
|
||||
var season = Seasons.FirstOrDefault(season => !string.IsNullOrEmpty(season.SeasonDownloadPath));
|
||||
|
||||
|
|
@ -587,8 +518,7 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
SeriesFolderPathExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SeriesFolderPathExists)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,35 +9,53 @@ namespace CRD.Utils.Structs;
|
|||
public class Languages{
|
||||
public static readonly LanguageItem[] languages ={
|
||||
new(){ CrLocale = "ja-JP", Locale = "ja", Code = "jpn", Name = "Japanese" },
|
||||
new(){ CrLocale = "en-US", Locale = "en", Code = "eng", Name = "English" },
|
||||
|
||||
new(){ CrLocale = "de-DE", Locale = "de", Code = "deu", Name = "German" },
|
||||
|
||||
new(){ CrLocale = "en-US", Locale = "en", Code = "eng", Name = "English" },
|
||||
new(){ CrLocale = "en-IN", Locale = "en-IN", Code = "eng", Name = "English (India)" },
|
||||
new(){ CrLocale = "es-LA", Locale = "es-LA", Code = "spa", Name = "Spanish", Language = "Latin American Spanish" },
|
||||
|
||||
new(){ CrLocale = "es-419", Locale = "es-419", Code = "spa-419", Name = "Spanish", Language = "Latin American Spanish" },
|
||||
new(){ CrLocale = "es-ES", Locale = "es-ES", Code = "spa-ES", Name = "Castilian", Language = "European Spanish" },
|
||||
|
||||
new(){ CrLocale = "pt-BR", Locale = "pt-BR", Code = "por", Name = "Portuguese", Language = "Brazilian Portuguese" },
|
||||
new(){ CrLocale = "pt-PT", Locale = "pt-PT", Code = "por", Name = "Portuguese (Portugal)", Language = "Portugues (Portugal)" },
|
||||
|
||||
new(){ CrLocale = "fr-FR", Locale = "fr", Code = "fra", Name = "French" },
|
||||
new(){ CrLocale = "ar-ME", Locale = "ar", Code = "ara-ME", Name = "Arabic" },
|
||||
new(){ CrLocale = "ar-SA", Locale = "ar", Code = "ara", Name = "Arabic (Saudi Arabia)" },
|
||||
new(){ CrLocale = "it-IT", Locale = "it", Code = "ita", Name = "Italian" },
|
||||
new(){ CrLocale = "ru-RU", Locale = "ru", Code = "rus", Name = "Russian" },
|
||||
new(){ CrLocale = "tr-TR", Locale = "tr", Code = "tur", Name = "Turkish" },
|
||||
new(){ CrLocale = "hi-IN", Locale = "hi", Code = "hin", Name = "Hindi" },
|
||||
// new(){ locale = "zh", code = "cmn", name = "Chinese (Mandarin, PRC)" },
|
||||
new(){ CrLocale = "zh-CN", Locale = "zh-CN", Code = "zho", Name = "Chinese (Mainland China)" },
|
||||
new(){ CrLocale = "zh-TW", Locale = "zh-TW", Code = "chi", Name = "Chinese (Taiwan)" },
|
||||
new(){ CrLocale = "zh-HK", Locale = "zh-HK", Code = "zho-HK", Name = "Chinese (Hong Kong)" },
|
||||
new(){ CrLocale = "ko-KR", Locale = "ko", Code = "kor", Name = "Korean" },
|
||||
new(){ CrLocale = "ca-ES", Locale = "ca-ES", Code = "cat", Name = "Catalan" },
|
||||
new(){ CrLocale = "pl-PL", Locale = "pl-PL", Code = "pol", Name = "Polish" },
|
||||
new(){ CrLocale = "th-TH", Locale = "th-TH", Code = "tha", Name = "Thai", Language = "ไทย" },
|
||||
new(){ CrLocale = "ta-IN", Locale = "ta-IN", Code = "tam", Name = "Tamil (India)", Language = "தமிழ்" },
|
||||
new(){ CrLocale = "ms-MY", Locale = "ms-MY", Code = "may", Name = "Malay (Malaysia)", Language = "Bahasa Melayu" },
|
||||
new(){ CrLocale = "vi-VN", Locale = "vi-VN", Code = "vie", Name = "Vietnamese", Language = "Tiếng Việt" },
|
||||
|
||||
new(){ CrLocale = "id-ID", Locale = "id-ID", Code = "ind", Name = "Indonesian", Language = "Bahasa Indonesia" },
|
||||
new(){ CrLocale = "ms-MY", Locale = "ms-MY", Code = "may", Name = "Malay (Malaysia)", Language = "Bahasa Melayu" },
|
||||
|
||||
new(){ CrLocale = "ca-ES", Locale = "ca-ES", Code = "cat", Name = "Catalan" },
|
||||
|
||||
new(){ CrLocale = "vi-VN", Locale = "vi-VN", Code = "vie", Name = "Vietnamese", Language = "Tiếng Việt" },
|
||||
new(){ CrLocale = "tr-TR", Locale = "tr", Code = "tur", Name = "Turkish" },
|
||||
new(){ CrLocale = "ru-RU", Locale = "ru", Code = "rus", Name = "Russian" },
|
||||
|
||||
new(){ CrLocale = "ar-SA", Locale = "ar-SA", Code = "ara", Name = "Arabic" },
|
||||
new(){ CrLocale = "hi-IN", Locale = "hi", Code = "hin", Name = "Hindi" },
|
||||
new(){ CrLocale = "ta-IN", Locale = "ta-IN", Code = "tam", Name = "Tamil (India)", Language = "தமிழ்" },
|
||||
new(){ CrLocale = "te-IN", Locale = "te-IN", Code = "tel", Name = "Telugu (India)", Language = "తెలుగు" },
|
||||
|
||||
new(){ CrLocale = "zh-CN", Locale = "zh-CN", Code = "zho", Name = "Chinese (Mainland China)" },
|
||||
new(){ CrLocale = "zh-HK", Locale = "zh-HK", Code = "zho-HK", Name = "Chinese (Hong Kong)" },
|
||||
new(){ CrLocale = "zh-TW", Locale = "zh-TW", Code = "chi", Name = "Chinese (Taiwan)" },
|
||||
|
||||
new(){ CrLocale = "ko-KR", Locale = "ko", Code = "kor", Name = "Korean" },
|
||||
new(){ CrLocale = "th-TH", Locale = "th-TH", Code = "tha", Name = "Thai", Language = "ไทย" },
|
||||
|
||||
};
|
||||
|
||||
public static readonly LanguageItem DEFAULT_lang = new LanguageItem{
|
||||
CrLocale = "und",
|
||||
Locale = "un",
|
||||
Code = "und",
|
||||
Name = string.Empty,
|
||||
Language = string.Empty
|
||||
};
|
||||
|
||||
|
||||
public static List<string> SortListByLangList(List<string> langList){
|
||||
var orderMap = languages.Select((value, index) => new{ Value = value.CrLocale, Index = index })
|
||||
|
|
@ -65,7 +83,7 @@ public class Languages{
|
|||
|
||||
public static LanguageItem FixAndFindCrLc(string cr_locale){
|
||||
if (string.IsNullOrEmpty(cr_locale)){
|
||||
return new LanguageItem();
|
||||
return DEFAULT_lang;
|
||||
}
|
||||
|
||||
string str = FixLanguageTag(cr_locale);
|
||||
|
|
@ -77,7 +95,7 @@ public class Languages{
|
|||
string fileName = $"{fnOutput}";
|
||||
|
||||
if (addIndexAndLangCode){
|
||||
fileName += $".{langItem.CrLocale}"; //.{subsIndex}
|
||||
fileName += $".{langItem.Locale}"; //.{subsIndex}
|
||||
}
|
||||
|
||||
//removed .{langItem.language} from file name at end
|
||||
|
|
@ -123,13 +141,7 @@ public class Languages{
|
|||
if (lang?.CrLocale != null){
|
||||
return lang;
|
||||
} else{
|
||||
return new LanguageItem{
|
||||
CrLocale = "und",
|
||||
Locale = "un",
|
||||
Code = "und",
|
||||
Name = string.Empty,
|
||||
Language = string.Empty
|
||||
};
|
||||
return DEFAULT_lang;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,13 +151,7 @@ public class Languages{
|
|||
if (filteredLocale != null){
|
||||
return (LanguageItem)filteredLocale;
|
||||
} else{
|
||||
return new LanguageItem{
|
||||
CrLocale = "und",
|
||||
Locale = "un",
|
||||
Code = "und",
|
||||
Name = string.Empty,
|
||||
Language = string.Empty
|
||||
};
|
||||
return DEFAULT_lang;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -501,28 +501,30 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
|
||||
[RelayCommand]
|
||||
public async Task DownloadSeasonAll(HistorySeason season){
|
||||
var downloadTasks = season.EpisodesList
|
||||
.Select(episode => episode.DownloadEpisode());
|
||||
|
||||
await Task.WhenAll(downloadTasks);
|
||||
foreach (var episode in season.EpisodesList){
|
||||
await episode.DownloadEpisode();
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public async Task DownloadSeasonMissing(HistorySeason season){
|
||||
var downloadTasks = season.EpisodesList
|
||||
.Where(episode => !episode.WasDownloaded)
|
||||
.Select(episode => episode.DownloadEpisode());
|
||||
var missingEpisodes = season.EpisodesList
|
||||
.Where(episode => !episode.WasDownloaded).ToList();
|
||||
|
||||
await Task.WhenAll(downloadTasks);
|
||||
if (missingEpisodes.Count == 0){
|
||||
MessageBus.Current.SendMessage(new ToastMessage($"There are no missing episodes", ToastType.Error, 3));
|
||||
} else{
|
||||
foreach (var episode in missingEpisodes){
|
||||
await episode.DownloadEpisode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public async Task DownloadSeasonMissingSonarr(HistorySeason season){
|
||||
var downloadTasks = season.EpisodesList
|
||||
.Where(episode => !episode.SonarrHasFile)
|
||||
.Select(episode => episode.DownloadEpisode());
|
||||
|
||||
await Task.WhenAll(downloadTasks);
|
||||
foreach (var episode in season.EpisodesList.Where(episode => !episode.SonarrHasFile)){
|
||||
await episode.DownloadEpisode();
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
|
|
|||
|
|
@ -136,32 +136,30 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
|||
|
||||
[RelayCommand]
|
||||
public async Task DownloadSeasonAll(HistorySeason season){
|
||||
var downloadTasks = season.EpisodesList
|
||||
.Select(episode => episode.DownloadEpisode());
|
||||
|
||||
await Task.WhenAll(downloadTasks);
|
||||
foreach (var episode in season.EpisodesList){
|
||||
await episode.DownloadEpisode();
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public async Task DownloadSeasonMissing(HistorySeason season){
|
||||
var downloadTasks = season.EpisodesList
|
||||
.Where(episode => !episode.WasDownloaded)
|
||||
.Select(episode => episode.DownloadEpisode()).ToList();
|
||||
var missingEpisodes = season.EpisodesList
|
||||
.Where(episode => !episode.WasDownloaded).ToList();
|
||||
|
||||
if (downloadTasks.Count == 0){
|
||||
if (missingEpisodes.Count == 0){
|
||||
MessageBus.Current.SendMessage(new ToastMessage($"There are no missing episodes", ToastType.Error, 3));
|
||||
} else{
|
||||
await Task.WhenAll(downloadTasks);
|
||||
foreach (var episode in missingEpisodes){
|
||||
await episode.DownloadEpisode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public async Task DownloadSeasonMissingSonarr(HistorySeason season){
|
||||
var downloadTasks = season.EpisodesList
|
||||
.Where(episode => !episode.SonarrHasFile)
|
||||
.Select(episode => episode.DownloadEpisode());
|
||||
|
||||
await Task.WhenAll(downloadTasks);
|
||||
foreach (var episode in season.EpisodesList.Where(episode => !episode.SonarrHasFile)){
|
||||
await episode.DownloadEpisode();
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
|
|
|||
|
|
@ -146,6 +146,9 @@ public partial class UpcomingPageViewModel : ViewModelBase{
|
|||
[ObservableProperty]
|
||||
private int _selectedIndex;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _quickAddMode;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
|
|
@ -251,6 +254,11 @@ public partial class UpcomingPageViewModel : ViewModelBase{
|
|||
|
||||
[RelayCommand]
|
||||
public async Task AddToHistory(AnilistSeries series){
|
||||
if (ProgramManager.Instance.FetchingData){
|
||||
MessageBus.Current.SendMessage(new ToastMessage($"History still loading", ToastType.Warning, 3));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(series.CrunchyrollID)){
|
||||
if (CrunchyrollManager.Instance.CrunOptions.History){
|
||||
series.IsInHistory = true;
|
||||
|
|
@ -431,7 +439,7 @@ public partial class UpcomingPageViewModel : ViewModelBase{
|
|||
}
|
||||
|
||||
public void SelectionChangedOfSeries(AnilistSeries? value){
|
||||
if (value != null) value.IsExpanded = !value.IsExpanded;
|
||||
if (value != null && !QuickAddMode) value.IsExpanded = !value.IsExpanded;
|
||||
SelectedSeries = null;
|
||||
SelectedIndex = -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
|
|||
|
||||
[ObservableProperty]
|
||||
private bool _history;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _historyCountMissing;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _historyIncludeCrArtists;
|
||||
|
|
@ -259,6 +262,7 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
|
|||
ProxyUsername = options.ProxyUsername ?? "";
|
||||
ProxyPassword = options.ProxyPassword ?? "";
|
||||
ProxyPort = options.ProxyPort;
|
||||
HistoryCountMissing = options.HistoryCountMissing;
|
||||
HistoryIncludeCrArtists = options.HistoryIncludeCrArtists;
|
||||
HistoryAddSpecials = options.HistoryAddSpecials;
|
||||
HistorySkipUnmonitored = options.HistorySkipUnmonitored;
|
||||
|
|
@ -287,42 +291,45 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
|
|||
return;
|
||||
}
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.DownloadFinishedPlaySound = DownloadFinishedPlaySound;
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.BackgroundImageBlurRadius = Math.Clamp((BackgroundImageBlurRadius ?? 0), 0, 40);
|
||||
CrunchyrollManager.Instance.CrunOptions.BackgroundImageOpacity = Math.Clamp((BackgroundImageOpacity ?? 0), 0, 1);
|
||||
var settings = CrunchyrollManager.Instance.CrunOptions;
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.RetryAttempts = Math.Clamp((int)(RetryAttempts ?? 0), 1, 10);
|
||||
CrunchyrollManager.Instance.CrunOptions.RetryDelay = Math.Clamp((int)(RetryDelay ?? 0), 1, 30);
|
||||
settings.DownloadFinishedPlaySound = DownloadFinishedPlaySound;
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.DownloadToTempFolder = DownloadToTempFolder;
|
||||
CrunchyrollManager.Instance.CrunOptions.HistoryAddSpecials = HistoryAddSpecials;
|
||||
CrunchyrollManager.Instance.CrunOptions.HistoryIncludeCrArtists = HistoryIncludeCrArtists;
|
||||
CrunchyrollManager.Instance.CrunOptions.HistorySkipUnmonitored = HistorySkipUnmonitored;
|
||||
CrunchyrollManager.Instance.CrunOptions.HistoryCountSonarr = HistoryCountSonarr;
|
||||
CrunchyrollManager.Instance.CrunOptions.DownloadSpeedLimit = Math.Clamp((int)(DownloadSpeed ?? 0), 0, 1000000000);
|
||||
CrunchyrollManager.Instance.CrunOptions.SimultaneousDownloads = Math.Clamp((int)(SimultaneousDownloads ?? 0), 1, 10);
|
||||
settings.BackgroundImageBlurRadius = Math.Clamp((BackgroundImageBlurRadius ?? 0), 0, 40);
|
||||
settings.BackgroundImageOpacity = Math.Clamp((BackgroundImageOpacity ?? 0), 0, 1);
|
||||
|
||||
settings.RetryAttempts = Math.Clamp((int)(RetryAttempts ?? 0), 1, 10);
|
||||
settings.RetryDelay = Math.Clamp((int)(RetryDelay ?? 0), 1, 30);
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.ProxyEnabled = ProxyEnabled;
|
||||
CrunchyrollManager.Instance.CrunOptions.ProxySocks = ProxySocks;
|
||||
CrunchyrollManager.Instance.CrunOptions.ProxyHost = ProxyHost;
|
||||
CrunchyrollManager.Instance.CrunOptions.ProxyPort = Math.Clamp((int)(ProxyPort ?? 0), 0, 65535);
|
||||
CrunchyrollManager.Instance.CrunOptions.ProxyUsername = ProxyUsername;
|
||||
CrunchyrollManager.Instance.CrunOptions.ProxyPassword = ProxyPassword;
|
||||
settings.DownloadToTempFolder = DownloadToTempFolder;
|
||||
settings.HistoryCountMissing = HistoryCountMissing;
|
||||
settings.HistoryAddSpecials = HistoryAddSpecials;
|
||||
settings.HistoryIncludeCrArtists = HistoryIncludeCrArtists;
|
||||
settings.HistorySkipUnmonitored = HistorySkipUnmonitored;
|
||||
settings.HistoryCountSonarr = HistoryCountSonarr;
|
||||
settings.DownloadSpeedLimit = Math.Clamp((int)(DownloadSpeed ?? 0), 0, 1000000000);
|
||||
settings.SimultaneousDownloads = Math.Clamp((int)(SimultaneousDownloads ?? 0), 1, 10);
|
||||
|
||||
settings.ProxyEnabled = ProxyEnabled;
|
||||
settings.ProxySocks = ProxySocks;
|
||||
settings.ProxyHost = ProxyHost;
|
||||
settings.ProxyPort = Math.Clamp((int)(ProxyPort ?? 0), 0, 65535);
|
||||
settings.ProxyUsername = ProxyUsername;
|
||||
settings.ProxyPassword = ProxyPassword;
|
||||
|
||||
string historyLang = SelectedHistoryLang.Content + "";
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.HistoryLang = historyLang != "default" ? historyLang : CrunchyrollManager.Instance.DefaultLocale;
|
||||
settings.HistoryLang = historyLang != "default" ? historyLang : CrunchyrollManager.Instance.DefaultLocale;
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.Theme = CurrentAppTheme?.Content + "";
|
||||
settings.Theme = CurrentAppTheme?.Content + "";
|
||||
|
||||
if (_faTheme.CustomAccentColor != (Application.Current?.PlatformSettings?.GetColorValues().AccentColor1)){
|
||||
CrunchyrollManager.Instance.CrunOptions.AccentColor = _faTheme.CustomAccentColor.ToString();
|
||||
settings.AccentColor = _faTheme.CustomAccentColor.ToString();
|
||||
} else{
|
||||
CrunchyrollManager.Instance.CrunOptions.AccentColor = string.Empty;
|
||||
settings.AccentColor = string.Empty;
|
||||
}
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.History = History;
|
||||
settings.History = History;
|
||||
|
||||
var props = new SonarrProperties();
|
||||
|
||||
|
|
@ -339,9 +346,9 @@ public partial class GeneralSettingsViewModel : ViewModelBase{
|
|||
props.ApiKey = SonarrApiKey;
|
||||
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.SonarrProperties = props;
|
||||
settings.SonarrProperties = props;
|
||||
|
||||
CrunchyrollManager.Instance.CrunOptions.LogMode = LogMode;
|
||||
settings.LogMode = LogMode;
|
||||
|
||||
CfgManager.WriteCrSettings();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,17 @@
|
|||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<StackPanel Grid.Column="1" Margin="10" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<StackPanel Grid.Column="1" Margin="10" HorizontalAlignment="Right" VerticalAlignment="Center" Orientation="Horizontal">
|
||||
|
||||
<ToggleButton Width="50" Height="50" BorderThickness="0" Margin="5 0"
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="{Binding QuickAddMode}"
|
||||
IsEnabled="{Binding !IsLoading}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:SymbolIcon Symbol="Add" FontSize="25" />
|
||||
<TextBlock Text="Fast" TextWrapping="Wrap" HorizontalAlignment="Center" FontSize="12"></TextBlock>
|
||||
</StackPanel>
|
||||
</ToggleButton>
|
||||
|
||||
<StackPanel>
|
||||
<ToggleButton x:Name="DropdownButtonSorting" Width="50" Height="50"
|
||||
|
|
@ -147,7 +157,7 @@
|
|||
</Grid.ColumnDefinitions>
|
||||
|
||||
|
||||
<Expander Grid.Column="0" Grid.ColumnSpan="2" ExpandDirection="Right" IsExpanded="{Binding IsExpanded}">
|
||||
<Expander Grid.Column="0" Grid.ColumnSpan="2" ExpandDirection="Right" IsExpanded="{Binding IsExpanded}" IsVisible="{Binding !$parent[UserControl].((vm:UpcomingPageViewModel)DataContext).QuickAddMode}">
|
||||
<Expander.Styles>
|
||||
<Style Selector="Expander:not(:expanded) /template/ ToggleButton#ExpanderHeader">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
|
|
@ -329,6 +339,49 @@
|
|||
|
||||
</StackPanel>
|
||||
|
||||
|
||||
<Grid Grid.Column="0" Grid.ColumnSpan="2" IsVisible="{Binding $parent[UserControl].((vm:UpcomingPageViewModel)DataContext).QuickAddMode}"
|
||||
Background="#90000000"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
|
||||
|
||||
<Button
|
||||
Background="Transparent"
|
||||
IsVisible="{Binding !IsInHistory}"
|
||||
BorderThickness="0"
|
||||
FontStyle="Italic"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
||||
Command="{Binding $parent[UserControl].((vm:UpcomingPageViewModel)DataContext).AddToHistory}"
|
||||
CommandParameter="{Binding}"
|
||||
IsEnabled="{Binding HasCrID}">
|
||||
<ToolTip.Tip>
|
||||
<TextBlock Text="Add to history" FontSize="15" />
|
||||
</ToolTip.Tip>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<controls:SymbolIcon Symbol="Library" FontSize="32" IsVisible="{Binding HasCrID}" />
|
||||
<controls:SymbolIcon Symbol="Add" FontSize="32" IsVisible="{Binding HasCrID}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
|
||||
<Button
|
||||
Background="Transparent"
|
||||
IsVisible="{Binding IsInHistory}"
|
||||
BorderThickness="0"
|
||||
FontStyle="Italic"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
||||
IsEnabled="False">
|
||||
<ToolTip.Tip>
|
||||
<TextBlock Text="Already in history" FontSize="15" />
|
||||
</ToolTip.Tip>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<controls:SymbolIcon Symbol="Library" FontSize="32" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
</Grid>
|
||||
|
||||
|
||||
</Grid>
|
||||
|
||||
</DataTemplate>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,12 @@
|
|||
</ComboBox>
|
||||
</controls:SettingsExpanderItem.Footer>
|
||||
</controls:SettingsExpanderItem>
|
||||
|
||||
<controls:SettingsExpanderItem Content="History count Missing" Description="The missing count (orange corner) shows all missing episodes, not just new ones">
|
||||
<controls:SettingsExpanderItem.Footer>
|
||||
<CheckBox IsChecked="{Binding HistoryCountMissing}"> </CheckBox>
|
||||
</controls:SettingsExpanderItem.Footer>
|
||||
</controls:SettingsExpanderItem>
|
||||
|
||||
<controls:SettingsExpanderItem Content="History Include CR Artists" Description="Add Crunchyroll artists (music) to the history">
|
||||
<controls:SettingsExpanderItem.Footer>
|
||||
|
|
@ -39,7 +45,7 @@
|
|||
</controls:SettingsExpanderItem.Footer>
|
||||
</controls:SettingsExpanderItem>
|
||||
|
||||
<controls:SettingsExpanderItem Content="History Add Specials" Description="Add specials to the queue if they weren't downloaded before">
|
||||
<controls:SettingsExpanderItem Content="History Add Specials" Description="Add specials to the queue/count if they weren't downloaded before">
|
||||
<controls:SettingsExpanderItem.Footer>
|
||||
<CheckBox IsChecked="{Binding HistoryAddSpecials}"> </CheckBox>
|
||||
</controls:SettingsExpanderItem.Footer>
|
||||
|
|
|
|||
Loading…
Reference in a new issue