From 40ccf7aa625bcf6020223e6ffd2904313ac059fa Mon Sep 17 00:00:00 2001 From: whimsical-c4lic0 Date: Mon, 9 Feb 2026 00:10:09 -0600 Subject: [PATCH] Refactor Streams entities into DTOs and application entities with standardized naming conventions and default values --- OF DL/Models/Dtos/Archived/ArchivedDto.cs | 1 + .../Dtos/{Archived => Common}/CountersDto.cs | 2 +- OF DL/Models/Dtos/Streams/InfoDto.cs | 11 + OF DL/Models/Dtos/Streams/ListItemDto.cs | 101 +++++++++ OF DL/Models/Dtos/Streams/MediumDto.cs | 37 ++++ OF DL/Models/Dtos/Streams/StreamsDto.cs | 17 ++ OF DL/Models/Entities/Streams/ListItem.cs | 41 ++++ OF DL/Models/Entities/Streams/Medium.cs | 14 ++ OF DL/Models/Entities/Streams/Streams.cs | 10 + .../Entities/Streams/StreamsCollection.cs | 8 + OF DL/Models/Mappers/StreamsMapper.cs | 108 +++++++++ OF DL/Models/Streams/Streams.cs | 209 ------------------ OF DL/Models/Streams/StreamsCollection.cs | 8 - OF DL/Program.cs | 4 +- OF DL/Services/APIService.cs | 114 +++++----- OF DL/Services/DownloadService.cs | 42 ++-- OF DL/Services/IAPIService.cs | 24 +- OF DL/Services/IDownloadService.cs | 12 +- 18 files changed, 457 insertions(+), 306 deletions(-) rename OF DL/Models/Dtos/{Archived => Common}/CountersDto.cs (93%) create mode 100644 OF DL/Models/Dtos/Streams/InfoDto.cs create mode 100644 OF DL/Models/Dtos/Streams/ListItemDto.cs create mode 100644 OF DL/Models/Dtos/Streams/MediumDto.cs create mode 100644 OF DL/Models/Dtos/Streams/StreamsDto.cs create mode 100644 OF DL/Models/Entities/Streams/ListItem.cs create mode 100644 OF DL/Models/Entities/Streams/Medium.cs create mode 100644 OF DL/Models/Entities/Streams/Streams.cs create mode 100644 OF DL/Models/Entities/Streams/StreamsCollection.cs create mode 100644 OF DL/Models/Mappers/StreamsMapper.cs delete mode 100644 OF DL/Models/Streams/Streams.cs delete mode 100644 OF DL/Models/Streams/StreamsCollection.cs diff --git a/OF DL/Models/Dtos/Archived/ArchivedDto.cs b/OF DL/Models/Dtos/Archived/ArchivedDto.cs index 80d373a..b9968d1 100644 --- a/OF DL/Models/Dtos/Archived/ArchivedDto.cs +++ b/OF DL/Models/Dtos/Archived/ArchivedDto.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using OF_DL.Models.Dtos.Common; namespace OF_DL.Models.Dtos.Archived; diff --git a/OF DL/Models/Dtos/Archived/CountersDto.cs b/OF DL/Models/Dtos/Common/CountersDto.cs similarity index 93% rename from OF DL/Models/Dtos/Archived/CountersDto.cs rename to OF DL/Models/Dtos/Common/CountersDto.cs index 32a515e..fe3c3b4 100644 --- a/OF DL/Models/Dtos/Archived/CountersDto.cs +++ b/OF DL/Models/Dtos/Common/CountersDto.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace OF_DL.Models.Dtos.Archived; +namespace OF_DL.Models.Dtos.Common; public class CountersDto { diff --git a/OF DL/Models/Dtos/Streams/InfoDto.cs b/OF DL/Models/Dtos/Streams/InfoDto.cs new file mode 100644 index 0000000..bd1e9b1 --- /dev/null +++ b/OF DL/Models/Dtos/Streams/InfoDto.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json; +using OF_DL.Models.Dtos.Common; + +namespace OF_DL.Models.Dtos.Streams; + +public class InfoDto +{ + [JsonProperty("source")] public SourceDto Source { get; set; } = new(); + + [JsonProperty("preview")] public PreviewDto Preview { get; set; } = new(); +} diff --git a/OF DL/Models/Dtos/Streams/ListItemDto.cs b/OF DL/Models/Dtos/Streams/ListItemDto.cs new file mode 100644 index 0000000..151ece4 --- /dev/null +++ b/OF DL/Models/Dtos/Streams/ListItemDto.cs @@ -0,0 +1,101 @@ +using Newtonsoft.Json; +using OF_DL.Models.Dtos.Common; +using OF_DL.Utils; + +namespace OF_DL.Models.Dtos.Streams; + +public class ListItemDto +{ + private string _rawText = ""; + + [JsonProperty("responseType")] public string ResponseType { get; set; } = ""; + + [JsonProperty("id")] public long Id { get; set; } + + [JsonProperty("postedAt")] public DateTime PostedAt { get; set; } + + [JsonProperty("postedAtPrecise")] public string PostedAtPrecise { get; set; } = ""; + + [JsonProperty("expiredAt")] public object ExpiredAt { get; set; } = new(); + + [JsonProperty("author")] public AuthorDto Author { get; set; } = new(); + + [JsonProperty("text")] public string Text { get; set; } = ""; + + [JsonProperty("rawText")] + public string RawText + { + get + { + if (string.IsNullOrEmpty(_rawText)) + { + _rawText = XmlUtils.EvaluateInnerText(Text); + } + + return _rawText; + } + set => _rawText = value; + } + + [JsonProperty("lockedText")] public bool? LockedText { get; set; } + + [JsonProperty("isFavorite")] public bool? IsFavorite { get; set; } + + [JsonProperty("canReport")] public bool? CanReport { get; set; } + + [JsonProperty("canDelete")] public bool? CanDelete { get; set; } + + [JsonProperty("canComment")] public bool? CanComment { get; set; } + + [JsonProperty("canEdit")] public bool? CanEdit { get; set; } + + [JsonProperty("isPinned")] public bool? IsPinned { get; set; } + + [JsonProperty("favoritesCount")] public int? FavoritesCount { get; set; } + + [JsonProperty("mediaCount")] public int? MediaCount { get; set; } + + [JsonProperty("isMediaReady")] public bool? IsMediaReady { get; set; } + + [JsonProperty("voting")] public object Voting { get; set; } = new(); + + [JsonProperty("isOpened")] public bool? IsOpened { get; set; } + + [JsonProperty("canToggleFavorite")] public bool? CanToggleFavorite { get; set; } + + [JsonProperty("streamId")] public int? StreamId { get; set; } + + [JsonProperty("price")] public string? Price { get; set; } + + [JsonProperty("hasVoting")] public bool? HasVoting { get; set; } + + [JsonProperty("isAddedToBookmarks")] public bool? IsAddedToBookmarks { get; set; } + + [JsonProperty("isArchived")] public bool? IsArchived { get; set; } + + [JsonProperty("isPrivateArchived")] public bool? IsPrivateArchived { get; set; } + + [JsonProperty("isDeleted")] public bool? IsDeleted { get; set; } + + [JsonProperty("hasUrl")] public bool? HasUrl { get; set; } + + [JsonProperty("isCouplePeopleMedia")] public bool? IsCouplePeopleMedia { get; set; } + + [JsonProperty("cantCommentReason")] public string CantCommentReason { get; set; } = ""; + + [JsonProperty("commentsCount")] public int? CommentsCount { get; set; } + + [JsonProperty("mentionedUsers")] public List MentionedUsers { get; set; } = []; + + [JsonProperty("linkedUsers")] public List LinkedUsers { get; set; } = []; + + [JsonProperty("tipsAmount")] public string TipsAmount { get; set; } = ""; + + [JsonProperty("tipsAmountRaw")] public string TipsAmountRaw { get; set; } = ""; + + [JsonProperty("media")] public List Media { get; set; } = []; + + [JsonProperty("canViewMedia")] public bool? CanViewMedia { get; set; } + + [JsonProperty("preview")] public List Preview { get; set; } = []; +} diff --git a/OF DL/Models/Dtos/Streams/MediumDto.cs b/OF DL/Models/Dtos/Streams/MediumDto.cs new file mode 100644 index 0000000..61040f1 --- /dev/null +++ b/OF DL/Models/Dtos/Streams/MediumDto.cs @@ -0,0 +1,37 @@ +using Newtonsoft.Json; +using OF_DL.Models.Dtos.Common; + +namespace OF_DL.Models.Dtos.Streams; + +public class MediumDto +{ + [JsonProperty("id")] public long Id { get; set; } + + [JsonProperty("type")] public string Type { get; set; } = ""; + + [JsonProperty("convertedToVideo")] public bool ConvertedToVideo { get; set; } + + [JsonProperty("canView")] public bool CanView { get; set; } + + [JsonProperty("hasError")] public bool HasError { get; set; } + + [JsonProperty("createdAt")] public DateTime? CreatedAt { get; set; } + + [JsonProperty("info")] public InfoDto Info { get; set; } = new(); + + [JsonProperty("source")] public SourceDto Source { get; set; } = new(); + + [JsonProperty("squarePreview")] public string SquarePreview { get; set; } = ""; + + [JsonProperty("full")] public string Full { get; set; } = ""; + + [JsonProperty("preview")] public string Preview { get; set; } = ""; + + [JsonProperty("thumb")] public string Thumb { get; set; } = ""; + + [JsonProperty("hasCustomPreview")] public bool HasCustomPreview { get; set; } + + [JsonProperty("files")] public FilesDto Files { get; set; } = new(); + + [JsonProperty("videoSources")] public VideoSourcesDto VideoSources { get; set; } = new(); +} diff --git a/OF DL/Models/Dtos/Streams/StreamsDto.cs b/OF DL/Models/Dtos/Streams/StreamsDto.cs new file mode 100644 index 0000000..480a494 --- /dev/null +++ b/OF DL/Models/Dtos/Streams/StreamsDto.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using OF_DL.Models.Dtos.Common; + +namespace OF_DL.Models.Dtos.Streams; + +public class StreamsDto +{ + [JsonProperty("list")] public List List { get; set; } = []; + + [JsonProperty("hasMore")] public bool HasMore { get; set; } + + [JsonProperty("headMarker")] public string HeadMarker { get; set; } = ""; + + [JsonProperty("tailMarker")] public string TailMarker { get; set; } = ""; + + [JsonProperty("counters")] public CountersDto Counters { get; set; } = new(); +} diff --git a/OF DL/Models/Entities/Streams/ListItem.cs b/OF DL/Models/Entities/Streams/ListItem.cs new file mode 100644 index 0000000..05bd254 --- /dev/null +++ b/OF DL/Models/Entities/Streams/ListItem.cs @@ -0,0 +1,41 @@ +using OF_DL.Models.Entities.Common; +using OF_DL.Utils; + +namespace OF_DL.Models.Entities.Streams; + +public class ListItem +{ + private string _rawText = ""; + + public long Id { get; set; } + + public DateTime PostedAt { get; set; } + + public Author? Author { get; set; } + + public string Text { get; set; } = ""; + + public string RawText + { + get + { + if (string.IsNullOrEmpty(_rawText)) + { + _rawText = XmlUtils.EvaluateInnerText(Text); + } + + return _rawText; + } + set => _rawText = value; + } + + public bool IsOpened { get; set; } + + public string? Price { get; set; } + + public bool IsArchived { get; set; } + + public List? Media { get; set; } + + public List? Preview { get; set; } +} diff --git a/OF DL/Models/Entities/Streams/Medium.cs b/OF DL/Models/Entities/Streams/Medium.cs new file mode 100644 index 0000000..0dbb768 --- /dev/null +++ b/OF DL/Models/Entities/Streams/Medium.cs @@ -0,0 +1,14 @@ +using OF_DL.Models.Entities.Common; + +namespace OF_DL.Models.Entities.Streams; + +public class Medium +{ + public long Id { get; set; } + + public string? Type { get; set; } + + public bool CanView { get; set; } + + public Files? Files { get; set; } +} diff --git a/OF DL/Models/Entities/Streams/Streams.cs b/OF DL/Models/Entities/Streams/Streams.cs new file mode 100644 index 0000000..55646fb --- /dev/null +++ b/OF DL/Models/Entities/Streams/Streams.cs @@ -0,0 +1,10 @@ +namespace OF_DL.Models.Entities.Streams; + +public class Streams +{ + public List List { get; set; } = []; + + public bool HasMore { get; set; } + + public string? TailMarker { get; set; } +} diff --git a/OF DL/Models/Entities/Streams/StreamsCollection.cs b/OF DL/Models/Entities/Streams/StreamsCollection.cs new file mode 100644 index 0000000..3355d01 --- /dev/null +++ b/OF DL/Models/Entities/Streams/StreamsCollection.cs @@ -0,0 +1,8 @@ +namespace OF_DL.Models.Entities.Streams; + +public class StreamsCollection +{ + public List StreamMedia { get; set; } = []; + public List StreamObjects { get; set; } = []; + public Dictionary Streams { get; set; } = new(); +} diff --git a/OF DL/Models/Mappers/StreamsMapper.cs b/OF DL/Models/Mappers/StreamsMapper.cs new file mode 100644 index 0000000..3b3e30d --- /dev/null +++ b/OF DL/Models/Mappers/StreamsMapper.cs @@ -0,0 +1,108 @@ +using OF_DL.Models.Dtos.Common; +using OF_DL.Models.Dtos.Streams; +using OF_DL.Models.Entities.Common; +using OF_DL.Models.Entities.Streams; + +namespace OF_DL.Models.Mappers; + +public static class StreamsMapper +{ + public static Streams FromDto(StreamsDto? dto) + { + Streams mapped = new() { HasMore = dto?.HasMore ?? false, TailMarker = dto?.TailMarker }; + + if (dto?.List == null) + { + return mapped; + } + + foreach (ListItemDto entry in dto.List) + { + mapped.List.Add(MapList(entry)); + } + + return mapped; + } + + private static ListItem MapList(ListItemDto dto) => + new() + { + Id = dto.Id, + PostedAt = dto.PostedAt, + Author = MapAuthor(dto.Author), + Text = dto.Text, + RawText = dto.RawText, + Price = dto.Price, + IsOpened = dto.IsOpened ?? false, + IsArchived = dto.IsArchived ?? false, + Media = MapMedia(dto.Media), + Preview = dto.Preview + }; + + private static Author? MapAuthor(AuthorDto? dto) => + dto == null ? null : new Author { Id = dto.Id }; + + private static List? MapMedia(List? media) => + media?.Select(MapMedium).ToList(); + + private static Medium MapMedium(MediumDto dto) => + new() { Id = dto.Id, Type = dto.Type, CanView = dto.CanView, Files = MapFiles(dto.Files) }; + + private static Files? MapFiles(FilesDto? dto) + { + if (dto == null) + { + return null; + } + + Full? full = MapFull(dto.Full); + Drm? drm = MapDrm(dto.Drm); + + if (full == null && drm == null) + { + return null; + } + + return new Files { Full = full, Drm = drm }; + } + + private static Full? MapFull(FullDto? dto) => + dto == null || string.IsNullOrEmpty(dto.Url) ? null : new Full { Url = dto.Url }; + + private static Drm? MapDrm(DrmDto? dto) + { + if (dto?.Manifest == null || string.IsNullOrEmpty(dto.Manifest.Dash)) + { + return null; + } + + Dash? dash = MapDash(dto.Signature.Dash); + if (dash == null) + { + return null; + } + + return new Drm + { + Manifest = new Manifest { Dash = dto.Manifest.Dash }, Signature = new Signature { Dash = dash } + }; + } + + private static Dash? MapDash(DashDto? dto) + { + if (dto == null || + string.IsNullOrEmpty(dto.CloudFrontPolicy) || + string.IsNullOrEmpty(dto.CloudFrontSignature) || + string.IsNullOrEmpty(dto.CloudFrontKeyPairId)) + { + return null; + } + + return new Dash + { + CloudFrontPolicy = dto.CloudFrontPolicy, + CloudFrontSignature = dto.CloudFrontSignature, + CloudFrontKeyPairId = dto.CloudFrontKeyPairId + }; + } +} diff --git a/OF DL/Models/Streams/Streams.cs b/OF DL/Models/Streams/Streams.cs deleted file mode 100644 index de993ef..0000000 --- a/OF DL/Models/Streams/Streams.cs +++ /dev/null @@ -1,209 +0,0 @@ -using Newtonsoft.Json; -using OF_DL.Utils; - -namespace OF_DL.Models.Streams; - -public class Streams -{ - public List list { get; set; } - public bool hasMore { get; set; } - public string headMarker { get; set; } - public string tailMarker { get; set; } - public Counters counters { get; set; } - - public class Author - { - public long id { get; set; } - public string _view { get; set; } - } - - public class Counters - { - public int audiosCount { get; set; } - public int photosCount { get; set; } - public int videosCount { get; set; } - public int mediasCount { get; set; } - public int postsCount { get; set; } - public int streamsCount { get; set; } - public int archivedPostsCount { get; set; } - } - - public class Files - { - public Full full { get; set; } - public Thumb thumb { get; set; } - public Preview preview { get; set; } - public SquarePreview squarePreview { get; set; } - public Drm drm { get; set; } - } - - public class Full - { - public string url { get; set; } - public int width { get; set; } - public int height { get; set; } - public long size { get; set; } - public List sources { get; set; } - } - - public class SquarePreview - { - public string url { get; set; } - public int width { get; set; } - public int height { get; set; } - public long size { get; set; } - } - - public class Thumb - { - public string url { get; set; } - public int width { get; set; } - public int height { get; set; } - public long size { get; set; } - } - - public class Info - { - public Source source { get; set; } - public Preview preview { get; set; } - } - - public class List - { - private string _rawText; - public string responseType { get; set; } - public long id { get; set; } - public DateTime postedAt { get; set; } - public string postedAtPrecise { get; set; } - public object expiredAt { get; set; } - public Author author { get; set; } - public string text { get; set; } - - public string rawText - { - get - { - if (string.IsNullOrEmpty(_rawText)) - { - _rawText = XmlUtils.EvaluateInnerText(text); - } - - return _rawText; - } - set => _rawText = value; - } - - public bool lockedText { get; set; } - public bool isFavorite { get; set; } - public bool canReport { get; set; } - public bool canDelete { get; set; } - public bool canComment { get; set; } - public bool canEdit { get; set; } - public bool isPinned { get; set; } - public int favoritesCount { get; set; } - public int mediaCount { get; set; } - public bool isMediaReady { get; set; } - public object voting { get; set; } - public bool isOpened { get; set; } - public bool canToggleFavorite { get; set; } - public int streamId { get; set; } - public string price { get; set; } - public bool hasVoting { get; set; } - public bool isAddedToBookmarks { get; set; } - public bool isArchived { get; set; } - public bool isPrivateArchived { get; set; } - public bool isDeleted { get; set; } - public bool hasUrl { get; set; } - public bool isCouplePeopleMedia { get; set; } - public string cantCommentReason { get; set; } - public int commentsCount { get; set; } - public List mentionedUsers { get; set; } - public List linkedUsers { get; set; } - public string tipsAmount { get; set; } - public string tipsAmountRaw { get; set; } - public List media { get; set; } - public bool canViewMedia { get; set; } - public List preview { get; set; } - } - - public class Medium - { - public long id { get; set; } - public string type { get; set; } - public bool convertedToVideo { get; set; } - public bool canView { get; set; } - public bool hasError { get; set; } - public DateTime? createdAt { get; set; } - public Info info { get; set; } - public Source source { get; set; } - public string squarePreview { get; set; } - public string full { get; set; } - public string preview { get; set; } - public string thumb { get; set; } - public bool hasCustomPreview { get; set; } - public Files files { get; set; } - public VideoSources videoSources { get; set; } - } - - public class Preview - { - public int width { get; set; } - public int height { get; set; } - public long size { get; set; } - public string url { get; set; } - } - - public class Source - { - public string source { get; set; } - public int width { get; set; } - public int height { get; set; } - public long size { get; set; } - public int duration { get; set; } - } - - public class VideoSources - { - [JsonProperty("720")] public object _720 { 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-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-Signature")] public string CloudFrontSignature { get; set; } - - [JsonProperty("CloudFront-Key-Pair-Id")] - public string CloudFrontKeyPairId { get; set; } - } -} diff --git a/OF DL/Models/Streams/StreamsCollection.cs b/OF DL/Models/Streams/StreamsCollection.cs deleted file mode 100644 index 126c112..0000000 --- a/OF DL/Models/Streams/StreamsCollection.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace OF_DL.Models.Streams; - -public class StreamsCollection -{ - public List StreamMedia = new(); - public List StreamObjects = new(); - public Dictionary Streams = new(); -} diff --git a/OF DL/Program.cs b/OF DL/Program.cs index 7cb09cc..629037f 100644 --- a/OF DL/Program.cs +++ b/OF DL/Program.cs @@ -7,13 +7,13 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OF_DL.CLI; using OF_DL.Models; -using OF_DL.Models.Streams; using OF_DL.Enumerations; using OF_DL.Helpers; using ArchivedEntities = OF_DL.Models.Entities.Archived; using MessageEntities = OF_DL.Models.Entities.Messages; using PostEntities = OF_DL.Models.Entities.Posts; using PurchasedEntities = OF_DL.Models.Entities.Purchased; +using StreamEntities = OF_DL.Models.Entities.Streams; using OF_DL.Services; using Serilog; using Spectre.Console; @@ -1764,7 +1764,7 @@ public class Program(IServiceProvider serviceProvider) IAPIService apiService = serviceProvider.GetRequiredService(); IDownloadService downloadService = serviceProvider.GetRequiredService(); - StreamsCollection streams = new(); + StreamEntities.StreamsCollection streams = new(); await AnsiConsole.Status() .StartAsync("[red]Getting Streams[/]", diff --git a/OF DL/Services/APIService.cs b/OF DL/Services/APIService.cs index 93983f5..f2e20c0 100644 --- a/OF DL/Services/APIService.cs +++ b/OF DL/Services/APIService.cs @@ -6,7 +6,6 @@ using System.Xml.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OF_DL.Models; -using OF_DL.Models.Streams; using OF_DL.Enumerations; using ArchivedDtos = OF_DL.Models.Dtos.Archived; using HighlightDtos = OF_DL.Models.Dtos.Highlights; @@ -15,13 +14,15 @@ using MessageDtos = OF_DL.Models.Dtos.Messages; using PostDtos = OF_DL.Models.Dtos.Posts; using PurchasedDtos = OF_DL.Models.Dtos.Purchased; using StoriesDtos = OF_DL.Models.Dtos.Stories; +using StreamsDtos = OF_DL.Models.Dtos.Streams; using ArchivedEntities = OF_DL.Models.Entities.Archived; using HighlightEntities = OF_DL.Models.Entities.Highlights; using ListEntities = OF_DL.Models.Entities.Lists; using MessageEntities = OF_DL.Models.Entities.Messages; using PostEntities = OF_DL.Models.Entities.Posts; using PurchasedEntities = OF_DL.Models.Entities.Purchased; -using StoriesEntities = OF_DL.Models.Entities.Stories; +using StoryEntities = OF_DL.Models.Entities.Stories; +using StreamEntities = OF_DL.Models.Entities.Streams; using OF_DL.Models.Mappers; using OF_DL.Widevine; using Serilog; @@ -396,9 +397,9 @@ public class APIService(IAuthService authService, IConfigService configService, List? storiesDto = JsonConvert.DeserializeObject>(body, m_JsonSerializerSettings); - List stories = StoriesMapper.FromDto(storiesDto); + List stories = StoriesMapper.FromDto(storiesDto); - foreach (StoriesEntities.Stories story in stories) + foreach (StoryEntities.Stories story in stories) { if (story.Media[0].CreatedAt.HasValue) { @@ -417,7 +418,7 @@ public class APIService(IAuthService authService, IConfigService configService, if (story.Media != null && story.Media.Count > 0) { - foreach (StoriesEntities.Medium medium in story.Media) + foreach (StoryEntities.Medium medium in story.Media) { await dbService.AddMedia(folder, medium.Id, story.Id, medium.Files.Full.Url, null, null, null, "Stories", @@ -1208,15 +1209,16 @@ public class APIService(IAuthService authService, IConfigService configService, return null; } - public async Task GetStreams(string endpoint, string folder, List paid_post_ids, + public async Task GetStreams(string endpoint, string folder, + List paid_post_ids, StatusContext ctx) { Log.Debug($"Calling GetStreams - {endpoint}"); try { - Streams streams = new(); - StreamsCollection streamsCollection = new(); + StreamEntities.Streams streams = new(); + StreamEntities.StreamsCollection streamsCollection = new(); int post_limit = 50; Dictionary getParams = new() { @@ -1239,29 +1241,33 @@ public class APIService(IAuthService authService, IConfigService configService, configService.CurrentConfig.CustomDate); string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient()); - streams = JsonConvert.DeserializeObject(body, m_JsonSerializerSettings); - ctx.Status($"[red]Getting Streams\n[/] [red]Found {streams.list.Count}[/]"); + StreamsDtos.StreamsDto? streamsDto = + JsonConvert.DeserializeObject(body, m_JsonSerializerSettings); + streams = StreamsMapper.FromDto(streamsDto); + ctx.Status($"[red]Getting Streams\n[/] [red]Found {streams.List.Count}[/]"); ctx.Spinner(Spinner.Known.Dots); ctx.SpinnerStyle(Style.Parse("blue")); - if (streams != null && streams.hasMore) + if (streams != null && streams.HasMore) { UpdateGetParamsForDateSelection( downloadDateSelection, ref getParams, - streams.tailMarker); + streams.TailMarker); while (true) { - Streams newstreams = new(); + StreamEntities.Streams newstreams = new(); string? loopbody = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient()); - newstreams = JsonConvert.DeserializeObject(loopbody, m_JsonSerializerSettings); + StreamsDtos.StreamsDto? newStreamsDto = + JsonConvert.DeserializeObject(loopbody, m_JsonSerializerSettings); + newstreams = StreamsMapper.FromDto(newStreamsDto); - streams.list.AddRange(newstreams.list); - ctx.Status($"[red]Getting Streams\n[/] [red]Found {streams.list.Count}[/]"); + streams.List.AddRange(newstreams.List); + ctx.Status($"[red]Getting Streams\n[/] [red]Found {streams.List.Count}[/]"); ctx.Spinner(Spinner.Known.Dots); ctx.SpinnerStyle(Style.Parse("blue")); - if (!newstreams.hasMore) + if (!newstreams.HasMore) { break; } @@ -1269,18 +1275,18 @@ public class APIService(IAuthService authService, IConfigService configService, UpdateGetParamsForDateSelection( downloadDateSelection, ref getParams, - newstreams.tailMarker); + newstreams.TailMarker); } } - foreach (Streams.List stream in streams.list) + foreach (StreamEntities.ListItem stream in streams.List) { List streamPreviewIds = new(); - if (stream.preview != null && stream.preview.Count > 0) + if (stream.Preview != null && stream.Preview.Count > 0) { - for (int i = 0; i < stream.preview.Count; i++) + for (int i = 0; i < stream.Preview.Count; i++) { - if (stream.preview[i] is long previewId) + if (stream.Preview[i] is long previewId) { if (!streamPreviewIds.Contains(previewId)) { @@ -1290,68 +1296,68 @@ public class APIService(IAuthService authService, IConfigService configService, } } - await dbService.AddPost(folder, stream.id, stream.text != null ? stream.text : string.Empty, - stream.price != null ? stream.price : "0", stream.price != null && stream.isOpened ? true : false, - stream.isArchived, stream.postedAt); + await dbService.AddPost(folder, stream.Id, stream.Text != null ? stream.Text : string.Empty, + stream.Price != null ? stream.Price : "0", stream.Price != null && stream.IsOpened ? true : false, + stream.IsArchived, stream.PostedAt); streamsCollection.StreamObjects.Add(stream); - if (stream.media != null && stream.media.Count > 0) + if (stream.Media != null && stream.Media.Count > 0) { - foreach (Streams.Medium medium in stream.media) + foreach (StreamEntities.Medium medium in stream.Media) { - if (medium.type == "photo" && !configService.CurrentConfig.DownloadImages) + if (medium.Type == "photo" && !configService.CurrentConfig.DownloadImages) { continue; } - if (medium.type == "video" && !configService.CurrentConfig.DownloadVideos) + if (medium.Type == "video" && !configService.CurrentConfig.DownloadVideos) { continue; } - if (medium.type == "gif" && !configService.CurrentConfig.DownloadVideos) + if (medium.Type == "gif" && !configService.CurrentConfig.DownloadVideos) { continue; } - if (medium.type == "audio" && !configService.CurrentConfig.DownloadAudios) + if (medium.Type == "audio" && !configService.CurrentConfig.DownloadAudios) { continue; } - if (medium.canView && medium.files?.drm == null) + if (medium.CanView && medium.Files?.Drm == null) { - bool has = paid_post_ids.Any(cus => cus.Equals(medium.id)); - if (!has && medium.canView && medium.files != null && medium.files.full != null && - !string.IsNullOrEmpty(medium.files.full.url)) + bool has = paid_post_ids.Any(cus => cus.Equals(medium.Id)); + if (!has && medium.CanView && medium.Files != null && medium.Files.Full != null && + !string.IsNullOrEmpty(medium.Files.Full.Url)) { - if (!streamsCollection.Streams.ContainsKey(medium.id)) + if (!streamsCollection.Streams.ContainsKey(medium.Id)) { - await dbService.AddMedia(folder, medium.id, stream.id, medium.files.full.url, null, + await dbService.AddMedia(folder, medium.Id, stream.Id, medium.Files.Full.Url, null, null, null, "Posts", - medium.type == "photo" ? "Images" : - medium.type == "video" || medium.type == "gif" ? "Videos" : - medium.type == "audio" ? "Audios" : null, - streamPreviewIds.Contains(medium.id) ? true : false, false, null); - streamsCollection.Streams.Add(medium.id, medium.files.full.url); + medium.Type == "photo" ? "Images" : + medium.Type == "video" || medium.Type == "gif" ? "Videos" : + medium.Type == "audio" ? "Audios" : null, + streamPreviewIds.Contains(medium.Id) ? true : false, false, null); + streamsCollection.Streams.Add(medium.Id, medium.Files.Full.Url); streamsCollection.StreamMedia.Add(medium); } } } - else if (medium.canView && medium.files != null && medium.files.drm != null) + else if (medium.CanView && medium.Files != null && medium.Files.Drm != null) { - bool has = paid_post_ids.Any(cus => cus.Equals(medium.id)); - if (!has && medium.files != null && medium.files.drm != null) + bool has = paid_post_ids.Any(cus => cus.Equals(medium.Id)); + if (!has && medium.Files != null && medium.Files.Drm != null) { - if (!streamsCollection.Streams.ContainsKey(medium.id)) + if (!streamsCollection.Streams.ContainsKey(medium.Id)) { - await dbService.AddMedia(folder, medium.id, stream.id, - medium.files.drm.manifest.dash, null, null, null, "Posts", - medium.type == "photo" ? "Images" : - medium.type == "video" || medium.type == "gif" ? "Videos" : - medium.type == "audio" ? "Audios" : null, - streamPreviewIds.Contains(medium.id) ? true : false, false, null); - streamsCollection.Streams.Add(medium.id, - $"{medium.files.drm.manifest.dash},{medium.files.drm.signature.dash.CloudFrontPolicy},{medium.files.drm.signature.dash.CloudFrontSignature},{medium.files.drm.signature.dash.CloudFrontKeyPairId},{medium.id},{stream.id}"); + await dbService.AddMedia(folder, medium.Id, stream.Id, + medium.Files.Drm.Manifest.Dash, null, null, null, "Posts", + medium.Type == "photo" ? "Images" : + medium.Type == "video" || medium.Type == "gif" ? "Videos" : + medium.Type == "audio" ? "Audios" : null, + streamPreviewIds.Contains(medium.Id) ? true : false, false, null); + streamsCollection.Streams.Add(medium.Id, + $"{medium.Files.Drm.Manifest.Dash},{medium.Files.Drm.Signature.Dash.CloudFrontPolicy},{medium.Files.Drm.Signature.Dash.CloudFrontSignature},{medium.Files.Drm.Signature.Dash.CloudFrontKeyPairId},{medium.Id},{stream.Id}"); streamsCollection.StreamMedia.Add(medium); } } diff --git a/OF DL/Services/DownloadService.cs b/OF DL/Services/DownloadService.cs index 9f5e7d8..91cffc2 100644 --- a/OF DL/Services/DownloadService.cs +++ b/OF DL/Services/DownloadService.cs @@ -4,13 +4,13 @@ using System.Xml.Linq; using FFmpeg.NET; using FFmpeg.NET.Events; using OF_DL.Models; -using OF_DL.Models.Streams; using OF_DL.Enumerations; using ArchivedEntities = OF_DL.Models.Entities.Archived; using CommonEntities = OF_DL.Models.Entities.Common; using MessageEntities = OF_DL.Models.Entities.Messages; using PostEntities = OF_DL.Models.Entities.Posts; using PurchasedEntities = OF_DL.Models.Entities.Purchased; +using StreamEntities = OF_DL.Models.Entities.Streams; using OF_DL.Utils; using Serilog; using Serilog.Events; @@ -1012,14 +1012,15 @@ public class DownloadService( } public async Task 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 users) + IProgressReporter progressReporter, string? filenameFormat, StreamEntities.ListItem? streamInfo, + StreamEntities.Medium? streamMedia, CommonEntities.Author? author, + Dictionary users) { string path; - if (configService.CurrentConfig.FolderPerPost && streamInfo != null && streamInfo?.id is not null && - streamInfo?.postedAt is not null) + if (configService.CurrentConfig.FolderPerPost && streamInfo != null && streamInfo?.Id is not null && + streamInfo?.PostedAt is not null) { - path = $"/Posts/Free/{streamInfo.id} {streamInfo.postedAt:yyyy-MM-dd HH-mm-ss}"; + path = $"/Posts/Free/{streamInfo.Id} {streamInfo.PostedAt:yyyy-MM-dd HH-mm-ss}"; } else { @@ -1967,8 +1968,9 @@ public class DownloadService( public async Task 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 users) + IProgressReporter progressReporter, string? filenameFormat, StreamEntities.ListItem? streamInfo, + StreamEntities.Medium? streamMedia, CommonEntities.Author? author, + Dictionary users) { try { @@ -1976,10 +1978,10 @@ public class DownloadService( string path; Uri uri = new(url); string filename = Path.GetFileName(uri.LocalPath).Split(".")[0]; - if (configService.CurrentConfig.FolderPerPost && streamInfo != null && streamInfo?.id is not null && - streamInfo?.postedAt is not null) + if (configService.CurrentConfig.FolderPerPost && streamInfo != null && streamInfo?.Id is not null && + streamInfo?.PostedAt is not null) { - path = $"/Posts/Free/{streamInfo.id} {streamInfo.postedAt:yyyy-MM-dd HH-mm-ss}/Videos"; + path = $"/Posts/Free/{streamInfo.Id} {streamInfo.PostedAt:yyyy-MM-dd HH-mm-ss}/Videos"; } else { @@ -2809,7 +2811,7 @@ public class DownloadService( public async Task DownloadStreams(string username, long userId, string path, Dictionary users, bool clientIdBlobMissing, bool devicePrivateKeyMissing, - StreamsCollection streams, IProgressReporter progressReporter) + StreamEntities.StreamsCollection streams, IProgressReporter progressReporter) { Log.Debug($"Calling DownloadStreams - {username}"); @@ -2853,14 +2855,14 @@ public class DownloadService( $"https://onlyfans.com/api2/v2/users/media/{parsed[4]}/drm/post/{parsed[5]}?type=widevine", pssh); - Streams.Medium? mediaInfo = streams.StreamMedia.FirstOrDefault(m => m.id == kvp.Key); - Streams.List? streamInfo = - streams.StreamObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true); + StreamEntities.Medium? mediaInfo = streams.StreamMedia.FirstOrDefault(m => m.Id == kvp.Key); + StreamEntities.ListItem? streamInfo = + streams.StreamObjects.FirstOrDefault(p => p?.Media?.Contains(mediaInfo) == true); isNew = await DownloadStreamsDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0], decryptionKey, path, lastModified, kvp.Key, "Streams", progressReporter, configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PostFileNameFormat ?? - string.Empty, streamInfo, mediaInfo, streamInfo?.author, users); + string.Empty, streamInfo, mediaInfo, streamInfo?.Author, users); } else { @@ -2869,12 +2871,12 @@ public class DownloadService( } else { - Streams.Medium? mediaInfo = streams.StreamMedia.FirstOrDefault(m => m.id == kvp.Key); - Streams.List? streamInfo = - streams.StreamObjects.FirstOrDefault(p => p?.media?.Contains(mediaInfo) == true); + StreamEntities.Medium? mediaInfo = streams.StreamMedia.FirstOrDefault(m => m.Id == kvp.Key); + StreamEntities.ListItem? streamInfo = + streams.StreamObjects.FirstOrDefault(p => p?.Media?.Contains(mediaInfo) == true); isNew = await DownloadStreamMedia(kvp.Value, path, kvp.Key, "Streams", progressReporter, configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PostFileNameFormat ?? - string.Empty, streamInfo, mediaInfo, streamInfo?.author, users); + string.Empty, streamInfo, mediaInfo, streamInfo?.Author, users); } if (isNew) diff --git a/OF DL/Services/IAPIService.cs b/OF DL/Services/IAPIService.cs index 14a487c..83671fa 100644 --- a/OF DL/Services/IAPIService.cs +++ b/OF DL/Services/IAPIService.cs @@ -1,11 +1,11 @@ using Newtonsoft.Json.Linq; using OF_DL.Models; -using OF_DL.Models.Streams; using OF_DL.Enumerations; -using OF_DL.Models.Entities.Purchased; using ArchivedEntities = OF_DL.Models.Entities.Archived; using MessageEntities = OF_DL.Models.Entities.Messages; using PostEntities = OF_DL.Models.Entities.Posts; +using PurchasedEntities = OF_DL.Models.Entities.Purchased; +using StreamEntities = OF_DL.Models.Entities.Streams; using Spectre.Console; namespace OF_DL.Services; @@ -22,20 +22,30 @@ public interface IAPIService Task> GetMedia(MediaType mediatype, string endpoint, string? username, string folder, List paid_post_ids); - Task GetPaidPosts(string endpoint, string folder, string username, List paid_post_ids, + Task GetPaidPosts(string endpoint, string folder, string username, + List paid_post_ids, StatusContext ctx); Task GetPosts(string endpoint, string folder, List paid_post_ids, StatusContext ctx); Task GetPost(string endpoint, string folder); - Task GetStreams(string endpoint, string folder, List paid_post_ids, StatusContext ctx); + + Task GetStreams(string endpoint, string folder, List paid_post_ids, + StatusContext ctx); + Task GetArchived(string endpoint, string folder, StatusContext ctx); Task GetMessages(string endpoint, string folder, StatusContext ctx); - Task GetPaidMessages(string endpoint, string folder, string username, StatusContext ctx); - Task GetPaidMessage(string endpoint, string folder); + + Task GetPaidMessages(string endpoint, string folder, string username, + StatusContext ctx); + + Task GetPaidMessage(string endpoint, string folder); Task> GetPurchasedTabUsers(string endpoint, Dictionary users); - Task> GetPurchasedTab(string endpoint, string folder, Dictionary users); + + Task> GetPurchasedTab(string endpoint, string folder, + Dictionary users); + Task GetUserInfo(string endpoint); Task GetUserInfoById(string endpoint); Dictionary GetDynamicHeaders(string path, string queryParam); diff --git a/OF DL/Services/IDownloadService.cs b/OF DL/Services/IDownloadService.cs index d0979ba..14f5f58 100644 --- a/OF DL/Services/IDownloadService.cs +++ b/OF DL/Services/IDownloadService.cs @@ -1,10 +1,10 @@ using OF_DL.Models; -using OF_DL.Models.Streams; using ArchivedEntities = OF_DL.Models.Entities.Archived; using CommonEntities = OF_DL.Models.Entities.Common; using MessageEntities = OF_DL.Models.Entities.Messages; using PostEntities = OF_DL.Models.Entities.Posts; using PurchasedEntities = OF_DL.Models.Entities.Purchased; +using StreamEntities = OF_DL.Models.Entities.Streams; namespace OF_DL.Services; @@ -95,12 +95,14 @@ public interface IDownloadService IProgressReporter progressReporter); Task 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 users); + IProgressReporter progressReporter, string? filenameFormat, StreamEntities.ListItem? streamInfo, + StreamEntities.Medium? streamMedia, CommonEntities.Author? author, + Dictionary users); Task 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, + string? filenameFormat, StreamEntities.ListItem? streamInfo, StreamEntities.Medium? streamMedia, + CommonEntities.Author? author, Dictionary users); Task DownloadSingleMessagePreviewDRMVideo(string policy, string signature, string kvp, string url, @@ -132,7 +134,7 @@ public interface IDownloadService IProgressReporter progressReporter); Task DownloadStreams(string username, long userId, string path, Dictionary users, - bool clientIdBlobMissing, bool devicePrivateKeyMissing, StreamsCollection streams, + bool clientIdBlobMissing, bool devicePrivateKeyMissing, StreamEntities.StreamsCollection streams, IProgressReporter progressReporter); Task DownloadFreePosts(string username, long userId, string path, Dictionary users,