Autoformat the entire solution
This commit is contained in:
parent
7af7bd8cfa
commit
6784ba0a18
@ -10,15 +10,9 @@ public class SpectreProgressReporter : IProgressReporter
|
||||
{
|
||||
private readonly ProgressTask _task;
|
||||
|
||||
public SpectreProgressReporter(ProgressTask task)
|
||||
{
|
||||
_task = task ?? throw new ArgumentNullException(nameof(task));
|
||||
}
|
||||
public SpectreProgressReporter(ProgressTask task) => _task = task ?? throw new ArgumentNullException(nameof(task));
|
||||
|
||||
public void ReportProgress(long increment)
|
||||
{
|
||||
_task.Increment(increment);
|
||||
}
|
||||
public void ReportProgress(long increment) => _task.Increment(increment);
|
||||
|
||||
public void ReportStatus(string message)
|
||||
{
|
||||
|
||||
@ -4,21 +4,18 @@ using Org.BouncyCastle.Crypto.Engines;
|
||||
using Org.BouncyCastle.Crypto.Macs;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace OF_DL.Crypto
|
||||
{
|
||||
namespace OF_DL.Crypto;
|
||||
|
||||
public class CryptoUtils
|
||||
{
|
||||
public static byte[] GetHMACSHA256Digest(byte[] data, byte[] key)
|
||||
{
|
||||
return new HMACSHA256(key).ComputeHash(data);
|
||||
}
|
||||
public static byte[] GetHMACSHA256Digest(byte[] data, byte[] key) => new HMACSHA256(key).ComputeHash(data);
|
||||
|
||||
public static byte[] GetCMACDigest(byte[] data, byte[] key)
|
||||
{
|
||||
IBlockCipher cipher = new AesEngine();
|
||||
IMac mac = new CMac(cipher, 128);
|
||||
|
||||
KeyParameter keyParam = new KeyParameter(key);
|
||||
KeyParameter keyParam = new(key);
|
||||
|
||||
mac.Init(keyParam);
|
||||
|
||||
@ -30,4 +27,3 @@ namespace OF_DL.Crypto
|
||||
return outBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace OF_DL.Crypto
|
||||
{
|
||||
namespace OF_DL.Crypto;
|
||||
|
||||
public class Padding
|
||||
{
|
||||
public static byte[] AddPKCS7Padding(byte[] data, int k)
|
||||
{
|
||||
int m = k - (data.Length % k);
|
||||
int m = k - data.Length % k;
|
||||
|
||||
byte[] padding = new byte[m];
|
||||
Array.Fill(padding, (byte)m);
|
||||
@ -20,18 +20,18 @@ namespace OF_DL.Crypto
|
||||
|
||||
public static byte[] RemovePKCS7Padding(byte[] paddedByteArray)
|
||||
{
|
||||
var last = paddedByteArray[^1];
|
||||
byte last = paddedByteArray[^1];
|
||||
if (paddedByteArray.Length <= last)
|
||||
{
|
||||
return paddedByteArray;
|
||||
}
|
||||
|
||||
return SubArray(paddedByteArray, 0, (paddedByteArray.Length - last));
|
||||
return SubArray(paddedByteArray, 0, paddedByteArray.Length - last);
|
||||
}
|
||||
|
||||
public static T[] SubArray<T>(T[] arr, int start, int length)
|
||||
{
|
||||
var result = new T[length];
|
||||
T[] result = new T[length];
|
||||
Buffer.BlockCopy(arr, start, result, 0, length);
|
||||
|
||||
return result;
|
||||
@ -45,7 +45,9 @@ namespace OF_DL.Crypto
|
||||
|
||||
int lmask = 0;
|
||||
for (int i = 0; i < 8 * emLen - (modBits - 1); i++)
|
||||
lmask = lmask >> 1 | 0x80;
|
||||
{
|
||||
lmask = (lmask >> 1) | 0x80;
|
||||
}
|
||||
|
||||
if (emLen < hLen + hLen + 2)
|
||||
{
|
||||
@ -65,7 +67,9 @@ namespace OF_DL.Crypto
|
||||
|
||||
byte[] maskedDb = new byte[dbMask.Length];
|
||||
for (int i = 0; i < dbMask.Length; i++)
|
||||
{
|
||||
maskedDb[i] = (byte)(db[i] ^ dbMask[i]);
|
||||
}
|
||||
|
||||
maskedDb[0] = (byte)(maskedDb[0] & ~lmask);
|
||||
|
||||
@ -86,13 +90,17 @@ namespace OF_DL.Crypto
|
||||
|
||||
byte[] seed = new byte[maskedSeed.Length];
|
||||
for (int i = 0; i < maskedSeed.Length; i++)
|
||||
{
|
||||
seed[i] = (byte)(maskedSeed[i] ^ seedMask[i]);
|
||||
}
|
||||
|
||||
byte[] dbMask = MGF1(seed, k - hLen - 1);
|
||||
|
||||
byte[] db = new byte[maskedDB.Length];
|
||||
for (int i = 0; i < maskedDB.Length; i++)
|
||||
{
|
||||
db[i] = (byte)(maskedDB[i] ^ dbMask[i]);
|
||||
}
|
||||
|
||||
int onePos = BitConverter.ToString(db[hLen..]).Replace("-", "").IndexOf("01") / 2;
|
||||
byte[] unpadded = db[(hLen + onePos + 1)..];
|
||||
@ -100,19 +108,19 @@ namespace OF_DL.Crypto
|
||||
return unpadded;
|
||||
}
|
||||
|
||||
static byte[] MGF1(byte[] seed, int maskLen)
|
||||
private static byte[] MGF1(byte[] seed, int maskLen)
|
||||
{
|
||||
SHA1 hobj = SHA1.Create();
|
||||
int hLen = hobj.HashSize / 8;
|
||||
List<byte> T = new List<byte>();
|
||||
for (int i = 0; i < (int)Math.Ceiling(((double)maskLen / (double)hLen)); i++)
|
||||
List<byte> T = new();
|
||||
for (int i = 0; i < (int)Math.Ceiling(maskLen / (double)hLen); i++)
|
||||
{
|
||||
byte[] c = BitConverter.GetBytes(i);
|
||||
Array.Reverse(c);
|
||||
byte[] digest = hobj.ComputeHash(seed.Concat(c).ToArray());
|
||||
T.AddRange(digest);
|
||||
}
|
||||
|
||||
return T.GetRange(0, maskLen).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
using Newtonsoft.Json;
|
||||
using OF_DL.Utils;
|
||||
|
||||
namespace OF_DL.Entities.Archived
|
||||
{
|
||||
namespace OF_DL.Entities.Archived;
|
||||
|
||||
public class Archived
|
||||
{
|
||||
public List<List> list { get; set; }
|
||||
@ -10,6 +10,7 @@ namespace OF_DL.Entities.Archived
|
||||
public string headMarker { get; set; }
|
||||
public string tailMarker { get; set; }
|
||||
public Counters counters { get; set; }
|
||||
|
||||
public class Author
|
||||
{
|
||||
public long id { get; set; }
|
||||
@ -29,11 +30,9 @@ namespace OF_DL.Entities.Archived
|
||||
|
||||
public class Dash
|
||||
{
|
||||
[JsonProperty("CloudFront-Policy")]
|
||||
public string CloudFrontPolicy { get; set; }
|
||||
[JsonProperty("CloudFront-Policy")] public string CloudFrontPolicy { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Signature")]
|
||||
public string CloudFrontSignature { get; set; }
|
||||
[JsonProperty("CloudFront-Signature")] public string CloudFrontSignature { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Key-Pair-Id")]
|
||||
public string CloudFrontKeyPairId { get; set; }
|
||||
@ -81,11 +80,9 @@ namespace OF_DL.Entities.Archived
|
||||
|
||||
public class Hls
|
||||
{
|
||||
[JsonProperty("CloudFront-Policy")]
|
||||
public string CloudFrontPolicy { get; set; }
|
||||
[JsonProperty("CloudFront-Policy")] public string CloudFrontPolicy { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Signature")]
|
||||
public string CloudFrontSignature { get; set; }
|
||||
[JsonProperty("CloudFront-Signature")] public string CloudFrontSignature { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Key-Pair-Id")]
|
||||
public string CloudFrontKeyPairId { get; set; }
|
||||
@ -99,6 +96,7 @@ namespace OF_DL.Entities.Archived
|
||||
|
||||
public class LinkedPost
|
||||
{
|
||||
private string _rawText;
|
||||
public string responseType { get; set; }
|
||||
public long? id { get; set; }
|
||||
public DateTime? postedAt { get; set; }
|
||||
@ -106,7 +104,7 @@ namespace OF_DL.Entities.Archived
|
||||
public object expiredAt { get; set; }
|
||||
public Author author { get; set; }
|
||||
public string text { get; set; }
|
||||
private string _rawText;
|
||||
|
||||
public string rawText
|
||||
{
|
||||
get
|
||||
@ -118,11 +116,9 @@ namespace OF_DL.Entities.Archived
|
||||
|
||||
return _rawText;
|
||||
}
|
||||
set
|
||||
{
|
||||
_rawText = value;
|
||||
}
|
||||
set => _rawText = value;
|
||||
}
|
||||
|
||||
public bool? lockedText { get; set; }
|
||||
public bool? isFavorite { get; set; }
|
||||
public bool? canReport { get; set; }
|
||||
@ -156,6 +152,7 @@ namespace OF_DL.Entities.Archived
|
||||
|
||||
public class List
|
||||
{
|
||||
private string _rawText;
|
||||
public string responseType { get; set; }
|
||||
public long id { get; set; }
|
||||
public DateTime postedAt { get; set; }
|
||||
@ -163,7 +160,7 @@ namespace OF_DL.Entities.Archived
|
||||
public object expiredAt { get; set; }
|
||||
public Author author { get; set; }
|
||||
public string text { get; set; }
|
||||
private string _rawText;
|
||||
|
||||
public string rawText
|
||||
{
|
||||
get
|
||||
@ -175,11 +172,9 @@ namespace OF_DL.Entities.Archived
|
||||
|
||||
return _rawText;
|
||||
}
|
||||
set
|
||||
{
|
||||
_rawText = value;
|
||||
}
|
||||
set => _rawText = value;
|
||||
}
|
||||
|
||||
public bool? lockedText { get; set; }
|
||||
public bool? isFavorite { get; set; }
|
||||
public bool? canReport { get; set; }
|
||||
@ -260,11 +255,8 @@ namespace OF_DL.Entities.Archived
|
||||
|
||||
public class VideoSources
|
||||
{
|
||||
[JsonProperty("720")]
|
||||
public string _720 { get; set; }
|
||||
[JsonProperty("720")] public string _720 { get; set; }
|
||||
|
||||
[JsonProperty("240")]
|
||||
public string _240 { get; set; }
|
||||
}
|
||||
[JsonProperty("240")] public string _240 { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
namespace OF_DL.Entities.Archived
|
||||
{
|
||||
namespace OF_DL.Entities.Archived;
|
||||
|
||||
public class ArchivedCollection
|
||||
{
|
||||
public Dictionary<long, string> ArchivedPosts = new Dictionary<long, string>();
|
||||
public List<Archived.List> ArchivedPostObjects = new List<Archived.List>();
|
||||
public List<Archived.Medium> ArchivedPostMedia = new List<Archived.Medium>();
|
||||
}
|
||||
public List<Archived.Medium> ArchivedPostMedia = new();
|
||||
public List<Archived.List> ArchivedPostObjects = new();
|
||||
public Dictionary<long, string> ArchivedPosts = new();
|
||||
}
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace OF_DL.Entities
|
||||
{
|
||||
namespace OF_DL.Entities;
|
||||
|
||||
public class Auth
|
||||
{
|
||||
public string? USER_ID { get; set; } = string.Empty;
|
||||
public string? USER_AGENT { get; set; } = string.Empty;
|
||||
public string? X_BC { get; set; } = string.Empty;
|
||||
public string? COOKIE { get; set; } = string.Empty;
|
||||
[JsonIgnore]
|
||||
public string? FFMPEG_PATH { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
[JsonIgnore] public string? FFMPEG_PATH { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
@ -1,22 +1,16 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace OF_DL.Entities
|
||||
{
|
||||
namespace OF_DL.Entities;
|
||||
|
||||
public class CDRMProjectRequest
|
||||
{
|
||||
[JsonProperty("pssh")]
|
||||
public string PSSH { get; set; } = "";
|
||||
[JsonProperty("pssh")] public string PSSH { get; set; } = "";
|
||||
|
||||
[JsonProperty("licurl")]
|
||||
public string LicenseURL { get; set; } = "";
|
||||
[JsonProperty("licurl")] public string LicenseURL { get; set; } = "";
|
||||
|
||||
[JsonProperty("headers")]
|
||||
public string Headers { get; set; } = "";
|
||||
[JsonProperty("headers")] public string Headers { get; set; } = "";
|
||||
|
||||
[JsonProperty("cookies")]
|
||||
public string Cookies { get; set; } = "";
|
||||
[JsonProperty("cookies")] public string Cookies { get; set; } = "";
|
||||
|
||||
[JsonProperty("data")]
|
||||
public string Data { get; set; } = "";
|
||||
}
|
||||
[JsonProperty("data")] public string Data { get; set; } = "";
|
||||
}
|
||||
|
||||
@ -3,65 +3,60 @@ using Newtonsoft.Json.Converters;
|
||||
using OF_DL.Enumerations;
|
||||
using Serilog;
|
||||
|
||||
namespace OF_DL.Entities
|
||||
{
|
||||
namespace OF_DL.Entities;
|
||||
|
||||
public class Config : IFileNameFormatConfig
|
||||
{
|
||||
[ToggleableConfig]
|
||||
public bool DownloadAvatarHeaderPhoto { get; set; } = true;
|
||||
[ToggleableConfig]
|
||||
public bool DownloadPaidPosts { get; set; } = true;
|
||||
[ToggleableConfig]
|
||||
public bool DownloadPosts { get; set; } = true;
|
||||
[ToggleableConfig]
|
||||
public bool DownloadArchived { get; set; } = true;
|
||||
[ToggleableConfig]
|
||||
public bool DownloadStreams { get; set; } = true;
|
||||
[ToggleableConfig]
|
||||
public bool DownloadStories { get; set; } = true;
|
||||
[ToggleableConfig]
|
||||
public bool DownloadHighlights { get; set; } = true;
|
||||
[ToggleableConfig]
|
||||
public bool DownloadMessages { get; set; } = true;
|
||||
[ToggleableConfig]
|
||||
public bool DownloadPaidMessages { get; set; } = true;
|
||||
[ToggleableConfig]
|
||||
public bool DownloadImages { get; set; } = true;
|
||||
[ToggleableConfig]
|
||||
public bool DownloadVideos { get; set; } = true;
|
||||
[ToggleableConfig]
|
||||
public bool DownloadAudios { get; set; } = true;
|
||||
[ToggleableConfig]
|
||||
public bool IncludeExpiredSubscriptions { get; set; } = false;
|
||||
[ToggleableConfig]
|
||||
public bool IncludeRestrictedSubscriptions { get; set; } = false;
|
||||
[ToggleableConfig]
|
||||
public bool SkipAds { get; set; } = false;
|
||||
[ToggleableConfig] public bool DownloadAvatarHeaderPhoto { get; set; } = true;
|
||||
|
||||
[ToggleableConfig] public bool DownloadPaidPosts { get; set; } = true;
|
||||
|
||||
[ToggleableConfig] public bool DownloadPosts { get; set; } = true;
|
||||
|
||||
[ToggleableConfig] public bool DownloadArchived { get; set; } = true;
|
||||
|
||||
[ToggleableConfig] public bool DownloadStreams { get; set; } = true;
|
||||
|
||||
[ToggleableConfig] public bool DownloadStories { get; set; } = true;
|
||||
|
||||
[ToggleableConfig] public bool DownloadHighlights { get; set; } = true;
|
||||
|
||||
[ToggleableConfig] public bool DownloadMessages { get; set; } = true;
|
||||
|
||||
[ToggleableConfig] public bool DownloadPaidMessages { get; set; } = true;
|
||||
|
||||
[ToggleableConfig] public bool DownloadImages { get; set; } = true;
|
||||
|
||||
[ToggleableConfig] public bool DownloadVideos { get; set; } = true;
|
||||
|
||||
[ToggleableConfig] public bool DownloadAudios { get; set; } = true;
|
||||
|
||||
[ToggleableConfig] public bool IncludeExpiredSubscriptions { get; set; } = false;
|
||||
|
||||
[ToggleableConfig] public bool IncludeRestrictedSubscriptions { get; set; } = false;
|
||||
|
||||
[ToggleableConfig] public bool SkipAds { get; set; } = false;
|
||||
|
||||
public string? DownloadPath { get; set; } = string.Empty;
|
||||
public string? PaidPostFileNameFormat { get; set; } = string.Empty;
|
||||
public string? PostFileNameFormat { get; set; } = string.Empty;
|
||||
public string? PaidMessageFileNameFormat { get; set; } = string.Empty;
|
||||
public string? MessageFileNameFormat { get; set; } = string.Empty;
|
||||
[ToggleableConfig]
|
||||
public bool RenameExistingFilesWhenCustomFormatIsSelected { get; set; } = false;
|
||||
|
||||
[ToggleableConfig] public bool RenameExistingFilesWhenCustomFormatIsSelected { get; set; } = false;
|
||||
|
||||
public int? Timeout { get; set; } = -1;
|
||||
[ToggleableConfig]
|
||||
public bool FolderPerPaidPost { get; set; } = false;
|
||||
[ToggleableConfig]
|
||||
public bool FolderPerPost { get; set; } = false;
|
||||
[ToggleableConfig]
|
||||
public bool FolderPerPaidMessage { get; set; } = false;
|
||||
[ToggleableConfig]
|
||||
public bool FolderPerMessage { get; set; } = false;
|
||||
[ToggleableConfig]
|
||||
public bool LimitDownloadRate { get; set; } = false;
|
||||
|
||||
[ToggleableConfig] public bool FolderPerPaidPost { get; set; } = false;
|
||||
|
||||
[ToggleableConfig] public bool FolderPerPost { get; set; } = false;
|
||||
|
||||
[ToggleableConfig] public bool FolderPerPaidMessage { get; set; } = false;
|
||||
|
||||
[ToggleableConfig] public bool FolderPerMessage { get; set; } = false;
|
||||
|
||||
[ToggleableConfig] public bool LimitDownloadRate { get; set; } = false;
|
||||
|
||||
public int DownloadLimitInMbPerSec { get; set; } = 4;
|
||||
|
||||
// Indicates if you want to download only on specific dates.
|
||||
[ToggleableConfig]
|
||||
public bool DownloadOnlySpecificDates { get; set; } = false;
|
||||
[ToggleableConfig] public bool DownloadOnlySpecificDates { get; set; } = false;
|
||||
|
||||
// This enum will define if we want data from before or after the CustomDate.
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
@ -71,53 +66,54 @@ namespace OF_DL.Entities
|
||||
[JsonConverter(typeof(ShortDateConverter))]
|
||||
public DateTime? CustomDate { get; set; } = null;
|
||||
|
||||
[ToggleableConfig]
|
||||
public bool ShowScrapeSize { get; set; } = false;
|
||||
[ToggleableConfig] public bool ShowScrapeSize { get; set; } = false;
|
||||
|
||||
[ToggleableConfig]
|
||||
public bool DownloadPostsIncrementally { get; set; } = false;
|
||||
[ToggleableConfig] public bool DownloadPostsIncrementally { get; set; } = false;
|
||||
|
||||
public bool NonInteractiveMode { get; set; } = false;
|
||||
public string NonInteractiveModeListName { get; set; } = string.Empty;
|
||||
[ToggleableConfig]
|
||||
public bool NonInteractiveModePurchasedTab { get; set; } = false;
|
||||
|
||||
[ToggleableConfig] public bool NonInteractiveModePurchasedTab { get; set; } = false;
|
||||
|
||||
public string? FFmpegPath { get; set; } = string.Empty;
|
||||
|
||||
[ToggleableConfig]
|
||||
public bool BypassContentForCreatorsWhoNoLongerExist { get; set; } = false;
|
||||
[ToggleableConfig] public bool BypassContentForCreatorsWhoNoLongerExist { get; set; } = false;
|
||||
|
||||
public Dictionary<string, CreatorConfig> CreatorConfigs { get; set; } = new Dictionary<string, CreatorConfig>();
|
||||
public Dictionary<string, CreatorConfig> CreatorConfigs { get; set; } = new();
|
||||
|
||||
[ToggleableConfig]
|
||||
public bool DownloadDuplicatedMedia { get; set; } = false;
|
||||
[ToggleableConfig] public bool DownloadDuplicatedMedia { get; set; } = false;
|
||||
|
||||
public string IgnoredUsersListName { get; set; } = string.Empty;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public LoggingLevel LoggingLevel { get; set; } = LoggingLevel.Error;
|
||||
|
||||
[ToggleableConfig]
|
||||
public bool IgnoreOwnMessages { get; set; } = false;
|
||||
[ToggleableConfig] public bool IgnoreOwnMessages { get; set; } = false;
|
||||
|
||||
[ToggleableConfig]
|
||||
public bool DisableBrowserAuth { get; set; } = false;
|
||||
[ToggleableConfig] public bool DisableBrowserAuth { get; set; } = false;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public VideoResolution DownloadVideoResolution { get; set; } = VideoResolution.source;
|
||||
|
||||
// When enabled, post/message text is stored as-is without XML stripping.
|
||||
[ToggleableConfig]
|
||||
public bool DisableTextSanitization { get; set; } = false;
|
||||
[ToggleableConfig] public bool DisableTextSanitization { get; set; } = false;
|
||||
|
||||
public string? PaidPostFileNameFormat { get; set; } = string.Empty;
|
||||
public string? PostFileNameFormat { get; set; } = string.Empty;
|
||||
public string? PaidMessageFileNameFormat { get; set; } = string.Empty;
|
||||
public string? MessageFileNameFormat { get; set; } = string.Empty;
|
||||
|
||||
public IFileNameFormatConfig GetCreatorFileNameFormatConfig(string username)
|
||||
{
|
||||
FileNameFormatConfig createFileNameFormatConfig = new FileNameFormatConfig();
|
||||
FileNameFormatConfig createFileNameFormatConfig = new();
|
||||
|
||||
Func<string?, string?, string?> func = (val1, val2) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(val1))
|
||||
{
|
||||
return val2;
|
||||
else
|
||||
}
|
||||
|
||||
return val1;
|
||||
};
|
||||
|
||||
@ -129,15 +125,23 @@ namespace OF_DL.Entities
|
||||
createFileNameFormatConfig.PaidPostFileNameFormat = creatorConfig.PaidPostFileNameFormat;
|
||||
}
|
||||
|
||||
createFileNameFormatConfig.PaidMessageFileNameFormat = func(createFileNameFormatConfig.PaidMessageFileNameFormat, PaidMessageFileNameFormat);
|
||||
createFileNameFormatConfig.PostFileNameFormat = func(createFileNameFormatConfig.PostFileNameFormat, PostFileNameFormat);
|
||||
createFileNameFormatConfig.MessageFileNameFormat = func(createFileNameFormatConfig.MessageFileNameFormat, MessageFileNameFormat);
|
||||
createFileNameFormatConfig.PaidPostFileNameFormat = func(createFileNameFormatConfig.PaidPostFileNameFormat, PaidPostFileNameFormat);
|
||||
createFileNameFormatConfig.PaidMessageFileNameFormat =
|
||||
func(createFileNameFormatConfig.PaidMessageFileNameFormat, PaidMessageFileNameFormat);
|
||||
createFileNameFormatConfig.PostFileNameFormat =
|
||||
func(createFileNameFormatConfig.PostFileNameFormat, PostFileNameFormat);
|
||||
createFileNameFormatConfig.MessageFileNameFormat =
|
||||
func(createFileNameFormatConfig.MessageFileNameFormat, MessageFileNameFormat);
|
||||
createFileNameFormatConfig.PaidPostFileNameFormat =
|
||||
func(createFileNameFormatConfig.PaidPostFileNameFormat, PaidPostFileNameFormat);
|
||||
|
||||
Log.Debug("PaidMessageFilenameFormat: {CombinedConfigPaidMessageFileNameFormat}", createFileNameFormatConfig.PaidMessageFileNameFormat);
|
||||
Log.Debug("PostFileNameFormat: {CombinedConfigPostFileNameFormat}", createFileNameFormatConfig.PostFileNameFormat);
|
||||
Log.Debug("MessageFileNameFormat: {CombinedConfigMessageFileNameFormat}", createFileNameFormatConfig.MessageFileNameFormat);
|
||||
Log.Debug("PaidPostFileNameFormat: {CombinedConfigPaidPostFileNameFormat}", createFileNameFormatConfig.PaidPostFileNameFormat);
|
||||
Log.Debug("PaidMessageFilenameFormat: {CombinedConfigPaidMessageFileNameFormat}",
|
||||
createFileNameFormatConfig.PaidMessageFileNameFormat);
|
||||
Log.Debug("PostFileNameFormat: {CombinedConfigPostFileNameFormat}",
|
||||
createFileNameFormatConfig.PostFileNameFormat);
|
||||
Log.Debug("MessageFileNameFormat: {CombinedConfigMessageFileNameFormat}",
|
||||
createFileNameFormatConfig.MessageFileNameFormat);
|
||||
Log.Debug("PaidPostFileNameFormat: {CombinedConfigPaidPostFileNameFormat}",
|
||||
createFileNameFormatConfig.PaidPostFileNameFormat);
|
||||
|
||||
return createFileNameFormatConfig;
|
||||
}
|
||||
@ -150,5 +154,3 @@ namespace OF_DL.Entities
|
||||
public string? PaidMessageFileNameFormat { get; set; }
|
||||
public string? MessageFileNameFormat { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace OF_DL.Entities
|
||||
{
|
||||
namespace OF_DL.Entities;
|
||||
|
||||
public class DynamicRules
|
||||
{
|
||||
[JsonProperty(PropertyName = "app-token")]
|
||||
public string? AppToken { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "app_token")]
|
||||
private string AppToken2 { set { AppToken = value; } }
|
||||
private string AppToken2
|
||||
{
|
||||
set => AppToken = value;
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "static_param")]
|
||||
public string? StaticParam { get; set; }
|
||||
@ -25,4 +28,3 @@ namespace OF_DL.Entities
|
||||
[JsonProperty(PropertyName = "checksum_indexes")]
|
||||
public List<int> ChecksumIndexes { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
namespace OF_DL.Entities
|
||||
{
|
||||
namespace OF_DL.Entities;
|
||||
|
||||
public class FileNameFormatConfig : IFileNameFormatConfig
|
||||
{
|
||||
public string? PaidPostFileNameFormat { get; set; }
|
||||
@ -7,5 +7,3 @@
|
||||
public string? PaidMessageFileNameFormat { get; set; }
|
||||
public string? MessageFileNameFormat { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace OF_DL.Entities.Highlights
|
||||
{
|
||||
namespace OF_DL.Entities.Highlights;
|
||||
|
||||
public class HighlightMedia
|
||||
{
|
||||
public long id { get; set; }
|
||||
@ -12,6 +12,7 @@ namespace OF_DL.Entities.Highlights
|
||||
public int storiesCount { get; set; }
|
||||
public DateTime? createdAt { get; set; }
|
||||
public List<Story> stories { get; set; }
|
||||
|
||||
public class Files
|
||||
{
|
||||
public Full full { get; set; }
|
||||
@ -61,11 +62,10 @@ namespace OF_DL.Entities.Highlights
|
||||
|
||||
public class Sources
|
||||
{
|
||||
[JsonProperty("720")]
|
||||
public string _720 { get; set; }
|
||||
[JsonProperty("720")] public string _720 { get; set; }
|
||||
|
||||
[JsonProperty("240")] public string _240 { get; set; }
|
||||
|
||||
[JsonProperty("240")]
|
||||
public string _240 { get; set; }
|
||||
public string w150 { get; set; }
|
||||
public string w480 { get; set; }
|
||||
}
|
||||
@ -100,4 +100,3 @@ namespace OF_DL.Entities.Highlights
|
||||
public long size { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
namespace OF_DL.Entities.Highlights
|
||||
{
|
||||
namespace OF_DL.Entities.Highlights;
|
||||
|
||||
public class Highlights
|
||||
{
|
||||
public List<List> list { get; set; }
|
||||
public bool hasMore { get; set; }
|
||||
|
||||
public class List
|
||||
{
|
||||
public long id { get; set; }
|
||||
@ -15,4 +16,3 @@ namespace OF_DL.Entities.Highlights
|
||||
public DateTime? createdAt { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
namespace OF_DL.Entities
|
||||
{
|
||||
namespace OF_DL.Entities;
|
||||
|
||||
public interface IFileNameFormatConfig
|
||||
{
|
||||
string? PaidPostFileNameFormat { get; set; }
|
||||
@ -7,5 +7,3 @@
|
||||
string? PaidMessageFileNameFormat { get; set; }
|
||||
string? MessageFileNameFormat { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
namespace OF_DL.Entities.Lists
|
||||
{
|
||||
namespace OF_DL.Entities.Lists;
|
||||
|
||||
public class UserList
|
||||
{
|
||||
public List<List> list { get; set; }
|
||||
public bool? hasMore { get; set; }
|
||||
|
||||
public class List
|
||||
{
|
||||
public string id { get; set; }
|
||||
@ -32,4 +33,3 @@
|
||||
public string _view { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
namespace OF_DL.Entities.Lists
|
||||
{
|
||||
namespace OF_DL.Entities.Lists;
|
||||
|
||||
public class UsersList
|
||||
{
|
||||
public string view { get; set; }
|
||||
@ -56,6 +56,7 @@ namespace OF_DL.Entities.Lists
|
||||
public bool? canTrialSend { get; set; }
|
||||
public bool? isBlocked { get; set; }
|
||||
public List<object> promoOffers { get; set; }
|
||||
|
||||
public class AvatarThumbs
|
||||
{
|
||||
public string c50 { get; set; }
|
||||
@ -160,6 +161,4 @@ namespace OF_DL.Entities.Lists
|
||||
public string? price { get; set; }
|
||||
public bool? canBuy { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
namespace OF_DL.Entities.Messages
|
||||
{
|
||||
namespace OF_DL.Entities.Messages;
|
||||
|
||||
public class MessageCollection
|
||||
{
|
||||
public Dictionary<long, string> Messages = new Dictionary<long, string>();
|
||||
public List<Messages.List> MessageObjects = new List<Messages.List>();
|
||||
public List<Messages.Medium> MessageMedia = new List<Messages.Medium>();
|
||||
}
|
||||
public List<Messages.Medium> MessageMedia = new();
|
||||
public List<Messages.List> MessageObjects = new();
|
||||
public Dictionary<long, string> Messages = new();
|
||||
}
|
||||
|
||||
@ -1,18 +1,17 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace OF_DL.Entities.Messages
|
||||
{
|
||||
namespace OF_DL.Entities.Messages;
|
||||
|
||||
public class Messages
|
||||
{
|
||||
public List<List> list { get; set; }
|
||||
public bool hasMore { get; set; }
|
||||
|
||||
public class Dash
|
||||
{
|
||||
[JsonProperty("CloudFront-Policy")]
|
||||
public string CloudFrontPolicy { get; set; }
|
||||
[JsonProperty("CloudFront-Policy")] public string CloudFrontPolicy { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Signature")]
|
||||
public string CloudFrontSignature { get; set; }
|
||||
[JsonProperty("CloudFront-Signature")] public string CloudFrontSignature { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Key-Pair-Id")]
|
||||
public string CloudFrontKeyPairId { get; set; }
|
||||
@ -66,11 +65,9 @@ namespace OF_DL.Entities.Messages
|
||||
|
||||
public class Hls
|
||||
{
|
||||
[JsonProperty("CloudFront-Policy")]
|
||||
public string CloudFrontPolicy { get; set; }
|
||||
[JsonProperty("CloudFront-Policy")] public string CloudFrontPolicy { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Signature")]
|
||||
public string CloudFrontSignature { get; set; }
|
||||
[JsonProperty("CloudFront-Signature")] public string CloudFrontSignature { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Key-Pair-Id")]
|
||||
public string CloudFrontKeyPairId { get; set; }
|
||||
@ -169,11 +166,8 @@ namespace OF_DL.Entities.Messages
|
||||
|
||||
public class VideoSources
|
||||
{
|
||||
[JsonProperty("720")]
|
||||
public string _720 { get; set; }
|
||||
[JsonProperty("720")] public string _720 { get; set; }
|
||||
|
||||
[JsonProperty("240")]
|
||||
public string _240 { get; set; }
|
||||
}
|
||||
[JsonProperty("240")] public string _240 { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using static OF_DL.Entities.Messages.Messages;
|
||||
|
||||
namespace OF_DL.Entities.Messages
|
||||
{
|
||||
namespace OF_DL.Entities.Messages;
|
||||
|
||||
public class AvatarThumbs
|
||||
{
|
||||
public string c50 { get; set; }
|
||||
@ -113,6 +113,3 @@ namespace OF_DL.Entities.Messages
|
||||
public bool canPurchase { get; set; }
|
||||
public bool canReport { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,16 +1,12 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace OF_DL.Entities
|
||||
{
|
||||
namespace OF_DL.Entities;
|
||||
|
||||
public class OFDLRequest
|
||||
{
|
||||
[JsonProperty("pssh")]
|
||||
public string PSSH { get; set; } = "";
|
||||
[JsonProperty("pssh")] public string PSSH { get; set; } = "";
|
||||
|
||||
[JsonProperty("licenceURL")]
|
||||
public string LicenseURL { get; set; } = "";
|
||||
[JsonProperty("licenceURL")] public string LicenseURL { get; set; } = "";
|
||||
|
||||
[JsonProperty("headers")]
|
||||
public string Headers { get; set; } = "";
|
||||
}
|
||||
[JsonProperty("headers")] public string Headers { get; set; } = "";
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ public class Post
|
||||
public string headMarker { get; set; }
|
||||
|
||||
public string tailMarker { get; set; }
|
||||
|
||||
public class Author
|
||||
{
|
||||
public long id { get; set; }
|
||||
@ -20,11 +21,9 @@ public class Post
|
||||
|
||||
public class Dash
|
||||
{
|
||||
[JsonProperty("CloudFront-Policy")]
|
||||
public string CloudFrontPolicy { get; set; }
|
||||
[JsonProperty("CloudFront-Policy")] public string CloudFrontPolicy { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Signature")]
|
||||
public string CloudFrontSignature { get; set; }
|
||||
[JsonProperty("CloudFront-Signature")] public string CloudFrontSignature { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Key-Pair-Id")]
|
||||
public string CloudFrontKeyPairId { get; set; }
|
||||
@ -72,11 +71,9 @@ public class Post
|
||||
|
||||
public class Hls
|
||||
{
|
||||
[JsonProperty("CloudFront-Policy")]
|
||||
public string CloudFrontPolicy { get; set; }
|
||||
[JsonProperty("CloudFront-Policy")] public string CloudFrontPolicy { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Signature")]
|
||||
public string CloudFrontSignature { get; set; }
|
||||
[JsonProperty("CloudFront-Signature")] public string CloudFrontSignature { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Key-Pair-Id")]
|
||||
public string CloudFrontKeyPairId { get; set; }
|
||||
@ -90,6 +87,7 @@ public class Post
|
||||
|
||||
public class List
|
||||
{
|
||||
private string _rawText;
|
||||
public string responseType { get; set; }
|
||||
public long id { get; set; }
|
||||
public DateTime postedAt { get; set; }
|
||||
@ -98,7 +96,6 @@ public class Post
|
||||
public Author author { get; set; }
|
||||
public string text { get; set; }
|
||||
|
||||
private string _rawText;
|
||||
public string rawText
|
||||
{
|
||||
get
|
||||
@ -110,11 +107,9 @@ public class Post
|
||||
|
||||
return _rawText;
|
||||
}
|
||||
set
|
||||
{
|
||||
_rawText = value;
|
||||
}
|
||||
set => _rawText = value;
|
||||
}
|
||||
|
||||
public bool? lockedText { get; set; }
|
||||
public bool? isFavorite { get; set; }
|
||||
public bool? canReport { get; set; }
|
||||
@ -197,11 +192,9 @@ public class Post
|
||||
|
||||
public class VideoSources
|
||||
{
|
||||
[JsonProperty("720")]
|
||||
public object _720 { get; set; }
|
||||
[JsonProperty("720")] public object _720 { get; set; }
|
||||
|
||||
[JsonProperty("240")]
|
||||
public object _240 { get; set; }
|
||||
[JsonProperty("240")] public object _240 { get; set; }
|
||||
}
|
||||
#pragma warning restore IDE1006 // Naming Styles
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
namespace OF_DL.Entities.Post
|
||||
{
|
||||
namespace OF_DL.Entities.Post;
|
||||
|
||||
public class PostCollection
|
||||
{
|
||||
public Dictionary<long, string> Posts = new Dictionary<long, string>();
|
||||
public List<Post.List> PostObjects = new List<Post.List>();
|
||||
public List<Post.Medium> PostMedia = new List<Post.Medium>();
|
||||
}
|
||||
public List<Post.Medium> PostMedia = new();
|
||||
public List<Post.List> PostObjects = new();
|
||||
public Dictionary<long, string> Posts = new();
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
using Newtonsoft.Json;
|
||||
using OF_DL.Utils;
|
||||
|
||||
namespace OF_DL.Entities.Post
|
||||
{
|
||||
namespace OF_DL.Entities.Post;
|
||||
|
||||
public class SinglePost
|
||||
{
|
||||
private string _rawText;
|
||||
public string responseType { get; set; }
|
||||
public long id { get; set; }
|
||||
public DateTime postedAt { get; set; }
|
||||
@ -12,7 +13,7 @@ namespace OF_DL.Entities.Post
|
||||
public object expiredAt { get; set; }
|
||||
public Author author { get; set; }
|
||||
public string text { get; set; }
|
||||
private string _rawText;
|
||||
|
||||
public string rawText
|
||||
{
|
||||
get
|
||||
@ -24,11 +25,9 @@ namespace OF_DL.Entities.Post
|
||||
|
||||
return _rawText;
|
||||
}
|
||||
set
|
||||
{
|
||||
_rawText = value;
|
||||
}
|
||||
set => _rawText = value;
|
||||
}
|
||||
|
||||
public bool lockedText { get; set; }
|
||||
public bool isFavorite { get; set; }
|
||||
public bool canReport { get; set; }
|
||||
@ -59,6 +58,7 @@ namespace OF_DL.Entities.Post
|
||||
public List<Medium> media { get; set; }
|
||||
public bool canViewMedia { get; set; }
|
||||
public List<object> preview { get; set; }
|
||||
|
||||
public class Author
|
||||
{
|
||||
public long id { get; set; }
|
||||
@ -143,19 +143,16 @@ namespace OF_DL.Entities.Post
|
||||
|
||||
public class VideoSources
|
||||
{
|
||||
[JsonProperty("720")]
|
||||
public string _720 { get; set; }
|
||||
[JsonProperty("720")] public string _720 { get; set; }
|
||||
|
||||
[JsonProperty("240")]
|
||||
public string _240 { get; set; }
|
||||
[JsonProperty("240")] public string _240 { get; set; }
|
||||
}
|
||||
|
||||
public class Dash
|
||||
{
|
||||
[JsonProperty("CloudFront-Policy")]
|
||||
public string CloudFrontPolicy { get; set; }
|
||||
[JsonProperty("CloudFront-Policy")] public string CloudFrontPolicy { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Signature")]
|
||||
public string CloudFrontSignature { get; set; }
|
||||
[JsonProperty("CloudFront-Signature")] public string CloudFrontSignature { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Key-Pair-Id")]
|
||||
public string CloudFrontKeyPairId { get; set; }
|
||||
@ -166,26 +163,26 @@ namespace OF_DL.Entities.Post
|
||||
public Manifest manifest { get; set; }
|
||||
public Signature signature { get; set; }
|
||||
}
|
||||
|
||||
public class Hls
|
||||
{
|
||||
[JsonProperty("CloudFront-Policy")]
|
||||
public string CloudFrontPolicy { get; set; }
|
||||
[JsonProperty("CloudFront-Policy")] public string CloudFrontPolicy { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Signature")]
|
||||
public string CloudFrontSignature { get; set; }
|
||||
[JsonProperty("CloudFront-Signature")] public string CloudFrontSignature { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Key-Pair-Id")]
|
||||
public string CloudFrontKeyPairId { get; set; }
|
||||
}
|
||||
|
||||
public class Manifest
|
||||
{
|
||||
public string? hls { get; set; }
|
||||
public string? dash { get; set; }
|
||||
}
|
||||
|
||||
public class Signature
|
||||
{
|
||||
public Hls hls { get; set; }
|
||||
public Dash dash { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
namespace OF_DL.Entities.Post
|
||||
{
|
||||
namespace OF_DL.Entities.Post;
|
||||
|
||||
public class SinglePostCollection
|
||||
{
|
||||
public Dictionary<long, string> SinglePosts = new Dictionary<long, string>();
|
||||
public List<SinglePost> SinglePostObjects = new List<SinglePost>();
|
||||
public List<SinglePost.Medium> SinglePostMedia = new List<SinglePost.Medium>();
|
||||
}
|
||||
public List<SinglePost.Medium> SinglePostMedia = new();
|
||||
public List<SinglePost> SinglePostObjects = new();
|
||||
public Dictionary<long, string> SinglePosts = new();
|
||||
}
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
using static OF_DL.Entities.Messages.Messages;
|
||||
|
||||
namespace OF_DL.Entities.Purchased
|
||||
{
|
||||
namespace OF_DL.Entities.Purchased;
|
||||
|
||||
public class PaidMessageCollection
|
||||
{
|
||||
public Dictionary<long, string> PaidMessages = new Dictionary<long, string>();
|
||||
public List<Purchased.List> PaidMessageObjects = new List<Purchased.List>();
|
||||
public List<Medium> PaidMessageMedia = new List<Medium>();
|
||||
}
|
||||
public List<Medium> PaidMessageMedia = new();
|
||||
public List<Purchased.List> PaidMessageObjects = new();
|
||||
public Dictionary<long, string> PaidMessages = new();
|
||||
}
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
using static OF_DL.Entities.Messages.Messages;
|
||||
|
||||
namespace OF_DL.Entities.Purchased
|
||||
{
|
||||
namespace OF_DL.Entities.Purchased;
|
||||
|
||||
public class PaidPostCollection
|
||||
{
|
||||
public Dictionary<long, string> PaidPosts = new Dictionary<long, string>();
|
||||
public List<Purchased.List> PaidPostObjects = new List<Purchased.List>();
|
||||
public List<Medium> PaidPostMedia = new List<Medium>();
|
||||
}
|
||||
public List<Medium> PaidPostMedia = new();
|
||||
public List<Purchased.List> PaidPostObjects = new();
|
||||
public Dictionary<long, string> PaidPosts = new();
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
using Newtonsoft.Json;
|
||||
using static OF_DL.Entities.Messages.Messages;
|
||||
|
||||
namespace OF_DL.Entities.Purchased
|
||||
{
|
||||
namespace OF_DL.Entities.Purchased;
|
||||
|
||||
public class Purchased
|
||||
{
|
||||
public List<List> list { get; set; }
|
||||
@ -13,6 +13,7 @@ namespace OF_DL.Entities.Purchased
|
||||
public long id { get; set; }
|
||||
public string _view { get; set; }
|
||||
}
|
||||
|
||||
public class Author
|
||||
{
|
||||
public long id { get; set; }
|
||||
@ -21,11 +22,9 @@ namespace OF_DL.Entities.Purchased
|
||||
|
||||
public class Hls
|
||||
{
|
||||
[JsonProperty("CloudFront-Policy")]
|
||||
public string CloudFrontPolicy { get; set; }
|
||||
[JsonProperty("CloudFront-Policy")] public string CloudFrontPolicy { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Signature")]
|
||||
public string CloudFrontSignature { get; set; }
|
||||
[JsonProperty("CloudFront-Signature")] public string CloudFrontSignature { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Key-Pair-Id")]
|
||||
public string CloudFrontKeyPairId { get; set; }
|
||||
@ -74,4 +73,3 @@ namespace OF_DL.Entities.Purchased
|
||||
public string dash { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
namespace OF_DL.Entities.Purchased
|
||||
{
|
||||
namespace OF_DL.Entities.Purchased;
|
||||
|
||||
public class PurchasedTabCollection
|
||||
{
|
||||
public long UserId { get; set; }
|
||||
public string Username { get; set; } = string.Empty;
|
||||
public PaidPostCollection PaidPosts { get; set; } = new PaidPostCollection();
|
||||
public PaidMessageCollection PaidMessages { get; set; } = new PaidMessageCollection();
|
||||
}
|
||||
public PaidPostCollection PaidPosts { get; set; } = new();
|
||||
public PaidMessageCollection PaidMessages { get; set; } = new();
|
||||
}
|
||||
|
||||
@ -1,15 +1,14 @@
|
||||
using OF_DL.Entities.Messages;
|
||||
using static OF_DL.Entities.Messages.Messages;
|
||||
|
||||
namespace OF_DL.Entities.Purchased
|
||||
{
|
||||
namespace OF_DL.Entities.Purchased;
|
||||
|
||||
public class SinglePaidMessageCollection
|
||||
{
|
||||
public Dictionary<long, string> SingleMessages = new Dictionary<long, string>();
|
||||
public List<SingleMessage> SingleMessageObjects = new List<SingleMessage>();
|
||||
public List<Medium> SingleMessageMedia = new List<Medium>();
|
||||
public List<Medium> PreviewSingleMessageMedia = new();
|
||||
|
||||
public Dictionary<long, string> PreviewSingleMessages = new Dictionary<long, string>();
|
||||
public List<Medium> PreviewSingleMessageMedia = new List<Medium>();
|
||||
}
|
||||
public Dictionary<long, string> PreviewSingleMessages = new();
|
||||
public List<Medium> SingleMessageMedia = new();
|
||||
public List<SingleMessage> SingleMessageObjects = new();
|
||||
public Dictionary<long, string> SingleMessages = new();
|
||||
}
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace OF_DL.Entities
|
||||
{
|
||||
namespace OF_DL.Entities;
|
||||
|
||||
public class ShortDateConverter : IsoDateTimeConverter
|
||||
{
|
||||
public ShortDateConverter()
|
||||
{
|
||||
DateTimeFormat = "yyyy-MM-dd";
|
||||
}
|
||||
}
|
||||
public ShortDateConverter() => DateTimeFormat = "yyyy-MM-dd";
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace OF_DL.Entities.Stories
|
||||
{
|
||||
namespace OF_DL.Entities.Stories;
|
||||
|
||||
public class Stories
|
||||
{
|
||||
public long id { get; set; }
|
||||
@ -13,6 +13,7 @@ namespace OF_DL.Entities.Stories
|
||||
public object question { get; set; }
|
||||
public bool canLike { get; set; }
|
||||
public bool isLiked { get; set; }
|
||||
|
||||
public class Files
|
||||
{
|
||||
public Full full { get; set; }
|
||||
@ -62,11 +63,10 @@ namespace OF_DL.Entities.Stories
|
||||
|
||||
public class Sources
|
||||
{
|
||||
[JsonProperty("720")]
|
||||
public object _720 { get; set; }
|
||||
[JsonProperty("720")] public object _720 { get; set; }
|
||||
|
||||
[JsonProperty("240")] public object _240 { get; set; }
|
||||
|
||||
[JsonProperty("240")]
|
||||
public object _240 { get; set; }
|
||||
public string w150 { get; set; }
|
||||
public string w480 { get; set; }
|
||||
}
|
||||
@ -88,4 +88,3 @@ namespace OF_DL.Entities.Stories
|
||||
public long size { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
using Newtonsoft.Json;
|
||||
using OF_DL.Utils;
|
||||
|
||||
namespace OF_DL.Entities.Streams
|
||||
{
|
||||
namespace OF_DL.Entities.Streams;
|
||||
|
||||
public class Streams
|
||||
{
|
||||
public List<List> list { get; set; }
|
||||
@ -10,6 +10,7 @@ namespace OF_DL.Entities.Streams
|
||||
public string headMarker { get; set; }
|
||||
public string tailMarker { get; set; }
|
||||
public Counters counters { get; set; }
|
||||
|
||||
public class Author
|
||||
{
|
||||
public long id { get; set; }
|
||||
@ -69,6 +70,7 @@ namespace OF_DL.Entities.Streams
|
||||
|
||||
public class List
|
||||
{
|
||||
private string _rawText;
|
||||
public string responseType { get; set; }
|
||||
public long id { get; set; }
|
||||
public DateTime postedAt { get; set; }
|
||||
@ -76,7 +78,7 @@ namespace OF_DL.Entities.Streams
|
||||
public object expiredAt { get; set; }
|
||||
public Author author { get; set; }
|
||||
public string text { get; set; }
|
||||
private string _rawText;
|
||||
|
||||
public string rawText
|
||||
{
|
||||
get
|
||||
@ -88,11 +90,9 @@ namespace OF_DL.Entities.Streams
|
||||
|
||||
return _rawText;
|
||||
}
|
||||
set
|
||||
{
|
||||
_rawText = value;
|
||||
}
|
||||
set => _rawText = value;
|
||||
}
|
||||
|
||||
public bool lockedText { get; set; }
|
||||
public bool isFavorite { get; set; }
|
||||
public bool canReport { get; set; }
|
||||
@ -164,48 +164,46 @@ namespace OF_DL.Entities.Streams
|
||||
|
||||
public class VideoSources
|
||||
{
|
||||
[JsonProperty("720")]
|
||||
public object _720 { get; set; }
|
||||
[JsonProperty("720")] public object _720 { get; set; }
|
||||
|
||||
[JsonProperty("240")]
|
||||
public object _240 { get; set; }
|
||||
[JsonProperty("240")] public object _240 { get; set; }
|
||||
}
|
||||
|
||||
public class Drm
|
||||
{
|
||||
public Manifest manifest { get; set; }
|
||||
public Signature signature { get; set; }
|
||||
}
|
||||
|
||||
public class Manifest
|
||||
{
|
||||
public string? hls { get; set; }
|
||||
public string? dash { get; set; }
|
||||
}
|
||||
|
||||
public class Signature
|
||||
{
|
||||
public Hls hls { get; set; }
|
||||
public Dash dash { get; set; }
|
||||
}
|
||||
|
||||
public class Hls
|
||||
{
|
||||
[JsonProperty("CloudFront-Policy")]
|
||||
public string CloudFrontPolicy { get; set; }
|
||||
[JsonProperty("CloudFront-Policy")] public string CloudFrontPolicy { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Signature")]
|
||||
public string CloudFrontSignature { get; set; }
|
||||
[JsonProperty("CloudFront-Signature")] public string CloudFrontSignature { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Key-Pair-Id")]
|
||||
public string CloudFrontKeyPairId { get; set; }
|
||||
}
|
||||
|
||||
public class Dash
|
||||
{
|
||||
[JsonProperty("CloudFront-Policy")]
|
||||
public string CloudFrontPolicy { get; set; }
|
||||
[JsonProperty("CloudFront-Policy")] public string CloudFrontPolicy { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Signature")]
|
||||
public string CloudFrontSignature { get; set; }
|
||||
[JsonProperty("CloudFront-Signature")] public string CloudFrontSignature { get; set; }
|
||||
|
||||
[JsonProperty("CloudFront-Key-Pair-Id")]
|
||||
public string CloudFrontKeyPairId { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
namespace OF_DL.Entities.Streams
|
||||
{
|
||||
namespace OF_DL.Entities.Streams;
|
||||
|
||||
public class StreamsCollection
|
||||
{
|
||||
public Dictionary<long, string> Streams = new Dictionary<long, string>();
|
||||
public List<Streams.List> StreamObjects = new List<Streams.List>();
|
||||
public List<Streams.Medium> StreamMedia = new List<Streams.Medium>();
|
||||
}
|
||||
public List<Streams.Medium> StreamMedia = new();
|
||||
public List<Streams.List> StreamObjects = new();
|
||||
public Dictionary<long, string> Streams = new();
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
namespace OF_DL.Entities
|
||||
{
|
||||
namespace OF_DL.Entities;
|
||||
|
||||
public class Subscriptions
|
||||
{
|
||||
public List<List> list { get; set; }
|
||||
public bool hasMore { get; set; }
|
||||
|
||||
public class AvatarThumbs
|
||||
{
|
||||
public string c50 { get; set; }
|
||||
@ -156,4 +157,3 @@ namespace OF_DL.Entities
|
||||
public bool? hasActivePaidSubscriptions { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
namespace OF_DL.Entities
|
||||
{
|
||||
namespace OF_DL.Entities;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal class ToggleableConfigAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
namespace OF_DL.Entities
|
||||
{
|
||||
namespace OF_DL.Entities;
|
||||
|
||||
public class User
|
||||
{
|
||||
public string view { get; set; }
|
||||
@ -88,6 +88,7 @@ namespace OF_DL.Entities
|
||||
public bool? isFriend { get; set; }
|
||||
public bool? isBlocked { get; set; }
|
||||
public bool? canReport { get; set; }
|
||||
|
||||
public class AvatarThumbs
|
||||
{
|
||||
public string c50 { get; set; }
|
||||
@ -182,4 +183,3 @@ namespace OF_DL.Entities
|
||||
public List<Subscribe>? subscribes { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,5 +3,5 @@ namespace OF_DL.Enumerations;
|
||||
public enum CustomFileNameOption
|
||||
{
|
||||
ReturnOriginal,
|
||||
ReturnEmpty,
|
||||
ReturnEmpty
|
||||
}
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
namespace OF_DL.Enumerations
|
||||
{
|
||||
namespace OF_DL.Enumerations;
|
||||
|
||||
public enum DownloadDateSelection
|
||||
{
|
||||
before,
|
||||
after,
|
||||
}
|
||||
after
|
||||
}
|
||||
|
||||
@ -1,30 +1,34 @@
|
||||
namespace OF_DL.Enumerations
|
||||
{
|
||||
namespace OF_DL.Enumerations;
|
||||
|
||||
public enum LoggingLevel
|
||||
{
|
||||
//
|
||||
// Summary:
|
||||
// Anything and everything you might want to know about a running block of code.
|
||||
Verbose,
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Internal system events that aren't necessarily observable from the outside.
|
||||
Debug,
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// The lifeblood of operational intelligence - things happen.
|
||||
Information,
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Service is degraded or endangered.
|
||||
Warning,
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Functionality is unavailable, invariants are broken or data is lost.
|
||||
Error,
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// If you have a pager, it goes off when one of these occurs.
|
||||
Fatal
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
namespace OF_DL.Enumerations
|
||||
{
|
||||
namespace OF_DL.Enumerations;
|
||||
|
||||
public enum MediaType
|
||||
{
|
||||
PaidPosts = 10,
|
||||
@ -10,4 +10,3 @@
|
||||
Messages = 60,
|
||||
PaidMessages = 70
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
namespace OF_DL.Enumerations
|
||||
{
|
||||
namespace OF_DL.Enumerations;
|
||||
|
||||
public enum VideoResolution
|
||||
{
|
||||
_240,
|
||||
_720,
|
||||
source
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
using OF_DL.Entities;
|
||||
using OF_DL.Services;
|
||||
|
||||
namespace OF_DL.Helpers
|
||||
{
|
||||
namespace OF_DL.Helpers;
|
||||
|
||||
internal interface IDownloadContext
|
||||
{
|
||||
public IFileNameFormatConfig FileNameFormatConfig { get; }
|
||||
@ -23,4 +23,3 @@ namespace OF_DL.Helpers
|
||||
public IDownloadService DownloadService { get; } = downloadService;
|
||||
public IFileNameFormatConfig FileNameFormatConfig { get; } = fileNameFormatConfig;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
namespace OF_DL.Helpers
|
||||
{
|
||||
namespace OF_DL.Helpers;
|
||||
|
||||
public interface IFileNameHelper
|
||||
{
|
||||
Task<string> BuildFilename(string fileFormat, Dictionary<string, string> values);
|
||||
Task<Dictionary<string, string>> GetFilename(object obj1, object obj2, object obj3, List<string> selectedProperties, string username, Dictionary<string, long> users = null);
|
||||
}
|
||||
|
||||
Task<Dictionary<string, string>> GetFilename(object obj1, object obj2, object obj3, List<string> selectedProperties,
|
||||
string username, Dictionary<string, long> users = null);
|
||||
}
|
||||
|
||||
@ -6,15 +6,15 @@ namespace OF_DL.Helpers;
|
||||
|
||||
public static class VersionHelper
|
||||
{
|
||||
private static readonly HttpClient httpClient = new HttpClient();
|
||||
private const string url = "https://git.ofdl.tools/api/v1/repos/sim0n00ps/OF-DL/releases/latest";
|
||||
private static readonly HttpClient httpClient = new();
|
||||
|
||||
public static async Task<string?> GetLatestReleaseTag(CancellationToken cancellationToken = default)
|
||||
{
|
||||
Log.Debug("Calling GetLatestReleaseTag");
|
||||
try
|
||||
{
|
||||
var response = await httpClient.GetAsync(url, cancellationToken);
|
||||
HttpResponseMessage response = await httpClient.GetAsync(url, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
@ -22,12 +22,13 @@ public static class VersionHelper
|
||||
return null;
|
||||
}
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
string body = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Log.Debug("GetLatestReleaseTag API Response: ");
|
||||
Log.Debug(body);
|
||||
|
||||
var versionCheckResponse = JsonConvert.DeserializeObject<LatestReleaseAPIResponse>(body);
|
||||
LatestReleaseAPIResponse? versionCheckResponse =
|
||||
JsonConvert.DeserializeObject<LatestReleaseAPIResponse>(body);
|
||||
|
||||
if (versionCheckResponse == null || versionCheckResponse.TagName == "")
|
||||
{
|
||||
@ -48,10 +49,13 @@ public static class VersionHelper
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
Console.WriteLine("\nInner Exception:");
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
1237
OF DL/Program.cs
1237
OF DL/Program.cs
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -4,10 +4,19 @@ using PuppeteerSharp;
|
||||
using PuppeteerSharp.BrowserData;
|
||||
using Serilog;
|
||||
|
||||
namespace OF_DL.Services
|
||||
{
|
||||
namespace OF_DL.Services;
|
||||
|
||||
public class AuthService : IAuthService
|
||||
{
|
||||
private const int LoginTimeout = 600000; // 10 minutes
|
||||
private const int FeedLoadTimeout = 60000; // 1 minute
|
||||
|
||||
private readonly string[] _desiredCookies =
|
||||
[
|
||||
"auth_id",
|
||||
"sess"
|
||||
];
|
||||
|
||||
private readonly LaunchOptions _options = new()
|
||||
{
|
||||
Headless = false,
|
||||
@ -17,15 +26,6 @@ namespace OF_DL.Services
|
||||
UserDataDir = Path.GetFullPath("chrome-data")
|
||||
};
|
||||
|
||||
private readonly string[] _desiredCookies =
|
||||
[
|
||||
"auth_id",
|
||||
"sess"
|
||||
];
|
||||
|
||||
private const int LoginTimeout = 600000; // 10 minutes
|
||||
private const int FeedLoadTimeout = 60000; // 1 minute
|
||||
|
||||
public Auth? CurrentAuth { get; set; }
|
||||
|
||||
public async Task<bool> LoadFromFileAsync(string filePath = "auth.json")
|
||||
@ -38,7 +38,7 @@ namespace OF_DL.Services
|
||||
return false;
|
||||
}
|
||||
|
||||
var json = await File.ReadAllTextAsync(filePath);
|
||||
string json = await File.ReadAllTextAsync(filePath);
|
||||
CurrentAuth = JsonConvert.DeserializeObject<Auth>(json);
|
||||
Log.Debug("Auth file loaded and deserialized successfully");
|
||||
return CurrentAuth != null;
|
||||
@ -78,7 +78,7 @@ namespace OF_DL.Services
|
||||
|
||||
try
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(CurrentAuth, Formatting.Indented);
|
||||
string json = JsonConvert.SerializeObject(CurrentAuth, Formatting.Indented);
|
||||
await File.WriteAllTextAsync(filePath, json);
|
||||
Log.Debug($"Auth saved to file: {filePath}");
|
||||
}
|
||||
@ -93,17 +93,19 @@ namespace OF_DL.Services
|
||||
string? executablePath = Environment.GetEnvironmentVariable("OFDL_PUPPETEER_EXECUTABLE_PATH");
|
||||
if (executablePath != null)
|
||||
{
|
||||
Log.Information("OFDL_PUPPETEER_EXECUTABLE_PATH environment variable found. Using browser executable path: {executablePath}", executablePath);
|
||||
Log.Information(
|
||||
"OFDL_PUPPETEER_EXECUTABLE_PATH environment variable found. Using browser executable path: {executablePath}",
|
||||
executablePath);
|
||||
_options.ExecutablePath = executablePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
var browserFetcher = new BrowserFetcher();
|
||||
var installedBrowsers = browserFetcher.GetInstalledBrowsers().ToList();
|
||||
BrowserFetcher browserFetcher = new();
|
||||
List<InstalledBrowser> installedBrowsers = browserFetcher.GetInstalledBrowsers().ToList();
|
||||
if (installedBrowsers.Count == 0)
|
||||
{
|
||||
Log.Information("Downloading browser.");
|
||||
var downloadedBrowser = await browserFetcher.DownloadAsync();
|
||||
InstalledBrowser? downloadedBrowser = await browserFetcher.DownloadAsync();
|
||||
Log.Information("Browser downloaded. Path: {executablePath}",
|
||||
downloadedBrowser.GetExecutablePath());
|
||||
_options.ExecutablePath = downloadedBrowser.GetExecutablePath();
|
||||
@ -121,10 +123,8 @@ namespace OF_DL.Services
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> GetBcToken(IPage page)
|
||||
{
|
||||
return await page.EvaluateExpressionAsync<string>("window.localStorage.getItem('bcTokenSha') || ''");
|
||||
}
|
||||
private async Task<string> GetBcToken(IPage page) =>
|
||||
await page.EvaluateExpressionAsync<string>("window.localStorage.getItem('bcTokenSha') || ''");
|
||||
|
||||
private async Task<Auth?> GetAuthFromBrowser(bool isDocker = false)
|
||||
{
|
||||
@ -171,9 +171,9 @@ namespace OF_DL.Services
|
||||
|
||||
await page.ReloadAsync();
|
||||
|
||||
await page.WaitForNavigationAsync(new NavigationOptions {
|
||||
WaitUntil = [WaitUntilNavigation.Networkidle2],
|
||||
Timeout = FeedLoadTimeout
|
||||
await page.WaitForNavigationAsync(new NavigationOptions
|
||||
{
|
||||
WaitUntil = [WaitUntilNavigation.Networkidle2], Timeout = FeedLoadTimeout
|
||||
});
|
||||
Log.Debug("DOM loaded. Getting BC token and cookies ...");
|
||||
|
||||
@ -212,13 +212,7 @@ namespace OF_DL.Services
|
||||
string cookies = string.Join(" ", mappedCookies.Keys.Where(key => _desiredCookies.Contains(key))
|
||||
.Select(key => $"${key}={mappedCookies[key]};"));
|
||||
|
||||
return new Auth()
|
||||
{
|
||||
COOKIE = cookies,
|
||||
USER_AGENT = userAgent,
|
||||
USER_ID = userId,
|
||||
X_BC = xBc
|
||||
};
|
||||
return new Auth { COOKIE = cookies, USER_AGENT = userAgent, USER_ID = userId, X_BC = xBc };
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -227,4 +221,3 @@ namespace OF_DL.Services
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
using System.Text;
|
||||
using Akka.Configuration;
|
||||
using Akka.Configuration.Hocon;
|
||||
using Newtonsoft.Json;
|
||||
using OF_DL.Entities;
|
||||
using Serilog;
|
||||
using System.Text;
|
||||
using OF_DL.Enumerations;
|
||||
using OF_DL.Utils;
|
||||
using Serilog;
|
||||
using Config = OF_DL.Entities.Config;
|
||||
|
||||
namespace OF_DL.Services
|
||||
{
|
||||
namespace OF_DL.Services;
|
||||
|
||||
public class ConfigService(ILoggingService loggingService) : IConfigService
|
||||
{
|
||||
public Config CurrentConfig { get; private set; } = new();
|
||||
@ -77,7 +79,7 @@ namespace OF_DL.Services
|
||||
|
||||
try
|
||||
{
|
||||
var hoconConfig = BuildHoconFromConfig(CurrentConfig);
|
||||
string hoconConfig = BuildHoconFromConfig(CurrentConfig);
|
||||
await File.WriteAllTextAsync(filePath, hoconConfig);
|
||||
Log.Debug($"Config saved to file: {filePath}");
|
||||
}
|
||||
@ -95,7 +97,7 @@ namespace OF_DL.Services
|
||||
loggingService.UpdateLoggingLevel(newConfig.LoggingLevel);
|
||||
|
||||
// Apply text sanitization preference globally
|
||||
OF_DL.Utils.XmlUtils.Passthrough = newConfig.DisableTextSanitization;
|
||||
XmlUtils.Passthrough = newConfig.DisableTextSanitization;
|
||||
|
||||
Log.Debug("Configuration updated");
|
||||
string configString = JsonConvert.SerializeObject(newConfig, Formatting.Indented);
|
||||
@ -105,17 +107,19 @@ namespace OF_DL.Services
|
||||
private async Task MigrateFromJsonToConfAsync()
|
||||
{
|
||||
if (!File.Exists("config.json"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Log.Debug("config.json found, migrating to config.conf");
|
||||
string jsonText = await File.ReadAllTextAsync("config.json");
|
||||
var jsonConfig = JsonConvert.DeserializeObject<Config>(jsonText);
|
||||
Config? jsonConfig = JsonConvert.DeserializeObject<Config>(jsonText);
|
||||
|
||||
if (jsonConfig != null)
|
||||
{
|
||||
var hoconConfig = BuildHoconFromConfig(jsonConfig);
|
||||
string hoconConfig = BuildHoconFromConfig(jsonConfig);
|
||||
await File.WriteAllTextAsync("config.conf", hoconConfig);
|
||||
File.Delete("config.json");
|
||||
Log.Information("config.conf created successfully from config.json");
|
||||
@ -133,7 +137,7 @@ namespace OF_DL.Services
|
||||
try
|
||||
{
|
||||
string hoconText = await File.ReadAllTextAsync(filePath);
|
||||
var hoconConfig = ConfigurationFactory.ParseString(hoconText);
|
||||
Akka.Configuration.Config? hoconConfig = ConfigurationFactory.ParseString(hoconText);
|
||||
|
||||
CurrentConfig = new Config
|
||||
{
|
||||
@ -158,23 +162,33 @@ namespace OF_DL.Services
|
||||
DownloadAudios = hoconConfig.GetBoolean("Download.Media.DownloadAudios"),
|
||||
IgnoreOwnMessages = hoconConfig.GetBoolean("Download.IgnoreOwnMessages"),
|
||||
DownloadPostsIncrementally = hoconConfig.GetBoolean("Download.DownloadPostsIncrementally"),
|
||||
BypassContentForCreatorsWhoNoLongerExist = hoconConfig.GetBoolean("Download.BypassContentForCreatorsWhoNoLongerExist"),
|
||||
BypassContentForCreatorsWhoNoLongerExist =
|
||||
hoconConfig.GetBoolean("Download.BypassContentForCreatorsWhoNoLongerExist"),
|
||||
DownloadDuplicatedMedia = hoconConfig.GetBoolean("Download.DownloadDuplicatedMedia"),
|
||||
SkipAds = hoconConfig.GetBoolean("Download.SkipAds"),
|
||||
DownloadPath = hoconConfig.GetString("Download.DownloadPath"),
|
||||
DownloadOnlySpecificDates = hoconConfig.GetBoolean("Download.DownloadOnlySpecificDates"),
|
||||
DownloadDateSelection = Enum.Parse<DownloadDateSelection>(hoconConfig.GetString("Download.DownloadDateSelection"), true),
|
||||
CustomDate = !string.IsNullOrWhiteSpace(hoconConfig.GetString("Download.CustomDate")) ? DateTime.Parse(hoconConfig.GetString("Download.CustomDate")) : null,
|
||||
DownloadDateSelection =
|
||||
Enum.Parse<DownloadDateSelection>(hoconConfig.GetString("Download.DownloadDateSelection"), true),
|
||||
CustomDate =
|
||||
!string.IsNullOrWhiteSpace(hoconConfig.GetString("Download.CustomDate"))
|
||||
? DateTime.Parse(hoconConfig.GetString("Download.CustomDate"))
|
||||
: null,
|
||||
ShowScrapeSize = hoconConfig.GetBoolean("Download.ShowScrapeSize"),
|
||||
DisableTextSanitization = bool.TryParse(hoconConfig.GetString("Download.DisableTextSanitization", "false"), out var dts) ? dts : false,
|
||||
DownloadVideoResolution = ParseVideoResolution(hoconConfig.GetString("Download.DownloadVideoResolution", "source")),
|
||||
DisableTextSanitization =
|
||||
bool.TryParse(hoconConfig.GetString("Download.DisableTextSanitization", "false"), out bool dts)
|
||||
? dts
|
||||
: false,
|
||||
DownloadVideoResolution =
|
||||
ParseVideoResolution(hoconConfig.GetString("Download.DownloadVideoResolution", "source")),
|
||||
|
||||
// File Settings
|
||||
PaidPostFileNameFormat = hoconConfig.GetString("File.PaidPostFileNameFormat"),
|
||||
PostFileNameFormat = hoconConfig.GetString("File.PostFileNameFormat"),
|
||||
PaidMessageFileNameFormat = hoconConfig.GetString("File.PaidMessageFileNameFormat"),
|
||||
MessageFileNameFormat = hoconConfig.GetString("File.MessageFileNameFormat"),
|
||||
RenameExistingFilesWhenCustomFormatIsSelected = hoconConfig.GetBoolean("File.RenameExistingFilesWhenCustomFormatIsSelected"),
|
||||
RenameExistingFilesWhenCustomFormatIsSelected =
|
||||
hoconConfig.GetBoolean("File.RenameExistingFilesWhenCustomFormatIsSelected"),
|
||||
|
||||
// Folder Settings
|
||||
FolderPerPaidPost = hoconConfig.GetBoolean("Folder.FolderPerPaidPost"),
|
||||
@ -193,7 +207,10 @@ namespace OF_DL.Services
|
||||
NonInteractiveModePurchasedTab = hoconConfig.GetBoolean("Interaction.NonInteractiveModePurchasedTab"),
|
||||
|
||||
// Performance Settings
|
||||
Timeout = string.IsNullOrWhiteSpace(hoconConfig.GetString("Performance.Timeout")) ? -1 : hoconConfig.GetInt("Performance.Timeout"),
|
||||
Timeout =
|
||||
string.IsNullOrWhiteSpace(hoconConfig.GetString("Performance.Timeout"))
|
||||
? -1
|
||||
: hoconConfig.GetInt("Performance.Timeout"),
|
||||
LimitDownloadRate = hoconConfig.GetBoolean("Performance.LimitDownloadRate"),
|
||||
DownloadLimitInMbPerSec = hoconConfig.GetInt("Performance.DownloadLimitInMbPerSec"),
|
||||
|
||||
@ -208,16 +225,17 @@ namespace OF_DL.Services
|
||||
ValidateFileNameFormat(CurrentConfig.MessageFileNameFormat, "MessageFileNameFormat");
|
||||
|
||||
// Load creator-specific configs
|
||||
var creatorConfigsSection = hoconConfig.GetConfig("CreatorConfigs");
|
||||
Akka.Configuration.Config? creatorConfigsSection = hoconConfig.GetConfig("CreatorConfigs");
|
||||
if (creatorConfigsSection != null)
|
||||
{
|
||||
foreach (var key in creatorConfigsSection.AsEnumerable())
|
||||
foreach (KeyValuePair<string, HoconValue> key in creatorConfigsSection.AsEnumerable())
|
||||
{
|
||||
var creatorKey = key.Key;
|
||||
var creatorHocon = creatorConfigsSection.GetConfig(creatorKey);
|
||||
string creatorKey = key.Key;
|
||||
Akka.Configuration.Config? creatorHocon = creatorConfigsSection.GetConfig(creatorKey);
|
||||
if (!CurrentConfig.CreatorConfigs.ContainsKey(creatorKey) && creatorHocon != null)
|
||||
{
|
||||
CurrentConfig.CreatorConfigs.Add(key.Key, new CreatorConfig
|
||||
CurrentConfig.CreatorConfigs.Add(key.Key,
|
||||
new CreatorConfig
|
||||
{
|
||||
PaidPostFileNameFormat = creatorHocon.GetString("PaidPostFileNameFormat"),
|
||||
PostFileNameFormat = creatorHocon.GetString("PostFileNameFormat"),
|
||||
@ -225,10 +243,14 @@ namespace OF_DL.Services
|
||||
MessageFileNameFormat = creatorHocon.GetString("MessageFileNameFormat")
|
||||
});
|
||||
|
||||
ValidateFileNameFormat(CurrentConfig.CreatorConfigs[key.Key].PaidPostFileNameFormat, $"{key.Key}.PaidPostFileNameFormat");
|
||||
ValidateFileNameFormat(CurrentConfig.CreatorConfigs[key.Key].PostFileNameFormat, $"{key.Key}.PostFileNameFormat");
|
||||
ValidateFileNameFormat(CurrentConfig.CreatorConfigs[key.Key].PaidMessageFileNameFormat, $"{key.Key}.PaidMessageFileNameFormat");
|
||||
ValidateFileNameFormat(CurrentConfig.CreatorConfigs[key.Key].MessageFileNameFormat, $"{key.Key}.MessageFileNameFormat");
|
||||
ValidateFileNameFormat(CurrentConfig.CreatorConfigs[key.Key].PaidPostFileNameFormat,
|
||||
$"{key.Key}.PaidPostFileNameFormat");
|
||||
ValidateFileNameFormat(CurrentConfig.CreatorConfigs[key.Key].PostFileNameFormat,
|
||||
$"{key.Key}.PostFileNameFormat");
|
||||
ValidateFileNameFormat(CurrentConfig.CreatorConfigs[key.Key].PaidMessageFileNameFormat,
|
||||
$"{key.Key}.PaidMessageFileNameFormat");
|
||||
ValidateFileNameFormat(CurrentConfig.CreatorConfigs[key.Key].MessageFileNameFormat,
|
||||
$"{key.Key}.MessageFileNameFormat");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -237,7 +259,7 @@ namespace OF_DL.Services
|
||||
loggingService.UpdateLoggingLevel(CurrentConfig.LoggingLevel);
|
||||
|
||||
// Apply text sanitization preference globally
|
||||
OF_DL.Utils.XmlUtils.Passthrough = CurrentConfig.DisableTextSanitization;
|
||||
XmlUtils.Passthrough = CurrentConfig.DisableTextSanitization;
|
||||
|
||||
Log.Debug("Configuration loaded successfully");
|
||||
string configString = JsonConvert.SerializeObject(CurrentConfig, Formatting.Indented);
|
||||
@ -254,15 +276,15 @@ namespace OF_DL.Services
|
||||
|
||||
private async Task CreateDefaultConfigFileAsync()
|
||||
{
|
||||
Config defaultConfig = new Config();
|
||||
var hoconConfig = BuildHoconFromConfig(defaultConfig);
|
||||
Config defaultConfig = new();
|
||||
string hoconConfig = BuildHoconFromConfig(defaultConfig);
|
||||
await File.WriteAllTextAsync("config.conf", hoconConfig);
|
||||
Log.Information("Created default config.conf file");
|
||||
}
|
||||
|
||||
private string BuildHoconFromConfig(Config config)
|
||||
{
|
||||
var hocon = new StringBuilder();
|
||||
StringBuilder hocon = new();
|
||||
|
||||
hocon.AppendLine("# Auth");
|
||||
hocon.AppendLine("Auth {");
|
||||
@ -292,7 +314,8 @@ namespace OF_DL.Services
|
||||
hocon.AppendLine(" }");
|
||||
hocon.AppendLine($" IgnoreOwnMessages = {config.IgnoreOwnMessages.ToString().ToLower()}");
|
||||
hocon.AppendLine($" DownloadPostsIncrementally = {config.DownloadPostsIncrementally.ToString().ToLower()}");
|
||||
hocon.AppendLine($" BypassContentForCreatorsWhoNoLongerExist = {config.BypassContentForCreatorsWhoNoLongerExist.ToString().ToLower()}");
|
||||
hocon.AppendLine(
|
||||
$" BypassContentForCreatorsWhoNoLongerExist = {config.BypassContentForCreatorsWhoNoLongerExist.ToString().ToLower()}");
|
||||
hocon.AppendLine($" DownloadDuplicatedMedia = {config.DownloadDuplicatedMedia.ToString().ToLower()}");
|
||||
hocon.AppendLine($" SkipAds = {config.SkipAds.ToString().ToLower()}");
|
||||
hocon.AppendLine($" DownloadPath = \"{config.DownloadPath}\"");
|
||||
@ -301,7 +324,8 @@ namespace OF_DL.Services
|
||||
hocon.AppendLine($" CustomDate = \"{config.CustomDate?.ToString("yyyy-MM-dd")}\"");
|
||||
hocon.AppendLine($" ShowScrapeSize = {config.ShowScrapeSize.ToString().ToLower()}");
|
||||
hocon.AppendLine($" DisableTextSanitization = {config.DisableTextSanitization.ToString().ToLower()}");
|
||||
hocon.AppendLine($" DownloadVideoResolution = \"{(config.DownloadVideoResolution == VideoResolution.source ? "source" : config.DownloadVideoResolution.ToString().TrimStart('_'))}\"");
|
||||
hocon.AppendLine(
|
||||
$" DownloadVideoResolution = \"{(config.DownloadVideoResolution == VideoResolution.source ? "source" : config.DownloadVideoResolution.ToString().TrimStart('_'))}\"");
|
||||
hocon.AppendLine("}");
|
||||
|
||||
hocon.AppendLine("# File Settings");
|
||||
@ -310,12 +334,13 @@ namespace OF_DL.Services
|
||||
hocon.AppendLine($" PostFileNameFormat = \"{config.PostFileNameFormat}\"");
|
||||
hocon.AppendLine($" PaidMessageFileNameFormat = \"{config.PaidMessageFileNameFormat}\"");
|
||||
hocon.AppendLine($" MessageFileNameFormat = \"{config.MessageFileNameFormat}\"");
|
||||
hocon.AppendLine($" RenameExistingFilesWhenCustomFormatIsSelected = {config.RenameExistingFilesWhenCustomFormatIsSelected.ToString().ToLower()}");
|
||||
hocon.AppendLine(
|
||||
$" RenameExistingFilesWhenCustomFormatIsSelected = {config.RenameExistingFilesWhenCustomFormatIsSelected.ToString().ToLower()}");
|
||||
hocon.AppendLine("}");
|
||||
|
||||
hocon.AppendLine("# Creator-Specific Configurations");
|
||||
hocon.AppendLine("CreatorConfigs {");
|
||||
foreach (var creatorConfig in config.CreatorConfigs)
|
||||
foreach (KeyValuePair<string, CreatorConfig> creatorConfig in config.CreatorConfigs)
|
||||
{
|
||||
hocon.AppendLine($" \"{creatorConfig.Key}\" {{");
|
||||
hocon.AppendLine($" PaidPostFileNameFormat = \"{creatorConfig.Value.PaidPostFileNameFormat}\"");
|
||||
@ -324,6 +349,7 @@ namespace OF_DL.Services
|
||||
hocon.AppendLine($" MessageFileNameFormat = \"{creatorConfig.Value.MessageFileNameFormat}\"");
|
||||
hocon.AppendLine(" }");
|
||||
}
|
||||
|
||||
hocon.AppendLine("}");
|
||||
|
||||
hocon.AppendLine("# Folder Settings");
|
||||
@ -337,7 +363,8 @@ namespace OF_DL.Services
|
||||
hocon.AppendLine("# Subscription Settings");
|
||||
hocon.AppendLine("Subscriptions {");
|
||||
hocon.AppendLine($" IncludeExpiredSubscriptions = {config.IncludeExpiredSubscriptions.ToString().ToLower()}");
|
||||
hocon.AppendLine($" IncludeRestrictedSubscriptions = {config.IncludeRestrictedSubscriptions.ToString().ToLower()}");
|
||||
hocon.AppendLine(
|
||||
$" IncludeRestrictedSubscriptions = {config.IncludeRestrictedSubscriptions.ToString().ToLower()}");
|
||||
hocon.AppendLine($" IgnoredUsersListName = \"{config.IgnoredUsersListName}\"");
|
||||
hocon.AppendLine("}");
|
||||
|
||||
@ -345,7 +372,8 @@ namespace OF_DL.Services
|
||||
hocon.AppendLine("Interaction {");
|
||||
hocon.AppendLine($" NonInteractiveMode = {config.NonInteractiveMode.ToString().ToLower()}");
|
||||
hocon.AppendLine($" NonInteractiveModeListName = \"{config.NonInteractiveModeListName}\"");
|
||||
hocon.AppendLine($" NonInteractiveModePurchasedTab = {config.NonInteractiveModePurchasedTab.ToString().ToLower()}");
|
||||
hocon.AppendLine(
|
||||
$" NonInteractiveModePurchasedTab = {config.NonInteractiveModePurchasedTab.ToString().ToLower()}");
|
||||
hocon.AppendLine("}");
|
||||
|
||||
hocon.AppendLine("# Performance Settings");
|
||||
@ -377,9 +405,10 @@ namespace OF_DL.Services
|
||||
private VideoResolution ParseVideoResolution(string value)
|
||||
{
|
||||
if (value.Equals("source", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return VideoResolution.source;
|
||||
}
|
||||
|
||||
return Enum.Parse<VideoResolution>("_" + value, ignoreCase: true);
|
||||
}
|
||||
return Enum.Parse<VideoResolution>("_" + value, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@ using System.Text;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Serilog;
|
||||
|
||||
namespace OF_DL.Services
|
||||
{
|
||||
namespace OF_DL.Services;
|
||||
|
||||
public class DBService(IConfigService configService) : IDBService
|
||||
{
|
||||
public async Task CreateDB(string folder)
|
||||
@ -23,7 +23,10 @@ namespace OF_DL.Services
|
||||
connection.Open();
|
||||
|
||||
// create the 'medias' table
|
||||
using (SqliteCommand cmd = new("CREATE TABLE IF NOT EXISTS medias (id INTEGER NOT NULL, media_id INTEGER, post_id INTEGER NOT NULL, link VARCHAR, directory VARCHAR, filename VARCHAR, size INTEGER, api_type VARCHAR, media_type VARCHAR, preview INTEGER, linked VARCHAR, downloaded INTEGER, created_at TIMESTAMP, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(media_id));", connection))
|
||||
using (SqliteCommand cmd =
|
||||
new(
|
||||
"CREATE TABLE IF NOT EXISTS medias (id INTEGER NOT NULL, media_id INTEGER, post_id INTEGER NOT NULL, link VARCHAR, directory VARCHAR, filename VARCHAR, size INTEGER, api_type VARCHAR, media_type VARCHAR, preview INTEGER, linked VARCHAR, downloaded INTEGER, created_at TIMESTAMP, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(media_id));",
|
||||
connection))
|
||||
{
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
@ -71,37 +74,55 @@ namespace OF_DL.Services
|
||||
}
|
||||
|
||||
// create the 'messages' table
|
||||
using (SqliteCommand cmd = new("CREATE TABLE IF NOT EXISTS messages (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, user_id INTEGER, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));", connection))
|
||||
using (SqliteCommand cmd =
|
||||
new(
|
||||
"CREATE TABLE IF NOT EXISTS messages (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, user_id INTEGER, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));",
|
||||
connection))
|
||||
{
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
// create the 'posts' table
|
||||
using (SqliteCommand cmd = new("CREATE TABLE IF NOT EXISTS posts (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));", connection))
|
||||
using (SqliteCommand cmd =
|
||||
new(
|
||||
"CREATE TABLE IF NOT EXISTS posts (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));",
|
||||
connection))
|
||||
{
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
// create the 'stories' table
|
||||
using (SqliteCommand cmd = new("CREATE TABLE IF NOT EXISTS stories (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));", connection))
|
||||
using (SqliteCommand cmd =
|
||||
new(
|
||||
"CREATE TABLE IF NOT EXISTS stories (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));",
|
||||
connection))
|
||||
{
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
// create the 'others' table
|
||||
using (SqliteCommand cmd = new("CREATE TABLE IF NOT EXISTS others (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));", connection))
|
||||
using (SqliteCommand cmd =
|
||||
new(
|
||||
"CREATE TABLE IF NOT EXISTS others (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));",
|
||||
connection))
|
||||
{
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
// create the 'products' table
|
||||
using (SqliteCommand cmd = new("CREATE TABLE IF NOT EXISTS products (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, title VARCHAR, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));", connection))
|
||||
using (SqliteCommand cmd =
|
||||
new(
|
||||
"CREATE TABLE IF NOT EXISTS products (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, title VARCHAR, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));",
|
||||
connection))
|
||||
{
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
// create the 'profiles' table
|
||||
using (SqliteCommand cmd = new("CREATE TABLE IF NOT EXISTS profiles (id INTEGER NOT NULL, user_id INTEGER NOT NULL, username VARCHAR NOT NULL, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(username));", connection))
|
||||
using (SqliteCommand cmd =
|
||||
new(
|
||||
"CREATE TABLE IF NOT EXISTS profiles (id INTEGER NOT NULL, user_id INTEGER NOT NULL, username VARCHAR NOT NULL, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(username));",
|
||||
connection))
|
||||
{
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
@ -115,8 +136,10 @@ namespace OF_DL.Services
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
Console.WriteLine("\nInner Exception:");
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -130,7 +153,10 @@ namespace OF_DL.Services
|
||||
|
||||
connection.Open();
|
||||
|
||||
using (SqliteCommand cmd = new("CREATE TABLE IF NOT EXISTS users (id INTEGER NOT NULL, user_id INTEGER NOT NULL, username VARCHAR NOT NULL, PRIMARY KEY(id), UNIQUE(username));", connection))
|
||||
using (SqliteCommand cmd =
|
||||
new(
|
||||
"CREATE TABLE IF NOT EXISTS users (id INTEGER NOT NULL, user_id INTEGER NOT NULL, username VARCHAR NOT NULL, PRIMARY KEY(id), UNIQUE(username));",
|
||||
connection))
|
||||
{
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
@ -138,14 +164,17 @@ namespace OF_DL.Services
|
||||
Log.Debug("Adding missing creators");
|
||||
foreach (KeyValuePair<string, long> user in users)
|
||||
{
|
||||
using (SqliteCommand checkCmd = new($"SELECT user_id, username FROM users WHERE user_id = @userId;", connection))
|
||||
using (SqliteCommand checkCmd = new("SELECT user_id, username FROM users WHERE user_id = @userId;",
|
||||
connection))
|
||||
{
|
||||
checkCmd.Parameters.AddWithValue("@userId", user.Value);
|
||||
using (var reader = await checkCmd.ExecuteReaderAsync())
|
||||
using (SqliteDataReader reader = await checkCmd.ExecuteReaderAsync())
|
||||
{
|
||||
if (!reader.Read())
|
||||
{
|
||||
using (SqliteCommand insertCmd = new($"INSERT INTO users (user_id, username) VALUES (@userId, @username);", connection))
|
||||
using (SqliteCommand insertCmd =
|
||||
new("INSERT INTO users (user_id, username) VALUES (@userId, @username);",
|
||||
connection))
|
||||
{
|
||||
insertCmd.Parameters.AddWithValue("@userId", user.Value);
|
||||
insertCmd.Parameters.AddWithValue("@username", user.Key);
|
||||
@ -170,8 +199,10 @@ namespace OF_DL.Services
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
Console.WriteLine("\nInner Exception:");
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,10 +215,11 @@ namespace OF_DL.Services
|
||||
|
||||
connection.Open();
|
||||
|
||||
using (SqliteCommand checkCmd = new($"SELECT user_id, username FROM users WHERE user_id = @userId;", connection))
|
||||
using (SqliteCommand checkCmd = new("SELECT user_id, username FROM users WHERE user_id = @userId;",
|
||||
connection))
|
||||
{
|
||||
checkCmd.Parameters.AddWithValue("@userId", user.Value);
|
||||
using (var reader = await checkCmd.ExecuteReaderAsync())
|
||||
using (SqliteDataReader reader = await checkCmd.ExecuteReaderAsync())
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
@ -196,7 +228,8 @@ namespace OF_DL.Services
|
||||
|
||||
if (storedUsername != user.Key)
|
||||
{
|
||||
using (SqliteCommand updateCmd = new($"UPDATE users SET username = @newUsername WHERE user_id = @userId;", connection))
|
||||
using (SqliteCommand updateCmd =
|
||||
new("UPDATE users SET username = @newUsername WHERE user_id = @userId;", connection))
|
||||
{
|
||||
updateCmd.Parameters.AddWithValue("@newUsername", user.Key);
|
||||
updateCmd.Parameters.AddWithValue("@userId", user.Value);
|
||||
@ -223,26 +256,32 @@ namespace OF_DL.Services
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
Console.WriteLine("\nInner Exception:");
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AddMessage(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived, DateTime created_at, long user_id)
|
||||
public async Task AddMessage(string folder, long post_id, string message_text, string price, bool is_paid,
|
||||
bool is_archived, DateTime created_at, long user_id)
|
||||
{
|
||||
try
|
||||
{
|
||||
using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
|
||||
connection.Open();
|
||||
await EnsureCreatedAtColumnExists(connection, "messages");
|
||||
using SqliteCommand cmd = new($"SELECT COUNT(*) FROM messages WHERE post_id=@post_id", connection);
|
||||
using SqliteCommand cmd = new("SELECT COUNT(*) FROM messages WHERE post_id=@post_id", connection);
|
||||
cmd.Parameters.AddWithValue("@post_id", post_id);
|
||||
int count = Convert.ToInt32(await cmd.ExecuteScalarAsync());
|
||||
if (count == 0)
|
||||
{
|
||||
// If the record doesn't exist, insert a new one
|
||||
using SqliteCommand insertCmd = new("INSERT INTO messages(post_id, text, price, paid, archived, created_at, user_id, record_created_at) VALUES(@post_id, @message_text, @price, @is_paid, @is_archived, @created_at, @user_id, @record_created_at)", connection);
|
||||
using SqliteCommand insertCmd =
|
||||
new(
|
||||
"INSERT INTO messages(post_id, text, price, paid, archived, created_at, user_id, record_created_at) VALUES(@post_id, @message_text, @price, @is_paid, @is_archived, @created_at, @user_id, @record_created_at)",
|
||||
connection);
|
||||
insertCmd.Parameters.AddWithValue("@post_id", post_id);
|
||||
insertCmd.Parameters.AddWithValue("@message_text", message_text ?? (object)DBNull.Value);
|
||||
insertCmd.Parameters.AddWithValue("@price", price ?? (object)DBNull.Value);
|
||||
@ -261,27 +300,33 @@ namespace OF_DL.Services
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
Console.WriteLine("\nInner Exception:");
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task AddPost(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived, DateTime created_at)
|
||||
public async Task AddPost(string folder, long post_id, string message_text, string price, bool is_paid,
|
||||
bool is_archived, DateTime created_at)
|
||||
{
|
||||
try
|
||||
{
|
||||
using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
|
||||
connection.Open();
|
||||
await EnsureCreatedAtColumnExists(connection, "posts");
|
||||
using SqliteCommand cmd = new($"SELECT COUNT(*) FROM posts WHERE post_id=@post_id", connection);
|
||||
using SqliteCommand cmd = new("SELECT COUNT(*) FROM posts WHERE post_id=@post_id", connection);
|
||||
cmd.Parameters.AddWithValue("@post_id", post_id);
|
||||
int count = Convert.ToInt32(await cmd.ExecuteScalarAsync());
|
||||
if (count == 0)
|
||||
{
|
||||
// If the record doesn't exist, insert a new one
|
||||
using SqliteCommand insertCmd = new("INSERT INTO posts(post_id, text, price, paid, archived, created_at, record_created_at) VALUES(@post_id, @message_text, @price, @is_paid, @is_archived, @created_at, @record_created_at)", connection);
|
||||
using SqliteCommand insertCmd =
|
||||
new(
|
||||
"INSERT INTO posts(post_id, text, price, paid, archived, created_at, record_created_at) VALUES(@post_id, @message_text, @price, @is_paid, @is_archived, @created_at, @record_created_at)",
|
||||
connection);
|
||||
insertCmd.Parameters.AddWithValue("@post_id", post_id);
|
||||
insertCmd.Parameters.AddWithValue("@message_text", message_text ?? (object)DBNull.Value);
|
||||
insertCmd.Parameters.AddWithValue("@price", price ?? (object)DBNull.Value);
|
||||
@ -299,27 +344,33 @@ namespace OF_DL.Services
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
Console.WriteLine("\nInner Exception:");
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task AddStory(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived, DateTime created_at)
|
||||
public async Task AddStory(string folder, long post_id, string message_text, string price, bool is_paid,
|
||||
bool is_archived, DateTime created_at)
|
||||
{
|
||||
try
|
||||
{
|
||||
using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
|
||||
connection.Open();
|
||||
await EnsureCreatedAtColumnExists(connection, "stories");
|
||||
using SqliteCommand cmd = new($"SELECT COUNT(*) FROM stories WHERE post_id=@post_id", connection);
|
||||
using SqliteCommand cmd = new("SELECT COUNT(*) FROM stories WHERE post_id=@post_id", connection);
|
||||
cmd.Parameters.AddWithValue("@post_id", post_id);
|
||||
int count = Convert.ToInt32(await cmd.ExecuteScalarAsync());
|
||||
if (count == 0)
|
||||
{
|
||||
// If the record doesn't exist, insert a new one
|
||||
using SqliteCommand insertCmd = new("INSERT INTO stories(post_id, text, price, paid, archived, created_at, record_created_at) VALUES(@post_id, @message_text, @price, @is_paid, @is_archived, @created_at, @record_created_at)", connection);
|
||||
using SqliteCommand insertCmd =
|
||||
new(
|
||||
"INSERT INTO stories(post_id, text, price, paid, archived, created_at, record_created_at) VALUES(@post_id, @message_text, @price, @is_paid, @is_archived, @created_at, @record_created_at)",
|
||||
connection);
|
||||
insertCmd.Parameters.AddWithValue("@post_id", post_id);
|
||||
insertCmd.Parameters.AddWithValue("@message_text", message_text ?? (object)DBNull.Value);
|
||||
insertCmd.Parameters.AddWithValue("@price", price ?? (object)DBNull.Value);
|
||||
@ -337,21 +388,25 @@ namespace OF_DL.Services
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
Console.WriteLine("\nInner Exception:");
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task AddMedia(string folder, long media_id, long post_id, string link, string? directory, string? filename, long? size, string api_type, string media_type, bool preview, bool downloaded, DateTime? created_at)
|
||||
public async Task AddMedia(string folder, long media_id, long post_id, string link, string? directory,
|
||||
string? filename, long? size, string api_type, string media_type, bool preview, bool downloaded,
|
||||
DateTime? created_at)
|
||||
{
|
||||
try
|
||||
{
|
||||
using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
|
||||
connection.Open();
|
||||
await EnsureCreatedAtColumnExists(connection, "medias");
|
||||
StringBuilder sql = new StringBuilder("SELECT COUNT(*) FROM medias WHERE media_id=@media_id");
|
||||
StringBuilder sql = new("SELECT COUNT(*) FROM medias WHERE media_id=@media_id");
|
||||
if (configService.CurrentConfig.DownloadDuplicatedMedia)
|
||||
{
|
||||
sql.Append(" and api_type=@api_type");
|
||||
@ -364,7 +419,9 @@ namespace OF_DL.Services
|
||||
if (count == 0)
|
||||
{
|
||||
// If the record doesn't exist, insert a new one
|
||||
using SqliteCommand insertCmd = new($"INSERT INTO medias(media_id, post_id, link, directory, filename, size, api_type, media_type, preview, downloaded, created_at, record_created_at) VALUES({media_id}, {post_id}, '{link}', '{directory?.ToString() ?? "NULL"}', '{filename?.ToString() ?? "NULL"}', {size?.ToString() ?? "NULL"}, '{api_type}', '{media_type}', {Convert.ToInt32(preview)}, {Convert.ToInt32(downloaded)}, '{created_at?.ToString("yyyy-MM-dd HH:mm:ss")}', '{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}')", connection);
|
||||
using SqliteCommand insertCmd = new(
|
||||
$"INSERT INTO medias(media_id, post_id, link, directory, filename, size, api_type, media_type, preview, downloaded, created_at, record_created_at) VALUES({media_id}, {post_id}, '{link}', '{directory ?? "NULL"}', '{filename ?? "NULL"}', {size?.ToString() ?? "NULL"}, '{api_type}', '{media_type}', {Convert.ToInt32(preview)}, {Convert.ToInt32(downloaded)}, '{created_at?.ToString("yyyy-MM-dd HH:mm:ss")}', '{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}')",
|
||||
connection);
|
||||
await insertCmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
@ -375,8 +432,10 @@ namespace OF_DL.Services
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
Console.WriteLine("\nInner Exception:");
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -390,7 +449,7 @@ namespace OF_DL.Services
|
||||
|
||||
using (SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db"))
|
||||
{
|
||||
StringBuilder sql = new StringBuilder("SELECT downloaded FROM medias WHERE media_id=@media_id");
|
||||
StringBuilder sql = new("SELECT downloaded FROM medias WHERE media_id=@media_id");
|
||||
if (configService.CurrentConfig.DownloadDuplicatedMedia)
|
||||
{
|
||||
sql.Append(" and api_type=@api_type");
|
||||
@ -402,6 +461,7 @@ namespace OF_DL.Services
|
||||
cmd.Parameters.AddWithValue("@api_type", api_type);
|
||||
downloaded = Convert.ToBoolean(await cmd.ExecuteScalarAsync());
|
||||
}
|
||||
|
||||
return downloaded;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -411,21 +471,27 @@ namespace OF_DL.Services
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
Console.WriteLine("\nInner Exception:");
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace);
|
||||
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
|
||||
ex.InnerException.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public async Task UpdateMedia(string folder, long media_id, string api_type, string directory, string filename, long size, bool downloaded, DateTime created_at)
|
||||
public async Task UpdateMedia(string folder, long media_id, string api_type, string directory, string filename,
|
||||
long size, bool downloaded, DateTime created_at)
|
||||
{
|
||||
using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
|
||||
connection.Open();
|
||||
|
||||
// Construct the update command
|
||||
StringBuilder sql = new StringBuilder("UPDATE medias SET directory=@directory, filename=@filename, size=@size, downloaded=@downloaded, created_at=@created_at WHERE media_id=@media_id");
|
||||
StringBuilder sql =
|
||||
new(
|
||||
"UPDATE medias SET directory=@directory, filename=@filename, size=@size, downloaded=@downloaded, created_at=@created_at WHERE media_id=@media_id");
|
||||
if (configService.CurrentConfig.DownloadDuplicatedMedia)
|
||||
{
|
||||
sql.Append(" and api_type=@api_type");
|
||||
@ -453,11 +519,13 @@ namespace OF_DL.Services
|
||||
using (SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db"))
|
||||
{
|
||||
connection.Open();
|
||||
using SqliteCommand cmd = new($"SELECT size FROM medias WHERE media_id=@media_id and api_type=@api_type", connection);
|
||||
using SqliteCommand cmd = new("SELECT size FROM medias WHERE media_id=@media_id and api_type=@api_type",
|
||||
connection);
|
||||
cmd.Parameters.AddWithValue("@media_id", media_id);
|
||||
cmd.Parameters.AddWithValue("@api_type", api_type);
|
||||
size = Convert.ToInt64(await cmd.ExecuteScalarAsync());
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -483,19 +551,20 @@ namespace OF_DL.Services
|
||||
ON P.post_id = m.post_id
|
||||
WHERE m.downloaded = 0
|
||||
)", connection);
|
||||
var scalarValue = await cmd.ExecuteScalarAsync();
|
||||
object? scalarValue = await cmd.ExecuteScalarAsync();
|
||||
if (scalarValue != null && scalarValue != DBNull.Value)
|
||||
{
|
||||
mostRecentDate = Convert.ToDateTime(scalarValue);
|
||||
}
|
||||
}
|
||||
|
||||
return mostRecentDate;
|
||||
}
|
||||
|
||||
private async Task EnsureCreatedAtColumnExists(SqliteConnection connection, string tableName)
|
||||
{
|
||||
using SqliteCommand cmd = new($"PRAGMA table_info({tableName});", connection);
|
||||
using var reader = await cmd.ExecuteReaderAsync();
|
||||
using SqliteDataReader reader = await cmd.ExecuteReaderAsync();
|
||||
bool columnExists = false;
|
||||
|
||||
while (await reader.ReadAsync())
|
||||
@ -509,9 +578,9 @@ namespace OF_DL.Services
|
||||
|
||||
if (!columnExists)
|
||||
{
|
||||
using SqliteCommand alterCmd = new($"ALTER TABLE {tableName} ADD COLUMN record_created_at TIMESTAMP;", connection);
|
||||
using SqliteCommand alterCmd = new($"ALTER TABLE {tableName} ADD COLUMN record_created_at TIMESTAMP;",
|
||||
connection);
|
||||
await alterCmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,12 @@
|
||||
using HtmlAgilityPack;
|
||||
using System.Reflection;
|
||||
using HtmlAgilityPack;
|
||||
|
||||
namespace OF_DL.Services;
|
||||
|
||||
namespace OF_DL.Services
|
||||
{
|
||||
public class FileNameService(IAuthService authService) : IFileNameService
|
||||
{
|
||||
public async Task<Dictionary<string, string>> GetFilename(object obj1, object obj2, object obj3, List<string> selectedProperties, string username, Dictionary<string, long> users = null)
|
||||
public async Task<Dictionary<string, string>> GetFilename(object obj1, object obj2, object obj3,
|
||||
List<string> selectedProperties, string username, Dictionary<string, long> users = null)
|
||||
{
|
||||
Dictionary<string, string> values = new();
|
||||
Type type1 = obj1.GetType();
|
||||
@ -30,11 +31,14 @@ namespace OF_DL.Services
|
||||
object policy = GetNestedPropertyValue(obj2, "files.drm.signature.dash.CloudFrontPolicy");
|
||||
object signature = GetNestedPropertyValue(obj2, "files.drm.signature.dash.CloudFrontSignature");
|
||||
object kvp = GetNestedPropertyValue(obj2, "files.drm.signature.dash.CloudFrontKeyPairId");
|
||||
DateTime lastModified = await DownloadService.GetDRMVideoLastModified(string.Join(",", mpdurl, policy, signature, kvp), authService.CurrentAuth);
|
||||
DateTime lastModified =
|
||||
await DownloadService.GetDRMVideoLastModified(string.Join(",", mpdurl, policy, signature, kvp),
|
||||
authService.CurrentAuth);
|
||||
values.Add(propertyName, lastModified.ToString("yyyy-MM-dd"));
|
||||
continue;
|
||||
}
|
||||
else if((fileProperty == null || drmProperty == null) && propertyName == "mediaCreatedAt")
|
||||
|
||||
if ((fileProperty == null || drmProperty == null) && propertyName == "mediaCreatedAt")
|
||||
{
|
||||
object source = GetNestedPropertyValue(obj2, "files.full.url");
|
||||
if (source != null)
|
||||
@ -43,8 +47,7 @@ namespace OF_DL.Services
|
||||
values.Add(propertyName, lastModified.ToString("yyyy-MM-dd"));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
object preview = GetNestedPropertyValue(obj2, "preview");
|
||||
if (preview != null)
|
||||
{
|
||||
@ -54,8 +57,8 @@ namespace OF_DL.Services
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
PropertyInfo? property = Array.Find(properties2, p => p.Name.Equals(propertyName.Replace("media", ""), StringComparison.OrdinalIgnoreCase));
|
||||
PropertyInfo? property = Array.Find(properties2,
|
||||
p => p.Name.Equals(propertyName.Replace("media", ""), StringComparison.OrdinalIgnoreCase));
|
||||
if (property != null)
|
||||
{
|
||||
object? propertyValue = property.GetValue(obj2);
|
||||
@ -79,7 +82,7 @@ namespace OF_DL.Services
|
||||
if (sourcePropertyValue != null)
|
||||
{
|
||||
Uri uri = new(sourcePropertyValue.ToString());
|
||||
string filename = System.IO.Path.GetFileName(uri.LocalPath);
|
||||
string filename = Path.GetFileName(uri.LocalPath);
|
||||
values.Add(propertyName, filename.Split(".")[0]);
|
||||
}
|
||||
else
|
||||
@ -89,7 +92,7 @@ namespace OF_DL.Services
|
||||
if (nestedPropertyValue != null)
|
||||
{
|
||||
Uri uri = new(nestedPropertyValue.ToString());
|
||||
string filename = System.IO.Path.GetFileName(uri.LocalPath);
|
||||
string filename = Path.GetFileName(uri.LocalPath);
|
||||
values.Add(propertyName, filename.Split(".")[0] + "_source");
|
||||
}
|
||||
}
|
||||
@ -106,30 +109,36 @@ namespace OF_DL.Services
|
||||
object nestedPropertyValue = GetNestedPropertyValue(obj3, propertyPath);
|
||||
if (nestedPropertyValue != null)
|
||||
{
|
||||
values.Add(propertyName, users.FirstOrDefault(u => u.Value == Convert.ToInt32(nestedPropertyValue.ToString())).Key);
|
||||
values.Add(propertyName,
|
||||
users.FirstOrDefault(u => u.Value == Convert.ToInt32(nestedPropertyValue.ToString())).Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (propertyName.Contains("text", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
PropertyInfo property = Array.Find(properties1, p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
|
||||
PropertyInfo property = Array.Find(properties1,
|
||||
p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
|
||||
if (property != null)
|
||||
{
|
||||
object propertyValue = property.GetValue(obj1);
|
||||
if (propertyValue != null)
|
||||
{
|
||||
var pageDoc = new HtmlDocument();
|
||||
HtmlDocument pageDoc = new();
|
||||
pageDoc.LoadHtml(propertyValue.ToString());
|
||||
var str = pageDoc.DocumentNode.InnerText;
|
||||
string str = pageDoc.DocumentNode.InnerText;
|
||||
if (str.Length > 100) // todo: add length limit to config
|
||||
{
|
||||
str = str.Substring(0, 100);
|
||||
}
|
||||
|
||||
values.Add(propertyName, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PropertyInfo property = Array.Find(properties1, p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
|
||||
PropertyInfo property = Array.Find(properties1,
|
||||
p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
|
||||
if (property != null)
|
||||
{
|
||||
object propertyValue = property.GetValue(obj1);
|
||||
@ -147,23 +156,13 @@ namespace OF_DL.Services
|
||||
}
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
static object GetNestedPropertyValue(object source, string propertyPath)
|
||||
{
|
||||
object value = source;
|
||||
foreach (var propertyName in propertyPath.Split('.'))
|
||||
{
|
||||
PropertyInfo property = value.GetType().GetProperty(propertyName) ?? throw new ArgumentException($"Property '{propertyName}' not found.");
|
||||
value = property.GetValue(value);
|
||||
}
|
||||
return value;
|
||||
return values;
|
||||
}
|
||||
|
||||
public async Task<string> BuildFilename(string fileFormat, Dictionary<string, string> values)
|
||||
{
|
||||
foreach (var kvp in values)
|
||||
foreach (KeyValuePair<string, string> kvp in values)
|
||||
{
|
||||
string placeholder = "{" + kvp.Key + "}";
|
||||
fileFormat = fileFormat.Replace(placeholder, kvp.Value);
|
||||
@ -172,10 +171,20 @@ namespace OF_DL.Services
|
||||
return RemoveInvalidFileNameChars($"{fileFormat}");
|
||||
}
|
||||
|
||||
private static string RemoveInvalidFileNameChars(string fileName)
|
||||
private static object GetNestedPropertyValue(object source, string propertyPath)
|
||||
{
|
||||
return string.IsNullOrEmpty(fileName) ? fileName : string.Concat(fileName.Split(Path.GetInvalidFileNameChars()));
|
||||
object value = source;
|
||||
foreach (string propertyName in propertyPath.Split('.'))
|
||||
{
|
||||
PropertyInfo property = value.GetType().GetProperty(propertyName) ??
|
||||
throw new ArgumentException($"Property '{propertyName}' not found.");
|
||||
value = property.GetValue(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static string RemoveInvalidFileNameChars(string fileName) => string.IsNullOrEmpty(fileName)
|
||||
? fileName
|
||||
: string.Concat(fileName.Split(Path.GetInvalidFileNameChars()));
|
||||
}
|
||||
|
||||
@ -8,8 +8,8 @@ using OF_DL.Entities.Streams;
|
||||
using OF_DL.Enumerations;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace OF_DL.Services
|
||||
{
|
||||
namespace OF_DL.Services;
|
||||
|
||||
public interface IAPIService
|
||||
{
|
||||
Task<string> GetDecryptionKeyCDRMProject(Dictionary<string, string> drmHeaders, string licenceURL, string pssh);
|
||||
@ -18,8 +18,13 @@ namespace OF_DL.Services
|
||||
Task<string> GetDRMMPDPSSH(string mpdUrl, string policy, string signature, string kvp);
|
||||
Task<Dictionary<string, long>> GetLists(string endpoint);
|
||||
Task<List<string>> GetListUsers(string endpoint);
|
||||
Task<Dictionary<long, string>> GetMedia(MediaType mediatype, string endpoint, string? username, string folder, List<long> paid_post_ids);
|
||||
Task<PaidPostCollection> GetPaidPosts(string endpoint, string folder, string username, List<long> paid_post_ids, StatusContext ctx);
|
||||
|
||||
Task<Dictionary<long, string>> GetMedia(MediaType mediatype, string endpoint, string? username, string folder,
|
||||
List<long> paid_post_ids);
|
||||
|
||||
Task<PaidPostCollection> GetPaidPosts(string endpoint, string folder, string username, List<long> paid_post_ids,
|
||||
StatusContext ctx);
|
||||
|
||||
Task<PostCollection> GetPosts(string endpoint, string folder, List<long> paid_post_ids, StatusContext ctx);
|
||||
Task<SinglePostCollection> GetPost(string endpoint, string folder);
|
||||
Task<StreamsCollection> GetStreams(string endpoint, string folder, List<long> paid_post_ids, StatusContext ctx);
|
||||
@ -36,4 +41,3 @@ namespace OF_DL.Services
|
||||
Task<Dictionary<string, long>> GetExpiredSubscriptions(string endpoint, bool includeRestrictedSubscriptions);
|
||||
Task<string> GetDecryptionKeyOFDL(Dictionary<string, string> drmHeaders, string licenceURL, string pssh);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using OF_DL.Entities;
|
||||
|
||||
namespace OF_DL.Services
|
||||
{
|
||||
namespace OF_DL.Services;
|
||||
|
||||
public interface IAuthService
|
||||
{
|
||||
Auth? CurrentAuth { get; set; }
|
||||
@ -9,4 +9,3 @@ namespace OF_DL.Services
|
||||
Task<bool> LoadFromBrowserAsync();
|
||||
Task SaveToFileAsync(string filePath = "auth.json");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using OF_DL.Entities;
|
||||
|
||||
namespace OF_DL.Services
|
||||
{
|
||||
namespace OF_DL.Services;
|
||||
|
||||
public interface IConfigService
|
||||
{
|
||||
Config CurrentConfig { get; }
|
||||
@ -10,4 +10,3 @@ namespace OF_DL.Services
|
||||
Task SaveConfigurationAsync(string filePath = "config.conf");
|
||||
void UpdateConfig(Config newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,27 @@
|
||||
namespace OF_DL.Services
|
||||
{
|
||||
namespace OF_DL.Services;
|
||||
|
||||
public interface IDBService
|
||||
{
|
||||
Task AddMessage(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived, DateTime created_at, long user_id);
|
||||
Task AddPost(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived, DateTime created_at);
|
||||
Task AddStory(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived, DateTime created_at);
|
||||
Task AddMessage(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived,
|
||||
DateTime created_at, long user_id);
|
||||
|
||||
Task AddPost(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived,
|
||||
DateTime created_at);
|
||||
|
||||
Task AddStory(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived,
|
||||
DateTime created_at);
|
||||
|
||||
Task CreateDB(string folder);
|
||||
Task CreateUsersDB(Dictionary<string, long> users);
|
||||
Task CheckUsername(KeyValuePair<string, long> user, string path);
|
||||
Task AddMedia(string folder, long media_id, long post_id, string link, string? directory, string? filename, long? size, string api_type, string media_type, bool preview, bool downloaded, DateTime? created_at);
|
||||
Task UpdateMedia(string folder, long media_id, string api_type, string directory, string filename, long size, bool downloaded, DateTime created_at);
|
||||
|
||||
Task AddMedia(string folder, long media_id, long post_id, string link, string? directory, string? filename,
|
||||
long? size, string api_type, string media_type, bool preview, bool downloaded, DateTime? created_at);
|
||||
|
||||
Task UpdateMedia(string folder, long media_id, string api_type, string directory, string filename, long size,
|
||||
bool downloaded, DateTime created_at);
|
||||
|
||||
Task<long> GetStoredFileSize(string folder, long media_id, string api_type);
|
||||
Task<bool> CheckDownloaded(string folder, long media_id, string api_type);
|
||||
Task<DateTime?> GetMostRecentPostDate(string folder);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,44 +7,128 @@ using OF_DL.Entities.Streams;
|
||||
using static OF_DL.Entities.Messages.Messages;
|
||||
using FromUser = OF_DL.Entities.Messages.FromUser;
|
||||
|
||||
namespace OF_DL.Services
|
||||
{
|
||||
namespace OF_DL.Services;
|
||||
|
||||
public interface IDownloadService
|
||||
{
|
||||
Task<long> CalculateTotalFileSize(List<string> urls);
|
||||
Task<bool> ProcessMediaDownload(string folder, long media_id, string api_type, string url, string path, string serverFileName, string resolvedFileName, string extension, IProgressReporter progressReporter);
|
||||
Task<bool> DownloadArchivedMedia(string url, string folder, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, Archived.List? messageInfo, Archived.Medium? messageMedia, Archived.Author? author, Dictionary<string, long> users);
|
||||
Task<bool> DownloadArchivedPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, Archived.List? postInfo, Archived.Medium? postMedia, Archived.Author? author, Dictionary<string, long> users);
|
||||
Task<bool> DownloadPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, IProgressReporter progressReporter, string filenameFormat, SinglePost postInfo, SinglePost.Medium postMedia, SinglePost.Author author, Dictionary<string, long> users);
|
||||
Task<bool> DownloadPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia, Post.Author? author, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> ProcessMediaDownload(string folder, long media_id, string api_type, string url, string path,
|
||||
string serverFileName, string resolvedFileName, string extension, IProgressReporter progressReporter);
|
||||
|
||||
Task<bool> DownloadArchivedMedia(string url, string folder, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, Archived.List? messageInfo,
|
||||
Archived.Medium? messageMedia, Archived.Author? author, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadArchivedPostDRMVideo(string policy, string signature, string kvp, string url,
|
||||
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, Archived.List? postInfo, Archived.Medium? postMedia,
|
||||
Archived.Author? author, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey,
|
||||
string folder, DateTime lastModified, long media_id, string api_type, IProgressReporter progressReporter,
|
||||
string filenameFormat, SinglePost postInfo, SinglePost.Medium postMedia, SinglePost.Author author,
|
||||
Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey,
|
||||
string folder, DateTime lastModified, long media_id, string api_type, IProgressReporter progressReporter,
|
||||
string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia, Post.Author? author,
|
||||
Dictionary<string, long> users);
|
||||
|
||||
Task DownloadAvatarHeader(string? avatarUrl, string? headerUrl, string folder, string username);
|
||||
Task<bool> DownloadMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, Messages.List? messageInfo, Messages.Medium? messageMedia, Messages.FromUser? fromUser, Dictionary<string, long> users);
|
||||
Task<bool> DownloadMessageMedia(string url, string folder, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, Messages.List? messageInfo, Messages.Medium? messageMedia, Messages.FromUser? fromUser, Dictionary<string, long> users);
|
||||
Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia, Post.Author? author, Dictionary<string, long> users);
|
||||
Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, SinglePost? postInfo, SinglePost.Medium? postMedia, SinglePost.Author? author, Dictionary<string, long> users);
|
||||
Task<bool> DownloadPurchasedMedia(string url, string folder, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, Purchased.List? messageInfo, Medium? messageMedia, Purchased.FromUser? fromUser, Dictionary<string, long> users);
|
||||
Task<bool> DownloadSinglePurchasedMedia(string url, string folder, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary<string, long> users);
|
||||
Task<bool> DownloadPurchasedMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, Purchased.List? messageInfo, Medium? messageMedia, Purchased.FromUser? fromUser, Dictionary<string, long> users);
|
||||
Task<bool> DownloadSinglePurchasedMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia, Entities.Messages.FromUser? fromUser, Dictionary<string, long> users);
|
||||
Task<bool> DownloadPurchasedPostDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, Purchased.List? postInfo, Medium? postMedia, Purchased.FromUser? fromUser, Dictionary<string, long> users);
|
||||
Task<bool> DownloadPurchasedPostMedia(string url, string folder, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, Purchased.List? messageInfo, Medium? messageMedia, Purchased.FromUser? fromUser, Dictionary<string, long> users);
|
||||
Task<bool> DownloadStoryMedia(string url, string folder, long media_id, string api_type, IProgressReporter progressReporter);
|
||||
Task<bool> DownloadStreamMedia(string url, string folder, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, Streams.List? streamInfo, Streams.Medium? streamMedia, Streams.Author? author, Dictionary<string, long> users);
|
||||
Task<bool> DownloadStreamsDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type, IProgressReporter progressReporter, string? filenameFormat, Streams.List? streamInfo, Streams.Medium? streamMedia, Streams.Author? author, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadMessageDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey,
|
||||
string folder, DateTime lastModified, long media_id, string api_type, IProgressReporter progressReporter,
|
||||
string? filenameFormat, List? messageInfo, Medium? messageMedia, Messages.FromUser? fromUser,
|
||||
Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadMessageMedia(string url, string folder, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, List? messageInfo, Medium? messageMedia,
|
||||
Messages.FromUser? fromUser, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, Post.List? postInfo, Post.Medium? postMedia,
|
||||
Post.Author? author, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadPostMedia(string url, string folder, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, SinglePost? postInfo, SinglePost.Medium? postMedia,
|
||||
SinglePost.Author? author, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadPurchasedMedia(string url, string folder, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, Purchased.List? messageInfo, Medium? messageMedia,
|
||||
Purchased.FromUser? fromUser, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadSinglePurchasedMedia(string url, string folder, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia,
|
||||
FromUser? fromUser, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadPurchasedMessageDRMVideo(string policy, string signature, string kvp, string url,
|
||||
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, Purchased.List? messageInfo, Medium? messageMedia,
|
||||
Purchased.FromUser? fromUser, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadSinglePurchasedMessageDRMVideo(string policy, string signature, string kvp, string url,
|
||||
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia,
|
||||
FromUser? fromUser, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadPurchasedPostDRMVideo(string policy, string signature, string kvp, string url,
|
||||
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, Purchased.List? postInfo, Medium? postMedia,
|
||||
Purchased.FromUser? fromUser, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadPurchasedPostMedia(string url, string folder, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, Purchased.List? messageInfo, Medium? messageMedia,
|
||||
Purchased.FromUser? fromUser, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadStoryMedia(string url, string folder, long media_id, string api_type,
|
||||
IProgressReporter progressReporter);
|
||||
|
||||
Task<bool> DownloadStreamMedia(string url, string folder, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, Streams.List? streamInfo,
|
||||
Streams.Medium? streamMedia, Streams.Author? author, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadStreamsDRMVideo(string policy, string signature, string kvp, string url, string decryptionKey,
|
||||
string folder, DateTime lastModified, long media_id, string api_type, IProgressReporter progressReporter,
|
||||
string? filenameFormat, Streams.List? streamInfo, Streams.Medium? streamMedia, Streams.Author? author,
|
||||
Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadSingleMessagePreviewDRMVideo(string policy, string signature, string kvp, string url,
|
||||
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia,
|
||||
FromUser? fromUser, Dictionary<string, long> users);
|
||||
|
||||
Task<bool> DownloadMessagePreviewMedia(string url, string folder, long media_id, string api_type,
|
||||
IProgressReporter progressReporter, string? filenameFormat, SingleMessage? messageInfo, Medium? messageMedia,
|
||||
FromUser? fromUser, Dictionary<string, long> users);
|
||||
Task<DownloadResult> DownloadHighlights(string username, long userId, string path, HashSet<long> paidPostIds, IProgressReporter progressReporter);
|
||||
Task<DownloadResult> DownloadStories(string username, long userId, string path, HashSet<long> paidPostIds, IProgressReporter progressReporter);
|
||||
Task<DownloadResult> DownloadArchived(string username, long userId, string path, Dictionary<string, long> users, bool clientIdBlobMissing, bool devicePrivateKeyMissing, ArchivedCollection archived, IProgressReporter progressReporter);
|
||||
Task<DownloadResult> DownloadMessages(string username, long userId, string path, Dictionary<string, long> users, bool clientIdBlobMissing, bool devicePrivateKeyMissing, MessageCollection messages, IProgressReporter progressReporter);
|
||||
Task<DownloadResult> DownloadPaidMessages(string username, string path, Dictionary<string, long> users, bool clientIdBlobMissing, bool devicePrivateKeyMissing, PaidMessageCollection paidMessageCollection, IProgressReporter progressReporter);
|
||||
Task<DownloadResult> DownloadStreams(string username, long userId, string path, Dictionary<string, long> users, bool clientIdBlobMissing, bool devicePrivateKeyMissing, StreamsCollection streams, IProgressReporter progressReporter);
|
||||
Task<DownloadResult> DownloadFreePosts(string username, long userId, string path, Dictionary<string, long> users, bool clientIdBlobMissing, bool devicePrivateKeyMissing, PostCollection posts, IProgressReporter progressReporter);
|
||||
Task<DownloadResult> DownloadPaidPosts(string username, long userId, string path, Dictionary<string, long> users, bool clientIdBlobMissing, bool devicePrivateKeyMissing, PaidPostCollection purchasedPosts, IProgressReporter progressReporter);
|
||||
}
|
||||
|
||||
Task<DownloadResult> DownloadHighlights(string username, long userId, string path, HashSet<long> paidPostIds,
|
||||
IProgressReporter progressReporter);
|
||||
|
||||
Task<DownloadResult> DownloadStories(string username, long userId, string path, HashSet<long> paidPostIds,
|
||||
IProgressReporter progressReporter);
|
||||
|
||||
Task<DownloadResult> DownloadArchived(string username, long userId, string path, Dictionary<string, long> users,
|
||||
bool clientIdBlobMissing, bool devicePrivateKeyMissing, ArchivedCollection archived,
|
||||
IProgressReporter progressReporter);
|
||||
|
||||
Task<DownloadResult> DownloadMessages(string username, long userId, string path, Dictionary<string, long> users,
|
||||
bool clientIdBlobMissing, bool devicePrivateKeyMissing, MessageCollection messages,
|
||||
IProgressReporter progressReporter);
|
||||
|
||||
Task<DownloadResult> DownloadPaidMessages(string username, string path, Dictionary<string, long> users,
|
||||
bool clientIdBlobMissing, bool devicePrivateKeyMissing, PaidMessageCollection paidMessageCollection,
|
||||
IProgressReporter progressReporter);
|
||||
|
||||
Task<DownloadResult> DownloadStreams(string username, long userId, string path, Dictionary<string, long> users,
|
||||
bool clientIdBlobMissing, bool devicePrivateKeyMissing, StreamsCollection streams,
|
||||
IProgressReporter progressReporter);
|
||||
|
||||
Task<DownloadResult> DownloadFreePosts(string username, long userId, string path, Dictionary<string, long> users,
|
||||
bool clientIdBlobMissing, bool devicePrivateKeyMissing, PostCollection posts,
|
||||
IProgressReporter progressReporter);
|
||||
|
||||
Task<DownloadResult> DownloadPaidPosts(string username, long userId, string path, Dictionary<string, long> users,
|
||||
bool clientIdBlobMissing, bool devicePrivateKeyMissing, PaidPostCollection purchasedPosts,
|
||||
IProgressReporter progressReporter);
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
namespace OF_DL.Services
|
||||
{
|
||||
namespace OF_DL.Services;
|
||||
|
||||
public interface IFileNameService
|
||||
{
|
||||
Task<string> BuildFilename(string fileFormat, Dictionary<string, string> values);
|
||||
Task<Dictionary<string, string>> GetFilename(object obj1, object obj2, object obj3, List<string> selectedProperties, string username, Dictionary<string, long> users = null);
|
||||
}
|
||||
|
||||
Task<Dictionary<string, string>> GetFilename(object obj1, object obj2, object obj3, List<string> selectedProperties,
|
||||
string username, Dictionary<string, long> users = null);
|
||||
}
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
using OF_DL.Enumerations;
|
||||
using Serilog.Core;
|
||||
|
||||
namespace OF_DL.Services
|
||||
{
|
||||
namespace OF_DL.Services;
|
||||
|
||||
public interface ILoggingService
|
||||
{
|
||||
LoggingLevelSwitch LevelSwitch { get; }
|
||||
void UpdateLoggingLevel(LoggingLevel newLevel);
|
||||
LoggingLevel GetCurrentLoggingLevel();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,18 +3,26 @@ using Serilog;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace OF_DL.Services
|
||||
{
|
||||
namespace OF_DL.Services;
|
||||
|
||||
public class LoggingService : ILoggingService
|
||||
{
|
||||
public LoggingLevelSwitch LevelSwitch { get; }
|
||||
|
||||
public LoggingService()
|
||||
{
|
||||
LevelSwitch = new LoggingLevelSwitch();
|
||||
InitializeLogger();
|
||||
}
|
||||
|
||||
public LoggingLevelSwitch LevelSwitch { get; }
|
||||
|
||||
public void UpdateLoggingLevel(LoggingLevel newLevel)
|
||||
{
|
||||
LevelSwitch.MinimumLevel = (LogEventLevel)newLevel;
|
||||
Log.Debug("Logging level updated to: {LoggingLevel}", newLevel);
|
||||
}
|
||||
|
||||
public LoggingLevel GetCurrentLoggingLevel() => (LoggingLevel)LevelSwitch.MinimumLevel;
|
||||
|
||||
private void InitializeLogger()
|
||||
{
|
||||
// Set the initial level to Error (until we've read from config)
|
||||
@ -27,16 +35,4 @@ namespace OF_DL.Services
|
||||
|
||||
Log.Debug("Logging service initialized");
|
||||
}
|
||||
|
||||
public void UpdateLoggingLevel(LoggingLevel newLevel)
|
||||
{
|
||||
LevelSwitch.MinimumLevel = (LogEventLevel)newLevel;
|
||||
Log.Debug("Logging level updated to: {LoggingLevel}", newLevel);
|
||||
}
|
||||
|
||||
public LoggingLevel GetCurrentLoggingLevel()
|
||||
{
|
||||
return (LoggingLevel)LevelSwitch.MinimumLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,22 +1,23 @@
|
||||
using OF_DL.Helpers;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using OF_DL.Helpers;
|
||||
|
||||
namespace OF_DL.Utils
|
||||
namespace OF_DL.Utils;
|
||||
|
||||
internal class HttpUtil
|
||||
{
|
||||
class HttpUtil
|
||||
public static HttpClient Client { get; set; } = new(new HttpClientHandler
|
||||
{
|
||||
public static HttpClient Client { get; set; } = new HttpClient(new HttpClientHandler
|
||||
{
|
||||
AllowAutoRedirect = true,
|
||||
AllowAutoRedirect = true
|
||||
//Proxy = null
|
||||
});
|
||||
|
||||
public static async Task<byte[]> PostData(string URL, Dictionary<string, string> headers, string postData)
|
||||
{
|
||||
var mediaType = postData.StartsWith("{") ? "application/json" : "application/x-www-form-urlencoded";
|
||||
var response = await PerformOperation(async () =>
|
||||
string mediaType = postData.StartsWith("{") ? "application/json" : "application/x-www-form-urlencoded";
|
||||
HttpResponseMessage response = await PerformOperation(async () =>
|
||||
{
|
||||
StringContent content = new StringContent(postData, Encoding.UTF8, mediaType);
|
||||
StringContent content = new(postData, Encoding.UTF8, mediaType);
|
||||
//ByteArrayContent content = new ByteArrayContent(postData);
|
||||
|
||||
return await Post(URL, headers, content);
|
||||
@ -28,9 +29,9 @@ namespace OF_DL.Utils
|
||||
|
||||
public static async Task<byte[]> PostData(string URL, Dictionary<string, string> headers, byte[] postData)
|
||||
{
|
||||
var response = await PerformOperation(async () =>
|
||||
HttpResponseMessage response = await PerformOperation(async () =>
|
||||
{
|
||||
ByteArrayContent content = new ByteArrayContent(postData);
|
||||
ByteArrayContent content = new(postData);
|
||||
|
||||
return await Post(URL, headers, content);
|
||||
});
|
||||
@ -39,11 +40,12 @@ namespace OF_DL.Utils
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static async Task<byte[]> PostData(string URL, Dictionary<string, string> headers, Dictionary<string, string> postData)
|
||||
public static async Task<byte[]> PostData(string URL, Dictionary<string, string> headers,
|
||||
Dictionary<string, string> postData)
|
||||
{
|
||||
var response = await PerformOperation(async () =>
|
||||
HttpResponseMessage response = await PerformOperation(async () =>
|
||||
{
|
||||
FormUrlEncodedContent content = new FormUrlEncodedContent(postData);
|
||||
FormUrlEncodedContent content = new(postData);
|
||||
|
||||
return await Post(URL, headers, content);
|
||||
});
|
||||
@ -54,10 +56,7 @@ namespace OF_DL.Utils
|
||||
|
||||
public static async Task<string> GetWebSource(string URL, Dictionary<string, string> headers = null)
|
||||
{
|
||||
var response = await PerformOperation(async () =>
|
||||
{
|
||||
return await Get(URL, headers);
|
||||
});
|
||||
HttpResponseMessage response = await PerformOperation(async () => { return await Get(URL, headers); });
|
||||
|
||||
byte[] bytes = await response.Content.ReadAsByteArrayAsync();
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
@ -65,71 +64,68 @@ namespace OF_DL.Utils
|
||||
|
||||
public static async Task<byte[]> GetBinary(string URL, Dictionary<string, string> headers = null)
|
||||
{
|
||||
var response = await PerformOperation(async () =>
|
||||
{
|
||||
return await Get(URL, headers);
|
||||
});
|
||||
HttpResponseMessage response = await PerformOperation(async () => { return await Get(URL, headers); });
|
||||
|
||||
byte[] bytes = await response.Content.ReadAsByteArrayAsync();
|
||||
return bytes;
|
||||
}
|
||||
public static string GetString(byte[] bytes)
|
||||
{
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
|
||||
public static string GetString(byte[] bytes) => Encoding.UTF8.GetString(bytes);
|
||||
|
||||
private static async Task<HttpResponseMessage> Get(string URL, Dictionary<string, string> headers = null)
|
||||
{
|
||||
HttpRequestMessage request = new HttpRequestMessage()
|
||||
{
|
||||
RequestUri = new Uri(URL),
|
||||
Method = HttpMethod.Get
|
||||
};
|
||||
HttpRequestMessage request = new() { RequestUri = new Uri(URL), Method = HttpMethod.Get };
|
||||
|
||||
if (headers != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, string> header in headers)
|
||||
{
|
||||
request.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return await Send(request);
|
||||
}
|
||||
|
||||
private static async Task<HttpResponseMessage> Post(string URL, Dictionary<string, string> headers, HttpContent content)
|
||||
private static async Task<HttpResponseMessage> Post(string URL, Dictionary<string, string> headers,
|
||||
HttpContent content)
|
||||
{
|
||||
HttpRequestMessage request = new HttpRequestMessage()
|
||||
{
|
||||
RequestUri = new Uri(URL),
|
||||
Method = HttpMethod.Post,
|
||||
Content = content
|
||||
};
|
||||
HttpRequestMessage request = new() { RequestUri = new Uri(URL), Method = HttpMethod.Post, Content = content };
|
||||
|
||||
if (headers != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, string> header in headers)
|
||||
{
|
||||
request.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return await Send(request);
|
||||
}
|
||||
|
||||
private static async Task<HttpResponseMessage> Send(HttpRequestMessage request)
|
||||
{
|
||||
return await Client.SendAsync(request);
|
||||
}
|
||||
private static async Task<HttpResponseMessage> Send(HttpRequestMessage request) => await Client.SendAsync(request);
|
||||
|
||||
private static async Task<HttpResponseMessage> PerformOperation(Func<Task<HttpResponseMessage>> operation)
|
||||
{
|
||||
var response = await operation();
|
||||
HttpResponseMessage response = await operation();
|
||||
|
||||
var retryCount = 0;
|
||||
int retryCount = 0;
|
||||
|
||||
while (retryCount < Constants.WIDEVINE_MAX_RETRIES && response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
|
||||
while (retryCount < Constants.WIDEVINE_MAX_RETRIES && response.StatusCode == HttpStatusCode.TooManyRequests)
|
||||
{
|
||||
//
|
||||
// We've hit a rate limit, so we should wait before retrying.
|
||||
//
|
||||
var retryAfterSeconds = Constants.WIDEVINE_RETRY_DELAY * (retryCount + 1); // Default retry time. Increases with each retry.
|
||||
int retryAfterSeconds =
|
||||
Constants.WIDEVINE_RETRY_DELAY * (retryCount + 1); // Default retry time. Increases with each retry.
|
||||
if (response.Headers.RetryAfter != null && response.Headers.RetryAfter.Delta.HasValue)
|
||||
{
|
||||
if (response.Headers.RetryAfter.Delta.Value.TotalSeconds > 0)
|
||||
retryAfterSeconds = (int)response.Headers.RetryAfter.Delta.Value.TotalSeconds + 1; // Add 1 second to ensure we wait a bit longer than the suggested time
|
||||
{
|
||||
retryAfterSeconds =
|
||||
(int)response.Headers.RetryAfter.Delta.Value.TotalSeconds +
|
||||
1; // Add 1 second to ensure we wait a bit longer than the suggested time
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(retryAfterSeconds * 1000); // Peform the delay
|
||||
@ -143,4 +139,3 @@ namespace OF_DL.Utils
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,15 +2,14 @@ using System.Reactive.Concurrency;
|
||||
|
||||
namespace OF_DL.Utils;
|
||||
|
||||
|
||||
public class ThrottledStream : Stream
|
||||
|
||||
{
|
||||
private readonly Stream parent;
|
||||
private readonly int maxBytesPerSecond;
|
||||
private readonly Stream parent;
|
||||
private readonly IScheduler scheduler;
|
||||
private readonly IStopwatch stopwatch;
|
||||
private readonly bool shouldThrottle;
|
||||
private readonly IStopwatch stopwatch;
|
||||
|
||||
private long processed;
|
||||
|
||||
@ -30,16 +29,39 @@ public class ThrottledStream : Stream
|
||||
}
|
||||
|
||||
|
||||
public override bool CanRead => parent.CanRead;
|
||||
|
||||
|
||||
public override bool CanSeek => parent.CanSeek;
|
||||
|
||||
|
||||
public override bool CanWrite => parent.CanWrite;
|
||||
|
||||
|
||||
public override long Length => parent.Length;
|
||||
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => parent.Position;
|
||||
set => parent.Position = value;
|
||||
}
|
||||
|
||||
|
||||
protected void Throttle(int bytes)
|
||||
{
|
||||
if (!shouldThrottle) return;
|
||||
if (!shouldThrottle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
processed += bytes;
|
||||
var targetTime = TimeSpan.FromSeconds((double)processed / maxBytesPerSecond);
|
||||
var actualTime = stopwatch.Elapsed;
|
||||
var sleep = targetTime - actualTime;
|
||||
TimeSpan targetTime = TimeSpan.FromSeconds((double)processed / maxBytesPerSecond);
|
||||
TimeSpan actualTime = stopwatch.Elapsed;
|
||||
TimeSpan sleep = targetTime - actualTime;
|
||||
if (sleep > TimeSpan.Zero)
|
||||
{
|
||||
using var waitHandle = new AutoResetEvent(initialState: false);
|
||||
using AutoResetEvent waitHandle = new(false);
|
||||
scheduler.Sleep(sleep).GetAwaiter().OnCompleted(() => waitHandle.Set());
|
||||
waitHandle.WaitOne();
|
||||
}
|
||||
@ -47,11 +69,15 @@ public class ThrottledStream : Stream
|
||||
|
||||
protected async Task ThrottleAsync(int bytes)
|
||||
{
|
||||
if (!shouldThrottle) return;
|
||||
if (!shouldThrottle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
processed += bytes;
|
||||
var targetTime = TimeSpan.FromSeconds((double)processed / maxBytesPerSecond);
|
||||
var actualTime = stopwatch.Elapsed;
|
||||
var sleep = targetTime - actualTime;
|
||||
TimeSpan targetTime = TimeSpan.FromSeconds((double)processed / maxBytesPerSecond);
|
||||
TimeSpan actualTime = stopwatch.Elapsed;
|
||||
TimeSpan sleep = targetTime - actualTime;
|
||||
|
||||
if (sleep > TimeSpan.Zero)
|
||||
{
|
||||
@ -61,7 +87,7 @@ public class ThrottledStream : Stream
|
||||
|
||||
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
var read = await parent.ReadAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false);
|
||||
int read = await parent.ReadAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false);
|
||||
await ThrottleAsync(read).ConfigureAwait(false);
|
||||
return read;
|
||||
}
|
||||
@ -79,71 +105,26 @@ public class ThrottledStream : Stream
|
||||
await parent.WriteAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await ThrottleAsync(buffer.Length).ConfigureAwait(false);
|
||||
await parent.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return parent.CanRead; }
|
||||
}
|
||||
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return parent.CanSeek; }
|
||||
}
|
||||
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return parent.CanWrite; }
|
||||
}
|
||||
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
parent.Flush();
|
||||
}
|
||||
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { return parent.Length; }
|
||||
}
|
||||
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return parent.Position;
|
||||
}
|
||||
set
|
||||
{
|
||||
parent.Position = value;
|
||||
}
|
||||
}
|
||||
public override void Flush() => parent.Flush();
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var read = parent.Read(buffer, offset, count);
|
||||
int read = parent.Read(buffer, offset, count);
|
||||
Throttle(read);
|
||||
return read;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
return parent.Seek(offset, origin);
|
||||
}
|
||||
public override long Seek(long offset, SeekOrigin origin) => parent.Seek(offset, origin);
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
parent.SetLength(value);
|
||||
}
|
||||
public override void SetLength(long value) => parent.SetLength(value);
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace OF_DL.Utils
|
||||
{
|
||||
namespace OF_DL.Utils;
|
||||
|
||||
internal static class XmlUtils
|
||||
{
|
||||
// When true, return original text without parsing/stripping.
|
||||
@ -16,13 +16,13 @@ namespace OF_DL.Utils
|
||||
|
||||
try
|
||||
{
|
||||
var parsedText = XElement.Parse($"<root>{xmlValue}</root>");
|
||||
XElement parsedText = XElement.Parse($"<root>{xmlValue}</root>");
|
||||
return parsedText.Value;
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,54 +4,55 @@ using System.Text;
|
||||
using OF_DL.Crypto;
|
||||
using ProtoBuf;
|
||||
|
||||
namespace OF_DL.Widevine
|
||||
{
|
||||
namespace OF_DL.Widevine;
|
||||
|
||||
public class CDM
|
||||
{
|
||||
static Dictionary<string, CDMDevice> Devices { get; } = new Dictionary<string, CDMDevice>()
|
||||
private static Dictionary<string, CDMDevice> Devices { get; } = new()
|
||||
{
|
||||
[Constants.DEVICE_NAME] = new CDMDevice(Constants.DEVICE_NAME, null, null, null)
|
||||
[Constants.DEVICE_NAME] = new CDMDevice(Constants.DEVICE_NAME)
|
||||
};
|
||||
static Dictionary<string, Session> Sessions { get; set; } = new Dictionary<string, Session>();
|
||||
|
||||
static byte[] CheckPSSH(string psshB64)
|
||||
private static Dictionary<string, Session> Sessions { get; } = new();
|
||||
|
||||
private static byte[] CheckPSSH(string psshB64)
|
||||
{
|
||||
byte[] systemID = new byte[] { 237, 239, 139, 169, 121, 214, 74, 206, 163, 200, 39, 220, 213, 29, 33, 237 };
|
||||
|
||||
if (psshB64.Length % 4 != 0)
|
||||
{
|
||||
psshB64 = psshB64.PadRight(psshB64.Length + (4 - (psshB64.Length % 4)), '=');
|
||||
psshB64 = psshB64.PadRight(psshB64.Length + (4 - psshB64.Length % 4), '=');
|
||||
}
|
||||
|
||||
byte[] pssh = Convert.FromBase64String(psshB64);
|
||||
|
||||
if (pssh.Length < 30)
|
||||
{
|
||||
return pssh;
|
||||
}
|
||||
|
||||
if (!pssh[12..28].SequenceEqual(systemID))
|
||||
{
|
||||
List<byte> newPssh = new List<byte>() { 0, 0, 0 };
|
||||
List<byte> newPssh = new() { 0, 0, 0 };
|
||||
newPssh.Add((byte)(32 + pssh.Length));
|
||||
newPssh.AddRange(Encoding.UTF8.GetBytes("pssh"));
|
||||
newPssh.AddRange(new byte[] { 0, 0, 0, 0 });
|
||||
newPssh.AddRange(systemID);
|
||||
newPssh.AddRange(new byte[] { 0, 0, 0, 0 });
|
||||
newPssh[31] = (byte)(pssh.Length);
|
||||
newPssh[31] = (byte)pssh.Length;
|
||||
newPssh.AddRange(pssh);
|
||||
|
||||
return newPssh.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
return pssh;
|
||||
}
|
||||
}
|
||||
|
||||
public static string OpenSession(string initDataB64, string deviceName, bool offline = false, bool raw = false)
|
||||
{
|
||||
byte[] initData = CheckPSSH(initDataB64);
|
||||
|
||||
var device = Devices[deviceName];
|
||||
CDMDevice device = Devices[deviceName];
|
||||
|
||||
byte[] sessionId = new byte[16];
|
||||
|
||||
@ -59,10 +60,12 @@ namespace OF_DL.Widevine
|
||||
{
|
||||
string randHex = "";
|
||||
|
||||
Random rand = new Random();
|
||||
Random rand = new();
|
||||
string choice = "ABCDEF0123456789";
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
randHex += choice[rand.Next(16)];
|
||||
}
|
||||
|
||||
string counter = "01";
|
||||
string rest = "00000000000000";
|
||||
@ -70,7 +73,7 @@ namespace OF_DL.Widevine
|
||||
}
|
||||
else
|
||||
{
|
||||
Random rand = new Random();
|
||||
Random rand = new();
|
||||
rand.NextBytes(sessionId);
|
||||
}
|
||||
|
||||
@ -95,7 +98,7 @@ namespace OF_DL.Widevine
|
||||
return BytesToHex(sessionId);
|
||||
}
|
||||
|
||||
static WidevineCencHeader ParseInitData(byte[] initData)
|
||||
private static WidevineCencHeader ParseInitData(byte[] initData)
|
||||
{
|
||||
WidevineCencHeader cencHeader;
|
||||
|
||||
@ -133,12 +136,10 @@ namespace OF_DL.Widevine
|
||||
//Logger.Verbose("CDM session closed");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
//Logger.Info($"Session {sessionId} not found");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SetServiceCertificate(string sessionId, byte[] certData)
|
||||
{
|
||||
@ -151,7 +152,7 @@ namespace OF_DL.Widevine
|
||||
return false;
|
||||
}
|
||||
|
||||
SignedMessage signedMessage = new SignedMessage();
|
||||
SignedMessage signedMessage = new();
|
||||
|
||||
try
|
||||
{
|
||||
@ -168,7 +169,8 @@ namespace OF_DL.Widevine
|
||||
try
|
||||
{
|
||||
//Logger.Debug("Service cert provided as signedmessage");
|
||||
serviceCertificate = Serializer.Deserialize<SignedDeviceCertificate>(new MemoryStream(signedMessage.Msg));
|
||||
serviceCertificate =
|
||||
Serializer.Deserialize<SignedDeviceCertificate>(new MemoryStream(signedMessage.Msg));
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -199,7 +201,7 @@ namespace OF_DL.Widevine
|
||||
return null;
|
||||
}
|
||||
|
||||
var session = Sessions[sessionId];
|
||||
Session session = Sessions[sessionId];
|
||||
|
||||
//Logger.Debug("Building license request");
|
||||
|
||||
@ -254,34 +256,35 @@ namespace OF_DL.Widevine
|
||||
{
|
||||
//Logger.Debug("Privacy mode & serivce certificate loaded, encrypting client id");
|
||||
|
||||
EncryptedClientIdentification encryptedClientIdProto = new EncryptedClientIdentification();
|
||||
EncryptedClientIdentification encryptedClientIdProto = new();
|
||||
|
||||
//Logger.Debug("Unencrypted client id " + Utils.SerializeToString(clientId));
|
||||
|
||||
using var memoryStream = new MemoryStream();
|
||||
using MemoryStream memoryStream = new();
|
||||
Serializer.Serialize(memoryStream, session.Device.ClientID);
|
||||
byte[] data = Padding.AddPKCS7Padding(memoryStream.ToArray(), 16);
|
||||
|
||||
using AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider
|
||||
using AesCryptoServiceProvider aesProvider = new()
|
||||
{
|
||||
BlockSize = 128,
|
||||
Padding = PaddingMode.PKCS7,
|
||||
Mode = CipherMode.CBC
|
||||
BlockSize = 128, Padding = PaddingMode.PKCS7, Mode = CipherMode.CBC
|
||||
};
|
||||
aesProvider.GenerateKey();
|
||||
aesProvider.GenerateIV();
|
||||
|
||||
using MemoryStream mstream = new MemoryStream();
|
||||
using CryptoStream cryptoStream = new CryptoStream(mstream, aesProvider.CreateEncryptor(aesProvider.Key, aesProvider.IV), CryptoStreamMode.Write);
|
||||
using MemoryStream mstream = new();
|
||||
using CryptoStream cryptoStream = new(mstream, aesProvider.CreateEncryptor(aesProvider.Key, aesProvider.IV),
|
||||
CryptoStreamMode.Write);
|
||||
cryptoStream.Write(data, 0, data.Length);
|
||||
encryptedClientIdProto.EncryptedClientId = mstream.ToArray();
|
||||
|
||||
using RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
|
||||
using RSACryptoServiceProvider RSA = new();
|
||||
RSA.ImportRSAPublicKey(session.ServiceCertificate.DeviceCertificate.PublicKey, out int bytesRead);
|
||||
encryptedClientIdProto.EncryptedPrivacyKey = RSA.Encrypt(aesProvider.Key, RSAEncryptionPadding.OaepSHA1);
|
||||
encryptedClientIdProto.EncryptedClientIdIv = aesProvider.IV;
|
||||
encryptedClientIdProto.ServiceId = Encoding.UTF8.GetString(session.ServiceCertificate.DeviceCertificate.ServiceId);
|
||||
encryptedClientIdProto.ServiceCertificateSerialNumber = session.ServiceCertificate.DeviceCertificate.SerialNumber;
|
||||
encryptedClientIdProto.ServiceId =
|
||||
Encoding.UTF8.GetString(session.ServiceCertificate.DeviceCertificate.ServiceId);
|
||||
encryptedClientIdProto.ServiceCertificateSerialNumber =
|
||||
session.ServiceCertificate.DeviceCertificate.SerialNumber;
|
||||
|
||||
licenseRequest.Msg.EncryptedClientId = encryptedClientIdProto;
|
||||
}
|
||||
@ -292,7 +295,7 @@ namespace OF_DL.Widevine
|
||||
|
||||
//Logger.Debug("Signing license request");
|
||||
|
||||
using (var memoryStream = new MemoryStream())
|
||||
using (MemoryStream memoryStream = new())
|
||||
{
|
||||
Serializer.Serialize(memoryStream, licenseRequest.Msg);
|
||||
byte[] data = memoryStream.ToArray();
|
||||
@ -304,7 +307,7 @@ namespace OF_DL.Widevine
|
||||
//Logger.Verbose("License request created");
|
||||
|
||||
byte[] requestBytes;
|
||||
using (var memoryStream = new MemoryStream())
|
||||
using (MemoryStream memoryStream = new())
|
||||
{
|
||||
Serializer.Serialize(memoryStream, licenseRequest);
|
||||
requestBytes = memoryStream.ToArray();
|
||||
@ -326,7 +329,7 @@ namespace OF_DL.Widevine
|
||||
throw new Exception("Session ID doesn't exist");
|
||||
}
|
||||
|
||||
var session = Sessions[sessionId];
|
||||
Session session = Sessions[sessionId];
|
||||
|
||||
if (session.LicenseRequest == null)
|
||||
{
|
||||
@ -351,7 +354,7 @@ namespace OF_DL.Widevine
|
||||
|
||||
try
|
||||
{
|
||||
var sessionKey = session.Device.Decrypt(session.License.SessionKey);
|
||||
byte[] sessionKey = session.Device.Decrypt(session.License.SessionKey);
|
||||
|
||||
if (sessionKey.Length != 16)
|
||||
{
|
||||
@ -372,11 +375,12 @@ namespace OF_DL.Widevine
|
||||
//Logger.Debug("Verifying license signature");
|
||||
|
||||
byte[] licenseBytes;
|
||||
using (var memoryStream = new MemoryStream())
|
||||
using (MemoryStream memoryStream = new())
|
||||
{
|
||||
Serializer.Serialize(memoryStream, signedLicense.Msg);
|
||||
licenseBytes = memoryStream.ToArray();
|
||||
}
|
||||
|
||||
byte[] hmacHash = CryptoUtils.GetHMACSHA256Digest(licenseBytes, session.DerivedKeys.Auth1);
|
||||
|
||||
if (!hmacHash.SequenceEqual(signedLicense.Signature))
|
||||
@ -389,7 +393,9 @@ namespace OF_DL.Widevine
|
||||
string type = key.Type.ToString();
|
||||
|
||||
if (type == "Signing")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
byte[] keyId;
|
||||
byte[] encryptedKey = key.Key;
|
||||
@ -402,17 +408,14 @@ namespace OF_DL.Widevine
|
||||
|
||||
byte[] decryptedKey;
|
||||
|
||||
using MemoryStream mstream = new MemoryStream();
|
||||
using AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider
|
||||
{
|
||||
Mode = CipherMode.CBC,
|
||||
Padding = PaddingMode.PKCS7
|
||||
};
|
||||
using CryptoStream cryptoStream = new CryptoStream(mstream, aesProvider.CreateDecryptor(session.DerivedKeys.Enc, iv), CryptoStreamMode.Write);
|
||||
using MemoryStream mstream = new();
|
||||
using AesCryptoServiceProvider aesProvider = new() { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 };
|
||||
using CryptoStream cryptoStream = new(mstream, aesProvider.CreateDecryptor(session.DerivedKeys.Enc, iv),
|
||||
CryptoStreamMode.Write);
|
||||
cryptoStream.Write(encryptedKey, 0, encryptedKey.Length);
|
||||
decryptedKey = mstream.ToArray();
|
||||
|
||||
List<string> permissions = new List<string>();
|
||||
List<string> permissions = new();
|
||||
if (type == "OperatorSession")
|
||||
{
|
||||
foreach (PropertyInfo perm in key._OperatorSessionKeyPermissions.GetType().GetProperties())
|
||||
@ -423,12 +426,10 @@ namespace OF_DL.Widevine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
session.ContentKeys.Add(new ContentKey
|
||||
{
|
||||
KeyID = keyId,
|
||||
Type = type,
|
||||
Bytes = decryptedKey,
|
||||
Permissions = permissions
|
||||
KeyID = keyId, Type = type, Bytes = decryptedKey, Permissions = permissions
|
||||
});
|
||||
}
|
||||
|
||||
@ -441,8 +442,10 @@ namespace OF_DL.Widevine
|
||||
|
||||
public static DerivedKeys DeriveKeys(byte[] message, byte[] key)
|
||||
{
|
||||
byte[] encKeyBase = Encoding.UTF8.GetBytes("ENCRYPTION").Concat(new byte[] { 0x0, }).Concat(message).Concat(new byte[] { 0x0, 0x0, 0x0, 0x80 }).ToArray();
|
||||
byte[] authKeyBase = Encoding.UTF8.GetBytes("AUTHENTICATION").Concat(new byte[] { 0x0, }).Concat(message).Concat(new byte[] { 0x0, 0x0, 0x2, 0x0 }).ToArray();
|
||||
byte[] encKeyBase = Encoding.UTF8.GetBytes("ENCRYPTION").Concat(new byte[] { 0x0 }).Concat(message)
|
||||
.Concat(new byte[] { 0x0, 0x0, 0x0, 0x80 }).ToArray();
|
||||
byte[] authKeyBase = Encoding.UTF8.GetBytes("AUTHENTICATION").Concat(new byte[] { 0x0 }).Concat(message)
|
||||
.Concat(new byte[] { 0x0, 0x0, 0x2, 0x0 }).ToArray();
|
||||
|
||||
byte[] encKey = new byte[] { 0x01 }.Concat(encKeyBase).ToArray();
|
||||
byte[] authKey1 = new byte[] { 0x01 }.Concat(authKeyBase).ToArray();
|
||||
@ -459,32 +462,21 @@ namespace OF_DL.Widevine
|
||||
byte[] authCmacCombined1 = authCmacKey1.Concat(authCmacKey2).ToArray();
|
||||
byte[] authCmacCombined2 = authCmacKey3.Concat(authCmacKey4).ToArray();
|
||||
|
||||
return new DerivedKeys
|
||||
{
|
||||
Auth1 = authCmacCombined1,
|
||||
Auth2 = authCmacCombined2,
|
||||
Enc = encCmacKey
|
||||
};
|
||||
return new DerivedKeys { Auth1 = authCmacCombined1, Auth2 = authCmacCombined2, Enc = encCmacKey };
|
||||
}
|
||||
|
||||
public static List<ContentKey> GetKeys(string sessionId)
|
||||
{
|
||||
if (Sessions.ContainsKey(sessionId))
|
||||
return Sessions[sessionId].ContentKeys;
|
||||
else
|
||||
{
|
||||
return Sessions[sessionId].ContentKeys;
|
||||
}
|
||||
|
||||
throw new Exception("Session not found");
|
||||
}
|
||||
}
|
||||
|
||||
private static string BytesToHex(byte[] data)
|
||||
{
|
||||
return BitConverter.ToString(data).Replace("-", "");
|
||||
private static string BytesToHex(byte[] data) => BitConverter.ToString(data).Replace("-", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
public static List<string> ProvideLicense(string requestB64, string licenseB64)
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
namespace OF_DL.Widevine
|
||||
{
|
||||
namespace OF_DL.Widevine;
|
||||
|
||||
public class CDMApi
|
||||
{
|
||||
string SessionId { get; set; }
|
||||
private string SessionId { get; set; }
|
||||
|
||||
public byte[] GetChallenge(string initDataB64, string certDataB64, bool offline = false, bool raw = false)
|
||||
{
|
||||
@ -17,9 +17,5 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<ContentKey> GetKeys()
|
||||
{
|
||||
return CDM.GetKeys(SessionId);
|
||||
}
|
||||
}
|
||||
public List<ContentKey> GetKeys() => CDM.GetKeys(SessionId);
|
||||
}
|
||||
|
||||
@ -7,17 +7,12 @@ using Org.BouncyCastle.Crypto.Signers;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using ProtoBuf;
|
||||
|
||||
namespace OF_DL.Widevine
|
||||
{
|
||||
namespace OF_DL.Widevine;
|
||||
|
||||
public class CDMDevice
|
||||
{
|
||||
public string DeviceName { get; set; }
|
||||
public ClientIdentification ClientID { get; set; }
|
||||
AsymmetricCipherKeyPair DeviceKeys { get; set; }
|
||||
|
||||
public virtual bool IsAndroid { get; set; } = true;
|
||||
|
||||
public CDMDevice(string deviceName, byte[] clientIdBlobBytes = null, byte[] privateKeyBytes = null, byte[] vmpBytes = null)
|
||||
public CDMDevice(string deviceName, byte[] clientIdBlobBytes = null, byte[] privateKeyBytes = null,
|
||||
byte[] vmpBytes = null)
|
||||
{
|
||||
DeviceName = deviceName;
|
||||
|
||||
@ -29,7 +24,9 @@ namespace OF_DL.Widevine
|
||||
string clientIDPath = Path.Join(Constants.DEVICES_FOLDER, deviceName, "device_client_id_blob");
|
||||
|
||||
if (!File.Exists(clientIDPath))
|
||||
{
|
||||
throw new Exception("No client id blob found");
|
||||
}
|
||||
|
||||
clientIdBlobBytes = File.ReadAllBytes(clientIDPath);
|
||||
}
|
||||
@ -38,36 +35,42 @@ namespace OF_DL.Widevine
|
||||
|
||||
if (privateKeyBytes != null)
|
||||
{
|
||||
using var reader = new StringReader(Encoding.UTF8.GetString(privateKeyBytes));
|
||||
using StringReader reader = new(Encoding.UTF8.GetString(privateKeyBytes));
|
||||
DeviceKeys = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
|
||||
}
|
||||
else if (File.Exists(privateKeyPath))
|
||||
{
|
||||
using var reader = File.OpenText(privateKeyPath);
|
||||
using StreamReader reader = File.OpenText(privateKeyPath);
|
||||
DeviceKeys = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
|
||||
}
|
||||
|
||||
if (vmpBytes != null)
|
||||
{
|
||||
var vmp = Serializer.Deserialize<FileHashes>(new MemoryStream(vmpBytes));
|
||||
FileHashes? vmp = Serializer.Deserialize<FileHashes>(new MemoryStream(vmpBytes));
|
||||
ClientID.FileHashes = vmp;
|
||||
}
|
||||
else if (File.Exists(vmpPath))
|
||||
{
|
||||
var vmp = Serializer.Deserialize<FileHashes>(new MemoryStream(File.ReadAllBytes(vmpPath)));
|
||||
FileHashes? vmp = Serializer.Deserialize<FileHashes>(new MemoryStream(File.ReadAllBytes(vmpPath)));
|
||||
ClientID.FileHashes = vmp;
|
||||
}
|
||||
}
|
||||
|
||||
public string DeviceName { get; set; }
|
||||
public ClientIdentification ClientID { get; set; }
|
||||
private AsymmetricCipherKeyPair DeviceKeys { get; }
|
||||
|
||||
public virtual bool IsAndroid { get; set; } = true;
|
||||
|
||||
public virtual byte[] Decrypt(byte[] data)
|
||||
{
|
||||
OaepEncoding eng = new OaepEncoding(new RsaEngine());
|
||||
OaepEncoding eng = new(new RsaEngine());
|
||||
eng.Init(false, DeviceKeys.Private);
|
||||
|
||||
int length = data.Length;
|
||||
int blockSize = eng.GetInputBlockSize();
|
||||
|
||||
List<byte> plainText = new List<byte>();
|
||||
List<byte> plainText = new();
|
||||
|
||||
for (int chunkPosition = 0; chunkPosition < length; chunkPosition += blockSize)
|
||||
{
|
||||
@ -80,11 +83,10 @@ namespace OF_DL.Widevine
|
||||
|
||||
public virtual byte[] Sign(byte[] data)
|
||||
{
|
||||
PssSigner eng = new PssSigner(new RsaEngine(), new Sha1Digest());
|
||||
PssSigner eng = new(new RsaEngine(), new Sha1Digest());
|
||||
|
||||
eng.Init(true, DeviceKeys.Private);
|
||||
eng.BlockUpdate(data, 0, data.Length);
|
||||
return eng.GenerateSignature();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
namespace OF_DL.Widevine
|
||||
{
|
||||
namespace OF_DL.Widevine;
|
||||
|
||||
public class Constants
|
||||
{
|
||||
public static string WORKING_FOLDER { get; set; } = System.IO.Path.GetFullPath(System.IO.Path.Join(System.IO.Directory.GetCurrentDirectory(), "cdm"));
|
||||
public static string DEVICES_FOLDER { get; set; } = System.IO.Path.GetFullPath(System.IO.Path.Join(WORKING_FOLDER, "devices"));
|
||||
public static string WORKING_FOLDER { get; set; } =
|
||||
Path.GetFullPath(Path.Join(Directory.GetCurrentDirectory(), "cdm"));
|
||||
|
||||
public static string DEVICES_FOLDER { get; set; } = Path.GetFullPath(Path.Join(WORKING_FOLDER, "devices"));
|
||||
public static string DEVICE_NAME { get; set; } = "chrome_1610";
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,39 +1,27 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace OF_DL.Widevine
|
||||
{
|
||||
namespace OF_DL.Widevine;
|
||||
|
||||
[Serializable]
|
||||
public class ContentKey
|
||||
{
|
||||
[JsonPropertyName("key_id")]
|
||||
public byte[] KeyID { get; set; }
|
||||
[JsonPropertyName("key_id")] public byte[] KeyID { get; set; }
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonPropertyName("type")] public string Type { get; set; }
|
||||
|
||||
[JsonPropertyName("bytes")]
|
||||
public byte[] Bytes { get; set; }
|
||||
[JsonPropertyName("bytes")] public byte[] Bytes { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
[JsonPropertyName("permissions")]
|
||||
public List<string> Permissions {
|
||||
get
|
||||
public List<string> Permissions
|
||||
{
|
||||
return PermissionsString.Split(",").ToList();
|
||||
}
|
||||
set
|
||||
{
|
||||
PermissionsString = string.Join(",", value);
|
||||
}
|
||||
get => PermissionsString.Split(",").ToList();
|
||||
set => PermissionsString = string.Join(",", value);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string PermissionsString { get; set; }
|
||||
[JsonIgnore] public string PermissionsString { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{BitConverter.ToString(KeyID).Replace("-", "").ToLower()}:{BitConverter.ToString(Bytes).Replace("-", "").ToLower()}";
|
||||
}
|
||||
}
|
||||
public override string ToString() =>
|
||||
$"{BitConverter.ToString(KeyID).Replace("-", "").ToLower()}:{BitConverter.ToString(Bytes).Replace("-", "").ToLower()}";
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
namespace OF_DL.Widevine
|
||||
{
|
||||
namespace OF_DL.Widevine;
|
||||
|
||||
public class DerivedKeys
|
||||
{
|
||||
public byte[] Auth1 { get; set; }
|
||||
public byte[] Auth2 { get; set; }
|
||||
public byte[] Enc { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,38 +1,43 @@
|
||||
namespace OF_DL.Widevine
|
||||
{
|
||||
class PSSHBox
|
||||
{
|
||||
static readonly byte[] PSSH_HEADER = new byte[] { 0x70, 0x73, 0x73, 0x68 };
|
||||
namespace OF_DL.Widevine;
|
||||
|
||||
public List<byte[]> KIDs { get; set; } = new List<byte[]>();
|
||||
public byte[] Data { get; set; }
|
||||
internal class PSSHBox
|
||||
{
|
||||
private static readonly byte[] PSSH_HEADER = new byte[] { 0x70, 0x73, 0x73, 0x68 };
|
||||
|
||||
PSSHBox(List<byte[]> kids, byte[] data)
|
||||
private PSSHBox(List<byte[]> kids, byte[] data)
|
||||
{
|
||||
KIDs = kids;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public List<byte[]> KIDs { get; set; } = new();
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
public static PSSHBox FromByteArray(byte[] psshbox)
|
||||
{
|
||||
using var stream = new System.IO.MemoryStream(psshbox);
|
||||
using MemoryStream stream = new(psshbox);
|
||||
|
||||
stream.Seek(4, System.IO.SeekOrigin.Current);
|
||||
stream.Seek(4, SeekOrigin.Current);
|
||||
byte[] header = new byte[4];
|
||||
stream.Read(header, 0, 4);
|
||||
|
||||
if (!header.SequenceEqual(PSSH_HEADER))
|
||||
{
|
||||
throw new Exception("Not a pssh box");
|
||||
}
|
||||
|
||||
stream.Seek(20, System.IO.SeekOrigin.Current);
|
||||
stream.Seek(20, SeekOrigin.Current);
|
||||
byte[] kidCountBytes = new byte[4];
|
||||
stream.Read(kidCountBytes, 0, 4);
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(kidCountBytes);
|
||||
}
|
||||
|
||||
uint kidCount = BitConverter.ToUInt32(kidCountBytes);
|
||||
|
||||
List<byte[]> kids = new List<byte[]>();
|
||||
List<byte[]> kids = new();
|
||||
for (int i = 0; i < kidCount; i++)
|
||||
{
|
||||
byte[] kid = new byte[16];
|
||||
@ -44,11 +49,16 @@
|
||||
stream.Read(dataLengthBytes);
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(dataLengthBytes);
|
||||
}
|
||||
|
||||
uint dataLength = BitConverter.ToUInt32(dataLengthBytes);
|
||||
|
||||
if (dataLength == 0)
|
||||
{
|
||||
return new PSSHBox(kids, null);
|
||||
}
|
||||
|
||||
byte[] data = new byte[dataLength];
|
||||
stream.Read(data);
|
||||
@ -56,4 +66,3 @@
|
||||
return new PSSHBox(kids, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,15 @@
|
||||
namespace OF_DL.Widevine
|
||||
namespace OF_DL.Widevine;
|
||||
|
||||
internal class Session
|
||||
{
|
||||
class Session
|
||||
public Session(byte[] sessionId, dynamic initData, CDMDevice device, bool offline)
|
||||
{
|
||||
SessionId = sessionId;
|
||||
InitData = initData;
|
||||
Offline = offline;
|
||||
Device = device;
|
||||
}
|
||||
|
||||
public byte[] SessionId { get; set; }
|
||||
public dynamic InitData { get; set; }
|
||||
public bool Offline { get; set; }
|
||||
@ -12,14 +20,5 @@
|
||||
public SignedLicense License { get; set; }
|
||||
public SignedDeviceCertificate ServiceCertificate { get; set; }
|
||||
public bool PrivacyMode { get; set; }
|
||||
public List<ContentKey> ContentKeys { get; set; } = new List<ContentKey>();
|
||||
|
||||
public Session(byte[] sessionId, dynamic initData, CDMDevice device, bool offline)
|
||||
{
|
||||
SessionId = sessionId;
|
||||
InitData = initData;
|
||||
Offline = offline;
|
||||
Device = device;
|
||||
}
|
||||
}
|
||||
public List<ContentKey> ContentKeys { get; set; } = new();
|
||||
}
|
||||
|
||||
@ -4,5 +4,38 @@
|
||||
"prefix": "30586",
|
||||
"suffix": "67000213",
|
||||
"checksum_constant": 521,
|
||||
"checksum_indexes": [ 0, 2, 3, 7, 7, 8, 8, 10, 11, 13, 14, 16, 17, 17, 17, 19, 19, 20, 21, 21, 23, 23, 24, 24, 27, 27, 29, 30, 31, 34, 35, 39 ]
|
||||
"checksum_indexes": [
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
7,
|
||||
7,
|
||||
8,
|
||||
8,
|
||||
10,
|
||||
11,
|
||||
13,
|
||||
14,
|
||||
16,
|
||||
17,
|
||||
17,
|
||||
17,
|
||||
19,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
21,
|
||||
23,
|
||||
23,
|
||||
24,
|
||||
24,
|
||||
27,
|
||||
27,
|
||||
29,
|
||||
30,
|
||||
31,
|
||||
34,
|
||||
35,
|
||||
39
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user