Compare commits

...

113 Commits

Author SHA1 Message Date
f950dc258f Merge pull request 'Remove $ from beginning of cookie variables' (#152) from whimsical-c4lic0/OF-DL:fix-invalid-auth-files into master
All checks were successful
Publish Docker image / Build and push Docker image to Gitea Registry (push) Successful in 21m7s
Publish release zip / build (push) Successful in 45s
Reviewed-on: #152
2026-02-27 18:44:03 +00:00
12d7d6793e Remove $ from beining of cookie variables 2026-02-27 12:40:41 -06:00
80cda0b8b5 Merge pull request 'Add missing folders to the release' (#151) from whimsical-c4lic0/OF-DL:add-missing-files-to-release into master
All checks were successful
Publish Docker image / Build and push Docker image to Gitea Registry (push) Successful in 20m36s
Publish release zip / build (push) Successful in 43s
Reviewed-on: #151
2026-02-27 13:28:57 +00:00
acc2aea459 Avoid including macOS and linux playwright binaries in release zip 2026-02-27 02:14:30 -06:00
eea0af0d82 Add missing playwright files to release zip 2026-02-27 02:02:26 -06:00
6c054454ed Add cdm and chromium-scripts contents to release zip 2026-02-27 01:37:21 -06:00
b5be1f350b merge upstream 2026-02-27 04:29:37 +00:00
3f51f469ab Update .gitea/workflows/publish-release.yml
All checks were successful
Publish Docker image / Build and push Docker image to Gitea Registry (push) Successful in 18m31s
Publish release zip / build (push) Successful in 40s
2026-02-27 00:25:50 +00:00
0b149e2547 Update .gitea/workflows/publish-release.yml
Some checks failed
Publish Docker image / Build and push Docker image to Gitea Registry (push) Successful in 17m3s
Publish release zip / build (push) Failing after 22s
2026-02-26 23:55:36 +00:00
fe941bb540 merge upstream 2026-02-20 16:57:31 +00:00
ba0347f86f Merge pull request 'Replace PuppeteerSharp with Playwright' (#44) from whimsical-c4lic0/OF-DL:replace-puppeteer-with-playwright into master
Some checks failed
Publish Docker image / Build and push Docker image to Gitea Registry (push) Successful in 19m17s
Publish release zip / build (push) Failing after 3s
Reviewed-on: #44
2026-02-20 10:39:25 +00:00
d81a4d60f9 Merge remote-tracking branch 'sim0n00ps/master'
# Conflicts:
#	OF DL.Core/Services/DownloadService.cs
2026-02-19 12:26:37 -06:00
22ad1c005b Merge remote-tracking branch 'sim0n00ps/master' into replace-puppeteer-with-playwright
# Conflicts:
#	.gitea/workflows/publish-release.yml
2026-02-19 12:25:27 -06:00
40a7687606 Merge pull request 'Detect if a single post is paid or free' (#144) from whimsical-c4lic0/OF-DL:detect-free-and-paid-single-posts into master
Reviewed-on: #144
2026-02-19 18:23:24 +00:00
ccb990675a Merge branch 'master' into detect-free-and-paid-single-posts 2026-02-19 18:22:51 +00:00
77bd5f7ed9 Merge pull request 'Fix custom filename formats for paid messages and paid posts' (#143) from whimsical-c4lic0/OF-DL:fix-custom-filename-formats into master
Reviewed-on: #143
2026-02-19 18:22:42 +00:00
70f69fb502 Merge remote-tracking branch 'sim0n00ps/master' into fix-custom-filename-formats
# Conflicts:
#	OF DL.Core/Services/DownloadService.cs
2026-02-19 12:21:06 -06:00
e22d2b63a2 Merge remote-tracking branch 'sim0n00ps/master' into detect-free-and-paid-single-posts
# Conflicts:
#	OF DL.Core/Services/DownloadService.cs
2026-02-19 12:20:23 -06:00
4b0bd4d676 Merge pull request 'Prevent partial DRM video downloads' (#142) from whimsical-c4lic0/OF-DL:fix-partial-video-downloads into master
Reviewed-on: #142
2026-02-19 18:03:11 +00:00
846b80a6c9 Merge branch 'detect-free-and-paid-single-posts'
# Conflicts:
#	OF DL.Core/Services/DownloadService.cs
2026-02-18 04:35:52 -06:00
605fbbda69 Merge branch 'fix-custom-filename-formats'
# Conflicts:
#	OF DL.Core/Services/DownloadService.cs
2026-02-18 04:35:01 -06:00
dce7e7a6bd Detect if a single post is paid or free 2026-02-18 02:30:47 -06:00
378a82548b Update linux installation docs 2026-02-17 12:37:28 -06:00
03dd66a842 Fix custom filename formats for paid messages and posts, and fix creator config empty strings 2026-02-16 02:56:03 -06:00
edc3d771d1 Fix misleading wording in download summary messages 2026-02-13 00:53:41 -06:00
b4aac13bc6 Compare downloaded DRM video durations against the duration reported by the MPD to ensure complete downloads 2026-02-13 00:51:57 -06:00
15a5a1d5f1 Merge branch 'master' into replace-puppeteer-with-playwright 2026-02-13 00:33:05 +00:00
aee920a9f1 Merge pull request 'Major refactor' (#141) from whimsical-c4lic0/OF-DL:refactor-architecture into master
Reviewed-on: #141
2026-02-13 00:21:58 +00:00
5f945aadfa Fix failing test 2026-02-12 10:57:01 -06:00
f16ad7f138 Improve CLI output to show counts of media 2026-02-12 10:48:44 -06:00
fdb6d55454 Fix ffmpeg logs path on windows 2026-02-12 09:50:59 -06:00
c2ab3dd79f Update work with recent major refactor 2026-02-11 13:20:06 -06:00
de97336f6c Merge branch 'refactor-architecture' into replace-puppeteer-with-playwright
# Conflicts:
#	Dockerfile
#	OF DL/Helpers/AuthHelper.cs
#	OF DL/OF DL.csproj
#	OF DL/Program.cs
2026-02-11 12:37:26 -06:00
fe0c81b2ac Remove BOM 2026-02-11 12:28:53 -06:00
3503fe1eb4 Fix capitalization in status message 2026-02-10 21:07:50 -06:00
6e0b4eba85 Upgrade dotnet from 8 to 10 2026-02-10 18:01:53 -06:00
f5a8c27cd1 Fix builds 2026-02-10 17:57:20 -06:00
a9a4c2ee20 Add checks for valid auth data before making API calls for media 2026-02-10 16:40:29 -06:00
94e135f168 Add additional AI generated unit tests 2026-02-10 16:14:31 -06:00
e7fd0ee138 Update AGENTS.md 2026-02-10 12:46:25 -06:00
d9825ae62b Add AI generated tests for ApiService and DownloadService 2026-02-10 12:44:52 -06:00
9794eacbc9 Update AGENTS.md to include testing and execution commands 2026-02-10 12:07:18 -06:00
4afa10186c Add generated docs to the .gitignore file 2026-02-10 12:03:34 -06:00
04004c7084 Refactor services 2026-02-10 12:01:33 -06:00
70738fd4ae Add unit tests for the mappers 2026-02-10 11:18:42 -06:00
e184df906f Add header comments and extract duplicated exception logging to a helper function 2026-02-10 10:30:05 -06:00
4889be1890 Update services to use coding and naming standards 2026-02-10 09:52:59 -06:00
4a218a3efe Fix remaining compiler warnings 2026-02-10 09:10:59 -06:00
85c9bc1f57 Fix docker builds 2026-02-10 03:06:15 -06:00
487de58274 Address compiler warning 2026-02-10 02:08:51 -06:00
f7f1fad92d Update AGENTS.md with updated models namespaces 2026-02-10 00:19:21 -06:00
ed06a5e514 Delete unused classes 2026-02-09 23:56:25 -06:00
974b0d4d7a Organize remaining model classes into similar namespaces 2026-02-09 23:56:09 -06:00
9766636d04 Extract repeated mapper methods into a common mapper class 2026-02-09 23:14:52 -06:00
ff431a377d Create OF DL.Core project to contain all the application logic for future GUI development 2026-02-09 22:39:23 -06:00
9fe84e9d9f Add AGENTS.md 2026-02-09 22:27:34 -06:00
4c680a40b5 Remove application logic from Program and continue to fix compiler warnings 2026-02-09 13:59:41 -06:00
17af1e8dfe Address compiler warnings 2026-02-09 04:48:21 -06:00
44a9fb1fcd Reduce duplicated code and simplify download media methods 2026-02-09 04:34:38 -06:00
a8b2acaad6 Fix custom filename format configs 2026-02-09 04:31:27 -06:00
a57af4042f Refactor remaining entities 2026-02-09 01:10:05 -06:00
fee9ca1e97 Update XML comments 2026-02-09 00:55:54 -06:00
407419a819 Replace string.Empty with "" 2026-02-09 00:55:28 -06:00
cd5c22d862 Refactor Subscriptions entities into DTOs and application entities with standardized naming conventions and default values 2026-02-09 00:46:19 -06:00
a9b135636b Refactor Users entities into DTOs and application entities with standardized naming conventions and default values 2026-02-09 00:31:52 -06:00
40ccf7aa62 Refactor Streams entities into DTOs and application entities with standardized naming conventions and default values 2026-02-09 00:10:09 -06:00
849fbbc919 Refactor Stories entities into DTOs and application entities with standardized naming conventions and default values 2026-02-08 23:27:36 -06:00
6c60509398 Refactor Purchased entities into DTOs and application entities with standardized naming conventions and default values 2026-02-08 22:58:05 -06:00
3c307bf7de Refactor Posts entities into DTOs and application entities with standardized naming conventions and default values 2026-02-08 21:04:09 -06:00
d8794ee219 Refactor Message entities into DTOs and application entities with standardized naming conventions and default values 2026-02-08 19:15:32 -06:00
911f98bc25 Refactor List entities into DTOs and application entities with standardized naming conventions and default values 2026-02-08 17:44:50 -06:00
2e3f17945e Refactor Highlight entities into DTOs and application entities with standardized naming conventions and default values 2026-02-08 16:16:38 -06:00
f243471b29 Remove BOM from files 2026-02-08 15:45:40 -06:00
35712da12d Refactor Archived entities into DTOs and application entities with standardized naming conventions and default values 2026-02-08 15:29:42 -06:00
50217a7642 Rename Entities to Models 2026-02-08 14:06:13 -06:00
6784ba0a18 Autoformat the entire solution 2026-02-06 01:42:57 -06:00
7af7bd8cfa Remove license header template and c++ rules 2026-02-06 01:42:01 -06:00
e9ab485188 Remove utils class in favor of a couple private functions 2026-02-06 01:35:50 -06:00
5df13775f0 Fix incorrect namespaces 2026-02-06 01:34:57 -06:00
86ee476dc5 Remove unused imports 2026-02-06 01:31:43 -06:00
7f1cd03f2f Update .editorconfig with additional rules from dotnet/runtime 2026-02-06 01:28:54 -06:00
4711c53746 Replace helper classes with services 2026-02-06 00:59:07 -06:00
d7bae3e260 Move config and logging into services 2026-02-05 02:21:05 -06:00
6c00f1a6aa Add helpers as services 2026-02-05 01:04:50 -06:00
a566cd0b71 Add dependency injection for config 2026-02-05 00:22:22 -06:00
e106fa2242 Document the stealth script creation process 2026-01-04 22:41:34 -06:00
568a071658 Merge branch 'master' of https://git.ofdl.tools/whimsical-c4lic0/OF-DL into replace-puppeteer-with-playwright 2026-01-04 22:30:33 -06:00
43fb74067c Remove checks for "upload" in media urls to stop media being excluded incorrectly
All checks were successful
Publish Docker image / Build and push Docker image to Gitea Registry (push) Successful in 3m25s
Publish release zip / build (push) Successful in 57s
2026-01-04 00:59:29 +00:00
1c0536e766 Merge pull request 'Remove the quotes around the boolean DisableBrowserAuth config option' (#126) from whimsical-c4lic0/OF-DL:fix-incorrect-config-type into master
All checks were successful
Publish Docker image / Build and push Docker image to Gitea Registry (push) Successful in 3m37s
Publish release zip / build (push) Successful in 58s
Reviewed-on: #126
2026-01-03 00:58:53 +00:00
cdca0d5a57 Merge pull request 'Upgrade puppeteer sharp to fix 1.9.18 browser based auth in docker' (#125) from whimsical-c4lic0/OF-DL:upgrade-puppeteer-sharp into master
Reviewed-on: #125
2026-01-03 00:58:43 +00:00
f4d1094c57 Remove the quotes around the boolean DisableBrowserAuth config option 2026-01-02 14:22:46 -06:00
d64012b9f1 Upgrade puppeteer sharp to fix issue with newer versions of chromium 2026-01-02 14:00:34 -06:00
bd0a2b6de6 Download Single Paid Message also download Preview Media
All checks were successful
Publish release zip / build (push) Successful in 58s
Publish Docker image / Build and push Docker image to Gitea Registry (push) Successful in 3m38s
2026-01-01 23:16:18 +00:00
d5aa568afb Merge pull request 'fix: force media to empty list when null to avoid object deref error during purchased media processing' (#105) from nyc_tk/OF-DL:fix-purchased-media-obj-deref-error into master
Reviewed-on: #105
2026-01-01 19:41:35 +00:00
4a07c03fc5 Merge pull request 'fix: updating Docker for ffmpeg 7.0 - requires Alpine v3.23 community packages' (#103) from nyc_tk/OF-DL:docker-update-ffmpeg into master
Reviewed-on: #103
2026-01-01 19:40:48 +00:00
fc4ecf9b5e Force media to empty list when null to avoid object deref error 2025-12-15 23:12:35 -05:00
fcd287027f Updating Docker for ffmpeg 7.0 - requires Alpine v3.23 community packages 2025-12-15 22:24:14 -05:00
167d6640e3 Move DisableBrowserAuth to Auth
All checks were successful
Publish Docker image / Build and push Docker image to Gitea Registry (push) Successful in 5m7s
Publish release zip / build (push) Successful in 1m4s
2025-12-14 18:47:18 +00:00
e9786f2341 Merge pull request 'Add additional logging for ffmpeg and prevent NRE errors from obscuring the actual errors from ffmpeg.' (#97) from ffmpeg_additional_logging into master
Reviewed-on: #97
2025-12-14 15:39:10 +00:00
Melithine
0eae466368 Add additional logging for ffmpeg and prevent NRE errors from obscuring the actual errors from ffmpeg. 2025-12-13 19:55:59 -08:00
7bd5971695 Merge branch 'master' into replace-puppeteer-with-playwright 2025-12-14 02:29:11 +00:00
616aaef1c8 Merge pull request 'Add doc site and Discord invite after the startup banner.' (#89) from header_update into master
All checks were successful
Publish Docker image / Build and push Docker image to Gitea Registry (push) Successful in 6m47s
Publish release zip / build (push) Successful in 1m6s
Reviewed-on: #89
Reviewed-by: whimsical-c4lic0 <whimsical-c4lic0@noreply.localhost>
2025-12-13 19:30:56 +00:00
34f055b00e Merge pull request 'Fix 32-bit integers' (#94) from whimsical-c4lic0/OF-DL:fix-32-bit-integers into master
Reviewed-on: #94
Reviewed-by: melithine <melithine@noreply.localhost>
2025-12-13 19:30:27 +00:00
e7e1556b3c Convert all sizes from int to long 2025-12-13 02:57:58 -06:00
74def34f96 Convert all ids from int to long 2025-12-13 02:52:15 -06:00
5c57178f5b Merge branch 'replace-puppeteer-with-playwright' of https://git.ofdl.tools/whimsical-c4lic0/OF-DL into replace-puppeteer-with-playwright 2025-12-13 02:09:33 -06:00
0572844ca8 Download chromium during runtime in docker (like windows) 2025-12-13 02:07:56 -06:00
e4eb6c0507 Merge branch 'master' into replace-puppeteer-with-playwright 2025-12-02 17:26:25 +00:00
d79733ec24 Merge branch 'master' into replace-puppeteer-with-playwright 2025-12-01 21:33:27 +00:00
a4d8676f2e Merge remote-tracking branch 'origin/master' into replace-puppeteer-with-playwright 2025-11-05 14:26:48 -06:00
f501a7e806 Merge remote-tracking branch 'origin/master' into replace-puppeteer-with-playwright 2025-10-31 17:43:27 -05:00
2b2206a0b4 merge upstream 2025-08-18 15:25:14 +00:00
3ef7895007 Replace PuppeteerSharp with Playwright 2025-07-30 17:30:37 -05:00
292 changed files with 19120 additions and 13408 deletions

View File

@ -1,9 +1,186 @@
# Editor configuration, see https://editorconfig.org # editorconfig.org
# top-most EditorConfig file
root = true root = true
# Default settings:
# A newline ending every file
# Use 4 spaces as indentation
[*] [*]
charset = utf-8 charset = utf-8
indent_style = space indent_style = space
indent_size = 4 indent_size = 4
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[project.json]
indent_size = 2
# Generated code
[*{_AssemblyInfo.cs,.notsupported.cs}]
generated_code = true
# C# files
[*.cs]
# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_switch_labels = true
csharp_indent_labels = one_less_than_current
# Modifier preferences
csharp_preferred_modifier_order = public, private, protected, internal, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async:suggestion
# avoid this. unless absolutely necessary
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
# Types: use keywords instead of BCL types, and permit var only when the type is clear
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = false:none
csharp_style_var_elsewhere = false:suggestion
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# name all constant fields using PascalCase
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.required_modifiers = const
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# static fields should have s_ prefix
dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion
dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static
dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected
dotnet_naming_style.static_prefix_style.required_prefix = s_
dotnet_naming_style.static_prefix_style.capitalization = camel_case
# internal and private fields should be _camelCase
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
# Code style defaults
csharp_using_directive_placement = outside_namespace:suggestion
dotnet_sort_system_directives_first = true
csharp_prefer_braces = true:silent
csharp_preserve_single_line_blocks = true:none
csharp_preserve_single_line_statements = false:none
csharp_prefer_static_local_function = true:suggestion
csharp_prefer_simple_using_statement = false:none
csharp_style_prefer_switch_expression = true:suggestion
dotnet_style_readonly_field = true:suggestion
# Expression-level preferences
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_auto_properties = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
csharp_prefer_simple_default_expression = true:suggestion
# Expression-bodied members
csharp_style_expression_bodied_methods = true:silent
csharp_style_expression_bodied_constructors = true:silent
csharp_style_expression_bodied_operators = true:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = true:silent
# Pattern matching
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
# Null checking preferences
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Other features
csharp_style_prefer_index_operator = false:none
csharp_style_prefer_range_operator = false:none
csharp_style_pattern_local_over_anonymous_function = false:none
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = do_not_ignore
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Xml project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}]
indent_size = 2
[*.{csproj,vbproj,proj,nativeproj,locproj}]
charset = utf-8
# Xml build files
[*.builds]
indent_size = 2
# Xml files
[*.{xml,stylecop,resx,ruleset}]
indent_size = 2
# Xml config files
[*.{props,targets,config,nuspec}]
indent_size = 2
# YAML config files
[*.{yml,yaml}]
indent_size = 2
# Shell scripts
[*.sh]
end_of_line = lf
[*.{cmd,bat}]
end_of_line = crlf

5
.gitattributes vendored
View File

@ -3,6 +3,11 @@
############################################################################### ###############################################################################
* text=auto * text=auto
###############################################################################
# Shell scripts should use LF line endings (avoid /bin/sh^M issues in containers)
###############################################################################
*.sh text eol=lf
############################################################################### ###############################################################################
# Set default behavior for command prompt diff. # Set default behavior for command prompt diff.
# #

View File

@ -29,12 +29,12 @@ jobs:
- name: Build for Windows and Linux - name: Build for Windows and Linux
run: | run: |
dotnet publish -p:Version=${{ steps.version.outputs.version }} \ dotnet publish "OF DL/OF DL.csproj" -p:Version=${{ steps.version.outputs.version }} \
-p:PackageVersion=${{ steps.version.outputs.version }} \ -p:PackageVersion=${{ steps.version.outputs.version }} \
-p:WarningLevel=0 -c Release -r win-x86 \ -p:WarningLevel=0 -c Release -r win-x86 \
--self-contained true -p:PublishSingleFile=true -o outwin --self-contained true -p:PublishSingleFile=true -o outwin
dotnet publish -p:Version=${{ steps.version.outputs.version }} \ dotnet publish "OF DL/OF DL.csproj" -p:Version=${{ steps.version.outputs.version }} \
-p:PackageVersion=${{ steps.version.outputs.version }} \ -p:PackageVersion=${{ steps.version.outputs.version }} \
-p:WarningLevel=0 -c Release -r linux-x64 \ -p:WarningLevel=0 -c Release -r linux-x64 \
--self-contained true -p:PublishSingleFile=true -o outlin --self-contained true -p:PublishSingleFile=true -o outlin
@ -52,12 +52,17 @@ jobs:
echo "➤ Creating folder for CDM" echo "➤ Creating folder for CDM"
mkdir -p cdm/devices/chrome_1610 mkdir -p cdm/devices/chrome_1610
echo "➤ Copying ffmpeg from user folder" echo "➤ Copying ffmpeg and ffprobe from user folder"
cp /home/rhys/ffmpeg/ffmpeg-7.1.1-essentials_build/bin/ffmpeg.exe . cp /home/rhys/ffmpeg/ffmpeg-7.1.1-essentials_build/bin/ffmpeg.exe .
cp /home/rhys/ffmpeg/ffmpeg-7.1.1-essentials_build/bin/ffprobe.exe .
cp /home/rhys/ffmpeg/ffmpeg-7.1.1-essentials_build/LICENSE LICENSE.ffmpeg cp /home/rhys/ffmpeg/ffmpeg-7.1.1-essentials_build/LICENSE LICENSE.ffmpeg
echo "➤ Remove unneeded playwright binaries"
rm -rf .playwright/node/darwin*
rm -rf .playwright/node/linux*
echo "➤ Creating release zip" echo "➤ Creating release zip"
zip ../OFDLV${{ steps.version.outputs.version }}.zip OF\ DL.exe e_sqlite3.dll rules.json config.conf cdm ffmpeg.exe LICENSE.ffmpeg zip -r ../OFDLV${{ steps.version.outputs.version }}.zip OF\ DL.exe e_sqlite3.dll rules.json config.conf cdm chromium-scripts .playwright playwright.ps1 ffmpeg.exe ffprobe.exe LICENSE.ffmpeg
cd .. cd ..
- name: Create release and upload artifact - name: Create release and upload artifact

3
.gitignore vendored
View File

@ -371,3 +371,6 @@ FodyWeavers.xsd
# venv # venv
venv/ venv/
# Generated docs
/site

238
AGENTS.md Normal file
View File

@ -0,0 +1,238 @@
# AGENTS.md
Note: Keep AGENTS.md updated as project structure, key services, or workflows change.
This repo is **OF DL** (also known as OF-DL), a C# console app that downloads media from a user's OnlyFans account(s).
This document is for AI agents helping developers modify the project. It focuses on architecture, data flow, and the
most important change points.
## Quick Flow
1. `Program.Main` builds DI, loads `config.conf`, and runs the interactive flow.
2. `StartupService.CheckVersionAsync` checks the latest release tag (`OFDLV*`) from `git.ofdl.tools` when not in DEBUG.
3. `StartupService.ValidateEnvironmentAsync` validates OS, FFmpeg, `rules.json`, and Widevine device files.
4. `AuthService` loads `auth.json` or opens a browser login (PuppeteerSharp) and persists auth data.
5. `ApiService` signs every API request with dynamic rules and the current auth.
6. `DownloadOrchestrationService` selects creators, prepares folders/DBs, and calls `DownloadService` per media type.
7. `DownloadService` downloads media, handles DRM, and records metadata in SQLite.
## Project Layout
- `OF DL/Program.cs` orchestrates startup, config/auth loading, and the interactive flow (CLI entrypoint).
- `OF DL/CLI/` contains Spectre.Console UI helpers and progress reporting (CLI-only).
- `OF DL.Core/Services/` contains application services (API, auth, download, config, DB, startup, logging, filenames).
- `OF DL.Core/Models/` holds configuration, auth, API request/response models, downloads/startup results, DTOs,
entities, and mapping helpers.
- `OF DL.Core/Widevine/` implements Widevine CDM handling and key derivation.
- `OF DL.Core/Helpers/`, `OF DL.Core/Utils/`, `OF DL.Core/Crypto/`, `OF DL.Core/Enumerations/` contain shared core
logic.
- `docs/` and `mkdocs.yml` define the documentation site.
- `site/` is generated MkDocs output and should not be edited by hand.
- `docker/` contains container entrypoint and supervisor configuration.
## Key Services
- `ApiService` (`OF DL.Core/Services/ApiService.cs`) builds signed headers, performs HTTP requests, and maps DTOs to
entities. It also handles DRM-related calls like MPD/PSSH extraction and license requests.
- `AuthService` (`OF DL.Core/Services/AuthService.cs`) loads `auth.json` or performs browser-based login with
PuppeteerSharp,
then persists auth. It also normalizes cookies.
- `ConfigService` (`OF DL.Core/Services/ConfigService.cs`) loads `config.conf` (HOCON), migrates legacy `config.json`,
and
updates global settings (logging, text sanitization).
- `DownloadService` (`OF DL.Core/Services/DownloadService.cs`) downloads all media (images, video, audio) and handles
DRM
video decryption and FFmpeg execution.
- `DownloadOrchestrationService` (`OF DL.Core/Services/DownloadOrchestrationService.cs`) coordinates user selection,
subscription lists, per-user folder prep, and per-media-type download execution.
- `DBService` (`OF DL.Core/Services/DBService.cs`) manages SQLite metadata DBs for downloaded media and a `users.db`
index.
- `StartupService` (`OF DL.Core/Services/StartupService.cs`) validates FFmpeg, rules.json, Widevine device files, and
performs release version checks.
- `LoggingService` (`OF DL.Core/Services/LoggingService.cs`) writes logs to `logs/OFDL.txt` and updates log level based
on
config.
- `FileNameService` (`OF DL.Core/Services/FileNameService.cs`) formats filenames using the custom format rules from
config.
## Models
- DTOs live under `OF DL.Core/Models/Dtos/` and mirror API response JSON.
- Entities live under `OF DL.Core/Models/Entities/` and represent the internal domain used by download logic.
- Mappers in `OF DL.Core/Models/Mappers/` convert DTOs into entities to isolate API changes from downstream logic.
- Non-DTO/Entity models are grouped by concern under `OF DL.Core/Models/OfdlApi/`, `Auth/`, `Config/`, `Downloads/`,
and `Startup/`.
- Classes in `OF DL.Core/Models/OfdlApi/` mirror request and response JOSN OF DL APIs (custom and gitea)
- Classes in `OF DL.Core/Models/Config/` are used for reading and storing application configuration
- Classes in `OF DL.Core/Models/Downloads/` contain counts and application state for downloads
## Configuration
- Primary config file is `config.conf` (HOCON). `ConfigService` migrates legacy `config.json` if found and creates a
default `config.conf` if missing.
- `Config` lives in `OF DL.Core/Models/Config/Config.cs` and is populated by `ConfigService.LoadConfigFromFileAsync`.
- `ConfigService.UpdateConfig` is the central place where runtime config changes are applied (logging level and text
sanitization).
- CLI flag `--non-interactive` forces non-interactive mode; `ConfigService.IsCliNonInteractive` and
`Config.NonInteractiveMode` both gate prompts.
- FFmpeg path is read from `config.conf`, `auth.json`, or auto-detected from PATH/current directory.
## Runtime Files (relative to the working directory)
- `config.conf`, `auth.json`, and `rules.json` are loaded from the current working directory.
- `cdm/` (Widevine device files), `chrome-data/` (Puppeteer profile), and `logs/` are created under the working
directory.
- `users.db` is stored at the working directory root.
## Execution and Testing
- .NET SDK: 8.x (`net8.0` for all projects).
- Build from the repo root:
```bash
dotnet build OF DL.sln
```
- Run from source (runtime files are read from the current working directory):
```bash
dotnet run --project "OF DL/OF DL.csproj"
```
- If you want a local `rules.json` fallback, run from `OF DL/` or copy `OF DL/rules.json` into your working directory.
- Run tests:
```bash
dotnet test "OF DL.Tests/OF DL.Tests.csproj"
```
- Optional coverage (coverlet collector):
```bash
dotnet test "OF DL.Tests/OF DL.Tests.csproj" --collect:"XPlat Code Coverage"
```
## Authentication Flow
- Auth data is stored in `auth.json` using the `Auth` model in `OF DL.Core/Models/Auth/Auth.cs`.
- `AuthService.LoadFromBrowserAsync` launches Chrome via PuppeteerSharp, waits for login, then extracts `auth_id` and
`sess` cookies, `bcTokenSha` from localStorage (used as `X_BC`), and `USER_AGENT` from the browser.
- `AuthService.ValidateCookieString` rewrites the cookie string so it contains only `auth_id` and `sess` and ensures a
trailing `;`.
- `AuthService` uses `chrome-data/` as its user data directory; `Logout` deletes `chrome-data` and `auth.json`.
Environment variables used by auth:
- `OFDL_DOCKER=true` toggles Docker-specific instructions and browser flags.
- `OFDL_PUPPETEER_EXECUTABLE_PATH` overrides the Chromium path for PuppeteerSharp.
## Dynamic Rules and Signature Headers
- All OnlyFans API requests use dynamic headers from `ApiService.GetDynamicHeaders`.
- Dynamic rules are fetched from `https://git.ofdl.tools/sim0n00ps/dynamic-rules/raw/branch/main/rules.json` with
fallback to local `rules.json` in the current working directory. The repo ships `OF DL/rules.json` as the default
rules file.
- Cache durations: 15 minutes for remote rules, 5 minutes for local rules.
- `DynamicRules` shape is defined in `OF DL.Core/Models/OfdlApi/DynamicRules.cs` and includes `app-token`,
`static_param`,
`prefix`, `suffix`, `checksum_constant`, and `checksum_indexes`.
Signature algorithm in `GetDynamicHeaders`:
- `input = "{static_param}\n{timestamp_ms}\n{path+query}\n{user_id}"`
- `hash = SHA1(input)` lower-case hex string
- `checksum = sum(hashString[index] char values) + checksum_constant`
- `sign = "{prefix}:{hash}:{checksum_hex}:{suffix}"`
Headers included in signed requests:
- `app-token`, `sign`, `time`, `user-id`, `user-agent`, `x-bc`, `cookie`.
## Widevine CDM and DRM Decryption
- Runtime Widevine device files are expected at `cdm/devices/chrome_1610/device_client_id_blob` and
`cdm/devices/chrome_1610/device_private_key` (relative to the working directory). Paths are defined in
`OF DL.Core/Widevine/Constants.cs` and validated in `StartupService`.
DRM flow is primarily in `DownloadService.GetDecryptionInfo` and `ApiService` DRM helpers:
- `ApiService.GetDRMMPDPSSH` downloads the MPD manifest and extracts the `cenc:pssh` value.
- `ApiService.GetDRMMPDLastModified` uses CloudFront signed cookies and returns MPD `Last-Modified`.
- `DownloadService.GetDecryptionInfo` builds DRM headers (via `GetDynamicHeaders`) and hits the license endpoint.
Two decryption paths exist:
- If CDM device files exist, `ApiService.GetDecryptionKeyCDM` uses `Widevine/CDMApi`.
- If missing, `ApiService.GetDecryptionKeyOFDL` calls `https://ofdl.tools/WV` as a fallback.
`DownloadService.DownloadDrmMedia` runs FFmpeg with `-cenc_decryption_key`, CloudFront cookies, and auth
cookies/user-agent. Output is written to `{filename}_source.mp4`, then moved and recorded in SQLite.
## Download Paths, Data, and Logs
- Default download root is `__user_data__/sites/OnlyFans/{username}` when `DownloadPath` is blank. This is computed in
`DownloadOrchestrationService.ResolveDownloadPath`.
- Each creator folder gets a `Metadata/user_data.db` (SQLite) managed by `DBService`.
- A global `users.db` in the working directory tracks subscribed creators and user IDs.
- Logs are written to `logs/OFDL.txt` (rolling daily); FFmpeg report files are also written under `logs/` when debug
logging is enabled.
## Docs (MkDocs)
- Docs source lives under `docs/` and configuration is in `mkdocs.yml`.
- Build the site with `mkdocs build --clean` (outputs to `site/`).
- Preview locally with `mkdocs serve`.
## CI/CD (Gitea Workflows)
- `/.gitea/workflows/publish-docs.yml` builds and deploys docs on tag pushes matching `OFDLV*` and on manual dispatch.
- `/.gitea/workflows/publish-docker.yml` builds multi-arch Docker images on `OFDLV*` tags and pushes to the Gitea
registry.
- `/.gitea/workflows/publish-release.yml` publishes Windows and Linux builds on `OFDLV*` tags and creates a draft
release.
## Docker Image
- Built via `/.gitea/workflows/publish-docker.yml` on tag pushes `OFDLV*`.
- Image tags: `git.ofdl.tools/sim0n00ps/of-dl:latest` and `git.ofdl.tools/sim0n00ps/of-dl:{version}`.
- Build args include `VERSION` (tag name with `OFDLV` stripped).
- Platforms: `linux/amd64` and `linux/arm64`.
- Runtime uses `/config` as the working directory and `/data` for downloads; `docker/entrypoint.sh` seeds
`/config/config.conf` and `/config/rules.json` from `/default-config`.
## Release Checklist
1. Update docs under `docs/` and verify locally with `mkdocs build --clean` or `mkdocs serve`.
2. Tag the release as `OFDLV{version}` and push the tag.
3. Verify the draft release artifact and publish the release in Gitea.
## Coding Style (from .editorconfig)
- Indentation: 4 spaces by default, 2 spaces for XML/YAML/project files. No tabs.
- Line endings: LF for `*.sh`, CRLF for `*.cmd`/`*.bat`.
- C# braces on new lines (`csharp_new_line_before_open_brace = all`).
- Prefer predefined types (`int`, `string`) and avoid `var` except when type is apparent.
- `using` directives go outside the namespace and `System` namespaces are sorted first.
- Private/internal fields use `_camelCase`; private/internal static fields use `s_` prefix.
- `const` fields should be PascalCase.
- Prefer braces for control blocks and expression-bodied members (silent preferences).
## Where to Look First
- `OF DL/Program.cs` for the execution path and menu flow.
- `OF DL.Core/Services/ApiService.cs` for OF API calls and header signing.
- `OF DL.Core/Services/DownloadService.cs` for downloads and DRM handling.
- `OF DL.Core/Services/DownloadOrchestrationService.cs` for creator selection and flow control.
- `OF DL.Core/Widevine/` for CDM key generation and license parsing.
- `OF DL.Core/Models/Config/Config.cs` and `OF DL.Core/Services/ConfigService.cs` for config shape and parsing.
- `OF DL.Core/Services/AuthService.cs` for user-facing authentication behavior and browser login flow.
- `docs/` for public documentation; update docs whenever user-facing behavior or configuration changes.
## Documentation updates for common changes:
- Config option added/removed/changed in `Config` or `config.conf`: update `docs/config/all-configuration-options.md` (
full spec), `docs/config/configuration.md` (organized list), and `docs/config/custom-filename-formats.md` if filename
tokens or formats are affected.
- Authentication flow changes (browser login, legacy methods, required fields): update `docs/config/auth.md`.
- CLI/menu flow or download workflow changes: update `docs/running-the-program.md`.
- Docker runtime or container flags/paths change: update `docs/installation/docker.md`.

View File

@ -1,18 +1,16 @@
FROM alpine:3.20 AS build FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
ARG VERSION ARG VERSION
RUN apk --no-cache --repository community add \
dotnet8-sdk
# Copy source code # Copy source code
COPY ["OF DL.sln", "/src/OF DL.sln"] COPY ["OF DL.sln", "/src/OF DL.sln"]
COPY ["OF DL", "/src/OF DL"] COPY ["OF DL", "/src/OF DL"]
COPY ["OF DL.Core", "/src/OF DL.Core"]
WORKDIR "/src" WORKDIR "/src"
# Build release # Build release
RUN dotnet publish -p:WarningLevel=0 -p:Version=$VERSION -c Release --self-contained true -p:PublishSingleFile=true -o out RUN dotnet publish "OF DL/OF DL.csproj" -p:WarningLevel=0 -p:Version=$VERSION -c Release -o out
# Generate default config.conf files # Generate default config.conf files
RUN /src/out/OF\ DL --non-interactive || true && \ RUN /src/out/OF\ DL --non-interactive || true && \
@ -21,21 +19,24 @@ RUN /src/out/OF\ DL --non-interactive || true && \
mv /src/updated_config.conf /src/config.conf mv /src/updated_config.conf /src/config.conf
FROM alpine:3.20 AS final FROM mcr.microsoft.com/dotnet/runtime:10.0 AS final
# Install dependencies # Install dependencies
RUN apk --no-cache --repository community add \ RUN apt-get update \
bash \ && apt-get install -y \
tini \ tini \
dotnet8-runtime \
ffmpeg \ ffmpeg \
udev \
ttf-freefont \
chromium \
supervisor \ supervisor \
xvfb \ xvfb \
x11vnc \ x11vnc \
novnc novnc \
npm \
&& rm -rf /var/lib/apt/lists/*
RUN npx playwright install-deps
RUN apt-get remove --purge -y npm \
&& apt-get autoremove -y
# Redirect webroot to vnc.html instead of displaying directory listing # Redirect webroot to vnc.html instead of displaying directory listing
RUN echo "<!DOCTYPE html><html><head><meta http-equiv=\"Refresh\" content=\"0; url='vnc.html'\" /></head><body></body></html>" > /usr/share/novnc/index.html RUN echo "<!DOCTYPE html><html><head><meta http-equiv=\"Refresh\" content=\"0; url='vnc.html'\" /></head><body></body></html>" > /usr/share/novnc/index.html
@ -54,13 +55,14 @@ COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY docker/entrypoint.sh /app/entrypoint.sh COPY docker/entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh
ENV DISPLAY=:0.0 \ ENV DEBIAN_FRONTEND="noninteractive" \
DISPLAY_WIDTH=1024 \ DISPLAY=:0.0 \
DISPLAY_WIDTH=1366 \
DISPLAY_HEIGHT=768 \ DISPLAY_HEIGHT=768 \
OFDL_PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium \ OFDL_DOCKER=true \
OFDL_DOCKER=true PLAYWRIGHT_BROWSERS_PATH=/config/chromium
EXPOSE 8080 EXPOSE 8080
WORKDIR /config WORKDIR /config
ENTRYPOINT ["/sbin/tini", "--"] ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["/app/entrypoint.sh"] CMD ["/app/entrypoint.sh"]

View File

@ -0,0 +1,29 @@
using System.Security.Cryptography;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Parameters;
namespace OF_DL.Crypto;
public class CryptoUtils
{
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(key);
mac.Init(keyParam);
mac.BlockUpdate(data, 0, data.Length);
byte[] outBytes = new byte[16];
mac.DoFinal(outBytes, 0);
return outBytes;
}
}

View File

@ -0,0 +1,127 @@
using System.Security.Cryptography;
namespace OF_DL.Crypto;
public class Padding
{
public static byte[] AddPKCS7Padding(byte[] data, int k)
{
int m = k - data.Length % k;
byte[] padding = new byte[m];
Array.Fill(padding, (byte)m);
byte[] paddedBytes = new byte[data.Length + padding.Length];
Buffer.BlockCopy(data, 0, paddedBytes, 0, data.Length);
Buffer.BlockCopy(padding, 0, paddedBytes, data.Length, padding.Length);
return paddedBytes;
}
public static byte[] RemovePKCS7Padding(byte[] paddedByteArray)
{
byte last = paddedByteArray[^1];
if (paddedByteArray.Length <= last)
{
return paddedByteArray;
}
return SubArray(paddedByteArray, 0, paddedByteArray.Length - last);
}
public static T[] SubArray<T>(T[] arr, int start, int length)
{
T[] result = new T[length];
Buffer.BlockCopy(arr, start, result, 0, length);
return result;
}
public static byte[] AddPssPadding(byte[] hash)
{
int modBits = 2048;
int hLen = 20;
int emLen = 256;
int lmask = 0;
for (int i = 0; i < 8 * emLen - (modBits - 1); i++)
{
lmask = (lmask >> 1) | 0x80;
}
// Commented out since the condition will always be false while emLen = 256 and hLen = 20
// if (emLen < hLen + hLen + 2)
// {
// return null;
// }
byte[] salt = new byte[hLen];
new Random().NextBytes(salt);
byte[] m_prime = Enumerable.Repeat((byte)0, 8).ToArray().Concat(hash).Concat(salt).ToArray();
byte[] h = SHA1.Create().ComputeHash(m_prime);
byte[] ps = Enumerable.Repeat((byte)0, emLen - hLen - hLen - 2).ToArray();
byte[] db = ps.Concat(new byte[] { 0x01 }).Concat(salt).ToArray();
byte[] dbMask = MGF1(h, emLen - hLen - 1);
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);
byte[] padded = maskedDb.Concat(h).Concat(new byte[] { 0xBC }).ToArray();
return padded;
}
public static byte[] RemoveOAEPPadding(byte[] data)
{
int k = 256;
int hLen = 20;
byte[] maskedSeed = data[1..(hLen + 1)];
byte[] maskedDB = data[(hLen + 1)..];
byte[] seedMask = MGF1(maskedDB, hLen);
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", StringComparison.Ordinal) / 2;
byte[] unpadded = db[(hLen + onePos + 1)..];
return unpadded;
}
private static byte[] MGF1(byte[] seed, int maskLen)
{
SHA1 hobj = SHA1.Create();
int hLen = hobj.HashSize / 8;
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();
}
}

View File

@ -0,0 +1,7 @@
namespace OF_DL.Enumerations;
public enum CustomFileNameOption
{
ReturnOriginal,
ReturnEmpty
}

View File

@ -0,0 +1,7 @@
namespace OF_DL.Enumerations;
public enum DownloadDateSelection
{
before,
after
}

View File

@ -0,0 +1,34 @@
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
}

View File

@ -0,0 +1,12 @@
namespace OF_DL.Enumerations;
public enum MediaType
{
PaidPosts = 10,
Posts = 20,
Archived = 30,
Stories = 40,
Highlights = 50,
Messages = 60,
PaidMessages = 70
}

View File

@ -0,0 +1,8 @@
namespace OF_DL.Enumerations;
public enum VideoResolution
{
_240,
_720,
source
}

View File

@ -0,0 +1,14 @@
namespace OF_DL.Helpers;
public static class Constants
{
public const string ApiUrl = "https://onlyfans.com/api2/v2";
public const int ApiPageSize = 50;
public const int WidevineRetryDelay = 10;
public const int WidevineMaxRetries = 3;
public const int DrmDownloadMaxRetries = 3;
}

View File

@ -0,0 +1,26 @@
using Serilog;
namespace OF_DL.Helpers;
internal static class ExceptionLoggerHelper
{
/// <summary>
/// Logs an exception to the console and Serilog with inner exception details.
/// </summary>
/// <param name="ex">The exception to log.</param>
public static void LogException(Exception ex)
{
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
Log.Error("Exception caught: {0}\n\nStackTrace: {1}", ex.Message, ex.StackTrace);
if (ex.InnerException == null)
{
return;
}
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);
}
}

View File

@ -1,20 +1,20 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using OF_DL.Entities; using OF_DL.Models.OfdlApi;
using Serilog; using Serilog;
namespace OF_DL.Helpers; namespace OF_DL.Helpers;
public static class VersionHelper 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 const string url = "https://git.ofdl.tools/api/v1/repos/sim0n00ps/OF-DL/releases/latest"; private static readonly HttpClient s_httpClient = new();
public static async Task<string?> GetLatestReleaseTag(CancellationToken cancellationToken = default) public static async Task<string?> GetLatestReleaseTag(CancellationToken cancellationToken = default)
{ {
Log.Debug("Calling GetLatestReleaseTag"); Log.Debug("Calling GetLatestReleaseTag");
try try
{ {
var response = await httpClient.GetAsync(url, cancellationToken); HttpResponseMessage response = await s_httpClient.GetAsync(Url, cancellationToken);
if (!response.IsSuccessStatusCode) if (!response.IsSuccessStatusCode)
{ {
@ -22,20 +22,20 @@ public static class VersionHelper
return null; return null;
} }
var body = await response.Content.ReadAsStringAsync(); string body = await response.Content.ReadAsStringAsync(cancellationToken);
Log.Debug("GetLatestReleaseTag API Response: "); Log.Debug("GetLatestReleaseTag API Response: {Body}", body);
Log.Debug(body);
var versionCheckResponse = JsonConvert.DeserializeObject<LatestReleaseAPIResponse>(body); LatestReleaseApiResponse? versionCheckResponse =
JsonConvert.DeserializeObject<LatestReleaseApiResponse>(body);
if (versionCheckResponse == null || versionCheckResponse.TagName == "") if (versionCheckResponse != null && versionCheckResponse.TagName != "")
{ {
Log.Debug("GetLatestReleaseTag did not return a valid tag name"); return versionCheckResponse.TagName;
return null;
} }
return versionCheckResponse.TagName; Log.Debug("GetLatestReleaseTag did not return a valid tag name");
return null;
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -48,10 +48,13 @@ public static class VersionHelper
if (ex.InnerException != null) if (ex.InnerException != null)
{ {
Console.WriteLine("\nInner Exception:"); Console.WriteLine("\nInner Exception:");
Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace); Console.WriteLine("Exception caught: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message, ex.InnerException.StackTrace); ex.InnerException.StackTrace);
Log.Error("Inner Exception: {0}\n\nStackTrace: {1}", ex.InnerException.Message,
ex.InnerException.StackTrace);
} }
} }
return null; return null;
} }
} }

21
OF DL.Core/Models/Auth.cs Normal file
View File

@ -0,0 +1,21 @@
using Newtonsoft.Json;
namespace OF_DL.Models;
public class Auth
{
[JsonProperty(PropertyName = "USER_ID")]
public string? UserId { get; set; } = "";
[JsonProperty(PropertyName = "USER_AGENT")]
public string? UserAgent { get; set; } = "";
[JsonProperty(PropertyName = "X_BC")] public string? XBc { get; set; } = "";
[JsonProperty(PropertyName = "COOKIE")]
public string? Cookie { get; set; } = "";
[JsonIgnore]
[JsonProperty(PropertyName = "FFMPEG_PATH")]
public string? FfmpegPath { get; set; } = "";
}

View File

@ -0,0 +1,158 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using OF_DL.Enumerations;
using Serilog;
namespace OF_DL.Models.Config;
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; }
[ToggleableConfig] public bool IncludeRestrictedSubscriptions { get; set; }
[ToggleableConfig] public bool SkipAds { get; set; }
public string? DownloadPath { get; set; } = "";
[ToggleableConfig] public bool RenameExistingFilesWhenCustomFormatIsSelected { get; set; }
public int? Timeout { get; set; } = -1;
[ToggleableConfig] public bool FolderPerPaidPost { get; set; }
[ToggleableConfig] public bool FolderPerPost { get; set; }
[ToggleableConfig] public bool FolderPerPaidMessage { get; set; }
[ToggleableConfig] public bool FolderPerMessage { get; set; }
[ToggleableConfig] public bool LimitDownloadRate { get; set; }
public int DownloadLimitInMbPerSec { get; set; } = 4;
// Indicates if you want to download only on specific dates.
[ToggleableConfig] public bool DownloadOnlySpecificDates { get; set; }
// This enum will define if we want data from before or after the CustomDate.
[JsonConverter(typeof(StringEnumConverter))]
public DownloadDateSelection DownloadDateSelection { get; set; } = DownloadDateSelection.before;
// This is the specific date used in combination with the above enum.
[JsonConverter(typeof(ShortDateConverter))]
public DateTime? CustomDate { get; set; } = null;
[ToggleableConfig] public bool ShowScrapeSize { get; set; }
[ToggleableConfig] public bool DownloadPostsIncrementally { get; set; }
public bool NonInteractiveMode { get; set; }
public string NonInteractiveModeListName { get; set; } = "";
[ToggleableConfig] public bool NonInteractiveModePurchasedTab { get; set; }
public string? FFmpegPath { get; set; } = "";
public string? FFprobePath { get; set; } = "";
[ToggleableConfig] public bool BypassContentForCreatorsWhoNoLongerExist { get; set; }
public Dictionary<string, CreatorConfig> CreatorConfigs { get; set; } = new();
[ToggleableConfig] public bool DownloadDuplicatedMedia { get; set; }
public string IgnoredUsersListName { get; set; } = "";
[JsonConverter(typeof(StringEnumConverter))]
public LoggingLevel LoggingLevel { get; set; } = LoggingLevel.Error;
[ToggleableConfig] public bool IgnoreOwnMessages { get; set; }
[ToggleableConfig] public bool DisableBrowserAuth { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public VideoResolution DownloadVideoResolution { get; set; } = VideoResolution.source;
public double DrmVideoDurationMatchThreshold { get; set; } = 0.98;
// When enabled, post/message text is stored as-is without XML stripping.
[ToggleableConfig] public bool DisableTextSanitization { get; set; }
public string? PaidPostFileNameFormat { get; set; } = "";
public string? PostFileNameFormat { get; set; } = "";
public string? PaidMessageFileNameFormat { get; set; } = "";
public string? MessageFileNameFormat { get; set; } = "";
public IFileNameFormatConfig GetCreatorFileNameFormatConfig(string username)
{
FileNameFormatConfig combinedFilenameFormatConfig = new()
{
PaidPostFileNameFormat = PaidPostFileNameFormat,
PostFileNameFormat = PostFileNameFormat,
PaidMessageFileNameFormat = PaidMessageFileNameFormat,
MessageFileNameFormat = MessageFileNameFormat
};
if (CreatorConfigs.TryGetValue(username, out CreatorConfig? creatorConfig))
{
if (!string.IsNullOrEmpty(creatorConfig.PaidPostFileNameFormat))
{
combinedFilenameFormatConfig.PaidPostFileNameFormat = creatorConfig.PaidPostFileNameFormat;
}
if (!string.IsNullOrEmpty(creatorConfig.PostFileNameFormat))
{
combinedFilenameFormatConfig.PostFileNameFormat = creatorConfig.PostFileNameFormat;
}
if (!string.IsNullOrEmpty(creatorConfig.PaidMessageFileNameFormat))
{
combinedFilenameFormatConfig.PaidMessageFileNameFormat = creatorConfig.PaidMessageFileNameFormat;
}
if (!string.IsNullOrEmpty(creatorConfig.MessageFileNameFormat))
{
combinedFilenameFormatConfig.MessageFileNameFormat = creatorConfig.MessageFileNameFormat;
}
}
Log.Debug("PaidMessageFilenameFormat: {CombinedConfigPaidMessageFileNameFormat}",
combinedFilenameFormatConfig.PaidMessageFileNameFormat);
Log.Debug("PostFileNameFormat: {CombinedConfigPostFileNameFormat}",
combinedFilenameFormatConfig.PostFileNameFormat);
Log.Debug("MessageFileNameFormat: {CombinedConfigMessageFileNameFormat}",
combinedFilenameFormatConfig.MessageFileNameFormat);
Log.Debug("PaidPostFileNameFormat: {CombinedConfigPaidPostFileNameFormat}",
combinedFilenameFormatConfig.PaidPostFileNameFormat);
return combinedFilenameFormatConfig;
}
private class ShortDateConverter : IsoDateTimeConverter
{
public ShortDateConverter() => DateTimeFormat = "yyyy-MM-dd";
}
}

View File

@ -0,0 +1,12 @@
namespace OF_DL.Models.Config;
public class CreatorConfig : IFileNameFormatConfig
{
public string? PaidPostFileNameFormat { get; set; }
public string? PostFileNameFormat { get; set; }
public string? PaidMessageFileNameFormat { get; set; }
public string? MessageFileNameFormat { get; set; }
}

View File

@ -0,0 +1,12 @@
namespace OF_DL.Models.Config;
public class FileNameFormatConfig : IFileNameFormatConfig
{
public string? PaidPostFileNameFormat { get; set; }
public string? PostFileNameFormat { get; set; }
public string? PaidMessageFileNameFormat { get; set; }
public string? MessageFileNameFormat { get; set; }
}

View File

@ -0,0 +1,12 @@
namespace OF_DL.Models.Config;
public interface IFileNameFormatConfig
{
string? PaidPostFileNameFormat { get; set; }
string? PostFileNameFormat { get; set; }
string? PaidMessageFileNameFormat { get; set; }
string? MessageFileNameFormat { get; set; }
}

View File

@ -0,0 +1,4 @@
namespace OF_DL.Models.Config;
[AttributeUsage(AttributeTargets.Property)]
internal class ToggleableConfigAttribute : Attribute;

View File

@ -0,0 +1,29 @@
namespace OF_DL.Models.Downloads;
public class CreatorDownloadResult
{
public int PaidPostCount { get; set; }
public int PostCount { get; set; }
public int ArchivedCount { get; set; }
public int StreamsCount { get; set; }
public int StoriesCount { get; set; }
public int HighlightsCount { get; set; }
public int MessagesCount { get; set; }
public int PaidMessagesCount { get; set; }
}
public class UserListResult
{
public Dictionary<string, long> Users { get; set; } = new();
public Dictionary<string, long> Lists { get; set; } = new();
public string? IgnoredListError { get; set; }
}

View File

@ -0,0 +1,37 @@
namespace OF_DL.Models.Downloads;
/// <summary>
/// Represents the result of a download operation.
/// </summary>
public class DownloadResult
{
/// <summary>
/// Total number of media items processed.
/// </summary>
public int TotalCount { get; set; }
/// <summary>
/// Number of newly downloaded media items.
/// </summary>
public int NewDownloads { get; set; }
/// <summary>
/// Number of media items that were already downloaded.
/// </summary>
public int ExistingDownloads { get; set; }
/// <summary>
/// The type of media downloaded (e.g., "Posts", "Messages", "Stories", etc.).
/// </summary>
public string MediaType { get; set; } = string.Empty;
/// <summary>
/// Indicates whether the download operation was successful.
/// </summary>
public bool Success { get; set; } = true;
/// <summary>
/// Optional error message if the download failed.
/// </summary>
public string? ErrorMessage { get; set; }
}

View File

@ -0,0 +1,17 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
namespace OF_DL.Models.Dtos.Archived;
public class ArchivedDto
{
[JsonProperty("list")] public List<ListItemDto> 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();
}

View File

@ -0,0 +1,11 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
namespace OF_DL.Models.Dtos.Archived;
public class InfoDto
{
[JsonProperty("source")] public SourceDto Source { get; set; } = new();
[JsonProperty("preview")] public PreviewDto Preview { get; set; } = new();
}

View File

@ -0,0 +1,96 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
using OF_DL.Utils;
namespace OF_DL.Models.Dtos.Archived;
public class LinkedPostDto
{
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 object StreamId { get; set; } = new();
[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<object> MentionedUsers { get; set; } = [];
[JsonProperty("linkedUsers")] public List<object> LinkedUsers { get; set; } = [];
[JsonProperty("media")] public List<MediumDto> Media { get; set; } = [];
[JsonProperty("canViewMedia")] public bool? CanViewMedia { get; set; }
[JsonProperty("preview")] public List<object> Preview { get; set; } = [];
}

View File

@ -0,0 +1,97 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
using OF_DL.Utils;
namespace OF_DL.Models.Dtos.Archived;
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 object StreamId { get; set; } = new();
[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("commentsCount")] public int? CommentsCount { get; set; }
[JsonProperty("mentionedUsers")] public List<object> MentionedUsers { get; set; } = [];
[JsonProperty("linkedUsers")] public List<object> LinkedUsers { get; set; } = [];
[JsonProperty("media")] public List<MediumDto> Media { get; set; } = [];
[JsonProperty("canViewMedia")] public bool? CanViewMedia { get; set; }
[JsonProperty("preview")] public List<object> Preview { get; set; } = [];
[JsonProperty("cantCommentReason")] public string CantCommentReason { get; set; } = "";
}

View File

@ -0,0 +1,35 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
namespace OF_DL.Models.Dtos.Archived;
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; } = new();
[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("files")] public FilesDto Files { get; set; } = new();
[JsonProperty("videoSources")] public VideoSourcesDto VideoSources { get; set; } = new();
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class AuthorDto
{
[JsonProperty("id")] public long Id { get; set; }
[JsonProperty("_view")] public string View { get; set; } = "";
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class AvatarThumbsDto
{
[JsonProperty("c50")] public string C50 { get; set; } = "";
[JsonProperty("c144")] public string C144 { get; set; } = "";
}

View File

@ -0,0 +1,20 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class CountersDto
{
[JsonProperty("audiosCount")] public int? AudiosCount { get; set; }
[JsonProperty("photosCount")] public int? PhotosCount { get; set; }
[JsonProperty("videosCount")] public int? VideosCount { get; set; }
[JsonProperty("mediasCount")] public int? MediasCount { get; set; }
[JsonProperty("postsCount")] public int? PostsCount { get; set; }
[JsonProperty("streamsCount")] public int? StreamsCount { get; set; }
[JsonProperty("archivedPostsCount")] public int? ArchivedPostsCount { get; set; }
}

View File

@ -0,0 +1,13 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class DashDto
{
[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; } = "";
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class DrmDto
{
[JsonProperty("manifest")] public ManifestDto Manifest { get; set; } = new();
[JsonProperty("signature")] public SignatureDto Signature { get; set; } = new();
}

View File

@ -0,0 +1,17 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class FilesDto
{
[JsonProperty("full")] public FullDto Full { get; set; } = new();
[JsonProperty("thumb")] public ThumbDto Thumb { get; set; } = new();
[JsonProperty("preview")] public PreviewDto Preview { get; set; } = new();
[JsonProperty("squarePreview")] public SquarePreviewDto SquarePreview { get; set; } = new();
[JsonProperty("drm")] public DrmDto? Drm { get; set; }
}

View File

@ -0,0 +1,16 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class FullDto
{
[JsonProperty("url")] public string Url { get; set; } = "";
[JsonProperty("width")] public int Width { get; set; }
[JsonProperty("height")] public int Height { get; set; }
[JsonProperty("size")] public long Size { get; set; }
[JsonProperty("sources")] public List<object> Sources { get; set; } = [];
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class HeaderSizeDto
{
[JsonProperty("width")] public int Width { get; set; }
[JsonProperty("height")] public int Height { get; set; }
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class HeaderThumbsDto
{
[JsonProperty("w480")] public string W480 { get; set; } = "";
[JsonProperty("w760")] public string W760 { get; set; } = "";
}

View File

@ -0,0 +1,13 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class HlsDto
{
[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; } = "";
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class ManifestDto
{
[JsonProperty("hls")] public string Hls { get; set; } = "";
[JsonProperty("dash")] public string Dash { get; set; } = "";
}

View File

@ -0,0 +1,16 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class PreviewDto
{
[JsonProperty("width")] public int? Width { get; set; }
[JsonProperty("height")] public int? Height { get; set; }
[JsonProperty("size")] public int? Size { get; set; }
[JsonProperty("url")] public string Url { get; set; } = "";
[JsonProperty("sources")] public SourcesDto Sources { get; set; } = new();
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class SignatureDto
{
[JsonProperty("hls")] public HlsDto Hls { get; set; } = new();
[JsonProperty("dash")] public DashDto Dash { get; set; } = new();
}

View File

@ -0,0 +1,18 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class SourceDto
{
[JsonProperty("url")] public string Url { get; set; } = "";
[JsonProperty("width")] public int Width { get; set; }
[JsonProperty("height")] public int Height { get; set; }
[JsonProperty("duration")] public int Duration { get; set; }
[JsonProperty("size")] public long Size { get; set; }
[JsonProperty("sources")] public SourcesDto Sources { get; set; } = new();
}

View File

@ -0,0 +1,14 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class SourcesDto
{
[JsonProperty("720")] public string _720 { get; set; } = "";
[JsonProperty("240")] public string _240 { get; set; } = "";
[JsonProperty("w150")] public string W150 { get; set; } = "";
[JsonProperty("w480")] public string W480 { get; set; } = "";
}

View File

@ -0,0 +1,16 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class SquarePreviewDto
{
[JsonProperty("url")] public string Url { get; set; } = "";
[JsonProperty("width")] public int Width { get; set; }
[JsonProperty("height")] public int Height { get; set; }
[JsonProperty("size")] public long Size { get; set; }
[JsonProperty("sources")] public SourcesDto Sources { get; set; } = new();
}

View File

@ -0,0 +1,44 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Subscriptions;
namespace OF_DL.Models.Dtos.Common;
public class SubscribedByDataDto
{
[JsonProperty("price")] public string? Price { get; set; }
[JsonProperty("newPrice")] public string? NewPrice { get; set; }
[JsonProperty("regularPrice")] public string? RegularPrice { get; set; }
[JsonProperty("subscribePrice")] public string? SubscribePrice { get; set; }
[JsonProperty("discountPercent")] public int? DiscountPercent { get; set; }
[JsonProperty("discountPeriod")] public int? DiscountPeriod { get; set; }
[JsonProperty("subscribeAt")] public DateTime? SubscribeAt { get; set; }
[JsonProperty("expiredAt")] public DateTime? ExpiredAt { get; set; }
[JsonProperty("renewedAt")] public DateTime? RenewedAt { get; set; }
[JsonProperty("discountFinishedAt")] public object? DiscountFinishedAt { get; set; } = new();
[JsonProperty("discountStartedAt")] public object? DiscountStartedAt { get; set; } = new();
[JsonProperty("status")] public string Status { get; set; } = "";
[JsonProperty("isMuted")] public bool? IsMuted { get; set; }
[JsonProperty("unsubscribeReason")] public string UnsubscribeReason { get; set; } = "";
[JsonProperty("duration")] public string Duration { get; set; } = "";
[JsonProperty("showPostsInFeed")] public bool? ShowPostsInFeed { get; set; }
[JsonProperty("subscribes")] public List<SubscribeDto> Subscribes { get; set; } = [];
[JsonProperty("hasActivePaidSubscriptions")]
public bool? HasActivePaidSubscriptions { get; set; }
}

View File

@ -0,0 +1,54 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Subscriptions;
namespace OF_DL.Models.Dtos.Common;
public class SubscribedOnDataDto
{
[JsonProperty("price")] public string? Price { get; set; }
[JsonProperty("newPrice")] public string? NewPrice { get; set; }
[JsonProperty("regularPrice")] public string? RegularPrice { get; set; }
[JsonProperty("subscribePrice")] public string? SubscribePrice { get; set; }
[JsonProperty("discountPercent")] public int? DiscountPercent { get; set; }
[JsonProperty("discountPeriod")] public int? DiscountPeriod { get; set; }
[JsonProperty("subscribeAt")] public DateTime? SubscribeAt { get; set; }
[JsonProperty("expiredAt")] public DateTime? ExpiredAt { get; set; }
[JsonProperty("renewedAt")] public DateTime? RenewedAt { get; set; }
[JsonProperty("discountFinishedAt")] public object? DiscountFinishedAt { get; set; } = new();
[JsonProperty("discountStartedAt")] public object? DiscountStartedAt { get; set; } = new();
[JsonProperty("status")] public object? Status { get; set; }
[JsonProperty("isMuted")] public bool? IsMuted { get; set; }
[JsonProperty("unsubscribeReason")] public string? UnsubscribeReason { get; set; } = "";
[JsonProperty("duration")] public string Duration { get; set; } = "";
[JsonProperty("tipsSumm")] public string? TipsSumm { get; set; }
[JsonProperty("subscribesSumm")] public string? SubscribesSumm { get; set; }
[JsonProperty("messagesSumm")] public string? MessagesSumm { get; set; }
[JsonProperty("postsSumm")] public string? PostsSumm { get; set; }
[JsonProperty("streamsSumm")] public string? StreamsSumm { get; set; }
[JsonProperty("totalSumm")] public string? TotalSumm { get; set; }
[JsonProperty("subscribes")] public List<SubscribeDto> Subscribes { get; set; } = [];
[JsonProperty("hasActivePaidSubscriptions")]
public bool? HasActivePaidSubscriptions { get; set; }
}

View File

@ -0,0 +1,14 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class ThumbDto
{
[JsonProperty("url")] public string Url { get; set; } = "";
[JsonProperty("width")] public int Width { get; set; }
[JsonProperty("height")] public int Height { get; set; }
[JsonProperty("size")] public long Size { get; set; }
}

View File

@ -0,0 +1,8 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class VideoDto
{
[JsonProperty("mp4")] public string Mp4 { get; set; } = "";
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Common;
public class VideoSourcesDto
{
[JsonProperty("720")] public string _720 { get; set; } = "";
[JsonProperty("240")] public string _240 { get; set; } = "";
}

View File

@ -0,0 +1,22 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Highlights;
public class HighlightMediaDto
{
[JsonProperty("id")] public long Id { get; set; }
[JsonProperty("userId")] public long UserId { get; set; }
[JsonProperty("title")] public string Title { get; set; } = "";
[JsonProperty("coverStoryId")] public long CoverStoryId { get; set; }
[JsonProperty("cover")] public string Cover { get; set; } = "";
[JsonProperty("storiesCount")] public int StoriesCount { get; set; }
[JsonProperty("createdAt")] public DateTime? CreatedAt { get; set; }
[JsonProperty("stories")] public List<StoryDto> Stories { get; set; } = [];
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Highlights;
public class HighlightsDto
{
[JsonProperty("list")] public List<ListItemDto> List { get; set; } = [];
[JsonProperty("hasMore")] public bool HasMore { get; set; }
}

View File

@ -0,0 +1,20 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Highlights;
public class ListItemDto
{
[JsonProperty("id")] public long Id { get; set; }
[JsonProperty("userId")] public long UserId { get; set; }
[JsonProperty("title")] public string Title { get; set; } = "";
[JsonProperty("coverStoryId")] public long CoverStoryId { get; set; }
[JsonProperty("cover")] public string Cover { get; set; } = "";
[JsonProperty("storiesCount")] public int StoriesCount { get; set; }
[JsonProperty("createdAt")] public DateTime? CreatedAt { get; set; }
}

View File

@ -0,0 +1,21 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
namespace OF_DL.Models.Dtos.Highlights;
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("files")] public FilesDto Files { get; set; } = new();
}

View File

@ -0,0 +1,24 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Highlights;
public class StoryDto
{
[JsonProperty("id")] public long Id { get; set; }
[JsonProperty("userId")] public long UserId { get; set; }
[JsonProperty("isWatched")] public bool IsWatched { get; set; }
[JsonProperty("isReady")] public bool IsReady { get; set; }
[JsonProperty("media")] public List<MediumDto> Media { get; set; } = [];
[JsonProperty("createdAt")] public DateTime? CreatedAt { get; set; }
[JsonProperty("question")] public object Question { get; set; } = new();
[JsonProperty("canLike")] public bool CanLike { get; set; }
[JsonProperty("isLiked")] public bool IsLiked { get; set; }
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Lists;
public class HeaderSizeDto
{
[JsonProperty("width")] public int? Width { get; set; }
[JsonProperty("height")] public int? Height { get; set; }
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Lists;
public class HeaderThumbsDto
{
[JsonProperty("w480")] public string W480 { get; set; } = "";
[JsonProperty("w760")] public string W760 { get; set; } = "";
}

View File

@ -0,0 +1,16 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Lists;
public class ListsStateDto
{
[JsonProperty("id")] public string Id { get; set; } = "";
[JsonProperty("type")] public string Type { get; set; } = "";
[JsonProperty("name")] public string Name { get; set; } = "";
[JsonProperty("hasUser")] public bool HasUser { get; set; }
[JsonProperty("canAddUser")] public bool CanAddUser { get; set; }
}

View File

@ -0,0 +1,38 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Lists;
public class SubscribeDto
{
[JsonProperty("id")] public object Id { get; set; } = new();
[JsonProperty("userId")] public long? UserId { get; set; }
[JsonProperty("subscriberId")] public int? SubscriberId { get; set; }
[JsonProperty("date")] public DateTime? Date { get; set; }
[JsonProperty("duration")] public int? Duration { get; set; }
[JsonProperty("startDate")] public DateTime? StartDate { get; set; }
[JsonProperty("expireDate")] public DateTime? ExpireDate { get; set; }
[JsonProperty("cancelDate")] public object CancelDate { get; set; } = new();
[JsonProperty("price")] public string? Price { get; set; }
[JsonProperty("regularPrice")] public string? RegularPrice { get; set; }
[JsonProperty("discount")] public string? Discount { get; set; }
[JsonProperty("action")] public string Action { get; set; } = "";
[JsonProperty("type")] public string Type { get; set; } = "";
[JsonProperty("offerStart")] public object OfferStart { get; set; } = new();
[JsonProperty("offerEnd")] public object OfferEnd { get; set; } = new();
[JsonProperty("isCurrent")] public bool? IsCurrent { get; set; }
}

View File

@ -0,0 +1,40 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Lists;
public class SubscribedByDataDto
{
[JsonProperty("price")] public string? Price { get; set; }
[JsonProperty("newPrice")] public string? NewPrice { get; set; }
[JsonProperty("regularPrice")] public string? RegularPrice { get; set; }
[JsonProperty("subscribePrice")] public string? SubscribePrice { get; set; }
[JsonProperty("discountPercent")] public string? DiscountPercent { get; set; }
[JsonProperty("discountPeriod")] public string? DiscountPeriod { get; set; }
[JsonProperty("subscribeAt")] public DateTime? SubscribeAt { get; set; }
[JsonProperty("expiredAt")] public DateTime? ExpiredAt { get; set; }
[JsonProperty("renewedAt")] public object RenewedAt { get; set; } = new();
[JsonProperty("discountFinishedAt")] public object DiscountFinishedAt { get; set; } = new();
[JsonProperty("discountStartedAt")] public object DiscountStartedAt { get; set; } = new();
[JsonProperty("status")] public string Status { get; set; } = "";
[JsonProperty("isMuted")] public bool? IsMuted { get; set; }
[JsonProperty("unsubscribeReason")] public string UnsubscribeReason { get; set; } = "";
[JsonProperty("duration")] public string Duration { get; set; } = "";
[JsonProperty("showPostsInFeed")] public bool? ShowPostsInFeed { get; set; }
[JsonProperty("subscribes")] public List<SubscribeDto> Subscribes { get; set; } = [];
}

View File

@ -0,0 +1,54 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Lists;
public class SubscribedOnDataDto
{
[JsonProperty("price")] public string? Price { get; set; }
[JsonProperty("newPrice")] public string? NewPrice { get; set; }
[JsonProperty("regularPrice")] public string? RegularPrice { get; set; }
[JsonProperty("subscribePrice")] public string? SubscribePrice { get; set; }
[JsonProperty("discountPercent")] public string? DiscountPercent { get; set; }
[JsonProperty("discountPeriod")] public string? DiscountPeriod { get; set; }
[JsonProperty("subscribeAt")] public DateTime? SubscribeAt { get; set; }
[JsonProperty("expiredAt")] public DateTime? ExpiredAt { get; set; }
[JsonProperty("renewedAt")] public object RenewedAt { get; set; } = new();
[JsonProperty("discountFinishedAt")] public object DiscountFinishedAt { get; set; } = new();
[JsonProperty("discountStartedAt")] public object DiscountStartedAt { get; set; } = new();
[JsonProperty("status")] public object Status { get; set; } = new();
[JsonProperty("isMuted")] public bool? IsMuted { get; set; }
[JsonProperty("unsubscribeReason")] public string UnsubscribeReason { get; set; } = "";
[JsonProperty("duration")] public string Duration { get; set; } = "";
[JsonProperty("tipsSumm")] public string? TipsSumm { get; set; }
[JsonProperty("subscribesSumm")] public string? SubscribesSumm { get; set; }
[JsonProperty("messagesSumm")] public string? MessagesSumm { get; set; }
[JsonProperty("postsSumm")] public string? PostsSumm { get; set; }
[JsonProperty("streamsSumm")] public string? StreamsSumm { get; set; }
[JsonProperty("totalSumm")] public string? TotalSumm { get; set; }
[JsonProperty("lastActivity")] public DateTime? LastActivity { get; set; }
[JsonProperty("recommendations")] public int? Recommendations { get; set; }
[JsonProperty("subscribes")] public List<object> Subscribes { get; set; } = [];
}

View File

@ -0,0 +1,16 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Lists;
public class SubscriptionBundleDto
{
[JsonProperty("id")] public long? Id { get; set; }
[JsonProperty("discount")] public string? Discount { get; set; }
[JsonProperty("duration")] public string? Duration { get; set; }
[JsonProperty("price")] public string? Price { get; set; }
[JsonProperty("canBuy")] public bool? CanBuy { get; set; }
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Lists;
public class UserListDto
{
[JsonProperty("list")] public List<UserListItemDto> List { get; set; } = [];
[JsonProperty("hasMore")] public bool? HasMore { get; set; }
}

View File

@ -0,0 +1,42 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Lists;
public class UserListItemDto
{
[JsonProperty("id")] public string Id { get; set; } = "";
[JsonProperty("type")] public string Type { get; set; } = "";
[JsonProperty("name")] public string Name { get; set; } = "";
[JsonProperty("usersCount")] public int? UsersCount { get; set; }
[JsonProperty("postsCount")] public int? PostsCount { get; set; }
[JsonProperty("canUpdate")] public bool? CanUpdate { get; set; }
[JsonProperty("canDelete")] public bool? CanDelete { get; set; }
[JsonProperty("canManageUsers")] public bool? CanManageUsers { get; set; }
[JsonProperty("canAddUsers")] public bool? CanAddUsers { get; set; }
[JsonProperty("canPinnedToFeed")] public bool? CanPinnedToFeed { get; set; }
[JsonProperty("isPinnedToFeed")] public bool? IsPinnedToFeed { get; set; }
[JsonProperty("canPinnedToChat")] public bool? CanPinnedToChat { get; set; }
[JsonProperty("isPinnedToChat")] public bool? IsPinnedToChat { get; set; }
[JsonProperty("order")] public string Order { get; set; } = "";
[JsonProperty("direction")] public string Direction { get; set; } = "";
[JsonProperty("users")] public List<UserListUserDto> Users { get; set; } = [];
[JsonProperty("customOrderUsersIds")] public List<object> CustomOrderUsersIds { get; set; } = [];
[JsonProperty("posts")] public List<object> Posts { get; set; } = [];
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Lists;
public class UserListUserDto
{
[JsonProperty("id")] public long? Id { get; set; }
[JsonProperty("_view")] public string View { get; set; } = "";
}

View File

@ -0,0 +1,121 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
namespace OF_DL.Models.Dtos.Lists;
public class UsersListDto
{
[JsonProperty("view")] public string View { get; set; } = "";
[JsonProperty("avatar")] public string Avatar { get; set; } = "";
[JsonProperty("avatarThumbs")] public AvatarThumbsDto AvatarThumbs { get; set; } = new();
[JsonProperty("header")] public string Header { get; set; } = "";
[JsonProperty("headerSize")] public HeaderSizeDto HeaderSize { get; set; } = new();
[JsonProperty("headerThumbs")] public HeaderThumbsDto HeaderThumbs { get; set; } = new();
[JsonProperty("id")] public long? Id { get; set; }
[JsonProperty("name")] public string Name { get; set; } = "";
[JsonProperty("username")] public string Username { get; set; } = "";
[JsonProperty("canLookStory")] public bool? CanLookStory { get; set; }
[JsonProperty("canCommentStory")] public bool? CanCommentStory { get; set; }
[JsonProperty("hasNotViewedStory")] public bool? HasNotViewedStory { get; set; }
[JsonProperty("isVerified")] public bool? IsVerified { get; set; }
[JsonProperty("canPayInternal")] public bool? CanPayInternal { get; set; }
[JsonProperty("hasScheduledStream")] public bool? HasScheduledStream { get; set; }
[JsonProperty("hasStream")] public bool? HasStream { get; set; }
[JsonProperty("hasStories")] public bool? HasStories { get; set; }
[JsonProperty("tipsEnabled")] public bool? TipsEnabled { get; set; }
[JsonProperty("tipsTextEnabled")] public bool? TipsTextEnabled { get; set; }
[JsonProperty("tipsMin")] public int? TipsMin { get; set; }
[JsonProperty("tipsMinInternal")] public int? TipsMinInternal { get; set; }
[JsonProperty("tipsMax")] public int? TipsMax { get; set; }
[JsonProperty("canEarn")] public bool? CanEarn { get; set; }
[JsonProperty("canAddSubscriber")] public bool? CanAddSubscriber { get; set; }
[JsonProperty("subscribePrice")] public string? SubscribePrice { get; set; }
[JsonProperty("subscriptionBundles")] public List<SubscriptionBundleDto> SubscriptionBundles { get; set; } = [];
[JsonProperty("displayName")] public string DisplayName { get; set; } = "";
[JsonProperty("notice")] public string Notice { get; set; } = "";
[JsonProperty("isPaywallRequired")] public bool? IsPaywallRequired { get; set; }
[JsonProperty("unprofitable")] public bool? Unprofitable { get; set; }
[JsonProperty("listsStates")] public List<ListsStateDto> ListsStates { get; set; } = [];
[JsonProperty("isMuted")] public bool? IsMuted { get; set; }
[JsonProperty("isRestricted")] public bool? IsRestricted { get; set; }
[JsonProperty("canRestrict")] public bool? CanRestrict { get; set; }
[JsonProperty("subscribedBy")] public bool? SubscribedBy { get; set; }
[JsonProperty("subscribedByExpire")] public bool? SubscribedByExpire { get; set; }
[JsonProperty("subscribedByExpireDate")]
public DateTime? SubscribedByExpireDate { get; set; }
[JsonProperty("subscribedByAutoprolong")]
public bool? SubscribedByAutoprolong { get; set; }
[JsonProperty("subscribedIsExpiredNow")]
public bool? SubscribedIsExpiredNow { get; set; }
[JsonProperty("currentSubscribePrice")]
public string? CurrentSubscribePrice { get; set; }
[JsonProperty("subscribedOn")] public bool? SubscribedOn { get; set; }
[JsonProperty("subscribedOnExpiredNow")]
public bool? SubscribedOnExpiredNow { get; set; }
[JsonProperty("subscribedOnDuration")] public string SubscribedOnDuration { get; set; } = "";
[JsonProperty("canReport")] public bool? CanReport { get; set; }
[JsonProperty("canReceiveChatMessage")]
public bool? CanReceiveChatMessage { get; set; }
[JsonProperty("hideChat")] public bool? HideChat { get; set; }
[JsonProperty("lastSeen")] public DateTime? LastSeen { get; set; }
[JsonProperty("isPerformer")] public bool? IsPerformer { get; set; }
[JsonProperty("isRealPerformer")] public bool? IsRealPerformer { get; set; }
[JsonProperty("subscribedByData")] public SubscribedByDataDto SubscribedByData { get; set; } = new();
[JsonProperty("subscribedOnData")] public SubscribedOnDataDto SubscribedOnData { get; set; } = new();
[JsonProperty("canTrialSend")] public bool? CanTrialSend { get; set; }
[JsonProperty("isBlocked")] public bool? IsBlocked { get; set; }
[JsonProperty("promoOffers")] public List<object> PromoOffers { get; set; } = [];
}

View File

@ -0,0 +1,98 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
namespace OF_DL.Models.Dtos.Messages;
public class FromUserDto
{
[JsonProperty("_view")] public string ViewRaw { get; set; } = "";
[JsonProperty("view")] public string View { get; set; } = "";
[JsonProperty("avatar")] public string Avatar { get; set; } = "";
[JsonProperty("avatarThumbs")] public AvatarThumbsDto AvatarThumbs { get; set; } = new();
[JsonProperty("header")] public string Header { get; set; } = "";
[JsonProperty("headerSize")] public HeaderSizeDto HeaderSize { get; set; } = new();
[JsonProperty("headerThumbs")] public HeaderThumbsDto HeaderThumbs { get; set; } = new();
[JsonProperty("id")] public long? Id { get; set; }
[JsonProperty("name")] public string Name { get; set; } = "";
[JsonProperty("username")] public string Username { get; set; } = "";
[JsonProperty("canLookStory")] public bool CanLookStory { get; set; }
[JsonProperty("canCommentStory")] public bool CanCommentStory { get; set; }
[JsonProperty("hasNotViewedStory")] public bool HasNotViewedStory { get; set; }
[JsonProperty("isVerified")] public bool IsVerified { get; set; }
[JsonProperty("canPayInternal")] public bool CanPayInternal { get; set; }
[JsonProperty("hasScheduledStream")] public bool HasScheduledStream { get; set; }
[JsonProperty("hasStream")] public bool HasStream { get; set; }
[JsonProperty("hasStories")] public bool HasStories { get; set; }
[JsonProperty("tipsEnabled")] public bool TipsEnabled { get; set; }
[JsonProperty("tipsTextEnabled")] public bool TipsTextEnabled { get; set; }
[JsonProperty("tipsMin")] public int TipsMin { get; set; }
[JsonProperty("tipsMinInternal")] public int TipsMinInternal { get; set; }
[JsonProperty("tipsMax")] public int TipsMax { get; set; }
[JsonProperty("canEarn")] public bool CanEarn { get; set; }
[JsonProperty("canAddSubscriber")] public bool CanAddSubscriber { get; set; }
[JsonProperty("subscribePrice")] public string SubscribePrice { get; set; } = "";
[JsonProperty("subscriptionBundles")] public List<object> SubscriptionBundles { get; set; } = [];
[JsonProperty("isPaywallRequired")] public bool IsPaywallRequired { get; set; }
[JsonProperty("listsStates")] public List<ListsStateDto> ListsStates { get; set; } = [];
[JsonProperty("isRestricted")] public bool IsRestricted { get; set; }
[JsonProperty("canRestrict")] public bool CanRestrict { get; set; }
[JsonProperty("subscribedBy")] public object SubscribedBy { get; set; } = new();
[JsonProperty("subscribedByExpire")] public object SubscribedByExpire { get; set; } = new();
[JsonProperty("subscribedByExpireDate")]
public DateTime? SubscribedByExpireDate { get; set; }
[JsonProperty("subscribedByAutoprolong")]
public object SubscribedByAutoprolong { get; set; } = new();
[JsonProperty("subscribedIsExpiredNow")]
public bool SubscribedIsExpiredNow { get; set; }
[JsonProperty("currentSubscribePrice")]
public object CurrentSubscribePrice { get; set; } = new();
[JsonProperty("subscribedOn")] public object SubscribedOn { get; set; } = new();
[JsonProperty("subscribedOnExpiredNow")]
public object SubscribedOnExpiredNow { get; set; } = new();
[JsonProperty("subscribedOnDuration")] public object SubscribedOnDuration { get; set; } = new();
[JsonProperty("callPrice")] public int CallPrice { get; set; }
[JsonProperty("lastSeen")] public DateTime? LastSeen { get; set; }
[JsonProperty("canReport")] public bool CanReport { get; set; }
}

View File

@ -0,0 +1,11 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
namespace OF_DL.Models.Dtos.Messages;
public class InfoDto
{
[JsonProperty("source")] public SourceDto Source { get; set; } = new();
[JsonProperty("preview")] public PreviewDto Preview { get; set; } = new();
}

View File

@ -0,0 +1,66 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Messages;
public class ListItemDto
{
[JsonProperty("responseType")] public string ResponseType { get; set; } = "";
[JsonProperty("text")] public string Text { get; set; } = "";
[JsonProperty("giphyId")] public object GiphyId { get; set; } = new();
[JsonProperty("lockedText")] public bool? LockedText { get; set; }
[JsonProperty("isFree")] public bool? IsFree { get; set; }
[JsonProperty("price")] public string Price { get; set; } = "";
[JsonProperty("isMediaReady")] public bool? IsMediaReady { get; set; }
[JsonProperty("mediaCount")] public int? MediaCount { get; set; }
[JsonProperty("media")] public List<MediumDto> Media { get; set; } = [];
[JsonProperty("previews")] public List<object> Previews { get; set; } = [];
[JsonProperty("isTip")] public bool? IsTip { get; set; }
[JsonProperty("isReportedByMe")] public bool? IsReportedByMe { get; set; }
[JsonProperty("isCouplePeopleMedia")] public bool? IsCouplePeopleMedia { get; set; }
[JsonProperty("queueId")] public object QueueId { get; set; } = new();
[JsonProperty("fromUser")] public FromUserDto FromUser { get; set; } = new();
[JsonProperty("isFromQueue")] public bool? IsFromQueue { get; set; }
[JsonProperty("canUnsendQueue")] public bool? CanUnsendQueue { get; set; }
[JsonProperty("unsendSecondsQueue")] public int? UnsendSecondsQueue { get; set; }
[JsonProperty("id")] public long Id { get; set; }
[JsonProperty("isOpened")] public bool? IsOpened { get; set; }
[JsonProperty("isNew")] public bool? IsNew { get; set; }
[JsonProperty("createdAt")] public DateTime? CreatedAt { get; set; }
[JsonProperty("changedAt")] public DateTime? ChangedAt { get; set; }
[JsonProperty("cancelSeconds")] public int? CancelSeconds { get; set; }
[JsonProperty("isLiked")] public bool? IsLiked { get; set; }
[JsonProperty("canPurchase")] public bool? CanPurchase { get; set; }
[JsonProperty("canPurchaseReason")] public string CanPurchaseReason { get; set; } = "";
[JsonProperty("canReport")] public bool? CanReport { get; set; }
[JsonProperty("canBePinned")] public bool? CanBePinned { get; set; }
[JsonProperty("isPinned")] public bool? IsPinned { get; set; }
}

View File

@ -0,0 +1,18 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Messages;
public class ListsStateDto
{
[JsonProperty("id")] public string Id { get; set; } = "";
[JsonProperty("type")] public string Type { get; set; } = "";
[JsonProperty("name")] public string Name { get; set; } = "";
[JsonProperty("hasUser")] public bool HasUser { get; set; }
[JsonProperty("canAddUser")] public bool CanAddUser { get; set; }
[JsonProperty("cannotAddUserReason")] public string CannotAddUserReason { get; set; } = "";
}

View File

@ -0,0 +1,37 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
namespace OF_DL.Models.Dtos.Messages;
public class MediumDto
{
[JsonProperty("id")] public long Id { get; set; }
[JsonProperty("canView")] public bool CanView { get; set; }
[JsonProperty("type")] public string Type { get; set; } = "";
[JsonProperty("src")] public string Src { get; set; } = "";
[JsonProperty("preview")] public string Preview { get; set; } = "";
[JsonProperty("thumb")] public string Thumb { get; set; } = "";
[JsonProperty("locked")] public object Locked { get; set; } = new();
[JsonProperty("duration")] public int? Duration { get; set; }
[JsonProperty("hasError")] public bool? HasError { get; set; }
[JsonProperty("squarePreview")] public string SquarePreview { get; set; } = "";
[JsonProperty("video")] public VideoDto Video { get; set; } = new();
[JsonProperty("videoSources")] public VideoSourcesDto VideoSources { get; set; } = new();
[JsonProperty("source")] public SourceDto Source { get; set; } = new();
[JsonProperty("info")] public InfoDto Info { get; set; } = new();
[JsonProperty("files")] public FilesDto Files { get; set; } = new();
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Messages;
public class MessagesDto
{
[JsonProperty("list")] public List<ListItemDto> List { get; set; } = [];
[JsonProperty("hasMore")] public bool HasMore { get; set; }
}

View File

@ -0,0 +1,60 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Messages;
public class SingleMessageDto
{
[JsonProperty("responseType")] public string ResponseType { get; set; } = "";
[JsonProperty("text")] public string Text { get; set; } = "";
[JsonProperty("giphyId")] public object GiphyId { get; set; } = new();
[JsonProperty("lockedText")] public bool LockedText { get; set; }
[JsonProperty("isFree")] public bool IsFree { get; set; }
[JsonProperty("price")] public double Price { get; set; }
[JsonProperty("isMediaReady")] public bool IsMediaReady { get; set; }
[JsonProperty("mediaCount")] public int MediaCount { get; set; }
[JsonProperty("media")] public List<MediumDto> Media { get; set; } = [];
[JsonProperty("previews")] public List<object> Previews { get; set; } = [];
[JsonProperty("isTip")] public bool IsTip { get; set; }
[JsonProperty("isReportedByMe")] public bool IsReportedByMe { get; set; }
[JsonProperty("isCouplePeopleMedia")] public bool IsCouplePeopleMedia { get; set; }
[JsonProperty("queueId")] public long QueueId { get; set; }
[JsonProperty("fromUser")] public FromUserDto FromUser { get; set; } = new();
[JsonProperty("isFromQueue")] public bool IsFromQueue { get; set; }
[JsonProperty("canUnsendQueue")] public bool CanUnsendQueue { get; set; }
[JsonProperty("unsendSecondsQueue")] public int UnsendSecondsQueue { get; set; }
[JsonProperty("id")] public long Id { get; set; }
[JsonProperty("isOpened")] public bool IsOpened { get; set; }
[JsonProperty("isNew")] public bool IsNew { get; set; }
[JsonProperty("createdAt")] public DateTime? CreatedAt { get; set; }
[JsonProperty("changedAt")] public DateTime? ChangedAt { get; set; }
[JsonProperty("cancelSeconds")] public int CancelSeconds { get; set; }
[JsonProperty("isLiked")] public bool IsLiked { get; set; }
[JsonProperty("canPurchase")] public bool CanPurchase { get; set; }
[JsonProperty("canReport")] public bool CanReport { get; set; }
}

View File

@ -0,0 +1,11 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
namespace OF_DL.Models.Dtos.Posts;
public class InfoDto
{
[JsonProperty("source")] public SourceDto Source { get; set; } = new();
[JsonProperty("preview")] public PreviewDto Preview { get; set; } = new();
}

View File

@ -0,0 +1,101 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
using OF_DL.Utils;
namespace OF_DL.Models.Dtos.Posts;
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 object StreamId { get; set; } = new();
[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("votingType")] public int? VotingType { get; set; }
[JsonProperty("commentsCount")] public int? CommentsCount { get; set; }
[JsonProperty("mentionedUsers")] public List<object> MentionedUsers { get; set; } = [];
[JsonProperty("linkedUsers")] public List<object> LinkedUsers { get; set; } = [];
[JsonProperty("canVote")] public bool? CanVote { get; set; }
[JsonProperty("media")] public List<MediumDto> Media { get; set; } = [];
[JsonProperty("canViewMedia")] public bool? CanViewMedia { get; set; }
[JsonProperty("preview")] public List<object> Preview { get; set; } = [];
}

View File

@ -0,0 +1,37 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
namespace OF_DL.Models.Dtos.Posts;
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; }
[JsonProperty("source")] public SourceDto? Source { get; set; }
[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();
}

View File

@ -0,0 +1,14 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Posts;
public class PostDto
{
[JsonProperty("list")] public List<ListItemDto> List { get; set; } = [];
[JsonProperty("hasMore")] public bool HasMore { get; set; }
[JsonProperty("headMarker")] public string HeadMarker { get; set; } = "";
[JsonProperty("tailMarker")] public string TailMarker { get; set; } = "";
}

View File

@ -0,0 +1,99 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
using OF_DL.Utils;
namespace OF_DL.Models.Dtos.Posts;
public class SinglePostDto
{
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 string 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("commentsCount")] public int CommentsCount { get; set; }
[JsonProperty("mentionedUsers")] public List<object> MentionedUsers { get; set; } = [];
[JsonProperty("linkedUsers")] public List<object> LinkedUsers { get; set; } = [];
[JsonProperty("tipsAmount")] public string TipsAmount { get; set; } = "";
[JsonProperty("tipsAmountRaw")] public string TipsAmountRaw { get; set; } = "";
[JsonProperty("media")] public List<MediumDto> Media { get; set; } = [];
[JsonProperty("canViewMedia")] public bool CanViewMedia { get; set; }
[JsonProperty("preview")] public List<object> Preview { get; set; } = [];
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Purchased;
public class FromUserDto
{
[JsonProperty("id")] public long Id { get; set; }
[JsonProperty("_view")] public string View { get; set; } = "";
}

View File

@ -0,0 +1,72 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
using MessageDtos = OF_DL.Models.Dtos.Messages;
namespace OF_DL.Models.Dtos.Purchased;
public class ListItemDto
{
[JsonProperty("responseType")] public string ResponseType { get; set; } = "";
[JsonProperty("text")] public string Text { get; set; } = "";
[JsonProperty("giphyId")] public object GiphyId { get; set; } = new();
[JsonProperty("lockedText")] public bool? LockedText { get; set; }
[JsonProperty("isFree")] public bool? IsFree { get; set; }
[JsonProperty("price")] public string? Price { get; set; }
[JsonProperty("isMediaReady")] public bool? IsMediaReady { get; set; }
[JsonProperty("mediaCount")] public int? MediaCount { get; set; }
[JsonProperty("media")] public List<MessageDtos.MediumDto>? Media { get; set; }
[JsonProperty("previews")] public List<object>? Previews { get; set; }
[JsonProperty("preview")] public List<object>? Preview { get; set; }
[JsonProperty("isTip")] public bool? IsTip { get; set; }
[JsonProperty("isReportedByMe")] public bool? IsReportedByMe { get; set; }
[JsonProperty("isCouplePeopleMedia")] public bool? IsCouplePeopleMedia { get; set; }
[JsonProperty("queueId")] public object QueueId { get; set; } = new();
[JsonProperty("fromUser")] public FromUserDto? FromUser { get; set; }
[JsonProperty("author")] public AuthorDto? Author { get; set; }
[JsonProperty("isFromQueue")] public bool? IsFromQueue { get; set; }
[JsonProperty("canUnsendQueue")] public bool? CanUnsendQueue { get; set; }
[JsonProperty("unsendSecondsQueue")] public int? UnsendSecondsQueue { get; set; }
[JsonProperty("id")] public long Id { get; set; }
[JsonProperty("isOpened")] public bool IsOpened { get; set; }
[JsonProperty("isNew")] public bool? IsNew { get; set; }
[JsonProperty("createdAt")] public DateTime? CreatedAt { get; set; }
[JsonProperty("postedAt")] public DateTime? PostedAt { get; set; }
[JsonProperty("changedAt")] public DateTime? ChangedAt { get; set; }
[JsonProperty("cancelSeconds")] public int? CancelSeconds { get; set; }
[JsonProperty("isLiked")] public bool? IsLiked { get; set; }
[JsonProperty("canPurchase")] public bool? CanPurchase { get; set; }
[JsonProperty("canReport")] public bool? CanReport { get; set; }
[JsonProperty("isCanceled")] public bool? IsCanceled { get; set; }
[JsonProperty("isArchived")] public bool? IsArchived { get; set; }
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Purchased;
public class PurchasedDto
{
[JsonProperty("list")] public List<ListItemDto> List { get; set; } = [];
[JsonProperty("hasMore")] public bool HasMore { get; set; }
}

View File

@ -0,0 +1,21 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
namespace OF_DL.Models.Dtos.Stories;
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("files")] public FilesDto Files { get; set; } = new();
}

View File

@ -0,0 +1,24 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Stories;
public class StoryDto
{
[JsonProperty("id")] public long Id { get; set; }
[JsonProperty("userId")] public long UserId { get; set; }
[JsonProperty("isWatched")] public bool IsWatched { get; set; }
[JsonProperty("isReady")] public bool IsReady { get; set; }
[JsonProperty("media")] public List<MediumDto> Media { get; set; } = [];
[JsonProperty("createdAt")] public DateTime? CreatedAt { get; set; }
[JsonProperty("question")] public object Question { get; set; } = new();
[JsonProperty("canLike")] public bool CanLike { get; set; }
[JsonProperty("isLiked")] public bool IsLiked { get; set; }
}

View File

@ -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();
}

View File

@ -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<object> MentionedUsers { get; set; } = [];
[JsonProperty("linkedUsers")] public List<object> LinkedUsers { get; set; } = [];
[JsonProperty("tipsAmount")] public string TipsAmount { get; set; } = "";
[JsonProperty("tipsAmountRaw")] public string TipsAmountRaw { get; set; } = "";
[JsonProperty("media")] public List<MediumDto> Media { get; set; } = [];
[JsonProperty("canViewMedia")] public bool? CanViewMedia { get; set; }
[JsonProperty("preview")] public List<object> Preview { get; set; } = [];
}

View File

@ -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();
}

View File

@ -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<ListItemDto> 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();
}

View File

@ -0,0 +1,111 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
namespace OF_DL.Models.Dtos.Subscriptions;
public class ListItemDto
{
[JsonProperty("view")] public string View { get; set; } = "";
[JsonProperty("avatar")] public string? Avatar { get; set; }
[JsonProperty("avatarThumbs")] public AvatarThumbsDto AvatarThumbs { get; set; } = new();
[JsonProperty("header")] public string? Header { get; set; }
[JsonProperty("headerSize")] public HeaderSizeDto HeaderSize { get; set; } = new();
[JsonProperty("headerThumbs")] public HeaderThumbsDto HeaderThumbs { get; set; } = new();
[JsonProperty("id")] public long Id { get; set; }
[JsonProperty("name")] public string Name { get; set; } = "";
[JsonProperty("username")] public string? Username { get; set; }
[JsonProperty("canLookStory")] public bool? CanLookStory { get; set; }
[JsonProperty("canCommentStory")] public bool? CanCommentStory { get; set; }
[JsonProperty("hasNotViewedStory")] public bool? HasNotViewedStory { get; set; }
[JsonProperty("isVerified")] public bool? IsVerified { get; set; }
[JsonProperty("canPayInternal")] public bool? CanPayInternal { get; set; }
[JsonProperty("hasScheduledStream")] public bool? HasScheduledStream { get; set; }
[JsonProperty("hasStream")] public bool? HasStream { get; set; }
[JsonProperty("hasStories")] public bool? HasStories { get; set; }
[JsonProperty("tipsEnabled")] public bool? TipsEnabled { get; set; }
[JsonProperty("tipsTextEnabled")] public bool? TipsTextEnabled { get; set; }
[JsonProperty("tipsMin")] public int? TipsMin { get; set; }
[JsonProperty("tipsMinInternal")] public int? TipsMinInternal { get; set; }
[JsonProperty("tipsMax")] public int? TipsMax { get; set; }
[JsonProperty("canEarn")] public bool? CanEarn { get; set; }
[JsonProperty("canAddSubscriber")] public bool? CanAddSubscriber { get; set; }
[JsonProperty("subscribePrice")] public string? SubscribePrice { get; set; }
[JsonProperty("isPaywallRequired")] public bool? IsPaywallRequired { get; set; }
[JsonProperty("unprofitable")] public bool? Unprofitable { get; set; }
[JsonProperty("listsStates")] public List<ListsStateDto> ListsStates { get; set; } = [];
[JsonProperty("isMuted")] public bool? IsMuted { get; set; }
[JsonProperty("isRestricted")] public bool? IsRestricted { get; set; }
[JsonProperty("canRestrict")] public bool? CanRestrict { get; set; }
[JsonProperty("subscribedBy")] public bool? SubscribedBy { get; set; }
[JsonProperty("subscribedByExpire")] public bool? SubscribedByExpire { get; set; }
[JsonProperty("subscribedByExpireDate")] public DateTime? SubscribedByExpireDate { get; set; }
[JsonProperty("subscribedByAutoprolong")] public bool? SubscribedByAutoprolong { get; set; }
[JsonProperty("subscribedIsExpiredNow")] public bool? SubscribedIsExpiredNow { get; set; }
[JsonProperty("currentSubscribePrice")] public string? CurrentSubscribePrice { get; set; }
[JsonProperty("subscribedOn")] public bool? SubscribedOn { get; set; }
[JsonProperty("subscribedOnExpiredNow")] public bool? SubscribedOnExpiredNow { get; set; }
[JsonProperty("subscribedOnDuration")] public string SubscribedOnDuration { get; set; } = "";
[JsonProperty("canReport")] public bool? CanReport { get; set; }
[JsonProperty("canReceiveChatMessage")] public bool? CanReceiveChatMessage { get; set; }
[JsonProperty("hideChat")] public bool? HideChat { get; set; }
[JsonProperty("lastSeen")] public DateTime? LastSeen { get; set; }
[JsonProperty("isPerformer")] public bool? IsPerformer { get; set; }
[JsonProperty("isRealPerformer")] public bool? IsRealPerformer { get; set; }
[JsonProperty("subscribedByData")] public SubscribedByDataDto SubscribedByData { get; set; } = new();
[JsonProperty("subscribedOnData")] public SubscribedOnDataDto SubscribedOnData { get; set; } = new();
[JsonProperty("canTrialSend")] public bool? CanTrialSend { get; set; }
[JsonProperty("isBlocked")] public bool? IsBlocked { get; set; }
[JsonProperty("displayName")] public string DisplayName { get; set; } = "";
[JsonProperty("notice")] public string Notice { get; set; } = "";
}

View File

@ -0,0 +1,16 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Subscriptions;
public class ListsStateDto
{
[JsonProperty("id")] public object Id { get; set; } = new();
[JsonProperty("type")] public string Type { get; set; } = "";
[JsonProperty("name")] public string Name { get; set; } = "";
[JsonProperty("hasUser")] public bool? HasUser { get; set; }
[JsonProperty("canAddUser")] public bool? CanAddUser { get; set; }
}

View File

@ -0,0 +1,38 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Subscriptions;
public class SubscribeDto
{
[JsonProperty("id")] public object Id { get; set; } = new();
[JsonProperty("userId")] public long? UserId { get; set; }
[JsonProperty("subscriberId")] public int? SubscriberId { get; set; }
[JsonProperty("date")] public DateTime? Date { get; set; }
[JsonProperty("duration")] public int? Duration { get; set; }
[JsonProperty("startDate")] public DateTime? StartDate { get; set; }
[JsonProperty("expireDate")] public DateTime? ExpireDate { get; set; }
[JsonProperty("cancelDate")] public object CancelDate { get; set; } = new();
[JsonProperty("price")] public string? Price { get; set; }
[JsonProperty("regularPrice")] public string? RegularPrice { get; set; }
[JsonProperty("discount")] public string? Discount { get; set; }
[JsonProperty("action")] public string Action { get; set; } = "";
[JsonProperty("type")] public string Type { get; set; } = "";
[JsonProperty("offerStart")] public object OfferStart { get; set; } = new();
[JsonProperty("offerEnd")] public object OfferEnd { get; set; } = new();
[JsonProperty("isCurrent")] public bool? IsCurrent { get; set; }
}

View File

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Subscriptions;
public class SubscriptionsDto
{
[JsonProperty("list")] public List<ListItemDto> List { get; set; } = [];
[JsonProperty("hasMore")] public bool HasMore { get; set; }
}

View File

@ -0,0 +1,16 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Users;
public class ListsStateDto
{
[JsonProperty("id")] public string Id { get; set; } = "";
[JsonProperty("type")] public string Type { get; set; } = "";
[JsonProperty("name")] public string Name { get; set; } = "";
[JsonProperty("hasUser")] public bool HasUser { get; set; }
[JsonProperty("canAddUser")] public bool CanAddUser { get; set; }
}

View File

@ -0,0 +1,38 @@
using Newtonsoft.Json;
namespace OF_DL.Models.Dtos.Users;
public class SubscribeDto
{
[JsonProperty("id")] public long? Id { get; set; }
[JsonProperty("userId")] public long? UserId { get; set; }
[JsonProperty("subscriberId")] public int? SubscriberId { get; set; }
[JsonProperty("date")] public DateTime? Date { get; set; }
[JsonProperty("duration")] public int? Duration { get; set; }
[JsonProperty("startDate")] public DateTime? StartDate { get; set; }
[JsonProperty("expireDate")] public DateTime? ExpireDate { get; set; }
[JsonProperty("cancelDate")] public object CancelDate { get; set; } = new();
[JsonProperty("price")] public string? Price { get; set; }
[JsonProperty("regularPrice")] public string? RegularPrice { get; set; }
[JsonProperty("discount")] public int? Discount { get; set; }
[JsonProperty("action")] public string Action { get; set; } = "";
[JsonProperty("type")] public string Type { get; set; } = "";
[JsonProperty("offerStart")] public object OfferStart { get; set; } = new();
[JsonProperty("offerEnd")] public object OfferEnd { get; set; } = new();
[JsonProperty("isCurrent")] public bool? IsCurrent { get; set; }
}

View File

@ -0,0 +1,179 @@
using Newtonsoft.Json;
using OF_DL.Models.Dtos.Common;
namespace OF_DL.Models.Dtos.Users;
public class UserDto
{
[JsonProperty("view")] public string View { get; set; } = "";
[JsonProperty("avatar")] public string? Avatar { get; set; }
[JsonProperty("avatarThumbs")] public AvatarThumbsDto AvatarThumbs { get; set; } = new();
[JsonProperty("header")] public string? Header { get; set; }
[JsonProperty("headerSize")] public HeaderSizeDto HeaderSize { get; set; } = new();
[JsonProperty("headerThumbs")] public HeaderThumbsDto HeaderThumbs { get; set; } = new();
[JsonProperty("id")] public long? Id { get; set; }
[JsonProperty("name")] public string? Name { get; set; }
[JsonProperty("username")] public string? Username { get; set; }
[JsonProperty("canLookStory")] public bool? CanLookStory { get; set; }
[JsonProperty("canCommentStory")] public bool? CanCommentStory { get; set; }
[JsonProperty("hasNotViewedStory")] public bool? HasNotViewedStory { get; set; }
[JsonProperty("isVerified")] public bool? IsVerified { get; set; }
[JsonProperty("canPayInternal")] public bool? CanPayInternal { get; set; }
[JsonProperty("hasScheduledStream")] public bool? HasScheduledStream { get; set; }
[JsonProperty("hasStream")] public bool? HasStream { get; set; }
[JsonProperty("hasStories")] public bool? HasStories { get; set; }
[JsonProperty("tipsEnabled")] public bool? TipsEnabled { get; set; }
[JsonProperty("tipsTextEnabled")] public bool? TipsTextEnabled { get; set; }
[JsonProperty("tipsMin")] public int? TipsMin { get; set; }
[JsonProperty("tipsMinInternal")] public int? TipsMinInternal { get; set; }
[JsonProperty("tipsMax")] public int? TipsMax { get; set; }
[JsonProperty("canEarn")] public bool? CanEarn { get; set; }
[JsonProperty("canAddSubscriber")] public bool? CanAddSubscriber { get; set; }
[JsonProperty("subscribePrice")] public string? SubscribePrice { get; set; }
[JsonProperty("displayName")] public string DisplayName { get; set; } = "";
[JsonProperty("notice")] public string Notice { get; set; } = "";
[JsonProperty("isPaywallRequired")] public bool? IsPaywallRequired { get; set; }
[JsonProperty("unprofitable")] public bool? Unprofitable { get; set; }
[JsonProperty("listsStates")] public List<ListsStateDto> ListsStates { get; set; } = [];
[JsonProperty("isMuted")] public bool? IsMuted { get; set; }
[JsonProperty("isRestricted")] public bool? IsRestricted { get; set; }
[JsonProperty("canRestrict")] public bool? CanRestrict { get; set; }
[JsonProperty("subscribedBy")] public bool? SubscribedBy { get; set; }
[JsonProperty("subscribedByExpire")] public bool? SubscribedByExpire { get; set; }
[JsonProperty("subscribedByExpireDate")] public DateTime? SubscribedByExpireDate { get; set; }
[JsonProperty("subscribedByAutoprolong")] public bool? SubscribedByAutoprolong { get; set; }
[JsonProperty("subscribedIsExpiredNow")] public bool? SubscribedIsExpiredNow { get; set; }
[JsonProperty("currentSubscribePrice")] public string? CurrentSubscribePrice { get; set; }
[JsonProperty("subscribedOn")] public bool? SubscribedOn { get; set; }
[JsonProperty("subscribedOnExpiredNow")] public bool? SubscribedOnExpiredNow { get; set; }
[JsonProperty("subscribedOnDuration")] public string SubscribedOnDuration { get; set; } = "";
[JsonProperty("joinDate")] public DateTime? JoinDate { get; set; }
[JsonProperty("isReferrerAllowed")] public bool? IsReferrerAllowed { get; set; }
[JsonProperty("about")] public string About { get; set; } = "";
[JsonProperty("rawAbout")] public string RawAbout { get; set; } = "";
[JsonProperty("website")] public object Website { get; set; } = new();
[JsonProperty("wishlist")] public object Wishlist { get; set; } = new();
[JsonProperty("location")] public object Location { get; set; } = new();
[JsonProperty("postsCount")] public int? PostsCount { get; set; }
[JsonProperty("archivedPostsCount")] public int? ArchivedPostsCount { get; set; }
[JsonProperty("privateArchivedPostsCount")] public int? PrivateArchivedPostsCount { get; set; }
[JsonProperty("photosCount")] public int? PhotosCount { get; set; }
[JsonProperty("videosCount")] public int? VideosCount { get; set; }
[JsonProperty("audiosCount")] public int? AudiosCount { get; set; }
[JsonProperty("mediasCount")] public int? MediasCount { get; set; }
[JsonProperty("lastSeen")] public DateTime? LastSeen { get; set; }
[JsonProperty("favoritesCount")] public int? FavoritesCount { get; set; }
[JsonProperty("favoritedCount")] public int? FavoritedCount { get; set; }
[JsonProperty("showPostsInFeed")] public bool? ShowPostsInFeed { get; set; }
[JsonProperty("canReceiveChatMessage")] public bool? CanReceiveChatMessage { get; set; }
[JsonProperty("isPerformer")] public bool? IsPerformer { get; set; }
[JsonProperty("isRealPerformer")] public bool? IsRealPerformer { get; set; }
[JsonProperty("isSpotifyConnected")] public bool? IsSpotifyConnected { get; set; }
[JsonProperty("subscribersCount")] public int? SubscribersCount { get; set; }
[JsonProperty("hasPinnedPosts")] public bool? HasPinnedPosts { get; set; }
[JsonProperty("hasLabels")] public bool? HasLabels { get; set; }
[JsonProperty("canChat")] public bool? CanChat { get; set; }
[JsonProperty("callPrice")] public string? CallPrice { get; set; }
[JsonProperty("isPrivateRestriction")] public bool? IsPrivateRestriction { get; set; }
[JsonProperty("showSubscribersCount")] public bool? ShowSubscribersCount { get; set; }
[JsonProperty("showMediaCount")] public bool? ShowMediaCount { get; set; }
[JsonProperty("subscribedByData")] public SubscribedByDataDto SubscribedByData { get; set; } = new();
[JsonProperty("subscribedOnData")] public SubscribedOnDataDto SubscribedOnData { get; set; } = new();
[JsonProperty("canPromotion")] public bool? CanPromotion { get; set; }
[JsonProperty("canCreatePromotion")] public bool? CanCreatePromotion { get; set; }
[JsonProperty("canCreateTrial")] public bool? CanCreateTrial { get; set; }
[JsonProperty("isAdultContent")] public bool? IsAdultContent { get; set; }
[JsonProperty("canTrialSend")] public bool? CanTrialSend { get; set; }
[JsonProperty("hadEnoughLastPhotos")] public bool? HadEnoughLastPhotos { get; set; }
[JsonProperty("hasLinks")] public bool? HasLinks { get; set; }
[JsonProperty("firstPublishedPostDate")] public DateTime? FirstPublishedPostDate { get; set; }
[JsonProperty("isSpringConnected")] public bool? IsSpringConnected { get; set; }
[JsonProperty("isFriend")] public bool? IsFriend { get; set; }
[JsonProperty("isBlocked")] public bool? IsBlocked { get; set; }
[JsonProperty("canReport")] public bool? CanReport { get; set; }
}

View File

@ -0,0 +1,10 @@
namespace OF_DL.Models.Entities.Archived;
public class Archived
{
public List<ListItem> List { get; set; } = [];
public bool HasMore { get; set; }
public string? TailMarker { get; set; }
}

View File

@ -0,0 +1,10 @@
namespace OF_DL.Models.Entities.Archived;
public class ArchivedCollection
{
public List<Medium> ArchivedPostMedia { get; set; } = [];
public List<ListItem> ArchivedPostObjects { get; set; } = [];
public Dictionary<long, string> ArchivedPosts { get; set; } = new();
}

View File

@ -0,0 +1,24 @@
using OF_DL.Models.Entities.Common;
namespace OF_DL.Models.Entities.Archived;
public class ListItem
{
public long Id { get; set; }
public DateTime PostedAt { get; set; }
public Author? Author { get; set; }
public string? Text { get; set; }
public string? Price { get; set; }
public bool IsOpened { get; set; }
public bool IsArchived { get; set; }
public List<Medium>? Media { get; set; }
public List<object>? Preview { get; set; }
}

View File

@ -0,0 +1,16 @@
using OF_DL.Models.Entities.Common;
namespace OF_DL.Models.Entities.Archived;
public class Medium
{
public long Id { get; set; }
public string? Type { get; set; }
public bool CanView { get; set; }
public Files? Files { get; set; }
public string? Preview { get; set; }
}

View File

@ -0,0 +1,6 @@
namespace OF_DL.Models.Entities.Common;
public class Author
{
public long Id { get; set; }
}

View File

@ -0,0 +1,10 @@
namespace OF_DL.Models.Entities.Common;
public class Dash
{
public string? CloudFrontPolicy { get; set; }
public string? CloudFrontSignature { get; set; }
public string? CloudFrontKeyPairId { get; set; }
}

Some files were not shown because too many files have changed in this diff Show More