Compare commits
No commits in common. "master" and "working_2025-05-13" have entirely different histories.
master
...
working_20
179
.editorconfig
179
.editorconfig
@ -1,186 +1,9 @@
|
||||
# editorconfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
# Default settings:
|
||||
# A newline ending every file
|
||||
# Use 4 spaces as indentation
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = 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
5
.gitattributes
vendored
@ -3,11 +3,6 @@
|
||||
###############################################################################
|
||||
* 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.
|
||||
#
|
||||
|
||||
@ -4,6 +4,10 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- 'OFDLV*'
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.gitea/workflows/publish-docs.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
|
||||
@ -29,54 +29,35 @@ jobs:
|
||||
|
||||
- name: Build for Windows and Linux
|
||||
run: |
|
||||
dotnet publish "OF DL.Cli/OF DL.Cli.csproj" -p:Version=${{ steps.version.outputs.version }} \
|
||||
dotnet publish -p:Version=${{ steps.version.outputs.version }} \
|
||||
-p:PackageVersion=${{ steps.version.outputs.version }} \
|
||||
-p:WarningLevel=0 -c Release -r win-x86 \
|
||||
--self-contained true -p:PublishSingleFile=true -o outwin-cli
|
||||
--self-contained true -p:PublishSingleFile=true -o outwin
|
||||
|
||||
dotnet publish "OF DL.Gui/OF DL.Gui.csproj" -p:Version=${{ steps.version.outputs.version }} \
|
||||
-p:PackageVersion=${{ steps.version.outputs.version }} \
|
||||
-p:WarningLevel=0 -c Release -r win-x86 \
|
||||
--self-contained true -p:PublishSingleFile=true -o outwin-gui
|
||||
|
||||
dotnet publish "OF DL.Cli/OF DL.Cli.csproj" -p:Version=${{ steps.version.outputs.version }} \
|
||||
dotnet publish -p:Version=${{ steps.version.outputs.version }} \
|
||||
-p:PackageVersion=${{ steps.version.outputs.version }} \
|
||||
-p:WarningLevel=0 -c Release -r linux-x64 \
|
||||
--self-contained true -p:PublishSingleFile=true -o outlin-cli
|
||||
--self-contained true -p:PublishSingleFile=true -o outlin
|
||||
|
||||
- name: Copy and patch extra files
|
||||
run: |
|
||||
cd outlin-cli
|
||||
chmod +x "OF DL.Cli"
|
||||
cp ./OF\ DL/rules.json outwin/
|
||||
chmod +x ./outlin/OF\ DL
|
||||
cd outwin
|
||||
|
||||
echo "➤ Running OF DL binary (timeout)"
|
||||
timeout --preserve-status --kill-after=5s 30s "./OF DL.Cli" --non-interactive || true
|
||||
timeout --preserve-status --kill-after=5s 30s ../outlin/OF\ DL --non-interactive || true
|
||||
echo "➤ Binary finished"
|
||||
|
||||
echo "➤ Combine OF DL.Cli and OF DL.Gui into a single release folder"
|
||||
cd ..
|
||||
mv outwin-gui outwin
|
||||
cd outwin
|
||||
mv "../outwin-cli/OF DL.Cli.exe" .
|
||||
mv ../outlin-cli/config.conf .
|
||||
rm *.pdb
|
||||
mv "OF DL.Gui.exe" "OF DL.exe"
|
||||
mv "OF DL.Cli.exe" "OF DL - Classic.exe"
|
||||
|
||||
echo "➤ Remove unneeded playwright binaries"
|
||||
rm -rf .playwright/node/darwin*
|
||||
rm -rf .playwright/node/linux*
|
||||
|
||||
echo "➤ Creating folder for CDM"
|
||||
mkdir -p cdm/devices/chrome_1610
|
||||
|
||||
echo "➤ Copying ffmpeg and ffprobe from user folder"
|
||||
echo "➤ Copying ffmpeg 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/ffprobe.exe .
|
||||
cp /home/rhys/ffmpeg/ffmpeg-7.1.1-essentials_build/LICENSE LICENSE.ffmpeg
|
||||
|
||||
echo "➤ Creating release zip"
|
||||
zip -r ../OFDLV${{ steps.version.outputs.version }}.zip .playwright *
|
||||
zip ../OFDLV${{ steps.version.outputs.version }}.zip OF\ DL.exe e_sqlite3.dll rules.json config.conf cdm ffmpeg.exe LICENSE.ffmpeg
|
||||
cd ..
|
||||
|
||||
- name: Create release and upload artifact
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@ -371,12 +371,3 @@ FodyWeavers.xsd
|
||||
|
||||
# venv
|
||||
venv/
|
||||
|
||||
# Generated docs
|
||||
/site
|
||||
|
||||
# Builds
|
||||
/outwin
|
||||
/outwin-cli
|
||||
/outwin-gui
|
||||
/outlin-cli
|
||||
|
||||
123
AGENTS.md
123
AGENTS.md
@ -1,123 +0,0 @@
|
||||
# AGENTS.md
|
||||
|
||||
## Purpose
|
||||
|
||||
OF DL (OF-DL) is a C# (`net10.0`) app suite with:
|
||||
|
||||
- A modern Avalonia GUI
|
||||
- A classic CLI
|
||||
- Shared core services for auth, API calls, downloads, DRM handling, and metadata storage
|
||||
|
||||
## Architecture at a glance
|
||||
|
||||
1. `AuthService` loads `auth.json` or runs browser login and saves auth.
|
||||
2. `ApiService` signs OnlyFans API requests with dynamic rules.
|
||||
3. `DownloadOrchestrationService` selects creators/lists and coordinates download jobs.
|
||||
4. `DownloadService` downloads/decrypts media and records metadata via `DBService`.
|
||||
|
||||
## Key directories
|
||||
|
||||
- `OF DL.Cli/`: CLI-specific UI/helpers
|
||||
- `OF DL.Gui/`: Avalonia UI, view models, windows
|
||||
- `OF DL.Core/Services/`: business logic (auth/api/download/config/startup/db/logging)
|
||||
- `OF DL.Core/Models/`: DTOs, entities, config/auth/download/startup models, mappers
|
||||
- `OF DL.Core/Widevine/`: Widevine CDM logic
|
||||
- `docs/`: MkDocs source
|
||||
- `docker/`: container entrypoint/runtime config
|
||||
|
||||
## Files and entry points to check first
|
||||
|
||||
- `OF DL.Gui/ViewModels/MainWindowViewModel.cs`
|
||||
- `OF DL.Gui/Views/MainWindow.axaml`
|
||||
- `OF DL.Core/Services/ApiService.cs`
|
||||
- `OF DL.Core/Services/AuthService.cs`
|
||||
- `OF DL.Core/Services/ConfigService.cs`
|
||||
- `OF DL.Core/Services/DownloadOrchestrationService.cs`
|
||||
- `OF DL.Core/Services/DownloadService.cs`
|
||||
- `OF DL.Core/Services/StartupService.cs`
|
||||
- `OF DL.Core/Services/DBService.cs`
|
||||
- `OF DL.Core/Helpers/EnvironmentHelper.cs`
|
||||
|
||||
## Runtime files (relative to working directory)
|
||||
|
||||
- `config.conf` (primary config)
|
||||
- `auth.json` (saved auth)
|
||||
- `rules.json` (dynamic rules fallback)
|
||||
- `users.db` (global user index)
|
||||
- `chromium-data/` (browser profile for auth)
|
||||
- `cdm/` (Widevine device files)
|
||||
|
||||
Default download root when `DownloadPath` is blank:
|
||||
|
||||
- `__user_data__/sites/OnlyFans/{username}`
|
||||
|
||||
## Commands
|
||||
|
||||
Build GUI:
|
||||
|
||||
```bash
|
||||
dotnet build "OF DL.Gui/OF DL.Gui.csproj"
|
||||
```
|
||||
|
||||
Build CLI:
|
||||
|
||||
```bash
|
||||
dotnet build "OF DL.Cli/OF DL.Cli.csproj"
|
||||
```
|
||||
|
||||
Tests:
|
||||
|
||||
```bash
|
||||
dotnet test "OF DL.Tests/OF DL.Tests.csproj"
|
||||
```
|
||||
|
||||
Coverage (optional):
|
||||
|
||||
```bash
|
||||
dotnet test "OF DL.Tests/OF DL.Tests.csproj" --collect:"XPlat Code Coverage"
|
||||
```
|
||||
|
||||
## High-impact technical details
|
||||
|
||||
Dynamic rules and request signing (`ApiService.GetDynamicHeaders`):
|
||||
|
||||
- Remote rules URL: `https://git.ofdl.tools/sim0n00ps/dynamic-rules/raw/branch/main/rules.json`
|
||||
- Falls back to local `rules.json`
|
||||
- Signed headers include `app-token`, `sign`, `time`, `user-id`, `user-agent`, `x-bc`, `cookie`
|
||||
|
||||
DRM decryption:
|
||||
|
||||
- Preferred: local CDM device files under `cdm/devices/chrome_1610/`
|
||||
- Fallback path exists when CDM files are missing (`ofdl.tools/WV`)
|
||||
- Primary flow is in `DownloadService` + DRM helpers in `ApiService`
|
||||
|
||||
## Documentation update rules
|
||||
|
||||
Update docs whenever user-facing behavior changes.
|
||||
|
||||
- Config shape/options changed: update
|
||||
- `docs/config/configuration.md`
|
||||
- `docs/config/all-configuration-options.md`
|
||||
- `docs/config/custom-filename-formats.md` (if filename tokens/formats changed)
|
||||
- Auth/login behavior changed: update `docs/config/auth.md`
|
||||
- GUI/CLI workflow changed: update
|
||||
- `docs/operation/modern-version.md`
|
||||
- `docs/operation/classic-version.md` (if applicable)
|
||||
- Docker runtime/paths changed: update `docs/installation/docker.md`
|
||||
|
||||
## Coding style essentials
|
||||
|
||||
Follow `.editorconfig`. Most important rules:
|
||||
|
||||
- 4 spaces (2 for XML/YAML/project files), no tabs
|
||||
- C# braces on new lines
|
||||
- Prefer predefined types (`int`, `string`)
|
||||
- `using` directives outside namespace, `System` first
|
||||
- Private fields `_camelCase`; private static `s_` prefix
|
||||
|
||||
## Agent guardrails
|
||||
|
||||
- Prefer small, targeted changes in the service/viewmodel responsible for the behavior.
|
||||
- Keep GUI and CLI behavior aligned when changes affect both.
|
||||
- Do not manually edit generated docs in `site/`.
|
||||
- If you add new significant workflow/service structure, update this file.
|
||||
62
Dockerfile
62
Dockerfile
@ -1,72 +1,66 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||
FROM alpine:3.20 AS build
|
||||
|
||||
ARG VERSION
|
||||
|
||||
RUN apk --no-cache --repository community add \
|
||||
dotnet8-sdk
|
||||
|
||||
# Copy source code
|
||||
COPY ["OF DL.sln", "/src/OF DL.sln"]
|
||||
COPY ["OF DL.Cli", "/src/OF DL.Cli"]
|
||||
COPY ["OF DL.Core", "/src/OF DL.Core"]
|
||||
COPY ["OF DL.Gui", "/src/OF DL.Gui"]
|
||||
COPY ["OF DL", "/src/OF DL"]
|
||||
|
||||
WORKDIR "/src"
|
||||
|
||||
# Build release
|
||||
RUN dotnet publish "OF DL.Gui/OF DL.Gui.csproj" -p:WarningLevel=0 -p:Version=$VERSION -c Release -o outgui \
|
||||
&& dotnet publish "OF DL.Cli/OF DL.Cli.csproj" -p:WarningLevel=0 -p:Version=$VERSION -c Release -o outcli \
|
||||
RUN dotnet publish -p:WarningLevel=0 -p:Version=$VERSION -c Release --self-contained true -p:PublishSingleFile=true -o out
|
||||
|
||||
# Generate default config.conf files
|
||||
&& /src/outcli/OF\ DL.Cli --non-interactive || true && \
|
||||
RUN /src/out/OF\ DL --non-interactive || true && \
|
||||
# Set download path in default config.conf to /data
|
||||
sed -e 's/DownloadPath = ""/DownloadPath = "\/data"/' /src/config.conf > /src/updated_config.conf && \
|
||||
mv /src/updated_config.conf /src/config.conf
|
||||
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/runtime:10.0 AS final
|
||||
FROM alpine:3.20 AS final
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y \
|
||||
RUN apk --no-cache --repository community add \
|
||||
bash \
|
||||
tini \
|
||||
dotnet8-runtime \
|
||||
ffmpeg \
|
||||
udev \
|
||||
ttf-freefont \
|
||||
chromium \
|
||||
supervisor \
|
||||
xvfb \
|
||||
x11vnc \
|
||||
novnc \
|
||||
npm \
|
||||
openbox \
|
||||
xclip \
|
||||
&& npx playwright install-deps \
|
||||
&& apt-get remove --purge -y npm \
|
||||
&& apt-get autoremove -y \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
novnc
|
||||
|
||||
# Redirect webroot to vnc.html instead of displaying directory listing
|
||||
&& 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
|
||||
|
||||
# Create directories for configuration and downloaded files
|
||||
&& mkdir -p /data /config /config/logs /default-config
|
||||
RUN mkdir /data /config /config/logs /default-config
|
||||
|
||||
# Copy release
|
||||
COPY --from=build /src/outgui /app
|
||||
ARG cli_src_dll="/src/outcli/*.dll"
|
||||
ARG cli_src_app="/src/outcli/OF DL.Cli"
|
||||
ARG cli_target="/app/OF DL.Cli"
|
||||
COPY --from=build ${cli_src_dll} ${cli_target}
|
||||
COPY --from=build ${cli_src_app} ${cli_target}
|
||||
COPY --from=build /src/out /app
|
||||
|
||||
# Copy default configuration files
|
||||
COPY --from=build /src/config.conf /default-config
|
||||
COPY --from=build ["/src/OF DL.Cli/rules.json", "/default-config"]
|
||||
COPY --from=build ["/src/OF DL/rules.json", "/default-config"]
|
||||
|
||||
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
COPY docker/entrypoint.sh /app/entrypoint.sh
|
||||
RUN chmod +x /app/entrypoint.sh
|
||||
|
||||
ENV DEBIAN_FRONTEND="noninteractive" \
|
||||
DISPLAY=:0.0 \
|
||||
DISPLAY_WIDTH=1366 \
|
||||
ENV DISPLAY=:0.0 \
|
||||
DISPLAY_WIDTH=1024 \
|
||||
DISPLAY_HEIGHT=768 \
|
||||
OFDL_DOCKER=true \
|
||||
PLAYWRIGHT_BROWSERS_PATH=/config/chromium
|
||||
OFDL_PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium \
|
||||
OFDL_DOCKER=true
|
||||
|
||||
EXPOSE 8080
|
||||
WORKDIR /config
|
||||
ENTRYPOINT ["/usr/bin/tini", "--", "/app/entrypoint.sh"]
|
||||
CMD []
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
CMD ["/app/entrypoint.sh"]
|
||||
|
||||
@ -1,136 +0,0 @@
|
||||
using OF_DL.Models.Downloads;
|
||||
using OF_DL.Services;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace OF_DL.CLI;
|
||||
|
||||
/// <summary>
|
||||
/// Spectre.Console implementation of IDownloadEventHandler.
|
||||
/// Handles all CLI-specific display logic for downloads.
|
||||
/// </summary>
|
||||
public class SpectreDownloadEventHandler : IDownloadEventHandler
|
||||
{
|
||||
public CancellationToken CancellationToken { get; } = CancellationToken.None;
|
||||
|
||||
public async Task<T> WithStatusAsync<T>(string statusMessage, Func<IStatusReporter, Task<T>> work)
|
||||
{
|
||||
TaskCompletionSource<T> tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
await AnsiConsole.Status()
|
||||
.StartAsync($"[red]{Markup.Escape(statusMessage)}[/]",
|
||||
async ctx =>
|
||||
{
|
||||
try
|
||||
{
|
||||
SpectreStatusReporter reporter = new(ctx);
|
||||
T result = await work(reporter);
|
||||
tcs.TrySetResult(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.TrySetException(ex);
|
||||
}
|
||||
});
|
||||
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
public async Task<T> WithProgressAsync<T>(string description, long maxValue, bool showSize,
|
||||
Func<IProgressReporter, Task<T>> work)
|
||||
{
|
||||
TaskCompletionSource<T> tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
await AnsiConsole.Progress()
|
||||
.Columns(GetProgressColumns(showSize))
|
||||
.StartAsync(async ctx =>
|
||||
{
|
||||
try
|
||||
{
|
||||
ProgressTask task = ctx.AddTask($"[red]{Markup.Escape(description)}[/]", false);
|
||||
task.MaxValue = maxValue;
|
||||
task.StartTask();
|
||||
|
||||
SpectreProgressReporter progressReporter = new(task);
|
||||
T result = await work(progressReporter);
|
||||
tcs.TrySetResult(result);
|
||||
|
||||
task.StopTask();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.TrySetException(ex);
|
||||
}
|
||||
});
|
||||
|
||||
return await tcs.Task;
|
||||
}
|
||||
|
||||
public void OnContentFound(string contentType, int mediaCount, int objectCount) =>
|
||||
AnsiConsole.Markup($"[red]Found {mediaCount} Media from {objectCount} {Markup.Escape(contentType)}\n[/]");
|
||||
|
||||
public void OnNoContentFound(string contentType) =>
|
||||
AnsiConsole.Markup($"[red]Found 0 {Markup.Escape(contentType)}\n[/]");
|
||||
|
||||
public void OnDownloadComplete(string contentType, DownloadResult result) =>
|
||||
AnsiConsole.Markup(
|
||||
$"[red]{Markup.Escape(contentType)} Media Already Downloaded: {result.ExistingDownloads} New {Markup.Escape(contentType)} Media Downloaded: {result.NewDownloads}[/]\n");
|
||||
|
||||
public void OnUserStarting(string username) =>
|
||||
AnsiConsole.Markup($"[red]\nScraping Data for {Markup.Escape(username)}\n[/]");
|
||||
|
||||
public void OnUserComplete(string username, CreatorDownloadResult result)
|
||||
{
|
||||
AnsiConsole.Markup("\n");
|
||||
AnsiConsole.Write(new BreakdownChart()
|
||||
.FullSize()
|
||||
.AddItem("Paid Posts", result.PaidPostCount, Color.Red)
|
||||
.AddItem("Posts", result.PostCount, Color.Blue)
|
||||
.AddItem("Archived", result.ArchivedCount, Color.Green)
|
||||
.AddItem("Streams", result.StreamsCount, Color.Purple)
|
||||
.AddItem("Stories", result.StoriesCount, Color.Yellow)
|
||||
.AddItem("Highlights", result.HighlightsCount, Color.Orange1)
|
||||
.AddItem("Messages", result.MessagesCount, Color.LightGreen)
|
||||
.AddItem("Paid Messages", result.PaidMessagesCount, Color.Aqua));
|
||||
AnsiConsole.Markup("\n");
|
||||
}
|
||||
|
||||
public void OnPurchasedTabUserComplete(string username, int paidPostCount, int paidMessagesCount)
|
||||
{
|
||||
AnsiConsole.Markup("\n");
|
||||
AnsiConsole.Write(new BreakdownChart()
|
||||
.FullSize()
|
||||
.AddItem("Paid Posts", paidPostCount, Color.Red)
|
||||
.AddItem("Paid Messages", paidMessagesCount, Color.Aqua));
|
||||
AnsiConsole.Markup("\n");
|
||||
}
|
||||
|
||||
public void OnScrapeComplete(TimeSpan elapsed) =>
|
||||
AnsiConsole.Markup($"[green]Scrape Completed in {elapsed.TotalMinutes:0.00} minutes\n[/]");
|
||||
|
||||
public void OnMessage(string message) => AnsiConsole.Markup($"[red]{Markup.Escape(message)}\n[/]");
|
||||
|
||||
private static ProgressColumn[] GetProgressColumns(bool showScrapeSize)
|
||||
{
|
||||
List<ProgressColumn> progressColumns;
|
||||
if (showScrapeSize)
|
||||
{
|
||||
progressColumns =
|
||||
[
|
||||
new TaskDescriptionColumn(),
|
||||
new ProgressBarColumn(),
|
||||
new PercentageColumn(),
|
||||
new DownloadedColumn(),
|
||||
new RemainingTimeColumn()
|
||||
];
|
||||
}
|
||||
else
|
||||
{
|
||||
progressColumns =
|
||||
[
|
||||
new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()
|
||||
];
|
||||
}
|
||||
|
||||
return progressColumns.ToArray();
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
using OF_DL.Services;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace OF_DL.CLI;
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of IProgressReporter that uses Spectre.Console's ProgressTask for CLI output.
|
||||
/// </summary>
|
||||
public class SpectreProgressReporter(ProgressTask task, CancellationToken cancellationToken = default) : IProgressReporter
|
||||
{
|
||||
private readonly ProgressTask _task = task ?? throw new ArgumentNullException(nameof(task));
|
||||
|
||||
public CancellationToken CancellationToken { get; } = cancellationToken;
|
||||
|
||||
public void ReportProgress(long increment) => _task.Increment(increment);
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
using OF_DL.Services;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace OF_DL.CLI;
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of IStatusReporter that uses Spectre.Console's StatusContext for CLI output.
|
||||
/// </summary>
|
||||
public class SpectreStatusReporter(StatusContext ctx) : IStatusReporter
|
||||
{
|
||||
public void ReportStatus(string message)
|
||||
{
|
||||
ctx.Status($"[red]{message}[/]");
|
||||
ctx.Spinner(Spinner.Known.Dots);
|
||||
ctx.SpinnerStyle(Style.Parse("blue"));
|
||||
}
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<RootNamespace>OF_DL</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<ApplicationIcon>Icon\download.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Icon\download.ico"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OF DL.Core\OF DL.Core.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Akka" Version="1.5.61" />
|
||||
<PackageReference Include="BouncyCastle.NetCore" Version="2.2.1"/>
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.4"/>
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.3"/>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.3"/>
|
||||
<PackageReference Include="Microsoft.Playwright" Version="1.58.0"/>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4"/>
|
||||
<PackageReference Include="protobuf-net" Version="3.2.56"/>
|
||||
<PackageReference Include="Serilog" Version="4.3.1"/>
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1"/>
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0"/>
|
||||
<PackageReference Include="System.Reactive" Version="6.1.0"/>
|
||||
<PackageReference Include="xFFmpeg.NET" Version="7.2.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Spectre.Console">
|
||||
<HintPath>References\Spectre.Console.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="rules.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="chromium-scripts/stealth.min.js">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -1,911 +0,0 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OF_DL.CLI;
|
||||
using OF_DL.Models;
|
||||
using OF_DL.Enumerations;
|
||||
using OF_DL.Helpers;
|
||||
using OF_DL.Models.Config;
|
||||
using OF_DL.Models.Downloads;
|
||||
using OF_DL.Models.Entities.Users;
|
||||
using OF_DL.Services;
|
||||
using Serilog;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace OF_DL;
|
||||
|
||||
public class Program(IServiceProvider serviceProvider)
|
||||
{
|
||||
private async Task LoadAuthFromBrowser()
|
||||
{
|
||||
IAuthService authService = serviceProvider.GetRequiredService<IAuthService>();
|
||||
|
||||
// Show the initial message
|
||||
AnsiConsole.MarkupLine("[yellow]Downloading dependencies. Please wait ...[/]");
|
||||
|
||||
// Show instructions based on the environment
|
||||
await Task.Delay(5000);
|
||||
if (EnvironmentHelper.IsRunningInDocker())
|
||||
{
|
||||
AnsiConsole.MarkupLine(
|
||||
"[yellow]In your web browser, navigate to the port forwarded from your docker container.[/]");
|
||||
AnsiConsole.MarkupLine(
|
||||
"[yellow]For instance, if your docker run command included \"-p 8080:8080\", open your web browser to \"http://localhost:8080\".[/]");
|
||||
AnsiConsole.MarkupLine(
|
||||
"[yellow]Once on that webpage, please use it to log in to your OF account. Do not navigate away from the page.[/]");
|
||||
}
|
||||
else
|
||||
{
|
||||
AnsiConsole.MarkupLine(
|
||||
"[yellow]In the new window that has opened, please log in to your OF account. Do not close the window or tab. Do not navigate away from the page.[/]\n");
|
||||
AnsiConsole.MarkupLine(
|
||||
"[yellow]If you use this method or encounter other issues while logging in, use one of the legacy authentication methods documented here:[/]");
|
||||
AnsiConsole.MarkupLine("[link]https://docs.ofdl.tools/config/auth/#legacy-methods[/]");
|
||||
}
|
||||
|
||||
// Load auth from browser using the service
|
||||
bool success = await authService.LoadFromBrowserAsync();
|
||||
|
||||
if (!success || authService.CurrentAuth == null)
|
||||
{
|
||||
AnsiConsole.MarkupLine(
|
||||
"\n[red]Authentication failed. Be sure to log into to OF using the new window that opened automatically.[/]");
|
||||
AnsiConsole.MarkupLine(
|
||||
"[red]The window will close automatically when the authentication process is finished.[/]");
|
||||
AnsiConsole.MarkupLine(
|
||||
"[red]If the problem persists, you may want to try using a legacy authentication method documented here:[/]\n");
|
||||
AnsiConsole.MarkupLine("[link]https://docs.ofdl.tools/config/auth/#legacy-methods[/]");
|
||||
AnsiConsole.MarkupLine("[red]Press any key to exit.[/]");
|
||||
Log.Error("auth invalid after attempt to get auth from browser");
|
||||
|
||||
Environment.Exit(2);
|
||||
}
|
||||
|
||||
await authService.SaveToFileAsync();
|
||||
}
|
||||
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
AnsiConsole.Write(new FigletText("Welcome to OF-DL").Color(Color.Red));
|
||||
AnsiConsole.Markup("Documentation: [link]https://docs.ofdl.tools/[/]\n");
|
||||
AnsiConsole.Markup("Discord server: [link]https://discord.com/invite/6bUW8EJ53j[/]\n\n");
|
||||
|
||||
ServiceCollection services = await ConfigureServices(args);
|
||||
ServiceProvider serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
Program program = serviceProvider.GetRequiredService<Program>();
|
||||
await program.RunAsync();
|
||||
}
|
||||
|
||||
private static async Task<ServiceCollection> ConfigureServices(string[] args)
|
||||
{
|
||||
// Set up dependency injection with LoggingService and ConfigService
|
||||
ServiceCollection services = new();
|
||||
services.AddSingleton<ILoggingService, LoggingService>();
|
||||
services.AddSingleton<IConfigService, ConfigService>();
|
||||
ServiceProvider tempServiceProvider = services.BuildServiceProvider();
|
||||
|
||||
ILoggingService loggingService = tempServiceProvider.GetRequiredService<ILoggingService>();
|
||||
IConfigService configService = tempServiceProvider.GetRequiredService<IConfigService>();
|
||||
|
||||
|
||||
if (!await configService.LoadConfigurationAsync(args))
|
||||
{
|
||||
AnsiConsole.MarkupLine("\n[red]config.conf is not valid, check your syntax![/]\n");
|
||||
if (!configService.IsCliNonInteractive)
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Press any key to exit.[/]");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
Environment.Exit(3);
|
||||
}
|
||||
|
||||
AnsiConsole.Markup("[green]config.conf located successfully!\n[/]");
|
||||
|
||||
// Set up full dependency injection with loaded config
|
||||
services = [];
|
||||
services.AddSingleton(loggingService);
|
||||
services.AddSingleton(configService);
|
||||
services.AddSingleton<IAuthService, AuthService>();
|
||||
services.AddSingleton<IApiService, ApiService>();
|
||||
services.AddSingleton<IDbService, DbService>();
|
||||
services.AddSingleton<IDownloadService, DownloadService>();
|
||||
services.AddSingleton<IFileNameService, FileNameService>();
|
||||
services.AddSingleton<IStartupService, StartupService>();
|
||||
services.AddSingleton<IDownloadOrchestrationService, DownloadOrchestrationService>();
|
||||
services.AddSingleton<Program>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private async Task RunAsync()
|
||||
{
|
||||
IConfigService configService = serviceProvider.GetRequiredService<IConfigService>();
|
||||
IAuthService authService = serviceProvider.GetRequiredService<IAuthService>();
|
||||
IStartupService startupService = serviceProvider.GetRequiredService<IStartupService>();
|
||||
IDownloadOrchestrationService orchestrationService =
|
||||
serviceProvider.GetRequiredService<IDownloadOrchestrationService>();
|
||||
|
||||
try
|
||||
{
|
||||
// Version check
|
||||
VersionCheckResult versionResult = await startupService.CheckVersionAsync();
|
||||
DisplayVersionResult(versionResult);
|
||||
|
||||
// Environment validation
|
||||
StartupResult startupResult = await startupService.ValidateEnvironmentAsync();
|
||||
DisplayStartupResult(startupResult);
|
||||
|
||||
if (!startupResult.IsWindowsVersionValid)
|
||||
{
|
||||
Console.Write(
|
||||
"This appears to be running on an older version of Windows which is not supported.\n\n");
|
||||
Console.Write(
|
||||
"OF-DL requires Windows 10 or higher when being run on Windows. Your reported version is: {0}\n\n",
|
||||
startupResult.OsVersionString);
|
||||
if (!configService.CurrentConfig.NonInteractiveMode)
|
||||
{
|
||||
Console.Write("Press any key to continue.\n");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
if (!startupResult.FfmpegFound)
|
||||
{
|
||||
if (!configService.CurrentConfig.NonInteractiveMode)
|
||||
{
|
||||
AnsiConsole.Markup(
|
||||
"[red]Cannot locate FFmpeg; please modify config.conf with the correct path. Press any key to exit.[/]");
|
||||
Console.ReadKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
AnsiConsole.Markup(
|
||||
"[red]Cannot locate FFmpeg; please modify config.conf with the correct path.[/]");
|
||||
}
|
||||
|
||||
Environment.Exit(4);
|
||||
}
|
||||
|
||||
if (!startupResult.FfprobeFound)
|
||||
{
|
||||
if (!configService.CurrentConfig.NonInteractiveMode)
|
||||
{
|
||||
AnsiConsole.Markup(
|
||||
"[red]Cannot locate FFprobe; please modify config.conf with the correct path. Press any key to exit.[/]");
|
||||
Console.ReadKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
AnsiConsole.Markup(
|
||||
"[red]Cannot locate FFprobe; please modify config.conf with the correct path.[/]");
|
||||
}
|
||||
|
||||
Environment.Exit(4);
|
||||
}
|
||||
|
||||
// Auth flow
|
||||
await HandleAuthFlow(authService, configService);
|
||||
|
||||
// Validate cookie string
|
||||
authService.ValidateCookieString();
|
||||
|
||||
// rules.json validation
|
||||
DisplayRulesJsonResult(startupResult, configService);
|
||||
|
||||
// NonInteractiveMode
|
||||
if (configService.CurrentConfig.NonInteractiveMode)
|
||||
{
|
||||
configService.CurrentConfig.NonInteractiveMode = true;
|
||||
Log.Debug("NonInteractiveMode = true");
|
||||
}
|
||||
|
||||
// Validate auth via API
|
||||
User? validate = await authService.ValidateAuthAsync();
|
||||
if (validate == null || (validate.Name == null && validate.Username == null))
|
||||
{
|
||||
Log.Error("Auth failed");
|
||||
authService.CurrentAuth = null;
|
||||
|
||||
if (!configService.CurrentConfig.NonInteractiveMode &&
|
||||
!configService.CurrentConfig.DisableBrowserAuth)
|
||||
{
|
||||
await LoadAuthFromBrowser();
|
||||
}
|
||||
|
||||
if (authService.CurrentAuth == null)
|
||||
{
|
||||
AnsiConsole.MarkupLine(
|
||||
"\n[red]Auth failed. Please try again or use other authentication methods detailed here:[/]\n");
|
||||
AnsiConsole.MarkupLine("[link]https://docs.ofdl.tools/config/auth[/]\n");
|
||||
if (!configService.CurrentConfig.NonInteractiveMode)
|
||||
{
|
||||
Console.WriteLine("\nPress any key to exit.");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
Environment.Exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
AnsiConsole.Markup(
|
||||
$"[green]Logged In successfully as {(!string.IsNullOrEmpty(validate?.Name) ? validate.Name : "Unknown Name")} {(!string.IsNullOrEmpty(validate?.Username) ? validate.Username : "Unknown Username")}\n[/]");
|
||||
|
||||
// Main download loop
|
||||
await DownloadAllData(orchestrationService, configService, startupResult);
|
||||
}
|
||||
catch (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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
if (!configService.CurrentConfig.NonInteractiveMode)
|
||||
{
|
||||
Console.WriteLine("\nPress any key to exit.");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
Environment.Exit(5);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadAllData(
|
||||
IDownloadOrchestrationService orchestrationService,
|
||||
IConfigService configService,
|
||||
StartupResult startupResult)
|
||||
{
|
||||
Config config = configService.CurrentConfig;
|
||||
SpectreDownloadEventHandler eventHandler = new();
|
||||
|
||||
Log.Debug("Calling DownloadAllData");
|
||||
|
||||
do
|
||||
{
|
||||
DateTime startTime = DateTime.Now;
|
||||
|
||||
UserListResult userListResult = await orchestrationService.GetAvailableUsersAsync();
|
||||
Dictionary<string, long> users = userListResult.Users;
|
||||
Dictionary<string, long> lists = userListResult.Lists;
|
||||
|
||||
if (userListResult.IgnoredListError != null)
|
||||
{
|
||||
AnsiConsole.Markup($"[red]{Markup.Escape(userListResult.IgnoredListError)}\n[/]");
|
||||
}
|
||||
|
||||
KeyValuePair<bool, Dictionary<string, long>> hasSelectedUsersKVP;
|
||||
if (config.NonInteractiveMode && config.NonInteractiveModePurchasedTab)
|
||||
{
|
||||
hasSelectedUsersKVP = new KeyValuePair<bool, Dictionary<string, long>>(true,
|
||||
new Dictionary<string, long> { { "PurchasedTab", 0 } });
|
||||
}
|
||||
else if (config.NonInteractiveMode && string.IsNullOrEmpty(config.NonInteractiveModeListName))
|
||||
{
|
||||
hasSelectedUsersKVP = new KeyValuePair<bool, Dictionary<string, long>>(true, users);
|
||||
}
|
||||
else if (config.NonInteractiveMode && !string.IsNullOrEmpty(config.NonInteractiveModeListName))
|
||||
{
|
||||
ListUserSelectionResult listSelectionResult =
|
||||
await orchestrationService.GetUsersForListAsync(config.NonInteractiveModeListName, users, lists);
|
||||
Dictionary<string, long> selectedUsers = listSelectionResult.SelectedUsers;
|
||||
hasSelectedUsersKVP = new KeyValuePair<bool, Dictionary<string, long>>(true, selectedUsers);
|
||||
}
|
||||
else
|
||||
{
|
||||
(bool IsExit, Dictionary<string, long>? selectedUsers) userSelectionResult =
|
||||
await HandleUserSelection(users, lists);
|
||||
|
||||
config = configService.CurrentConfig;
|
||||
hasSelectedUsersKVP = new KeyValuePair<bool, Dictionary<string, long>>(userSelectionResult.IsExit,
|
||||
userSelectionResult.selectedUsers ?? []);
|
||||
}
|
||||
|
||||
if (hasSelectedUsersKVP.Key &&
|
||||
hasSelectedUsersKVP.Value.ContainsKey("SinglePost"))
|
||||
{
|
||||
await HandleSinglePostDownload(orchestrationService, users, startupResult, eventHandler);
|
||||
}
|
||||
else if (hasSelectedUsersKVP.Key &&
|
||||
hasSelectedUsersKVP.Value.ContainsKey("PurchasedTab"))
|
||||
{
|
||||
await orchestrationService.DownloadPurchasedTabAsync(users,
|
||||
startupResult.ClientIdBlobMissing, startupResult.DevicePrivateKeyMissing, eventHandler);
|
||||
|
||||
DateTime endTime = DateTime.Now;
|
||||
eventHandler.OnScrapeComplete(endTime - startTime);
|
||||
}
|
||||
else if (hasSelectedUsersKVP.Key &&
|
||||
hasSelectedUsersKVP.Value.ContainsKey("SingleMessage"))
|
||||
{
|
||||
await HandleSingleMessageDownload(orchestrationService, users, startupResult, eventHandler);
|
||||
}
|
||||
else if (hasSelectedUsersKVP.Key &&
|
||||
!hasSelectedUsersKVP.Value.ContainsKey("ConfigChanged"))
|
||||
{
|
||||
foreach (KeyValuePair<string, long> user in hasSelectedUsersKVP.Value)
|
||||
{
|
||||
string path = orchestrationService.ResolveDownloadPath(user.Key);
|
||||
Log.Debug($"Download path: {path}");
|
||||
|
||||
await orchestrationService.DownloadCreatorContentAsync(
|
||||
user.Key, user.Value, path, users,
|
||||
startupResult.ClientIdBlobMissing, startupResult.DevicePrivateKeyMissing,
|
||||
eventHandler);
|
||||
}
|
||||
|
||||
DateTime endTime = DateTime.Now;
|
||||
eventHandler.OnScrapeComplete(endTime - startTime);
|
||||
}
|
||||
else if (hasSelectedUsersKVP.Key &&
|
||||
hasSelectedUsersKVP.Value.ContainsKey("ConfigChanged"))
|
||||
{
|
||||
// Config was changed, loop will re-read
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (!config.NonInteractiveMode);
|
||||
}
|
||||
|
||||
private async Task HandleSinglePostDownload(
|
||||
IDownloadOrchestrationService orchestrationService,
|
||||
Dictionary<string, long> users,
|
||||
StartupResult startupResult,
|
||||
IDownloadEventHandler eventHandler)
|
||||
{
|
||||
AnsiConsole.Markup(
|
||||
"[red]To find an individual post URL, click on the ... at the top right corner of the post and select 'Copy link to post'.\n\nTo return to the main menu, enter 'back' or 'exit' when prompted for the URL.\n\n[/]");
|
||||
string postUrl = AnsiConsole.Prompt(
|
||||
new TextPrompt<string>("[red]Please enter a post URL: [/]")
|
||||
.ValidationErrorMessage("[red]Please enter a valid post URL[/]")
|
||||
.Validate(url =>
|
||||
{
|
||||
Log.Debug($"Single Post URL: {url}");
|
||||
Regex regex = new("https://onlyfans\\.com/[0-9]+/[A-Za-z0-9]+", RegexOptions.IgnoreCase);
|
||||
if (regex.IsMatch(url))
|
||||
{
|
||||
return ValidationResult.Success();
|
||||
}
|
||||
|
||||
if (url == "" || url == "exit" || url == "back")
|
||||
{
|
||||
return ValidationResult.Success();
|
||||
}
|
||||
|
||||
Log.Error("Post URL invalid");
|
||||
return ValidationResult.Error("[red]Please enter a valid post URL[/]");
|
||||
}));
|
||||
|
||||
if (postUrl != "" && postUrl != "exit" && postUrl != "back")
|
||||
{
|
||||
long postId = Convert.ToInt64(postUrl.Split("/")[3]);
|
||||
string username = postUrl.Split("/")[4];
|
||||
|
||||
Log.Debug($"Single Post ID: {postId}");
|
||||
Log.Debug($"Single Post Creator: {username}");
|
||||
|
||||
if (users.ContainsKey(username))
|
||||
{
|
||||
string path = orchestrationService.ResolveDownloadPath(username);
|
||||
Log.Debug($"Download path: {path}");
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
AnsiConsole.Markup($"[red]Created folder for {Markup.Escape(username)}\n[/]");
|
||||
Log.Debug($"Created folder for {username}");
|
||||
}
|
||||
else
|
||||
{
|
||||
AnsiConsole.Markup($"[red]Folder for {Markup.Escape(username)} already created\n[/]");
|
||||
}
|
||||
|
||||
IDbService dbService = serviceProvider.GetRequiredService<IDbService>();
|
||||
await dbService.CreateDb(path);
|
||||
|
||||
await orchestrationService.DownloadSinglePostAsync(username, postId, path, users,
|
||||
startupResult.ClientIdBlobMissing, startupResult.DevicePrivateKeyMissing, eventHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleSingleMessageDownload(
|
||||
IDownloadOrchestrationService orchestrationService,
|
||||
Dictionary<string, long> users,
|
||||
StartupResult startupResult,
|
||||
IDownloadEventHandler eventHandler)
|
||||
{
|
||||
AnsiConsole.Markup(
|
||||
"[red]To find an individual message URL, note that you can only do so for PPV messages that you have unlocked. Go the main OnlyFans timeline, click on the Purchased tab, find the relevant message, click on the ... at the top right corner of the message, and select 'Copy link to message'. For all other messages, you cannot scrape them individually, you must scrape all messages from that creator.\n\nTo return to the main menu, enter 'back' or 'exit' when prompted for the URL.\n\n[/]");
|
||||
string messageUrl = AnsiConsole.Prompt(
|
||||
new TextPrompt<string>("[red]Please enter a message URL: [/]")
|
||||
.ValidationErrorMessage("[red]Please enter a valid message URL[/]")
|
||||
.Validate(url =>
|
||||
{
|
||||
Log.Debug($"Single Paid Message URL: {url}");
|
||||
Regex regex = new("https://onlyfans\\.com/my/chats/chat/[0-9]+/\\?firstId=[0-9]+$",
|
||||
RegexOptions.IgnoreCase);
|
||||
if (regex.IsMatch(url))
|
||||
{
|
||||
return ValidationResult.Success();
|
||||
}
|
||||
|
||||
if (url == "" || url == "back" || url == "exit")
|
||||
{
|
||||
return ValidationResult.Success();
|
||||
}
|
||||
|
||||
Log.Error("Message URL invalid");
|
||||
return ValidationResult.Error("[red]Please enter a valid message URL[/]");
|
||||
}));
|
||||
|
||||
if (messageUrl != "" && messageUrl != "exit" && messageUrl != "back")
|
||||
{
|
||||
long messageId = Convert.ToInt64(messageUrl.Split("?firstId=")[1]);
|
||||
long userId = Convert.ToInt64(messageUrl.Split("/")[6]);
|
||||
|
||||
Log.Debug($"Message ID: {messageId}");
|
||||
Log.Debug($"User ID: {userId}");
|
||||
|
||||
string? username = await orchestrationService.ResolveUsernameAsync(userId);
|
||||
Log.Debug("Content creator: {Username}", username);
|
||||
|
||||
if (username == null)
|
||||
{
|
||||
Log.Error("Could not resolve username for user ID: {userId}", userId);
|
||||
AnsiConsole.MarkupLine("[red]Could not resolve username for user ID[/]");
|
||||
return;
|
||||
}
|
||||
|
||||
string path = orchestrationService.ResolveDownloadPath(username);
|
||||
Log.Debug($"Download path: {path}");
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
AnsiConsole.Markup($"[red]Created folder for {Markup.Escape(username)}\n[/]");
|
||||
Log.Debug($"Created folder for {username}");
|
||||
}
|
||||
else
|
||||
{
|
||||
AnsiConsole.Markup($"[red]Folder for {Markup.Escape(username)} already created\n[/]");
|
||||
Log.Debug($"Folder for {username} already created");
|
||||
}
|
||||
|
||||
IDbService dbService = serviceProvider.GetRequiredService<IDbService>();
|
||||
await dbService.CreateDb(path);
|
||||
|
||||
await orchestrationService.DownloadSinglePaidMessageAsync(username, messageId, path, users,
|
||||
startupResult.ClientIdBlobMissing, startupResult.DevicePrivateKeyMissing, eventHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool IsExit, Dictionary<string, long>? selectedUsers)> HandleUserSelection(
|
||||
Dictionary<string, long> users, Dictionary<string, long> lists)
|
||||
{
|
||||
IConfigService configService = serviceProvider.GetRequiredService<IConfigService>();
|
||||
IAuthService authService = serviceProvider.GetRequiredService<IAuthService>();
|
||||
IApiService apiService = serviceProvider.GetRequiredService<IApiService>();
|
||||
ILoggingService loggingService = serviceProvider.GetRequiredService<ILoggingService>();
|
||||
|
||||
bool hasSelectedUsers = false;
|
||||
Dictionary<string, long> selectedUsers = new();
|
||||
Config currentConfig = configService.CurrentConfig;
|
||||
|
||||
while (!hasSelectedUsers)
|
||||
{
|
||||
List<string> mainMenuOptions = GetMainMenuOptions(users, lists);
|
||||
|
||||
string mainMenuSelection = AnsiConsole.Prompt(
|
||||
new SelectionPrompt<string>()
|
||||
.Title(
|
||||
"[red]Select Accounts to Scrape | Select All = All Accounts | List = Download content from users on List | Custom = Specific Account(s)[/]")
|
||||
.AddChoices(mainMenuOptions)
|
||||
);
|
||||
|
||||
switch (mainMenuSelection)
|
||||
{
|
||||
case "[red]Select All[/]":
|
||||
selectedUsers = users;
|
||||
hasSelectedUsers = true;
|
||||
break;
|
||||
case "[red]List[/]":
|
||||
while (true)
|
||||
{
|
||||
MultiSelectionPrompt<string> listSelectionPrompt = new();
|
||||
listSelectionPrompt.Title = "[red]Select List[/]";
|
||||
listSelectionPrompt.PageSize = 10;
|
||||
listSelectionPrompt.AddChoice("[red]Go Back[/]");
|
||||
foreach (string key in lists.Keys.Select(k => $"[red]{k}[/]").ToList())
|
||||
{
|
||||
listSelectionPrompt.AddChoice(key);
|
||||
}
|
||||
|
||||
List<string> listSelection = AnsiConsole.Prompt(listSelectionPrompt);
|
||||
|
||||
if (listSelection.Contains("[red]Go Back[/]"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
hasSelectedUsers = true;
|
||||
List<string> listUsernames = new();
|
||||
foreach (string item in listSelection)
|
||||
{
|
||||
long listId = lists[item.Replace("[red]", "").Replace("[/]", "")];
|
||||
List<string> usernames = await apiService.GetListUsers($"/lists/{listId}/users") ?? [];
|
||||
foreach (string user in usernames)
|
||||
{
|
||||
listUsernames.Add(user);
|
||||
}
|
||||
}
|
||||
|
||||
selectedUsers = users.Where(x => listUsernames.Contains($"{x.Key}"))
|
||||
.ToDictionary(x => x.Key, x => x.Value);
|
||||
AnsiConsole.Markup(string.Format("[red]Downloading from List(s): {0}[/]",
|
||||
string.Join(", ", listSelection)));
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case "[red]Custom[/]":
|
||||
while (true)
|
||||
{
|
||||
MultiSelectionPrompt<string> selectedNamesPrompt = new();
|
||||
selectedNamesPrompt.MoreChoicesText("[grey](Move up and down to reveal more choices)[/]");
|
||||
selectedNamesPrompt.InstructionsText(
|
||||
"[grey](Press <space> to select, <enter> to accept)[/]\n[grey](Press A-Z to easily navigate the list)[/]");
|
||||
selectedNamesPrompt.Title("[red]Select users[/]");
|
||||
selectedNamesPrompt.PageSize(10);
|
||||
selectedNamesPrompt.AddChoice("[red]Go Back[/]");
|
||||
foreach (string key in users.Keys.OrderBy(k => k).Select(k => $"[red]{k}[/]").ToList())
|
||||
{
|
||||
selectedNamesPrompt.AddChoice(key);
|
||||
}
|
||||
|
||||
List<string> userSelection = AnsiConsole.Prompt(selectedNamesPrompt);
|
||||
if (userSelection.Contains("[red]Go Back[/]"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
hasSelectedUsers = true;
|
||||
selectedUsers = users.Where(x => userSelection.Contains($"[red]{x.Key}[/]"))
|
||||
.ToDictionary(x => x.Key, x => x.Value);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case "[red]Download Single Post[/]":
|
||||
return (true, new Dictionary<string, long> { { "SinglePost", 0 } });
|
||||
case "[red]Download Single Paid Message[/]":
|
||||
return (true, new Dictionary<string, long> { { "SingleMessage", 0 } });
|
||||
case "[red]Download Purchased Tab[/]":
|
||||
return (true, new Dictionary<string, long> { { "PurchasedTab", 0 } });
|
||||
case "[red]Edit config.conf[/]":
|
||||
while (true)
|
||||
{
|
||||
List<(string Name, bool Value)> toggleableProps = configService.GetToggleableProperties();
|
||||
List<(string choice, bool isSelected)> choices = new() { ("[red]Go Back[/]", false) };
|
||||
foreach ((string Name, bool Value) prop in toggleableProps)
|
||||
{
|
||||
choices.Add(($"[red]{prop.Name}[/]", prop.Value));
|
||||
}
|
||||
|
||||
MultiSelectionPrompt<string> multiSelectionPrompt = new MultiSelectionPrompt<string>()
|
||||
.Title("[red]Edit config.conf[/]")
|
||||
.PageSize(25);
|
||||
|
||||
foreach ((string choice, bool isSelected) choice in choices)
|
||||
{
|
||||
multiSelectionPrompt.AddChoices(choice.choice, selectionItem =>
|
||||
{
|
||||
if (choice.isSelected)
|
||||
{
|
||||
selectionItem.Select();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
List<string> configOptions = AnsiConsole.Prompt(multiSelectionPrompt);
|
||||
|
||||
if (configOptions.Contains("[red]Go Back[/]"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Extract plain names from selections
|
||||
List<string> selectedNames = configOptions
|
||||
.Select(o => o.Replace("[red]", "").Replace("[/]", ""))
|
||||
.ToList();
|
||||
|
||||
bool configChanged = configService.ApplyToggleableSelections(selectedNames);
|
||||
await configService.SaveConfigurationAsync();
|
||||
currentConfig = configService.CurrentConfig;
|
||||
|
||||
if (configChanged)
|
||||
{
|
||||
return (true, new Dictionary<string, long> { { "ConfigChanged", 0 } });
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case "[red]Change logging level[/]":
|
||||
while (true)
|
||||
{
|
||||
List<(string choice, bool isSelected)> choices = [("[red]Go Back[/]", false)];
|
||||
|
||||
foreach (string name in typeof(LoggingLevel).GetEnumNames())
|
||||
{
|
||||
string itemLabel = $"[red]{name}[/]";
|
||||
choices.Add(new ValueTuple<string, bool>(itemLabel,
|
||||
name == loggingService.GetCurrentLoggingLevel().ToString()));
|
||||
}
|
||||
|
||||
SelectionPrompt<string> selectionPrompt = new SelectionPrompt<string>()
|
||||
.Title("[red]Select logging level[/]")
|
||||
.PageSize(25);
|
||||
|
||||
foreach ((string choice, bool isSelected) choice in choices)
|
||||
{
|
||||
selectionPrompt.AddChoice(choice.choice);
|
||||
}
|
||||
|
||||
string levelOption = AnsiConsole.Prompt(selectionPrompt);
|
||||
|
||||
if (levelOption.Contains("[red]Go Back[/]"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
levelOption = levelOption.Replace("[red]", "").Replace("[/]", "");
|
||||
LoggingLevel newLogLevel =
|
||||
(LoggingLevel)Enum.Parse(typeof(LoggingLevel), levelOption, true);
|
||||
|
||||
Log.Debug($"Logging level changed to: {levelOption}");
|
||||
|
||||
Config newConfig = currentConfig;
|
||||
newConfig.LoggingLevel = newLogLevel;
|
||||
currentConfig = newConfig;
|
||||
|
||||
configService.UpdateConfig(newConfig);
|
||||
await configService.SaveConfigurationAsync();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case "[red]Logout and Exit[/]":
|
||||
authService.Logout();
|
||||
return (false, null);
|
||||
case "[red]Exit[/]":
|
||||
return (false, null);
|
||||
}
|
||||
}
|
||||
|
||||
return (true, selectedUsers);
|
||||
}
|
||||
|
||||
public static List<string> GetMainMenuOptions(Dictionary<string, long> users, Dictionary<string, long> lists)
|
||||
{
|
||||
if (lists.Count > 0)
|
||||
{
|
||||
return new List<string>
|
||||
{
|
||||
"[red]Select All[/]",
|
||||
"[red]List[/]",
|
||||
"[red]Custom[/]",
|
||||
"[red]Download Single Post[/]",
|
||||
"[red]Download Single Paid Message[/]",
|
||||
"[red]Download Purchased Tab[/]",
|
||||
"[red]Edit config.conf[/]",
|
||||
"[red]Change logging level[/]",
|
||||
"[red]Logout and Exit[/]",
|
||||
"[red]Exit[/]"
|
||||
};
|
||||
}
|
||||
|
||||
return new List<string>
|
||||
{
|
||||
"[red]Select All[/]",
|
||||
"[red]Custom[/]",
|
||||
"[red]Download Single Post[/]",
|
||||
"[red]Download Single Paid Message[/]",
|
||||
"[red]Download Purchased Tab[/]",
|
||||
"[red]Edit config.conf[/]",
|
||||
"[red]Change logging level[/]",
|
||||
"[red]Logout and Exit[/]",
|
||||
"[red]Exit[/]"
|
||||
};
|
||||
}
|
||||
|
||||
private async Task HandleAuthFlow(IAuthService authService, IConfigService configService)
|
||||
{
|
||||
if (await authService.LoadFromFileAsync())
|
||||
{
|
||||
AnsiConsole.Markup("[green]auth.json located successfully!\n[/]");
|
||||
}
|
||||
else if (File.Exists("auth.json"))
|
||||
{
|
||||
Log.Information("Auth file found but could not be deserialized");
|
||||
|
||||
if (configService.CurrentConfig.NonInteractiveMode)
|
||||
{
|
||||
AnsiConsole.MarkupLine(
|
||||
"\n[red]auth.json has invalid JSON syntax. The file can be generated automatically when OF-DL is run in the standard, interactive mode.[/]\n");
|
||||
AnsiConsole.MarkupLine(
|
||||
"[red]You may also want to try using the browser extension which is documented here:[/]\n");
|
||||
AnsiConsole.MarkupLine("[link]https://docs.ofdl.tools/config/auth/#legacy-methods[/]");
|
||||
Environment.Exit(2);
|
||||
}
|
||||
|
||||
if (!configService.CurrentConfig.DisableBrowserAuth)
|
||||
{
|
||||
await LoadAuthFromBrowser();
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowAuthMissingError(configService.CurrentConfig.NonInteractiveMode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (configService.CurrentConfig.NonInteractiveMode)
|
||||
{
|
||||
ShowAuthMissingError(configService.CurrentConfig.NonInteractiveMode);
|
||||
}
|
||||
else if (!configService.CurrentConfig.DisableBrowserAuth)
|
||||
{
|
||||
await LoadAuthFromBrowser();
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowAuthMissingError(configService.CurrentConfig.NonInteractiveMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ShowAuthMissingError(bool nonInteractiveMode)
|
||||
{
|
||||
AnsiConsole.MarkupLine(
|
||||
"\n[red]auth.json is missing. The file can be generated automatically when OF-DL is run in the standard, interactive mode.[/]\n");
|
||||
AnsiConsole.MarkupLine(
|
||||
"[red]You may also want to try using the browser extension which is documented here:[/]\n");
|
||||
AnsiConsole.MarkupLine("[link]https://docs.ofdl.tools/config/auth/#legacy-methods[/]");
|
||||
|
||||
if (!nonInteractiveMode)
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Press any key to exit.[/]");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
Environment.Exit(2);
|
||||
}
|
||||
|
||||
private static void DisplayVersionResult(VersionCheckResult result)
|
||||
{
|
||||
if (result.TimedOut)
|
||||
{
|
||||
AnsiConsole.Markup("[yellow]Version check timed out after 30 seconds.\n[/]");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.CheckFailed)
|
||||
{
|
||||
AnsiConsole.Markup("[yellow]Failed to verify that OF-DL is up-to-date.\n[/]");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.LocalVersion == null || result.LatestVersion == null)
|
||||
{
|
||||
// Debug mode or no version info
|
||||
AnsiConsole.Markup("[yellow]Running in Debug/Local mode. Version check skipped.\n[/]");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.IsUpToDate)
|
||||
{
|
||||
AnsiConsole.Markup("[green]You are running OF-DL version " +
|
||||
$"{result.LocalVersion.Major}.{result.LocalVersion.Minor}.{result.LocalVersion.Build}\n[/]");
|
||||
AnsiConsole.Markup("[green]Latest Release version: " +
|
||||
$"{result.LatestVersion.Major}.{result.LatestVersion.Minor}.{result.LatestVersion.Build}\n[/]");
|
||||
}
|
||||
else
|
||||
{
|
||||
AnsiConsole.Markup("[red]You are running OF-DL version " +
|
||||
$"{result.LocalVersion.Major}.{result.LocalVersion.Minor}.{result.LocalVersion.Build}\n[/]");
|
||||
AnsiConsole.Markup("[red]Please update to the current release, " +
|
||||
$"{result.LatestVersion.Major}.{result.LatestVersion.Minor}.{result.LatestVersion.Build}: [link=https://git.ofdl.tools/sim0n00ps/OF-DL/releases]https://git.ofdl.tools/sim0n00ps/OF-DL/releases[/]\n[/]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void DisplayStartupResult(StartupResult result)
|
||||
{
|
||||
// OS
|
||||
if (result is { IsWindowsVersionValid: true, OsVersionString: not null } &&
|
||||
EnvironmentHelper.IsRunningOnWindows())
|
||||
{
|
||||
AnsiConsole.Markup("[green]Valid version of Windows found.\n[/]");
|
||||
}
|
||||
|
||||
// FFmpeg
|
||||
if (result.FfmpegFound)
|
||||
{
|
||||
AnsiConsole.Markup(
|
||||
result is { FfmpegPathAutoDetected: true, FfmpegPath: not null }
|
||||
? $"[green]FFmpeg located successfully. Path auto-detected: {Markup.Escape(result.FfmpegPath)}\n[/]"
|
||||
: "[green]FFmpeg located successfully\n[/]");
|
||||
|
||||
AnsiConsole.Markup(result.FfmpegVersion != null
|
||||
? $"[green]ffmpeg version detected as {Markup.Escape(result.FfmpegVersion)}[/]\n"
|
||||
: "[yellow]ffmpeg version could not be parsed[/]\n");
|
||||
}
|
||||
|
||||
// FFprobe
|
||||
if (result.FfprobeFound)
|
||||
{
|
||||
AnsiConsole.Markup(
|
||||
result is { FfprobePathAutoDetected: true, FfprobePath: not null }
|
||||
? $"[green]FFprobe located successfully. Path auto-detected: {Markup.Escape(result.FfprobePath)}\n[/]"
|
||||
: "[green]FFprobe located successfully\n[/]");
|
||||
|
||||
AnsiConsole.Markup(result.FfprobeVersion != null
|
||||
? $"[green]FFprobe version detected as {Markup.Escape(result.FfprobeVersion)}[/]\n"
|
||||
: "[yellow]FFprobe version could not be parsed[/]\n");
|
||||
}
|
||||
|
||||
// Widevine
|
||||
if (!result.ClientIdBlobMissing)
|
||||
{
|
||||
AnsiConsole.Markup("[green]device_client_id_blob located successfully![/]\n");
|
||||
}
|
||||
|
||||
if (!result.DevicePrivateKeyMissing)
|
||||
{
|
||||
AnsiConsole.Markup("[green]device_private_key located successfully![/]\n");
|
||||
}
|
||||
|
||||
if (result.ClientIdBlobMissing || result.DevicePrivateKeyMissing)
|
||||
{
|
||||
AnsiConsole.Markup(
|
||||
"[yellow]device_client_id_blob and/or device_private_key missing, https://ofdl.tools/ will be used instead for DRM protected videos\n[/]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void DisplayRulesJsonResult(StartupResult result, IConfigService configService)
|
||||
{
|
||||
if (result.RulesJsonExists)
|
||||
{
|
||||
if (result.RulesJsonValid)
|
||||
{
|
||||
AnsiConsole.Markup("[green]rules.json located successfully!\n[/]");
|
||||
}
|
||||
else
|
||||
{
|
||||
AnsiConsole.MarkupLine("\n[red]rules.json is not valid, check your JSON syntax![/]\n");
|
||||
AnsiConsole.MarkupLine("[red]Please ensure you are using the latest version of the software.[/]\n");
|
||||
Log.Error("rules.json processing failed: {Error}", result.RulesJsonError);
|
||||
|
||||
if (!configService.CurrentConfig.NonInteractiveMode)
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Press any key to exit.[/]");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
Environment.Exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,112 +0,0 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v7.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v7.0": {
|
||||
"Spectre.Console/0.0.0-preview.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.SourceLink.GitHub": "1.1.1",
|
||||
"MinVer": "4.2.0",
|
||||
"Roslynator.Analyzers": "4.1.2",
|
||||
"StyleCop.Analyzers": "1.2.0-beta.435",
|
||||
"System.Memory": "4.5.5",
|
||||
"Wcwidth.Sources": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"Spectre.Console.dll": {}
|
||||
}
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git/1.1.1": {},
|
||||
"Microsoft.SourceLink.Common/1.1.1": {},
|
||||
"Microsoft.SourceLink.GitHub/1.1.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Build.Tasks.Git": "1.1.1",
|
||||
"Microsoft.SourceLink.Common": "1.1.1"
|
||||
}
|
||||
},
|
||||
"MinVer/4.2.0": {},
|
||||
"Roslynator.Analyzers/4.1.2": {},
|
||||
"StyleCop.Analyzers/1.2.0-beta.435": {
|
||||
"dependencies": {
|
||||
"StyleCop.Analyzers.Unstable": "1.2.0.435"
|
||||
}
|
||||
},
|
||||
"StyleCop.Analyzers.Unstable/1.2.0.435": {},
|
||||
"System.Memory/4.5.5": {},
|
||||
"Wcwidth.Sources/1.0.0": {}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"Spectre.Console/0.0.0-preview.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git/1.1.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-AT3HlgTjsqHnWpBHSNeR0KxbLZD7bztlZVj7I8vgeYG9SYqbeFGh0TM/KVtC6fg53nrWHl3VfZFvb5BiQFcY6Q==",
|
||||
"path": "microsoft.build.tasks.git/1.1.1",
|
||||
"hashPath": "microsoft.build.tasks.git.1.1.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.SourceLink.Common/1.1.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-WMcGpWKrmJmzrNeuaEb23bEMnbtR/vLmvZtkAP5qWu7vQsY59GqfRJd65sFpBszbd2k/bQ8cs8eWawQKAabkVg==",
|
||||
"path": "microsoft.sourcelink.common/1.1.1",
|
||||
"hashPath": "microsoft.sourcelink.common.1.1.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub/1.1.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-IaJGnOv/M7UQjRJks7B6p7pbPnOwisYGOIzqCz5ilGFTApZ3ktOR+6zJ12ZRPInulBmdAf1SrGdDG2MU8g6XTw==",
|
||||
"path": "microsoft.sourcelink.github/1.1.1",
|
||||
"hashPath": "microsoft.sourcelink.github.1.1.1.nupkg.sha512"
|
||||
},
|
||||
"MinVer/4.2.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Po4tv+sri1jsaebQ8F6+yD5ru9Gas5mR111F6HR2ULqwflvjjZXMstpeOc1GHMJeQa3g4E3b8MX8K2cShkuUAg==",
|
||||
"path": "minver/4.2.0",
|
||||
"hashPath": "minver.4.2.0.nupkg.sha512"
|
||||
},
|
||||
"Roslynator.Analyzers/4.1.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-bNl3GRSBFjJymYnwq/IRDD9MOSZz9VKdGk9RsN0MWIXoSRnVKQv84f6s9nLE13y20lZgMZKlDqGw2uInBH4JgA==",
|
||||
"path": "roslynator.analyzers/4.1.2",
|
||||
"hashPath": "roslynator.analyzers.4.1.2.nupkg.sha512"
|
||||
},
|
||||
"StyleCop.Analyzers/1.2.0-beta.435": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-TADk7vdGXtfTnYCV7GyleaaRTQjfoSfZXprQrVMm7cSJtJbFc1QIbWPyLvrgrfGdfHbGmUPvaN4ODKNxg2jgPQ==",
|
||||
"path": "stylecop.analyzers/1.2.0-beta.435",
|
||||
"hashPath": "stylecop.analyzers.1.2.0-beta.435.nupkg.sha512"
|
||||
},
|
||||
"StyleCop.Analyzers.Unstable/1.2.0.435": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ouwPWZxbOV3SmCZxIRqHvljkSzkCyi1tDoMzQtDb/bRP8ctASV/iRJr+A2Gdj0QLaLmWnqTWDrH82/iP+X80Lg==",
|
||||
"path": "stylecop.analyzers.unstable/1.2.0.435",
|
||||
"hashPath": "stylecop.analyzers.unstable.1.2.0.435.nupkg.sha512"
|
||||
},
|
||||
"System.Memory/4.5.5": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
|
||||
"path": "system.memory/4.5.5",
|
||||
"hashPath": "system.memory.4.5.5.nupkg.sha512"
|
||||
},
|
||||
"Wcwidth.Sources/1.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-86tmwfGXRz7GJQXBnoTFoMvqSqd6irfkEkRzQFR54W/nweaR8cUvzY8x++z+B/+eUPSuqD2Ah1iPJHgthy4pzg==",
|
||||
"path": "wcwidth.sources/1.0.0",
|
||||
"hashPath": "wcwidth.sources.1.0.0.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
# Stealth Script Creation
|
||||
|
||||
## Requirements
|
||||
|
||||
- NodeJS (with npx CLI tool)
|
||||
|
||||
## Instructions
|
||||
|
||||
- Open a terminal in this directory (OF DL.Cli/chromium-scripts)
|
||||
- Run `npx -y extract-stealth-evasions`
|
||||
- Copy the `stealth.js` file into the other chromium-scripts directory as well (`OF DL.Gui/chromium-scripts/`)
|
||||
|
||||
## References
|
||||
|
||||
See the readme.md file and source code for the stealth
|
||||
script [here](https://github.com/berstend/puppeteer-extra/tree/master/packages/extract-stealth-evasions).
|
||||
7
OF DL.Cli/chromium-scripts/stealth.min.js
vendored
7
OF DL.Cli/chromium-scripts/stealth.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,41 +0,0 @@
|
||||
{
|
||||
"app-token": "33d57ade8c02dbc5a333db99ff9ae26a",
|
||||
"static_param": "RyY8GpixStP90t68HWIJ8Qzo745n0hy0",
|
||||
"prefix": "30586",
|
||||
"suffix": "67000213",
|
||||
"checksum_constant": 521,
|
||||
"checksum_indexes": [
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
7,
|
||||
7,
|
||||
8,
|
||||
8,
|
||||
10,
|
||||
11,
|
||||
13,
|
||||
14,
|
||||
16,
|
||||
17,
|
||||
17,
|
||||
17,
|
||||
19,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
21,
|
||||
23,
|
||||
23,
|
||||
24,
|
||||
24,
|
||||
27,
|
||||
27,
|
||||
29,
|
||||
30,
|
||||
31,
|
||||
34,
|
||||
35,
|
||||
39
|
||||
]
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,127 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
namespace OF_DL.Enumerations;
|
||||
|
||||
public enum CustomFileNameOption
|
||||
{
|
||||
ReturnOriginal,
|
||||
ReturnEmpty
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
namespace OF_DL.Enumerations;
|
||||
|
||||
public enum DownloadDateSelection
|
||||
{
|
||||
before,
|
||||
after
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
namespace OF_DL.Enumerations;
|
||||
|
||||
public enum LoggingLevel
|
||||
{
|
||||
//
|
||||
// Summary:
|
||||
// Anything and everything you might want to know about a running block of code.
|
||||
Verbose,
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Internal system events that aren't necessarily observable from the outside.
|
||||
Debug,
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// The lifeblood of operational intelligence - things happen.
|
||||
Information,
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Service is degraded or endangered.
|
||||
Warning,
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Functionality is unavailable, invariants are broken or data is lost.
|
||||
Error,
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// If you have a pager, it goes off when one of these occurs.
|
||||
Fatal
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
namespace OF_DL.Enumerations;
|
||||
|
||||
public enum MediaType
|
||||
{
|
||||
PaidPosts = 10,
|
||||
Posts = 20,
|
||||
Archived = 30,
|
||||
Stories = 40,
|
||||
Highlights = 50,
|
||||
Messages = 60,
|
||||
PaidMessages = 70
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace OF_DL.Enumerations;
|
||||
|
||||
public enum Theme
|
||||
{
|
||||
light,
|
||||
dark
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
namespace OF_DL.Enumerations;
|
||||
|
||||
public enum VideoResolution
|
||||
{
|
||||
_240,
|
||||
_720,
|
||||
source
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
namespace OF_DL.Helpers;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public const string DiscordInviteUrl = "https://discord.com/invite/6bUW8EJ53j";
|
||||
|
||||
public const string DocumentationUrl = "https://docs.ofdl.tools/";
|
||||
|
||||
public const string LegacyAuthDocumentationUrl = "https://docs.ofdl.tools/config/auth/#legacy-methods";
|
||||
|
||||
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;
|
||||
|
||||
public const int ApiRetryMaxAttempts = 5;
|
||||
|
||||
public const int ApiRetryBaseDelayMs = 500;
|
||||
|
||||
public const int ApiRetryMaxDelayMs = 8000;
|
||||
|
||||
public const int DownloadRetryMaxAttempts = 4;
|
||||
|
||||
public const int DownloadRetryBaseDelayMs = 1000;
|
||||
|
||||
public const int DownloadRetryMaxDelayMs = 15000;
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
namespace OF_DL.Helpers;
|
||||
|
||||
public static class EnvironmentHelper
|
||||
{
|
||||
private const string DockerEnvironmentVariableName = "OFDL_DOCKER";
|
||||
|
||||
public static bool IsRunningInDocker()
|
||||
{
|
||||
string? dockerValue = Environment.GetEnvironmentVariable(DockerEnvironmentVariableName);
|
||||
return string.Equals(dockerValue, "true", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsRunningOnWindows() => OperatingSystem.IsWindows();
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using OF_DL.Models.OfdlApi;
|
||||
using Serilog;
|
||||
|
||||
namespace OF_DL.Helpers;
|
||||
|
||||
public static class VersionHelper
|
||||
{
|
||||
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)
|
||||
{
|
||||
Log.Debug("Calling GetLatestReleaseTag");
|
||||
try
|
||||
{
|
||||
HttpResponseMessage response = await s_httpClient.GetAsync(Url, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
Log.Debug("GetLatestReleaseTag did not return a Success Status Code");
|
||||
return null;
|
||||
}
|
||||
|
||||
string body = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
|
||||
Log.Debug("GetLatestReleaseTag API Response: {Body}", body);
|
||||
|
||||
LatestReleaseApiResponse? versionCheckResponse =
|
||||
JsonConvert.DeserializeObject<LatestReleaseApiResponse>(body);
|
||||
|
||||
if (versionCheckResponse != null && versionCheckResponse.TagName != "")
|
||||
{
|
||||
return versionCheckResponse.TagName;
|
||||
}
|
||||
|
||||
Log.Debug("GetLatestReleaseTag did not return a valid tag name");
|
||||
return null;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw; // Rethrow timeout exceptions to be handled by the caller
|
||||
}
|
||||
catch (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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,168 +0,0 @@
|
||||
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;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public Theme Theme { get; set; } = Theme.dark;
|
||||
|
||||
[ToggleableConfig] public bool HideMissingCdmKeysWarning { get; set; }
|
||||
|
||||
[ToggleableConfig] public bool HideShowScrapeSizeWarning { get; set; }
|
||||
|
||||
[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";
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
namespace OF_DL.Models.Config;
|
||||
|
||||
public interface IFileNameFormatConfig
|
||||
{
|
||||
string? PaidPostFileNameFormat { get; set; }
|
||||
|
||||
string? PostFileNameFormat { get; set; }
|
||||
|
||||
string? PaidMessageFileNameFormat { get; set; }
|
||||
|
||||
string? MessageFileNameFormat { get; set; }
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
namespace OF_DL.Models.Config;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal class ToggleableConfigAttribute : Attribute;
|
||||
@ -1,36 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
|
||||
public class ListUserSelectionResult
|
||||
{
|
||||
public Dictionary<string, long> SelectedUsers { get; set; } = new();
|
||||
|
||||
public List<string> UnavailableUsernames { get; set; } = [];
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,96 +0,0 @@
|
||||
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; } = [];
|
||||
}
|
||||
@ -1,97 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
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; } = [];
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace OF_DL.Models.Dtos.Common;
|
||||
|
||||
public class VideoDto
|
||||
{
|
||||
[JsonProperty("mp4")] public string Mp4 { get; set; } = "";
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
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; } = [];
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
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; } = [];
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
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; } = [];
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
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; } = [];
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,121 +0,0 @@
|
||||
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; } = [];
|
||||
}
|
||||
@ -1,98 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,66 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,101 +0,0 @@
|
||||
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; } = [];
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,99 +0,0 @@
|
||||
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; } = [];
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
@ -1,72 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,101 +0,0 @@
|
||||
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; } = [];
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,111 +0,0 @@
|
||||
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; } = "";
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user