mirror of
https://github.com/Crunchy-DL/Crunchy-Downloader.git
synced 2026-04-22 09:21:57 +00:00
Add - Added background image option to the Appearance settings Add - Background Image Settings - Added new options to control opacity and blur radius Add - Added "Couldn't sync dubs" status if the syncing failed Add - Added functionality to combine multiple episodes from the same season into a single entry in the calendar Add - Added video resolution display next to dubs/subs in the downloads tab Add - Added Cloudflare check to image loading Add - Added hardsub selection if the current is not available Add - Added part size setting to configure the size of parts downloaded at the same time Add - Added quality override to history series Add - Added history marker to search results to indicate if a series is already in the user's history Add - Added seasons tab for seasonal releases (Spring, Summer, Fall, Winter) Add - Added potential releases and release times for the current day and the next week to the custom calendar Chg - Changed Calendar cards background color for improved visibility Chg - Combined Appearance settings into a single section in the settings tab Chg - Consolidated Debug settings into one settings expander for better organization Chg - Changed time sync to now check both the start and end of the video Chg - Changed encoding progress to be displayed by the progress bar Chg - Updated the functionality for hiding dubs in the custom calendar Chg - Adjusted Dub sync to improve accuracy, resolving issues where it failed for more episodes than expected Chg - Subtitles and dubs are now sorted according to the order selected in the MKV file Chg - Changed logout behavior to correctly log out if login fails when starting the downloader Chg - Changed that all downloaded files are removed if an in-progress download is removed from the queue Chg - Changed default profile image Chg - Updated used packages to the newest version Chg - Separated settings to separate tabs Fix - Fixed some series didn't get added to the history Fix - Fixed an issue with file path length that prevented some files from being accessed properly Fix - Fixed an issue where file names exceeded the maximum allowable length, causing errors Fix - Fixed an issue where refreshing a series could get stuck Fix - Fixed a crash that could happen with the syncing Fix - Fixed an issue where the download status showed "Done" while moving files from the temp folder Fix - Fixed an issue where cookies were not being utilized correctly Fix - Resolved issues with displaying dates in UTC format Fix - Fixed an issue with incorrect calendar grouping Fix - Fixed an issue with the previous week navigation in the calendar Fix - Fixed an issue where the calendar would not display correctly when not logged in Fix - Fixed incorrect FFmpeg check for other OS (Linux/macOS) Fix - Fixed an issue where image loading used a different HTTP client
166 lines
No EOL
6.2 KiB
C#
166 lines
No EOL
6.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Xml.Linq;
|
|
using CRD.Utils.HLS;
|
|
using CRD.Utils.Parser;
|
|
using CRD.Utils.Parser.Utils;
|
|
using CRD.Utils.Structs;
|
|
|
|
namespace CRD.Utils;
|
|
|
|
public class Segment{
|
|
public string uri{ get; set; }
|
|
public double timeline{ get; set; }
|
|
public double duration{ get; set; }
|
|
public Map map{ get; set; }
|
|
|
|
public ByteRange? byteRange { get; set; }
|
|
public double? number{ get; set; }
|
|
public double? presentationTime{ get; set; }
|
|
}
|
|
|
|
public class Map{
|
|
public string uri { get; set; }
|
|
|
|
public ByteRange? byteRange { get; set; }
|
|
}
|
|
|
|
public class PlaylistItem{
|
|
public string? pssh{ get; set; }
|
|
public int bandwidth{ get; set; }
|
|
public List<Segment> segments{ get; set; }
|
|
}
|
|
|
|
public class AudioPlaylist : PlaylistItem{
|
|
public LanguageItem? language{ get; set; }
|
|
public bool @default{ get; set; }
|
|
}
|
|
|
|
public class VideoPlaylist : PlaylistItem{
|
|
public Quality quality{ get; set; }
|
|
}
|
|
|
|
public class VideoItem: VideoPlaylist{
|
|
public string resolutionText{ get; set; }
|
|
}
|
|
|
|
public class AudioItem: AudioPlaylist{
|
|
public string resolutionText{ get; set; }
|
|
}
|
|
|
|
public class Quality{
|
|
public int width{ get; set; }
|
|
public int height{ get; set; }
|
|
}
|
|
|
|
public class MPDParsed{
|
|
public Dictionary<string, ServerData> Data{ get; set; }
|
|
}
|
|
|
|
public class ServerData{
|
|
public List<AudioPlaylist> audio{ get; set; }
|
|
public List<VideoPlaylist> video{ get; set; }
|
|
}
|
|
|
|
public static class MPDParser{
|
|
public static MPDParsed Parse(string manifest, LanguageItem? language, string? url){
|
|
if (!manifest.Contains("BaseURL") && url != null){
|
|
XDocument doc = XDocument.Parse(manifest);
|
|
XElement mpd = doc.Element("MPD");
|
|
mpd.AddFirst(new XElement("BaseURL", url));
|
|
manifest = doc.ToString();
|
|
}
|
|
|
|
dynamic parsed = DashParser.Parse(manifest);
|
|
|
|
MPDParsed ret = new MPDParsed{ Data = new Dictionary<string, ServerData>() };
|
|
|
|
foreach (var item in parsed.mediaGroups.AUDIO.audio.Values){
|
|
foreach (var playlist in item.playlists){
|
|
var host = new Uri(playlist.resolvedUri).Host;
|
|
EnsureHostEntryExists(ret, host);
|
|
|
|
List<dynamic> segments = playlist.segments;
|
|
|
|
if (ObjectUtilities.GetMemberValue(playlist,"sidx") != null && segments.Count == 0){
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
var foundLanguage = Languages.FindLang(Languages.languages.FirstOrDefault(a => a.Code == item.language).CrLocale ?? "unknown");
|
|
LanguageItem? audioLang = item.language != null ? foundLanguage : (language != null ? language : foundLanguage);
|
|
|
|
var pItem = new AudioPlaylist{
|
|
bandwidth = playlist.attributes.BANDWIDTH,
|
|
language = audioLang,
|
|
@default = item.@default,
|
|
segments = segments.Select(segment => new Segment{
|
|
duration = segment.duration,
|
|
map = new Map{uri = segment.map.resolvedUri,byteRange = ObjectUtilities.GetMemberValue(segment.map,"byterange")},
|
|
number = segment.number,
|
|
presentationTime = segment.presentationTime,
|
|
timeline = segment.timeline,
|
|
uri = segment.resolvedUri,
|
|
byteRange = ObjectUtilities.GetMemberValue(segment,"byterange")
|
|
}).ToList()
|
|
};
|
|
|
|
var contentProtectionDict = (IDictionary<string, dynamic>)ObjectUtilities.GetMemberValue(playlist,"contentProtection");
|
|
|
|
if (contentProtectionDict != null && contentProtectionDict.ContainsKey("com.widevine.alpha") && contentProtectionDict["com.widevine.alpha"].pssh != null)
|
|
pItem.pssh = ArrayBufferToBase64(contentProtectionDict["com.widevine.alpha"].pssh);
|
|
|
|
ret.Data[host].audio.Add(pItem);
|
|
}
|
|
}
|
|
|
|
foreach (var playlist in parsed.playlists){
|
|
var host = new Uri(playlist.resolvedUri).Host;
|
|
EnsureHostEntryExists(ret, host);
|
|
|
|
List<dynamic> segments = playlist.segments;
|
|
|
|
if (ObjectUtilities.GetMemberValue(playlist,"sidx") != null && segments.Count == 0){
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
|
|
dynamic resolution = ObjectUtilities.GetMemberValue(playlist.attributes,"RESOLUTION");
|
|
resolution = resolution != null ? resolution : new Quality();
|
|
|
|
var pItem = new VideoPlaylist{
|
|
bandwidth = playlist.attributes.BANDWIDTH,
|
|
quality = new Quality{height = resolution.height,width = resolution.width},
|
|
segments = segments.Select(segment => new Segment{
|
|
duration = segment.duration,
|
|
map = new Map{uri = segment.map.resolvedUri,byteRange = ObjectUtilities.GetMemberValue(segment.map,"byterange")},
|
|
number = segment.number,
|
|
presentationTime = segment.presentationTime,
|
|
timeline = segment.timeline,
|
|
uri = segment.resolvedUri,
|
|
byteRange = ObjectUtilities.GetMemberValue(segment,"byterange")
|
|
}).ToList()
|
|
};
|
|
|
|
var contentProtectionDict = (IDictionary<string, dynamic>)ObjectUtilities.GetMemberValue(playlist,"contentProtection");
|
|
|
|
if (contentProtectionDict != null && contentProtectionDict.ContainsKey("com.widevine.alpha") && contentProtectionDict["com.widevine.alpha"].pssh != null)
|
|
pItem.pssh = ArrayBufferToBase64(contentProtectionDict["com.widevine.alpha"].pssh);
|
|
|
|
|
|
ret.Data[host].video.Add(pItem);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
private static void EnsureHostEntryExists(MPDParsed ret, string host){
|
|
if (!ret.Data.ContainsKey(host)){
|
|
ret.Data[host] = new ServerData{ audio = new List<AudioPlaylist>(), video = new List<VideoPlaylist>() };
|
|
}
|
|
}
|
|
|
|
public static string ArrayBufferToBase64(byte[] buffer){
|
|
return Convert.ToBase64String(buffer);
|
|
}
|
|
} |