diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 783f650..38d9787 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,10 +24,10 @@ jobs: 9.0.x - name: Restore dependencies - run: dotnet restore TiktokExplode.NET.sln + run: dotnet restore TiktokExplode.NET.slnx - name: Build - run: dotnet build TiktokExplode.NET.sln --no-restore --configuration Release + run: dotnet build TiktokExplode.NET.slnx --no-restore --configuration Release - name: Run tests run: dotnet test TiktokExplode.Tests/TiktokExplode.Tests.csproj --no-build --configuration Release --logger "console;verbosity=normal" @@ -50,10 +50,10 @@ jobs: 9.0.x - name: Restore dependencies - run: dotnet restore TiktokExplode.NET.sln + run: dotnet restore TiktokExplode.NET.slnx - name: Pack - run: dotnet pack TiktokExplode.NET.sln --no-restore --configuration Release + run: dotnet pack TiktokExplode.NET.slnx --no-restore --configuration Release - name: Push to NuGet run: dotnet nuget push "**/*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c35d662..18ab83f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,7 +7,7 @@ "type": "process", "args": [ "build", - "${workspaceFolder}/TiktokExplode.NET.sln", + "${workspaceFolder}/TiktokExplode.NET.slnx", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary;ForceNoAlign" ], @@ -19,7 +19,7 @@ "type": "process", "args": [ "publish", - "${workspaceFolder}/TiktokExplode.NET.sln", + "${workspaceFolder}/TiktokExplode.NET.slnx", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary;ForceNoAlign" ], diff --git a/README.md b/README.md index d9273c7..56e51ad 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![NuGet](https://img.shields.io/nuget/v/TiktokExplode.svg?label=TiktokExplode)](https://www.nuget.org/packages/TiktokExplode) [![NuGet](https://img.shields.io/nuget/v/TiktokExplode.Infrastructure.svg?label=TiktokExplode.Infrastructure)](https://www.nuget.org/packages/TiktokExplode.Infrastructure) [![NuGet](https://img.shields.io/nuget/v/TiktokExplode.All.svg?label=TiktokExplode.All)](https://www.nuget.org/packages/TiktokExplode.All) +[![NuGet](https://img.shields.io/nuget/v/TiktokExplode.Extensions.DependencyInjection.svg?label=TiktokExplode.Extensions.DependencyInjection)](https://www.nuget.org/packages/TiktokExplode.Extensions.DependencyInjection) [![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-512BD4)](https://dotnet.microsoft.com) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) @@ -46,6 +47,14 @@ dotnet add package TiktokExplode.Infrastructure > `TiktokExplode.Infrastructure` automatically brings in `TiktokExplode` (domain) as a transitive dependency. > Install `TiktokExplode` alone only if you need the domain models/interfaces without the infrastructure. +**Using Microsoft.Extensions.DependencyInjection?** + +``` +dotnet add package TiktokExplode.Extensions.DependencyInjection +``` + +> Adds `AddTiktokExplode()` on `IServiceCollection`. See the [Dependency Injection](#dependency-injection) section. + > **Note:** `TiktokExplode.Infrastructure` depends on [Microsoft.Playwright](https://playwright.dev/dotnet/). After installation, run the following once to download the browser binaries: > > ``` @@ -205,6 +214,47 @@ Returned by `DownloadAsync` and `DownloadWatermarkedAsync`. Implements `IAsyncDi --- +## Dependency Injection + +`TiktokExplode.Extensions.DependencyInjection` provides a fluent `AddTiktokExplode()` extension method for registering all TiktokExplode services into the .NET DI container. + +```csharp +// Default — Playwright fetcher, all defaults +services.AddTiktokExplode(); + +// Custom — Playwright with visible browser window +services.AddTiktokExplode(b => b + .UsePlaywrightFetcher(o => o.Headless = false)); + +// HTTP fetcher — lighter, no browser dependency +services.AddTiktokExplode(b => b + .UseHttpFetcher(o => o.WarmupDelay = TimeSpan.Zero) + .ConfigureTiktok(o => o.MaxWafRetries = 5)); +``` + +Registered services: + +| Service | Implementation | Lifetime | +| --- | --- | --- | +| `IVideoClient` | `TiktokClient` | Singleton | +| `IPageFetcher` | `PlaywrightFetcher` or `HttpFetcher` | Singleton | +| `TikTokOptions` | — | Singleton | +| `PlaywrightFetcherOptions` or `HttpFetcherOptions` | — | Singleton | + +```csharp +// Consume in your services via constructor injection +public class MyService(IVideoClient client) +{ + public async Task GetTitleAsync(string url) + { + var video = await client.GetVideoAsync(url); + return video.Description; + } +} +``` + +--- + ## Error Handling ```csharp @@ -255,6 +305,8 @@ TiktokExplode.Infrastructure/ # HTTP + browser automation (Playwright + AngleSha Common/ # StreamExtensions, TiktokClientExtensions TiktokExplode.All/ # Meta-package — installs both packages above in one command + +TiktokExplode.Extensions.DependencyInjection/ # AddTiktokExplode() for Microsoft.Extensions.DI ``` --- diff --git a/TiktokExplode.Extensions.DependencyInjection/README.md b/TiktokExplode.Extensions.DependencyInjection/README.md new file mode 100644 index 0000000..4f66be2 --- /dev/null +++ b/TiktokExplode.Extensions.DependencyInjection/README.md @@ -0,0 +1,87 @@ +# TiktokExplode.Extensions.DependencyInjection + +[![NuGet](https://img.shields.io/nuget/v/TiktokExplode.Extensions.DependencyInjection.svg)](https://www.nuget.org/packages/TiktokExplode.Extensions.DependencyInjection) +[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-512BD4)](https://dotnet.microsoft.com) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Ts-Pytham/TiktokExplode/blob/master/LICENSE) + +`Microsoft.Extensions.DependencyInjection` integration for [TiktokExplode](https://github.com/Ts-Pytham/TiktokExplode). + +Provides `AddTiktokExplode()` — a fluent extension method on `IServiceCollection` that registers `IVideoClient` and lets you choose between the Playwright (default) or HTTP fetcher strategy. + +--- + +## Installation + +``` +dotnet add package TiktokExplode.Extensions.DependencyInjection +``` + +--- + +## Usage + +```csharp +// Default — Playwright fetcher, all defaults +services.AddTiktokExplode(); + +// Playwright with a visible browser window (useful for debugging) +services.AddTiktokExplode(b => b + .UsePlaywrightFetcher(o => o.Headless = false)); + +// HTTP fetcher — no browser dependency, lighter footprint +services.AddTiktokExplode(b => b + .UseHttpFetcher(o => o.WarmupDelay = TimeSpan.Zero) + .ConfigureTiktok(o => o.MaxWafRetries = 5)); +``` + +Then inject `IVideoClient` normally: + +```csharp +public class MyService(IVideoClient client) +{ + public async Task GetTitleAsync(string url) + { + var video = await client.GetVideoAsync(url); + return video.Description; + } +} +``` + +--- + +## Registered services + +| Service | Implementation | Lifetime | +| --- | --- | --- | +| `IVideoClient` | `TiktokClient` | Singleton | +| `IPageFetcher` | `PlaywrightFetcher` or `HttpFetcher` | Singleton | +| `TikTokOptions` | — | Singleton | +| `PlaywrightFetcherOptions` or `HttpFetcherOptions` | — | Singleton | + +--- + +## Builder API + +| Method | Description | +| --- | --- | +| `ConfigureTiktok(Action?)` | Configures WAF retry count and base delay | +| `UsePlaywrightFetcher(Action?)` | Uses a real Chromium browser via Playwright (default) | +| `UseHttpFetcher(Action?)` | Uses a plain `HttpClient` — faster start, may be WAF-blocked | + +> **Note:** `UsePlaywrightFetcher` and `UseHttpFetcher` are mutually exclusive — the last one called wins. + +--- + +## Related packages + +| Package | Description | +| --- | --- | +| [`TiktokExplode`](https://www.nuget.org/packages/TiktokExplode) | Domain layer — models, interfaces, exceptions | +| [`TiktokExplode.Infrastructure`](https://www.nuget.org/packages/TiktokExplode.Infrastructure) | HTTP + Playwright fetchers, parser, download client | +| [`TiktokExplode.All`](https://www.nuget.org/packages/TiktokExplode.All) | Meta-package — installs domain + infrastructure together | + +--- + +## License + +MIT — see [LICENSE](https://github.com/Ts-Pytham/TiktokExplode/blob/master/LICENSE) for details. diff --git a/TiktokExplode.Extensions.DependencyInjection/TiktokExplode.Extensions.DependencyInjection.csproj b/TiktokExplode.Extensions.DependencyInjection/TiktokExplode.Extensions.DependencyInjection.csproj new file mode 100644 index 0000000..21ac446 --- /dev/null +++ b/TiktokExplode.Extensions.DependencyInjection/TiktokExplode.Extensions.DependencyInjection.csproj @@ -0,0 +1,42 @@ + + + + net8.0;net9.0 + enable + enable + latest + TiktokExplode.Extensions.DependencyInjection + TiktokExplode.Extensions.DependencyInjection + true + + + + TiktokExplode.Extensions.DependencyInjection + 1.0.0 + Ts-Pytham + Ts-Pytham + Microsoft.Extensions.DependencyInjection integration for TiktokExplode. Provides AddTiktokExplode() with a fluent builder to register IVideoClient and choose between Playwright or HTTP fetcher strategies. + tiktok;downloader;dependency-injection;di;extensions;dotnet + MIT + https://github.com/Ts-Pytham/TiktokExplode + https://github.com/Ts-Pytham/TiktokExplode + git + README.md + icon.png + + + + + + + + + + + + + + + + + diff --git a/TiktokExplode.Extensions.DependencyInjection/TiktokExplodeBuilder.cs b/TiktokExplode.Extensions.DependencyInjection/TiktokExplodeBuilder.cs new file mode 100644 index 0000000..45d034f --- /dev/null +++ b/TiktokExplode.Extensions.DependencyInjection/TiktokExplodeBuilder.cs @@ -0,0 +1,89 @@ +using Microsoft.Extensions.DependencyInjection; +using TiktokExplode.Domain.Abstractions; +using TiktokExplode.Infrastructure.Clients; +using TiktokExplode.Infrastructure.Fetchers; +using TiktokExplode.Infrastructure.Options; + +namespace TiktokExplode.Extensions.DependencyInjection; + +/// +/// Fluent builder for configuring TiktokExplode services before they are registered +/// into an . +/// Obtain an instance through . +/// +public sealed class TiktokExplodeBuilder(IServiceCollection services) +{ + private readonly TikTokOptions _tiktokOptions = new(); + private Action _fetcherRegistration = RegisterPlaywright(new()); + + /// + /// Configures the WAF-retry behaviour of TiktokClient. + /// + /// Delegate that mutates a instance. + /// The same builder for chaining. + public TiktokExplodeBuilder ConfigureTiktok(Action? options = null) + { + options?.Invoke(_tiktokOptions); + return this; + } + + /// + /// Configures the Playwright-based page fetcher as the active . + /// This is the default strategy — call this only when you need to customise the options. + /// + /// Delegate that mutates a instance. + /// The same builder for chaining. + public TiktokExplodeBuilder UsePlaywrightFetcher(Action? options = null) + { + var playwrightOptions = new PlaywrightFetcherOptions(); + options?.Invoke(playwrightOptions); + _fetcherRegistration = RegisterPlaywright(playwrightOptions); + return this; + } + + /// + /// Configures the lightweight HTTP-based page fetcher as the active . + /// This strategy avoids the Playwright browser dependency but may be blocked by TikTok's WAF. + /// + /// Delegate that mutates an instance. + /// The same builder for chaining. + public TiktokExplodeBuilder UseHttpFetcher(Action? options = null) + { + var httpFetcherOptions = new HttpFetcherOptions(); + options?.Invoke(httpFetcherOptions); + _fetcherRegistration = RegisterHttp(httpFetcherOptions); + return this; + } + + /// + /// Applies all pending registrations to the underlying . + /// Registers , the chosen , + /// and as singletons. + /// + /// The service collection for further chaining. + public IServiceCollection Build() + { + services.AddSingleton(_tiktokOptions); + _fetcherRegistration(services); + services.AddSingleton(); + return services; + } + + private static Action RegisterPlaywright(PlaywrightFetcherOptions options) + { + return services => + { + services.AddSingleton(options); + services.AddSingleton(); + }; + } + + private static Action RegisterHttp(HttpFetcherOptions options) + { + return services => + { + services.AddSingleton(options); + services.AddSingleton(); + }; + } +} diff --git a/TiktokExplode.Extensions.DependencyInjection/TiktokExplodeServiceCollectionExtensions.cs b/TiktokExplode.Extensions.DependencyInjection/TiktokExplodeServiceCollectionExtensions.cs new file mode 100644 index 0000000..63d898b --- /dev/null +++ b/TiktokExplode.Extensions.DependencyInjection/TiktokExplodeServiceCollectionExtensions.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace TiktokExplode.Extensions.DependencyInjection; + +/// +/// Extension methods on for registering TiktokExplode services. +/// +public static class TiktokExplodeServiceCollectionExtensions +{ + extension(IServiceCollection services) + { + /// + /// Registers TiktokExplode services into the service collection. + /// By default, uses the Playwright-based fetcher with default options. + /// + /// + /// Optional delegate to customise the fetcher strategy and options via . + /// + /// The same for chaining. + /// + /// + /// // Default — Playwright fetcher, all defaults + /// services.AddTiktokExplode(); + /// + /// // Custom — HTTP fetcher, no warmup delay + /// services.AddTiktokExplode(b => b + /// .UseHttpFetcher(o => o.WarmupDelay = TimeSpan.Zero) + /// .ConfigureTiktok(o => o.MaxWafRetries = 5)); + /// + /// + public IServiceCollection AddTiktokExplode( + Action? configure = null) + { + var builder = new TiktokExplodeBuilder(services); + configure?.Invoke(builder); + + return builder.Build(); + } + } +} diff --git a/TiktokExplode.Infrastructure/Options/HttpFetcherOptions.cs b/TiktokExplode.Infrastructure/Options/HttpFetcherOptions.cs index 876f9b2..2fa9896 100644 --- a/TiktokExplode.Infrastructure/Options/HttpFetcherOptions.cs +++ b/TiktokExplode.Infrastructure/Options/HttpFetcherOptions.cs @@ -11,11 +11,11 @@ public sealed class HttpFetcherOptions /// cookies before they are needed. /// Defaults to 1 200 ms. /// - public TimeSpan WarmupDelay { get; init; } = TimeSpan.FromMilliseconds(1200); + public TimeSpan WarmupDelay { get; set; } = TimeSpan.FromMilliseconds(1200); /// /// The User-Agent header value sent with all HTTP requests. /// Defaults to a recent Chrome on Windows UA string. /// - public string UserAgent { get; init; } = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"; + public string UserAgent { get; set; } = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"; } diff --git a/TiktokExplode.Infrastructure/Options/PlaywrightFetcherOptions.cs b/TiktokExplode.Infrastructure/Options/PlaywrightFetcherOptions.cs index be0e894..13b20e1 100644 --- a/TiktokExplode.Infrastructure/Options/PlaywrightFetcherOptions.cs +++ b/TiktokExplode.Infrastructure/Options/PlaywrightFetcherOptions.cs @@ -10,18 +10,18 @@ public sealed class PlaywrightFetcherOptions /// installed browser, or to use Playwright's bundled Chromium. /// Defaults to . /// - public string? BrowserChannel { get; init; } + public string? BrowserChannel { get; set; } /// /// Whether to run the browser in headless mode (no visible window). /// Set to to show the browser during debugging. /// Defaults to . /// - public bool Headless { get; init; } = true; + public bool Headless { get; set; } = true; /// /// Maximum time in milliseconds to wait for a page navigation to complete. /// Defaults to 30 000 ms (30 seconds). /// - public float PageTimeoutMs { get; init; } = 30_000; + public float PageTimeoutMs { get; set; } = 30_000; } diff --git a/TiktokExplode.NET.sln b/TiktokExplode.NET.sln deleted file mode 100644 index 4cbacc7..0000000 --- a/TiktokExplode.NET.sln +++ /dev/null @@ -1,90 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.12.35527.113 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TiktokExplode", "TiktokExplode\TiktokExplode.csproj", "{BCB37BB2-8991-4472-AB1C-387287754A6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TiktokExplode.Infrastructure", "TiktokExplode.Infrastructure\TiktokExplode.Infrastructure.csproj", "{FC753C86-0676-4703-8493-14C78797D0B0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TiktokExplode.Tests", "TiktokExplode.Tests\TiktokExplode.Tests.csproj", "{13AF0AAF-E912-408E-952F-1F5543392630}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TiktokExplode.Console", "TiktokExplode.Console\TiktokExplode.Console.csproj", "{C9454807-B7A3-43B5-8617-07959434F208}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TiktokExplode.All", "TiktokExplode.All\TiktokExplode.All.csproj", "{AEDE1986-2C80-4437-8418-28C9946EAE91}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BCB37BB2-8991-4472-AB1C-387287754A6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BCB37BB2-8991-4472-AB1C-387287754A6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BCB37BB2-8991-4472-AB1C-387287754A6E}.Debug|x64.ActiveCfg = Debug|Any CPU - {BCB37BB2-8991-4472-AB1C-387287754A6E}.Debug|x64.Build.0 = Debug|Any CPU - {BCB37BB2-8991-4472-AB1C-387287754A6E}.Debug|x86.ActiveCfg = Debug|Any CPU - {BCB37BB2-8991-4472-AB1C-387287754A6E}.Debug|x86.Build.0 = Debug|Any CPU - {BCB37BB2-8991-4472-AB1C-387287754A6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BCB37BB2-8991-4472-AB1C-387287754A6E}.Release|Any CPU.Build.0 = Release|Any CPU - {BCB37BB2-8991-4472-AB1C-387287754A6E}.Release|x64.ActiveCfg = Release|Any CPU - {BCB37BB2-8991-4472-AB1C-387287754A6E}.Release|x64.Build.0 = Release|Any CPU - {BCB37BB2-8991-4472-AB1C-387287754A6E}.Release|x86.ActiveCfg = Release|Any CPU - {BCB37BB2-8991-4472-AB1C-387287754A6E}.Release|x86.Build.0 = Release|Any CPU - {FC753C86-0676-4703-8493-14C78797D0B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FC753C86-0676-4703-8493-14C78797D0B0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FC753C86-0676-4703-8493-14C78797D0B0}.Debug|x64.ActiveCfg = Debug|Any CPU - {FC753C86-0676-4703-8493-14C78797D0B0}.Debug|x64.Build.0 = Debug|Any CPU - {FC753C86-0676-4703-8493-14C78797D0B0}.Debug|x86.ActiveCfg = Debug|Any CPU - {FC753C86-0676-4703-8493-14C78797D0B0}.Debug|x86.Build.0 = Debug|Any CPU - {FC753C86-0676-4703-8493-14C78797D0B0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FC753C86-0676-4703-8493-14C78797D0B0}.Release|Any CPU.Build.0 = Release|Any CPU - {FC753C86-0676-4703-8493-14C78797D0B0}.Release|x64.ActiveCfg = Release|Any CPU - {FC753C86-0676-4703-8493-14C78797D0B0}.Release|x64.Build.0 = Release|Any CPU - {FC753C86-0676-4703-8493-14C78797D0B0}.Release|x86.ActiveCfg = Release|Any CPU - {FC753C86-0676-4703-8493-14C78797D0B0}.Release|x86.Build.0 = Release|Any CPU - {13AF0AAF-E912-408E-952F-1F5543392630}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {13AF0AAF-E912-408E-952F-1F5543392630}.Debug|Any CPU.Build.0 = Debug|Any CPU - {13AF0AAF-E912-408E-952F-1F5543392630}.Debug|x64.ActiveCfg = Debug|Any CPU - {13AF0AAF-E912-408E-952F-1F5543392630}.Debug|x64.Build.0 = Debug|Any CPU - {13AF0AAF-E912-408E-952F-1F5543392630}.Debug|x86.ActiveCfg = Debug|Any CPU - {13AF0AAF-E912-408E-952F-1F5543392630}.Debug|x86.Build.0 = Debug|Any CPU - {13AF0AAF-E912-408E-952F-1F5543392630}.Release|Any CPU.ActiveCfg = Release|Any CPU - {13AF0AAF-E912-408E-952F-1F5543392630}.Release|Any CPU.Build.0 = Release|Any CPU - {13AF0AAF-E912-408E-952F-1F5543392630}.Release|x64.ActiveCfg = Release|Any CPU - {13AF0AAF-E912-408E-952F-1F5543392630}.Release|x64.Build.0 = Release|Any CPU - {13AF0AAF-E912-408E-952F-1F5543392630}.Release|x86.ActiveCfg = Release|Any CPU - {13AF0AAF-E912-408E-952F-1F5543392630}.Release|x86.Build.0 = Release|Any CPU - {C9454807-B7A3-43B5-8617-07959434F208}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C9454807-B7A3-43B5-8617-07959434F208}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C9454807-B7A3-43B5-8617-07959434F208}.Debug|x64.ActiveCfg = Debug|Any CPU - {C9454807-B7A3-43B5-8617-07959434F208}.Debug|x64.Build.0 = Debug|Any CPU - {C9454807-B7A3-43B5-8617-07959434F208}.Debug|x86.ActiveCfg = Debug|Any CPU - {C9454807-B7A3-43B5-8617-07959434F208}.Debug|x86.Build.0 = Debug|Any CPU - {C9454807-B7A3-43B5-8617-07959434F208}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C9454807-B7A3-43B5-8617-07959434F208}.Release|Any CPU.Build.0 = Release|Any CPU - {C9454807-B7A3-43B5-8617-07959434F208}.Release|x64.ActiveCfg = Release|Any CPU - {C9454807-B7A3-43B5-8617-07959434F208}.Release|x64.Build.0 = Release|Any CPU - {C9454807-B7A3-43B5-8617-07959434F208}.Release|x86.ActiveCfg = Release|Any CPU - {C9454807-B7A3-43B5-8617-07959434F208}.Release|x86.Build.0 = Release|Any CPU - {AEDE1986-2C80-4437-8418-28C9946EAE91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AEDE1986-2C80-4437-8418-28C9946EAE91}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AEDE1986-2C80-4437-8418-28C9946EAE91}.Debug|x64.ActiveCfg = Debug|Any CPU - {AEDE1986-2C80-4437-8418-28C9946EAE91}.Debug|x64.Build.0 = Debug|Any CPU - {AEDE1986-2C80-4437-8418-28C9946EAE91}.Debug|x86.ActiveCfg = Debug|Any CPU - {AEDE1986-2C80-4437-8418-28C9946EAE91}.Debug|x86.Build.0 = Debug|Any CPU - {AEDE1986-2C80-4437-8418-28C9946EAE91}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AEDE1986-2C80-4437-8418-28C9946EAE91}.Release|Any CPU.Build.0 = Release|Any CPU - {AEDE1986-2C80-4437-8418-28C9946EAE91}.Release|x64.ActiveCfg = Release|Any CPU - {AEDE1986-2C80-4437-8418-28C9946EAE91}.Release|x64.Build.0 = Release|Any CPU - {AEDE1986-2C80-4437-8418-28C9946EAE91}.Release|x86.ActiveCfg = Release|Any CPU - {AEDE1986-2C80-4437-8418-28C9946EAE91}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/TiktokExplode.NET.slnx b/TiktokExplode.NET.slnx new file mode 100644 index 0000000..c36b719 --- /dev/null +++ b/TiktokExplode.NET.slnx @@ -0,0 +1,8 @@ + + + + + + + +