Major refactor #141

Merged
sim0n00ps merged 55 commits from whimsical-c4lic0/OF-DL:refactor-architecture into master 2026-02-13 00:21:58 +00:00
13 changed files with 363 additions and 405 deletions
Showing only changes of commit 4889be1890 - Show all commits

View File

@ -37,8 +37,8 @@ using SinglePostCollection = OF_DL.Models.Entities.Posts.SinglePostCollection;
namespace OF_DL.Services;
public class ApiService(IAuthService authService, IConfigService configService, IDBService dbService)
: IAPIService
public class ApiService(IAuthService authService, IConfigService configService, IDbService dbService)
: IApiService
{
private const int MaxAttempts = 30;
private const int DelayBetweenAttempts = 3000;
@ -200,9 +200,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
response.EnsureSuccessStatusCode();
string body = await response.Content.ReadAsStringAsync();
//if the content creator doesnt exist, we get a 200 response, but the content isnt usable
//so let's not throw an exception, since "content creator no longer exists" is handled elsewhere
//which means we wont get loads of exceptions
// if the content creator doesn't exist, we get a 200 response, but the content isn't usable
// so let's not throw an exception, since "content creator no longer exists" is handled elsewhere
// which means we won't get loads of exceptions
if (body.Equals("[]"))
{
return null;
@ -2897,7 +2897,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
public async Task<string> GetDRMMPDPSSH(string mpdUrl, string policy, string signature, string kvp)
public async Task<string> GetDrmMpdPssh(string mpdUrl, string policy, string signature, string kvp)
{
try
{
@ -2941,9 +2941,9 @@ public class ApiService(IAuthService authService, IConfigService configService,
}
public async Task<DateTime> GetDRMMPDLastModified(string mpdUrl, string policy, string signature, string kvp)
public async Task<DateTime> GetDrmMpdLastModified(string mpdUrl, string policy, string signature, string kvp)
{
Log.Debug("Calling GetDRMMPDLastModified");
Log.Debug("Calling GetDrmMpdLastModified");
Log.Debug($"mpdUrl: {mpdUrl}");
Log.Debug($"policy: {policy}");
Log.Debug($"signature: {signature}");
@ -2989,10 +2989,10 @@ public class ApiService(IAuthService authService, IConfigService configService,
return DateTime.Now;
}
public async Task<string> GetDecryptionKeyOFDL(Dictionary<string, string> drmHeaders, string licenceUrl,
public async Task<string> GetDecryptionKeyOfdl(Dictionary<string, string> drmHeaders, string licenceUrl,
string pssh)
{
Log.Debug("Calling GetDecryptionOFDL");
Log.Debug("Calling GetDecryptionKeyOfdl");
try
{
@ -3052,7 +3052,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
return string.Empty;
}
public async Task<string> GetDecryptionKeyCDM(Dictionary<string, string> drmHeaders, string licenceUrl,
public async Task<string> GetDecryptionKeyCdm(Dictionary<string, string> drmHeaders, string licenceUrl,
string pssh)
{
Log.Debug("Calling GetDecryptionKeyCDM");

View File

@ -161,8 +161,8 @@ public class AuthService(IServiceProvider serviceProvider) : IAuthService
public async Task<UserEntities.User?> ValidateAuthAsync()
{
// Resolve IAPIService lazily to avoid circular dependency
IAPIService apiService = serviceProvider.GetRequiredService<IAPIService>();
// Resolve IApiService lazily to avoid circular dependency
IApiService apiService = serviceProvider.GetRequiredService<IApiService>();
return await apiService.GetUserInfo("/users/me");
}

View File

@ -4,9 +4,9 @@ using Serilog;
namespace OF_DL.Services;
public class DBService(IConfigService configService) : IDBService
public class DbService(IConfigService configService) : IDbService
{
public async Task CreateDB(string folder)
public async Task CreateDb(string folder)
{
try
{
@ -18,12 +18,12 @@ public class DBService(IConfigService configService) : IDBService
string dbFilePath = $"{folder}/Metadata/user_data.db";
// connect to the new database file
using SqliteConnection connection = new($"Data Source={dbFilePath}");
await using SqliteConnection connection = new($"Data Source={dbFilePath}");
// open the connection
connection.Open();
// create the 'medias' table
using (SqliteCommand cmd =
await using (SqliteCommand cmd =
new(
"CREATE TABLE IF NOT EXISTS medias (id INTEGER NOT NULL, media_id INTEGER, post_id INTEGER NOT NULL, link VARCHAR, directory VARCHAR, filename VARCHAR, size INTEGER, api_type VARCHAR, media_type VARCHAR, preview INTEGER, linked VARCHAR, downloaded INTEGER, created_at TIMESTAMP, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(media_id));",
connection))
@ -36,7 +36,7 @@ public class DBService(IConfigService configService) : IDBService
//
// Alter existing databases to create unique constraint on `medias`
//
using (SqliteCommand cmd = new(@"
await using (SqliteCommand cmd = new(@"
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
@ -74,7 +74,7 @@ public class DBService(IConfigService configService) : IDBService
}
// create the 'messages' table
using (SqliteCommand cmd =
await using (SqliteCommand cmd =
new(
"CREATE TABLE IF NOT EXISTS messages (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, user_id INTEGER, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));",
connection))
@ -83,7 +83,7 @@ public class DBService(IConfigService configService) : IDBService
}
// create the 'posts' table
using (SqliteCommand cmd =
await using (SqliteCommand cmd =
new(
"CREATE TABLE IF NOT EXISTS posts (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));",
connection))
@ -92,7 +92,7 @@ public class DBService(IConfigService configService) : IDBService
}
// create the 'stories' table
using (SqliteCommand cmd =
await using (SqliteCommand cmd =
new(
"CREATE TABLE IF NOT EXISTS stories (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));",
connection))
@ -101,7 +101,7 @@ public class DBService(IConfigService configService) : IDBService
}
// create the 'others' table
using (SqliteCommand cmd =
await using (SqliteCommand cmd =
new(
"CREATE TABLE IF NOT EXISTS others (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));",
connection))
@ -110,7 +110,7 @@ public class DBService(IConfigService configService) : IDBService
}
// create the 'products' table
using (SqliteCommand cmd =
await using (SqliteCommand cmd =
new(
"CREATE TABLE IF NOT EXISTS products (id INTEGER NOT NULL, post_id INTEGER NOT NULL, text VARCHAR, price INTEGER, paid INTEGER, archived BOOLEAN, created_at TIMESTAMP, title VARCHAR, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(post_id));",
connection))
@ -119,7 +119,7 @@ public class DBService(IConfigService configService) : IDBService
}
// create the 'profiles' table
using (SqliteCommand cmd =
await using (SqliteCommand cmd =
new(
"CREATE TABLE IF NOT EXISTS profiles (id INTEGER NOT NULL, user_id INTEGER NOT NULL, username VARCHAR NOT NULL, record_created_at TIMESTAMP, PRIMARY KEY(id), UNIQUE(username));",
connection))
@ -144,16 +144,16 @@ public class DBService(IConfigService configService) : IDBService
}
}
public async Task CreateUsersDB(Dictionary<string, long> users)
public async Task CreateUsersDb(Dictionary<string, long> users)
{
try
{
using SqliteConnection connection = new($"Data Source={Directory.GetCurrentDirectory()}/users.db");
await using SqliteConnection connection = new($"Data Source={Directory.GetCurrentDirectory()}/users.db");
Log.Debug("Database data source: " + connection.DataSource);
connection.Open();
using (SqliteCommand cmd =
await using (SqliteCommand cmd =
new(
"CREATE TABLE IF NOT EXISTS users (id INTEGER NOT NULL, user_id INTEGER NOT NULL, username VARCHAR NOT NULL, PRIMARY KEY(id), UNIQUE(username));",
connection))
@ -164,31 +164,25 @@ public class DBService(IConfigService configService) : IDBService
Log.Debug("Adding missing creators");
foreach (KeyValuePair<string, long> user in users)
{
using (SqliteCommand checkCmd = new("SELECT user_id, username FROM users WHERE user_id = @userId;",
connection))
{
await using SqliteCommand checkCmd = new("SELECT user_id, username FROM users WHERE user_id = @userId;",
connection);
checkCmd.Parameters.AddWithValue("@userId", user.Value);
using (SqliteDataReader reader = await checkCmd.ExecuteReaderAsync())
{
await using SqliteDataReader reader = await checkCmd.ExecuteReaderAsync();
if (!reader.Read())
{
using (SqliteCommand insertCmd =
await using SqliteCommand insertCmd =
new("INSERT INTO users (user_id, username) VALUES (@userId, @username);",
connection))
{
connection);
insertCmd.Parameters.AddWithValue("@userId", user.Value);
insertCmd.Parameters.AddWithValue("@username", user.Key);
await insertCmd.ExecuteNonQueryAsync();
Log.Debug("Inserted new creator: " + user.Key);
}
}
else
{
Log.Debug("Creator " + user.Key + " already exists");
}
}
}
}
connection.Close();
}
@ -211,11 +205,11 @@ public class DBService(IConfigService configService) : IDBService
{
try
{
using SqliteConnection connection = new($"Data Source={Directory.GetCurrentDirectory()}/users.db");
await using SqliteConnection connection = new($"Data Source={Directory.GetCurrentDirectory()}/users.db");
connection.Open();
using (SqliteCommand checkCmd = new("SELECT user_id, username FROM users WHERE user_id = @userId;",
await using (SqliteCommand checkCmd = new("SELECT user_id, username FROM users WHERE user_id = @userId;",
connection))
{
checkCmd.Parameters.AddWithValue("@userId", user.Value);
@ -264,16 +258,16 @@ public class DBService(IConfigService configService) : IDBService
}
}
public async Task AddMessage(string folder, long post_id, string message_text, string price, bool is_paid,
bool is_archived, DateTime created_at, long user_id)
public async Task AddMessage(string folder, long postId, string messageText, string price, bool isPaid,
bool isArchived, DateTime createdAt, long userId)
{
try
{
using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
await using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
connection.Open();
await EnsureCreatedAtColumnExists(connection, "messages");
using SqliteCommand cmd = new("SELECT COUNT(*) FROM messages WHERE post_id=@post_id", connection);
cmd.Parameters.AddWithValue("@post_id", post_id);
await using SqliteCommand cmd = new("SELECT COUNT(*) FROM messages WHERE post_id=@post_id", connection);
cmd.Parameters.AddWithValue("@post_id", postId);
int count = Convert.ToInt32(await cmd.ExecuteScalarAsync());
if (count == 0)
{
@ -282,13 +276,13 @@ public class DBService(IConfigService configService) : IDBService
new(
"INSERT INTO messages(post_id, text, price, paid, archived, created_at, user_id, record_created_at) VALUES(@post_id, @message_text, @price, @is_paid, @is_archived, @created_at, @user_id, @record_created_at)",
connection);
insertCmd.Parameters.AddWithValue("@post_id", post_id);
insertCmd.Parameters.AddWithValue("@message_text", message_text);
insertCmd.Parameters.AddWithValue("@post_id", postId);
insertCmd.Parameters.AddWithValue("@message_text", messageText);
insertCmd.Parameters.AddWithValue("@price", price);
insertCmd.Parameters.AddWithValue("@is_paid", is_paid);
insertCmd.Parameters.AddWithValue("@is_archived", is_archived);
insertCmd.Parameters.AddWithValue("@created_at", created_at);
insertCmd.Parameters.AddWithValue("@user_id", user_id);
insertCmd.Parameters.AddWithValue("@is_paid", isPaid);
insertCmd.Parameters.AddWithValue("@is_archived", isArchived);
insertCmd.Parameters.AddWithValue("@created_at", createdAt);
insertCmd.Parameters.AddWithValue("@user_id", userId);
insertCmd.Parameters.AddWithValue("@record_created_at", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
await insertCmd.ExecuteNonQueryAsync();
}
@ -309,30 +303,30 @@ public class DBService(IConfigService configService) : IDBService
}
public async Task AddPost(string folder, long post_id, string message_text, string price, bool is_paid,
bool is_archived, DateTime created_at)
public async Task AddPost(string folder, long postId, string messageText, string price, bool isPaid,
bool isArchived, DateTime createdAt)
{
try
{
using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
await using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
connection.Open();
await EnsureCreatedAtColumnExists(connection, "posts");
using SqliteCommand cmd = new("SELECT COUNT(*) FROM posts WHERE post_id=@post_id", connection);
cmd.Parameters.AddWithValue("@post_id", post_id);
await using SqliteCommand cmd = new("SELECT COUNT(*) FROM posts WHERE post_id=@post_id", connection);
cmd.Parameters.AddWithValue("@post_id", postId);
int count = Convert.ToInt32(await cmd.ExecuteScalarAsync());
if (count == 0)
{
// If the record doesn't exist, insert a new one
using SqliteCommand insertCmd =
await using SqliteCommand insertCmd =
new(
"INSERT INTO posts(post_id, text, price, paid, archived, created_at, record_created_at) VALUES(@post_id, @message_text, @price, @is_paid, @is_archived, @created_at, @record_created_at)",
connection);
insertCmd.Parameters.AddWithValue("@post_id", post_id);
insertCmd.Parameters.AddWithValue("@message_text", message_text);
insertCmd.Parameters.AddWithValue("@post_id", postId);
insertCmd.Parameters.AddWithValue("@message_text", messageText);
insertCmd.Parameters.AddWithValue("@price", price);
insertCmd.Parameters.AddWithValue("@is_paid", is_paid);
insertCmd.Parameters.AddWithValue("@is_archived", is_archived);
insertCmd.Parameters.AddWithValue("@created_at", created_at);
insertCmd.Parameters.AddWithValue("@is_paid", isPaid);
insertCmd.Parameters.AddWithValue("@is_archived", isArchived);
insertCmd.Parameters.AddWithValue("@created_at", createdAt);
insertCmd.Parameters.AddWithValue("@record_created_at", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
await insertCmd.ExecuteNonQueryAsync();
}
@ -353,30 +347,30 @@ public class DBService(IConfigService configService) : IDBService
}
public async Task AddStory(string folder, long post_id, string message_text, string price, bool is_paid,
bool is_archived, DateTime created_at)
public async Task AddStory(string folder, long postId, string messageText, string price, bool isPaid,
bool isArchived, DateTime createdAt)
{
try
{
using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
await using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
connection.Open();
await EnsureCreatedAtColumnExists(connection, "stories");
using SqliteCommand cmd = new("SELECT COUNT(*) FROM stories WHERE post_id=@post_id", connection);
cmd.Parameters.AddWithValue("@post_id", post_id);
await using SqliteCommand cmd = new("SELECT COUNT(*) FROM stories WHERE post_id=@post_id", connection);
cmd.Parameters.AddWithValue("@post_id", postId);
int count = Convert.ToInt32(await cmd.ExecuteScalarAsync());
if (count == 0)
{
// If the record doesn't exist, insert a new one
using SqliteCommand insertCmd =
await using SqliteCommand insertCmd =
new(
"INSERT INTO stories(post_id, text, price, paid, archived, created_at, record_created_at) VALUES(@post_id, @message_text, @price, @is_paid, @is_archived, @created_at, @record_created_at)",
connection);
insertCmd.Parameters.AddWithValue("@post_id", post_id);
insertCmd.Parameters.AddWithValue("@message_text", message_text);
insertCmd.Parameters.AddWithValue("@post_id", postId);
insertCmd.Parameters.AddWithValue("@message_text", messageText);
insertCmd.Parameters.AddWithValue("@price", price);
insertCmd.Parameters.AddWithValue("@is_paid", is_paid);
insertCmd.Parameters.AddWithValue("@is_archived", is_archived);
insertCmd.Parameters.AddWithValue("@created_at", created_at);
insertCmd.Parameters.AddWithValue("@is_paid", isPaid);
insertCmd.Parameters.AddWithValue("@is_archived", isArchived);
insertCmd.Parameters.AddWithValue("@created_at", createdAt);
insertCmd.Parameters.AddWithValue("@record_created_at", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
await insertCmd.ExecuteNonQueryAsync();
}
@ -397,13 +391,13 @@ public class DBService(IConfigService configService) : IDBService
}
public async Task AddMedia(string folder, long media_id, long post_id, string link, string? directory,
string? filename, long? size, string api_type, string media_type, bool preview, bool downloaded,
DateTime? created_at)
public async Task AddMedia(string folder, long mediaId, long postId, string link, string? directory,
string? filename, long? size, string apiType, string mediaType, bool preview, bool downloaded,
DateTime? createdAt)
{
try
{
using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
await using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
connection.Open();
await EnsureCreatedAtColumnExists(connection, "medias");
StringBuilder sql = new("SELECT COUNT(*) FROM medias WHERE media_id=@media_id");
@ -412,15 +406,15 @@ public class DBService(IConfigService configService) : IDBService
sql.Append(" and api_type=@api_type");
}
using SqliteCommand cmd = new(sql.ToString(), connection);
cmd.Parameters.AddWithValue("@media_id", media_id);
cmd.Parameters.AddWithValue("@api_type", api_type);
await using SqliteCommand cmd = new(sql.ToString(), connection);
cmd.Parameters.AddWithValue("@media_id", mediaId);
cmd.Parameters.AddWithValue("@api_type", apiType);
int count = Convert.ToInt32(cmd.ExecuteScalar());
if (count == 0)
{
// If the record doesn't exist, insert a new one
using SqliteCommand insertCmd = new(
$"INSERT INTO medias(media_id, post_id, link, directory, filename, size, api_type, media_type, preview, downloaded, created_at, record_created_at) VALUES({media_id}, {post_id}, '{link}', '{directory ?? "NULL"}', '{filename ?? "NULL"}', {size?.ToString() ?? "NULL"}, '{api_type}', '{media_type}', {Convert.ToInt32(preview)}, {Convert.ToInt32(downloaded)}, '{created_at?.ToString("yyyy-MM-dd HH:mm:ss")}', '{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}')",
await using SqliteCommand insertCmd = new(
$"INSERT INTO medias(media_id, post_id, link, directory, filename, size, api_type, media_type, preview, downloaded, created_at, record_created_at) VALUES({mediaId}, {postId}, '{link}', '{directory ?? "NULL"}', '{filename ?? "NULL"}', {size?.ToString() ?? "NULL"}, '{apiType}', '{mediaType}', {Convert.ToInt32(preview)}, {Convert.ToInt32(downloaded)}, '{createdAt?.ToString("yyyy-MM-dd HH:mm:ss")}', '{DateTime.Now:yyyy-MM-dd HH:mm:ss}')",
connection);
await insertCmd.ExecuteNonQueryAsync();
}
@ -441,12 +435,10 @@ public class DBService(IConfigService configService) : IDBService
}
public async Task<bool> CheckDownloaded(string folder, long media_id, string api_type)
public async Task<bool> CheckDownloaded(string folder, long mediaId, string apiType)
{
try
{
bool downloaded;
await using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
StringBuilder sql = new("SELECT downloaded FROM medias WHERE media_id=@media_id");
if (configService.CurrentConfig.DownloadDuplicatedMedia)
@ -456,9 +448,9 @@ public class DBService(IConfigService configService) : IDBService
connection.Open();
await using SqliteCommand cmd = new(sql.ToString(), connection);
cmd.Parameters.AddWithValue("@media_id", media_id);
cmd.Parameters.AddWithValue("@api_type", api_type);
downloaded = Convert.ToBoolean(await cmd.ExecuteScalarAsync());
cmd.Parameters.AddWithValue("@media_id", mediaId);
cmd.Parameters.AddWithValue("@api_type", apiType);
bool downloaded = Convert.ToBoolean(await cmd.ExecuteScalarAsync());
return downloaded;
}
@ -480,10 +472,10 @@ public class DBService(IConfigService configService) : IDBService
}
public async Task UpdateMedia(string folder, long media_id, string api_type, string directory, string filename,
long size, bool downloaded, DateTime created_at)
public async Task UpdateMedia(string folder, long mediaId, string apiType, string directory, string filename,
long size, bool downloaded, DateTime createdAt)
{
using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
await using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
connection.Open();
// Construct the update command
@ -496,33 +488,30 @@ public class DBService(IConfigService configService) : IDBService
}
// Create a new command object
using SqliteCommand command = new(sql.ToString(), connection);
await using SqliteCommand command = new(sql.ToString(), connection);
// Add parameters to the command object
command.Parameters.AddWithValue("@directory", directory);
command.Parameters.AddWithValue("@filename", filename);
command.Parameters.AddWithValue("@size", size);
command.Parameters.AddWithValue("@downloaded", downloaded ? 1 : 0);
command.Parameters.AddWithValue("@created_at", created_at);
command.Parameters.AddWithValue("@media_id", media_id);
command.Parameters.AddWithValue("@api_type", api_type);
command.Parameters.AddWithValue("@created_at", createdAt);
command.Parameters.AddWithValue("@media_id", mediaId);
command.Parameters.AddWithValue("@api_type", apiType);
// Execute the command
await command.ExecuteNonQueryAsync();
}
public async Task<long> GetStoredFileSize(string folder, long media_id, string api_type)
{
long size;
using (SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db"))
public async Task<long> GetStoredFileSize(string folder, long mediaId, string apiType)
{
await using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
connection.Open();
using SqliteCommand cmd = new("SELECT size FROM medias WHERE media_id=@media_id and api_type=@api_type",
await using SqliteCommand cmd = new("SELECT size FROM medias WHERE media_id=@media_id and api_type=@api_type",
connection);
cmd.Parameters.AddWithValue("@media_id", media_id);
cmd.Parameters.AddWithValue("@api_type", api_type);
size = Convert.ToInt64(await cmd.ExecuteScalarAsync());
}
cmd.Parameters.AddWithValue("@media_id", mediaId);
cmd.Parameters.AddWithValue("@api_type", apiType);
long size = Convert.ToInt64(await cmd.ExecuteScalarAsync());
return size;
}
@ -530,10 +519,9 @@ public class DBService(IConfigService configService) : IDBService
public async Task<DateTime?> GetMostRecentPostDate(string folder)
{
DateTime? mostRecentDate = null;
using (SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db"))
{
await using SqliteConnection connection = new($"Data Source={folder}/Metadata/user_data.db");
connection.Open();
using SqliteCommand cmd = new(@"
await using SqliteCommand cmd = new(@"
SELECT
MIN(created_at) AS created_at
FROM (
@ -554,15 +542,14 @@ public class DBService(IConfigService configService) : IDBService
{
mostRecentDate = Convert.ToDateTime(scalarValue);
}
}
return mostRecentDate;
}
private async Task EnsureCreatedAtColumnExists(SqliteConnection connection, string tableName)
{
using SqliteCommand cmd = new($"PRAGMA table_info({tableName});", connection);
using SqliteDataReader reader = await cmd.ExecuteReaderAsync();
await using SqliteCommand cmd = new($"PRAGMA table_info({tableName});", connection);
await using SqliteDataReader reader = await cmd.ExecuteReaderAsync();
bool columnExists = false;
while (await reader.ReadAsync())
@ -576,7 +563,7 @@ public class DBService(IConfigService configService) : IDBService
if (!columnExists)
{
using SqliteCommand alterCmd = new($"ALTER TABLE {tableName} ADD COLUMN record_created_at TIMESTAMP;",
await using SqliteCommand alterCmd = new($"ALTER TABLE {tableName} ADD COLUMN record_created_at TIMESTAMP;",
connection);
await alterCmd.ExecuteNonQueryAsync();
}

View File

@ -10,10 +10,10 @@ using UserEntities = OF_DL.Models.Entities.Users;
namespace OF_DL.Services;
public class DownloadOrchestrationService(
IAPIService apiService,
IApiService apiService,
IConfigService configService,
IDownloadService downloadService,
IDBService dbService) : IDownloadOrchestrationService
IDbService dbService) : IDownloadOrchestrationService
{
public List<long> PaidPostIds { get; } = new();
@ -85,7 +85,7 @@ public class DownloadOrchestrationService(
}
}
await dbService.CreateUsersDB(result.Users);
await dbService.CreateUsersDb(result.Users);
return result;
}
@ -113,7 +113,7 @@ public class DownloadOrchestrationService(
Log.Debug($"Created folder for {username}");
}
await dbService.CreateDB(path);
await dbService.CreateDb(path);
}
public async Task<CreatorDownloadResult> DownloadCreatorContentAsync(
@ -346,7 +346,7 @@ public class DownloadOrchestrationService(
}
await apiService.GetUserInfo($"/users/{user.Key}");
await dbService.CreateDB(path);
await dbService.CreateDb(path);
}
string basePath = !string.IsNullOrEmpty(config.DownloadPath)

View File

@ -19,9 +19,9 @@ namespace OF_DL.Services;
public class DownloadService(
IAuthService authService,
IConfigService configService,
IDBService dbService,
IDbService dbService,
IFileNameService fileNameService,
IAPIService apiService)
IApiService apiService)
: IDownloadService
{
private TaskCompletionSource<bool> _completionSource = new();
@ -67,7 +67,7 @@ public class DownloadService(
Directory.CreateDirectory(folder + subFolder);
}
List<string> md5Hashes = CalculateFolderMD5(folder + subFolder);
List<string> md5Hashes = CalculateFolderMd5(folder + subFolder);
Uri uri = new(url);
string destinationPath = $"{folder}{subFolder}/";
@ -205,7 +205,7 @@ public class DownloadService(
}
private async Task OnFFMPEGDownloadComplete(string tempFilename, DateTime lastModified, string folder, string path,
string customFileName, string filename, long media_id, string api_type, IProgressReporter progressReporter)
string customFileName, string filename, long mediaId, string apiType, IProgressReporter progressReporter)
{
try
{
@ -223,16 +223,9 @@ public class DownloadService(
long fileSizeInBytes = new FileInfo(!string.IsNullOrEmpty(customFileName)
? folder + path + "/" + customFileName + ".mp4"
: tempFilename).Length;
if (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
progressReporter.ReportProgress(configService.CurrentConfig.ShowScrapeSize ? fileSizeInBytes : 1);
await dbService.UpdateMedia(folder, media_id, api_type, folder + path,
await dbService.UpdateMedia(folder, mediaId, apiType, folder + path,
!string.IsNullOrEmpty(customFileName) ? customFileName + ".mp4" : filename + "_source.mp4",
fileSizeInBytes, true, lastModified);
}
@ -266,50 +259,45 @@ public class DownloadService(
_completionSource.TrySetResult(false);
}
private static List<string> CalculateFolderMD5(string folder)
private static List<string> CalculateFolderMd5(string folder)
{
List<string> md5Hashes = new();
if (Directory.Exists(folder))
List<string> md5Hashes = [];
if (!Directory.Exists(folder))
{
return md5Hashes;
}
string[] files = Directory.GetFiles(folder);
foreach (string file in files)
{
md5Hashes.Add(CalculateMD5(file));
}
}
md5Hashes.AddRange(files.Select(CalculateMd5));
return md5Hashes;
}
private static string CalculateMD5(string filePath)
{
using (MD5 md5 = MD5.Create())
{
using (FileStream stream = File.OpenRead(filePath))
private static string CalculateMd5(string filePath)
{
using MD5 md5 = MD5.Create();
using FileStream stream = File.OpenRead(filePath);
byte[] hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
}
/// <summary>
/// </summary>
/// <param name="path"></param>
/// <param name="url"></param>
/// <param name="folder"></param>
/// <param name="media_id"></param>
/// <param name="api_type"></param>
/// <param name="mediaId"></param>
/// <param name="apiType"></param>
/// <param name="progressReporter"></param>
/// <param name="serverFileName"></param>
/// <param name="resolvedFileName"></param>
/// <returns></returns>
protected async Task<bool> CreateDirectoriesAndDownloadMedia(string path,
private async Task<bool> CreateDirectoriesAndDownloadMedia(string path,
string url,
string folder,
long media_id,
string api_type,
long mediaId,
string apiType,
IProgressReporter progressReporter,
string serverFileName,
string resolvedFileName)
@ -325,7 +313,7 @@ public class DownloadService(
path = UpdatePathBasedOnExtension(folder, path, extension);
return await ProcessMediaDownload(folder, media_id, api_type, url, path, serverFileName, resolvedFileName,
return await ProcessMediaDownload(folder, mediaId, apiType, url, path, serverFileName, resolvedFileName,
extension, progressReporter);
}
catch (Exception ex)
@ -461,12 +449,12 @@ public class DownloadService(
if (uri.Host == "cdn3.onlyfans.com" && uri.LocalPath.Contains("/dash/files"))
{
string[] messageUrlParsed = url.Split(',');
string mpdURL = messageUrlParsed[0];
string mpdUrl = messageUrlParsed[0];
string policy = messageUrlParsed[1];
string signature = messageUrlParsed[2];
string kvp = messageUrlParsed[3];
mpdURL = mpdURL.Replace(".mpd", "_source.mp4");
mpdUrl = mpdUrl.Replace(".mpd", "_source.mp4");
using HttpClient client = new();
client.DefaultRequestHeaders.Add("Cookie",
@ -474,7 +462,7 @@ public class DownloadService(
client.DefaultRequestHeaders.Add("User-Agent", authService.CurrentAuth.UserAgent);
using HttpResponseMessage response =
await client.GetAsync(mpdURL, HttpCompletionOption.ResponseHeadersRead);
await client.GetAsync(mpdUrl, HttpCompletionOption.ResponseHeadersRead);
if (response.IsSuccessStatusCode)
{
fileSize = response.Content.Headers.ContentLength ?? 0;
@ -500,7 +488,7 @@ public class DownloadService(
return fileSize;
}
public static async Task<DateTime> GetDRMVideoLastModified(string url, Auth auth)
public static async Task<DateTime> GetDrmVideoLastModified(string url, Auth auth)
{
string[] messageUrlParsed = url.Split(',');
string mpdUrl = messageUrlParsed[0];
@ -532,15 +520,12 @@ public class DownloadService(
using HttpClient client = new();
using HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
if (response.IsSuccessStatusCode)
if (!response.IsSuccessStatusCode)
{
if (response.Content.Headers.LastModified != null)
{
return response.Content.Headers.LastModified.Value.DateTime;
}
return DateTime.Now;
}
return DateTime.Now;
return response.Content.Headers.LastModified?.DateTime ?? DateTime.Now;
}
/// <summary>
@ -636,8 +621,8 @@ public class DownloadService(
/// Handles new media by downloading and updating the database.
/// </summary>
/// <param name="folder"></param>
/// <param name="media_id"></param>
/// <param name="api_type"></param>
/// <param name="mediaId"></param>
/// <param name="apiType"></param>
/// <param name="url"></param>
/// <param name="path"></param>
/// <param name="serverFilename"></param>
@ -646,8 +631,8 @@ public class DownloadService(
/// <param name="progressReporter"></param>
/// <returns>A Task resulting in a boolean indicating whether the media is newly downloaded or not.</returns>
private async Task<bool> HandleNewMedia(string folder,
long media_id,
string api_type,
long mediaId,
string apiType,
string url,
string path,
string serverFilename,
@ -696,32 +681,18 @@ public class DownloadService(
fileSizeInBytes = GetLocalFileSize(finalPath);
lastModified = File.GetLastWriteTime(finalPath);
if (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
progressReporter.ReportProgress(configService.CurrentConfig.ShowScrapeSize ? fileSizeInBytes : 1);
status = false;
}
// Handle the case where the file has been downloaded in the past with a custom filename.
//but it has downloaded outsite of this application so it doesn't exist in the database
// but it has downloaded outside of this application so it doesn't exist in the database
// this is a bit improbable but we should check for that.
else if (File.Exists(fullPathWithTheNewFileName))
{
fileSizeInBytes = GetLocalFileSize(fullPathWithTheNewFileName);
lastModified = File.GetLastWriteTime(fullPathWithTheNewFileName);
if (configService.CurrentConfig.ShowScrapeSize)
{
progressReporter.ReportProgress(fileSizeInBytes);
}
else
{
progressReporter.ReportProgress(1);
}
progressReporter.ReportProgress(configService.CurrentConfig.ShowScrapeSize ? fileSizeInBytes : 1);
status = false;
}
@ -733,9 +704,9 @@ public class DownloadService(
}
//finaly check which filename we should use. Custom or the server one.
//if a custom is used, then the servefilename will be different from the resolved filename.
//if a custom is used, then the serverFilename will be different from the resolved filename.
string finalName = serverFilename == resolvedFilename ? serverFilename : resolvedFilename;
await dbService.UpdateMedia(folder, media_id, api_type, folder + path, finalName + extension, fileSizeInBytes,
await dbService.UpdateMedia(folder, mediaId, apiType, folder + path, finalName + extension, fileSizeInBytes,
true, lastModified);
return status;
}
@ -745,16 +716,16 @@ public class DownloadService(
/// Handles media that has been previously downloaded and updates the task accordingly.
/// </summary>
/// <param name="folder"></param>
/// <param name="media_id"></param>
/// <param name="api_type"></param>
/// <param name="mediaId"></param>
/// <param name="apiType"></param>
/// <param name="progressReporter"></param>
/// <returns>A boolean indicating whether the media is newly downloaded or not.</returns>
private async Task<bool> HandlePreviouslyDownloadedMediaAsync(string folder, long media_id, string api_type,
private async Task<bool> HandlePreviouslyDownloadedMediaAsync(string folder, long mediaId, string apiType,
IProgressReporter progressReporter)
{
if (configService.CurrentConfig.ShowScrapeSize)
{
long size = await dbService.GetStoredFileSize(folder, media_id, api_type);
long size = await dbService.GetStoredFileSize(folder, mediaId, apiType);
progressReporter.ReportProgress(size);
}
else
@ -791,11 +762,12 @@ public class DownloadService(
Stream body = await response.Content.ReadAsStreamAsync();
// Wrap the body stream with the ThrottledStream to limit read rate.
using (ThrottledStream throttledStream = new(body,
await using (ThrottledStream throttledStream = new(body,
configService.CurrentConfig.DownloadLimitInMbPerSec * 1_000_000,
configService.CurrentConfig.LimitDownloadRate))
{
using FileStream fileStream = new(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None, 16384,
await using FileStream fileStream = new(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None,
16384,
true);
byte[] buffer = new byte[16384];
int read;
@ -854,7 +826,7 @@ public class DownloadService(
return totalFileSize;
}
public async Task<bool> DownloadMedia(string url, string folder, long media_id, string api_type,
public async Task<bool> DownloadMedia(string url, string folder, long mediaId, string apiType,
IProgressReporter progressReporter, string path,
string? filenameFormat, object? postInfo, object? postMedia,
object? author, Dictionary<string, long> users)
@ -864,12 +836,12 @@ public class DownloadService(
string resolvedFilename = await GenerateCustomFileName(filename, filenameFormat, postInfo, postMedia, author,
folder.Split("/")[^1], users, fileNameService, CustomFileNameOption.ReturnOriginal);
return await CreateDirectoriesAndDownloadMedia(path, url, folder, media_id, api_type, progressReporter,
return await CreateDirectoriesAndDownloadMedia(path, url, folder, mediaId, apiType, progressReporter,
filename, resolvedFilename);
}
public async Task<bool> DownloadDRMVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
public async Task<bool> DownloadDrmVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long mediaId, string apiType,
IProgressReporter progressReporter, string path,
string? filenameFormat, object? postInfo, object? postMedia,
object? author, Dictionary<string, long> users)
@ -902,14 +874,14 @@ public class DownloadService(
string customFileName = await GenerateCustomFileName(filename, filenameFormat, postInfo, postMedia, author,
folder.Split("/")[^1], users, fileNameService, CustomFileNameOption.ReturnEmpty);
if (!await dbService.CheckDownloaded(folder, media_id, api_type))
if (!await dbService.CheckDownloaded(folder, mediaId, apiType))
{
if (!string.IsNullOrEmpty(customFileName)
? !File.Exists(folder + path + "/" + customFileName + ".mp4")
: !File.Exists(folder + path + "/" + filename + "_source.mp4"))
{
return await DownloadDrmMedia(authService.CurrentAuth.UserAgent, policy, signature, kvp,
authService.CurrentAuth.Cookie, url, decryptionKey, folder, lastModified, media_id, api_type,
authService.CurrentAuth.Cookie, url, decryptionKey, folder, lastModified, mediaId, apiType,
progressReporter, customFileName, filename, path);
}
@ -918,7 +890,7 @@ public class DownloadService(
: folder + path + "/" + filename + "_source.mp4").Length;
ReportProgress(progressReporter, fileSizeInBytes);
await dbService.UpdateMedia(folder, media_id, api_type, folder + path,
await dbService.UpdateMedia(folder, mediaId, apiType, folder + path,
!string.IsNullOrEmpty(customFileName) ? customFileName + ".mp4" : filename + "_source.mp4",
fileSizeInBytes, true, lastModified);
}
@ -946,15 +918,15 @@ public class DownloadService(
return false;
}
long size = await dbService.GetStoredFileSize(folder, media_id, api_type);
await dbService.UpdateMedia(folder, media_id, api_type, folder + path, customFileName + ".mp4",
long size = await dbService.GetStoredFileSize(folder, mediaId, apiType);
await dbService.UpdateMedia(folder, mediaId, apiType, folder + path, customFileName + ".mp4",
size, true, lastModified);
}
}
if (configService.CurrentConfig.ShowScrapeSize)
{
long size = await dbService.GetStoredFileSize(folder, media_id, api_type);
long size = await dbService.GetStoredFileSize(folder, mediaId, apiType);
progressReporter.ReportProgress(size);
}
else
@ -982,26 +954,17 @@ public class DownloadService(
return false;
}
private void ReportProgress(IProgressReporter reporter, long sizeOrCount)
{
if (configService.CurrentConfig.ShowScrapeSize)
{
reporter.ReportProgress(sizeOrCount);
}
else
{
reporter.ReportProgress(1);
}
}
private void ReportProgress(IProgressReporter reporter, long sizeOrCount) =>
reporter.ReportProgress(configService.CurrentConfig.ShowScrapeSize ? sizeOrCount : 1);
public async Task<(string decryptionKey, DateTime lastModified)?> GetDecryptionInfo(
string mpdUrl, string policy, string signature, string kvp,
string mediaId, string contentId, string drmType,
bool clientIdBlobMissing, bool devicePrivateKeyMissing)
{
string pssh = await apiService.GetDRMMPDPSSH(mpdUrl, policy, signature, kvp);
string pssh = await apiService.GetDrmMpdPssh(mpdUrl, policy, signature, kvp);
DateTime lastModified = await apiService.GetDRMMPDLastModified(mpdUrl, policy, signature, kvp);
DateTime lastModified = await apiService.GetDrmMpdLastModified(mpdUrl, policy, signature, kvp);
Dictionary<string, string> drmHeaders =
apiService.GetDynamicHeaders($"/api2/v2/users/media/{mediaId}/drm/{drmType}/{contentId}",
"?type=widevine");
@ -1009,8 +972,8 @@ public class DownloadService(
$"https://onlyfans.com/api2/v2/users/media/{mediaId}/drm/{drmType}/{contentId}?type=widevine";
string decryptionKey = clientIdBlobMissing || devicePrivateKeyMissing
? await apiService.GetDecryptionKeyOFDL(drmHeaders, licenseUrl, pssh)
: await apiService.GetDecryptionKeyCDM(drmHeaders, licenseUrl, pssh);
? await apiService.GetDecryptionKeyOfdl(drmHeaders, licenseUrl, pssh)
: await apiService.GetDecryptionKeyCdm(drmHeaders, licenseUrl, pssh);
return (decryptionKey, lastModified);
}
@ -1041,10 +1004,10 @@ public class DownloadService(
int oldHighlightsCount = 0;
int newHighlightsCount = 0;
foreach (KeyValuePair<long, string> highlightKVP in highlights)
foreach (KeyValuePair<long, string> highlightKvp in highlights)
{
bool isNew =
await DownloadMedia(highlightKVP.Value, path, highlightKVP.Key, "Stories", progressReporter,
await DownloadMedia(highlightKvp.Value, path, highlightKvp.Key, "Stories", progressReporter,
"/Stories/Free", null, null, null, null, new Dictionary<string, long>());
if (isNew)
{
@ -1095,9 +1058,9 @@ public class DownloadService(
int oldStoriesCount = 0;
int newStoriesCount = 0;
foreach (KeyValuePair<long, string> storyKVP in stories)
foreach (KeyValuePair<long, string> storyKvp in stories)
{
bool isNew = await DownloadMedia(storyKVP.Value, path, storyKVP.Key, "Stories", progressReporter,
bool isNew = await DownloadMedia(storyKvp.Value, path, storyKvp.Key, "Stories", progressReporter,
"/Stories/Free", null, null, null, null, new Dictionary<string, long>());
if (isNew)
{
@ -1146,20 +1109,20 @@ public class DownloadService(
int oldArchivedCount = 0;
int newArchivedCount = 0;
foreach (KeyValuePair<long, string> archivedKVP in archived.ArchivedPosts)
foreach (KeyValuePair<long, string> archivedKvp in archived.ArchivedPosts)
{
bool isNew;
ArchivedEntities.Medium? mediaInfo =
archived.ArchivedPostMedia.FirstOrDefault(m => m.Id == archivedKVP.Key);
archived.ArchivedPostMedia.FirstOrDefault(m => m.Id == archivedKvp.Key);
ArchivedEntities.ListItem? postInfo = mediaInfo == null
? null
: archived.ArchivedPostObjects.FirstOrDefault(p => p.Media?.Contains(mediaInfo) == true);
string filenameFormat =
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PostFileNameFormat ?? "";
if (archivedKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
if (archivedKvp.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] parsed = archivedKVP.Value.Split(',');
string[] parsed = archivedKvp.Value.Split(',');
(string decryptionKey, DateTime lastModified)? drmInfo = await GetDecryptionInfo(parsed[0], parsed[1],
parsed[2], parsed[3],
parsed[4], parsed[5], "post", clientIdBlobMissing, devicePrivateKeyMissing);
@ -1168,14 +1131,14 @@ public class DownloadService(
continue;
}
isNew = await DownloadDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, archivedKVP.Key, "Posts",
isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, archivedKvp.Key, "Posts",
progressReporter, "/Archived/Posts/Free/Videos", filenameFormat,
postInfo, mediaInfo, postInfo?.Author, users);
}
else
{
isNew = await DownloadMedia(archivedKVP.Value, path, archivedKVP.Key, "Posts", progressReporter,
isNew = await DownloadMedia(archivedKvp.Value, path, archivedKvp.Key, "Posts", progressReporter,
"/Archived/Posts/Free", filenameFormat, postInfo, mediaInfo, postInfo?.Author, users);
}
@ -1226,12 +1189,12 @@ public class DownloadService(
int oldMessagesCount = 0;
int newMessagesCount = 0;
foreach (KeyValuePair<long, string> messageKVP in messages.Messages)
foreach (KeyValuePair<long, string> messageKvp in messages.Messages)
{
bool isNew;
MessageEntities.Medium? mediaInfo = messages.MessageMedia.FirstOrDefault(m => m.Id == messageKVP.Key);
MessageEntities.Medium? mediaInfo = messages.MessageMedia.FirstOrDefault(m => m.Id == messageKvp.Key);
MessageEntities.ListItem? messageInfo = messages.MessageObjects.FirstOrDefault(p =>
p.Media?.Any(m => m.Id == messageKVP.Key) == true);
p.Media?.Any(m => m.Id == messageKvp.Key) == true);
string filenameFormat =
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).MessageFileNameFormat ?? "";
string messagePath = configService.CurrentConfig.FolderPerMessage && messageInfo != null &&
@ -1239,9 +1202,9 @@ public class DownloadService(
? $"/Messages/Free/{messageInfo.Id} {messageInfo.CreatedAt.Value:yyyy-MM-dd HH-mm-ss}"
: "/Messages/Free";
if (messageKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
if (messageKvp.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] parsed = messageKVP.Value.Split(',');
string[] parsed = messageKvp.Value.Split(',');
(string decryptionKey, DateTime lastModified)? drmInfo = await GetDecryptionInfo(parsed[0], parsed[1],
parsed[2], parsed[3],
parsed[4], parsed[5], "message", clientIdBlobMissing, devicePrivateKeyMissing);
@ -1250,14 +1213,14 @@ public class DownloadService(
continue;
}
isNew = await DownloadDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, messageKVP.Key, "Messages",
isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, messageKvp.Key, "Messages",
progressReporter, messagePath + "/Videos", filenameFormat,
messageInfo, mediaInfo, messageInfo?.FromUser, users);
}
else
{
isNew = await DownloadMedia(messageKVP.Value, path, messageKVP.Key, "Messages", progressReporter,
isNew = await DownloadMedia(messageKvp.Value, path, messageKvp.Key, "Messages", progressReporter,
messagePath, filenameFormat, messageInfo, mediaInfo, messageInfo?.FromUser, users);
}
@ -1335,7 +1298,7 @@ public class DownloadService(
continue;
}
isNew = await DownloadDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0],
isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, kvpEntry.Key, "Messages",
progressReporter, paidMsgPath + "/Videos", filenameFormat,
messageInfo, mediaInfo, messageInfo?.FromUser, users);
@ -1415,7 +1378,7 @@ public class DownloadService(
continue;
}
isNew = await DownloadDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0],
isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, kvpEntry.Key, "Streams",
progressReporter, streamPath + "/Videos", filenameFormat,
streamInfo, mediaInfo, streamInfo?.Author, users);
@ -1471,10 +1434,10 @@ public class DownloadService(
int oldCount = 0, newCount = 0;
foreach (KeyValuePair<long, string> postKVP in posts.Posts)
foreach (KeyValuePair<long, string> postKvp in posts.Posts)
{
bool isNew;
PostEntities.Medium? mediaInfo = posts.PostMedia.FirstOrDefault(m => m.Id == postKVP.Key);
PostEntities.Medium? mediaInfo = posts.PostMedia.FirstOrDefault(m => m.Id == postKvp.Key);
PostEntities.ListItem? postInfo = mediaInfo == null
? null
: posts.PostObjects.FirstOrDefault(p => p.Media?.Contains(mediaInfo) == true);
@ -1484,9 +1447,9 @@ public class DownloadService(
? $"/Posts/Free/{postInfo.Id} {postInfo.PostedAt:yyyy-MM-dd HH-mm-ss}"
: "/Posts/Free";
if (postKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
if (postKvp.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] parsed = postKVP.Value.Split(',');
string[] parsed = postKvp.Value.Split(',');
(string decryptionKey, DateTime lastModified)? drmInfo = await GetDecryptionInfo(parsed[0], parsed[1],
parsed[2], parsed[3],
parsed[4], parsed[5], "post", clientIdBlobMissing, devicePrivateKeyMissing);
@ -1495,14 +1458,14 @@ public class DownloadService(
continue;
}
isNew = await DownloadDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, postKVP.Key, "Posts",
isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, postKvp.Key, "Posts",
progressReporter, postPath + "/Videos", filenameFormat,
postInfo, mediaInfo, postInfo?.Author, users);
}
else
{
isNew = await DownloadMedia(postKVP.Value, path, postKVP.Key, "Posts", progressReporter,
isNew = await DownloadMedia(postKvp.Value, path, postKvp.Key, "Posts", progressReporter,
postPath, filenameFormat, postInfo, mediaInfo, postInfo?.Author, users);
}
@ -1551,13 +1514,13 @@ public class DownloadService(
int oldCount = 0, newCount = 0;
foreach (KeyValuePair<long, string> postKVP in purchasedPosts.PaidPosts)
foreach (KeyValuePair<long, string> postKvp in purchasedPosts.PaidPosts)
{
bool isNew;
MessageEntities.Medium? mediaInfo =
purchasedPosts.PaidPostMedia.FirstOrDefault(m => m.Id == postKVP.Key);
purchasedPosts.PaidPostMedia.FirstOrDefault(m => m.Id == postKvp.Key);
PurchasedEntities.ListItem? postInfo =
purchasedPosts.PaidPostObjects.FirstOrDefault(p => p.Media?.Any(m => m.Id == postKVP.Key) == true);
purchasedPosts.PaidPostObjects.FirstOrDefault(p => p.Media?.Any(m => m.Id == postKvp.Key) == true);
string filenameFormat =
configService.CurrentConfig.GetCreatorFileNameFormatConfig(username).PostFileNameFormat ?? "";
string paidPostPath = configService.CurrentConfig.FolderPerPaidPost && postInfo != null &&
@ -1565,9 +1528,9 @@ public class DownloadService(
? $"/Posts/Paid/{postInfo.Id} {postInfo.PostedAt.Value:yyyy-MM-dd HH-mm-ss}"
: "/Posts/Paid";
if (postKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
if (postKvp.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] parsed = postKVP.Value.Split(',');
string[] parsed = postKvp.Value.Split(',');
(string decryptionKey, DateTime lastModified)? drmInfo = await GetDecryptionInfo(parsed[0], parsed[1],
parsed[2], parsed[3],
parsed[4], parsed[5], "post", clientIdBlobMissing, devicePrivateKeyMissing);
@ -1576,14 +1539,14 @@ public class DownloadService(
continue;
}
isNew = await DownloadDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, postKVP.Key, "Posts",
isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, postKvp.Key, "Posts",
progressReporter, paidPostPath + "/Videos", filenameFormat,
postInfo, mediaInfo, postInfo?.FromUser, users);
}
else
{
isNew = await DownloadMedia(postKVP.Value, path, postKVP.Key, "Posts", progressReporter,
isNew = await DownloadMedia(postKvp.Value, path, postKvp.Key, "Posts", progressReporter,
paidPostPath, filenameFormat, postInfo, mediaInfo, postInfo?.FromUser, users);
}
@ -1622,14 +1585,14 @@ public class DownloadService(
int oldCount = 0, newCount = 0;
foreach (KeyValuePair<long, string> purchasedPostKVP in purchasedPosts.PaidPosts)
foreach (KeyValuePair<long, string> purchasedPostKvp in purchasedPosts.PaidPosts)
{
bool isNew;
MessageEntities.Medium? mediaInfo =
purchasedPosts?.PaidPostMedia?.FirstOrDefault(m => m.Id == purchasedPostKVP.Key);
purchasedPosts?.PaidPostMedia?.FirstOrDefault(m => m.Id == purchasedPostKvp.Key);
PurchasedEntities.ListItem? postInfo = mediaInfo != null
? purchasedPosts?.PaidPostObjects?.FirstOrDefault(p =>
p.Media?.Any(m => m.Id == purchasedPostKVP.Key) == true)
p.Media?.Any(m => m.Id == purchasedPostKvp.Key) == true)
: null;
string filenameFormat = configService.CurrentConfig.GetCreatorFileNameFormatConfig(username)
.PaidPostFileNameFormat ?? "";
@ -1638,9 +1601,9 @@ public class DownloadService(
? $"/Posts/Paid/{postInfo.Id} {postInfo.PostedAt.Value:yyyy-MM-dd HH-mm-ss}"
: "/Posts/Paid";
if (purchasedPostKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
if (purchasedPostKvp.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] parsed = purchasedPostKVP.Value.Split(',');
string[] parsed = purchasedPostKvp.Value.Split(',');
(string decryptionKey, DateTime lastModified)? drmInfo =
await GetDecryptionInfo(parsed[0], parsed[1], parsed[2], parsed[3],
parsed[4], parsed[5], "post", clientIdBlobMissing, devicePrivateKeyMissing);
@ -1649,15 +1612,15 @@ public class DownloadService(
continue;
}
isNew = await DownloadDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, purchasedPostKVP.Key,
isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, purchasedPostKvp.Key,
"Posts", progressReporter, paidPostPath + "/Videos", filenameFormat,
postInfo, mediaInfo, postInfo?.FromUser, users);
}
else
{
isNew = await DownloadMedia(purchasedPostKVP.Value, path,
purchasedPostKVP.Key, "Posts", progressReporter,
isNew = await DownloadMedia(purchasedPostKvp.Value, path,
purchasedPostKvp.Key, "Posts", progressReporter,
paidPostPath, filenameFormat, postInfo, mediaInfo, postInfo?.FromUser, users);
}
@ -1696,14 +1659,14 @@ public class DownloadService(
int oldCount = 0, newCount = 0;
foreach (KeyValuePair<long, string> paidMessageKVP in paidMessageCollection.PaidMessages)
foreach (KeyValuePair<long, string> paidMessageKvp in paidMessageCollection.PaidMessages)
{
bool isNew;
MessageEntities.Medium? mediaInfo =
paidMessageCollection.PaidMessageMedia.FirstOrDefault(m => m.Id == paidMessageKVP.Key);
paidMessageCollection.PaidMessageMedia.FirstOrDefault(m => m.Id == paidMessageKvp.Key);
PurchasedEntities.ListItem? messageInfo =
paidMessageCollection.PaidMessageObjects.FirstOrDefault(p =>
p.Media?.Any(m => m.Id == paidMessageKVP.Key) == true);
p.Media?.Any(m => m.Id == paidMessageKvp.Key) == true);
string filenameFormat = configService.CurrentConfig.GetCreatorFileNameFormatConfig(username)
.PaidMessageFileNameFormat ?? "";
string paidMsgPath = configService.CurrentConfig.FolderPerPaidMessage && messageInfo != null &&
@ -1711,9 +1674,9 @@ public class DownloadService(
? $"/Messages/Paid/{messageInfo.Id} {messageInfo.CreatedAt.Value:yyyy-MM-dd HH-mm-ss}"
: "/Messages/Paid";
if (paidMessageKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
if (paidMessageKvp.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] parsed = paidMessageKVP.Value.Split(',');
string[] parsed = paidMessageKvp.Value.Split(',');
(string decryptionKey, DateTime lastModified)? drmInfo =
await GetDecryptionInfo(parsed[0], parsed[1], parsed[2], parsed[3],
parsed[4], parsed[5], "message", clientIdBlobMissing, devicePrivateKeyMissing);
@ -1722,15 +1685,15 @@ public class DownloadService(
continue;
}
isNew = await DownloadDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, paidMessageKVP.Key,
isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, paidMessageKvp.Key,
"Messages", progressReporter, paidMsgPath + "/Videos", filenameFormat,
messageInfo, mediaInfo, messageInfo?.FromUser, users);
}
else
{
isNew = await DownloadMedia(paidMessageKVP.Value, path,
paidMessageKVP.Key, "Messages", progressReporter,
isNew = await DownloadMedia(paidMessageKvp.Value, path,
paidMessageKvp.Key, "Messages", progressReporter,
paidMsgPath, filenameFormat, messageInfo, mediaInfo, messageInfo?.FromUser, users);
}
@ -1769,9 +1732,9 @@ public class DownloadService(
int oldCount = 0, newCount = 0;
foreach (KeyValuePair<long, string> postKVP in post.SinglePosts)
foreach (KeyValuePair<long, string> postKvp in post.SinglePosts)
{
PostEntities.Medium? mediaInfo = post.SinglePostMedia.FirstOrDefault(m => m.Id == postKVP.Key);
PostEntities.Medium? mediaInfo = post.SinglePostMedia.FirstOrDefault(m => m.Id == postKvp.Key);
PostEntities.SinglePost? postInfo = mediaInfo == null
? null
: post.SinglePostObjects.FirstOrDefault(p => p.Media?.Contains(mediaInfo) == true);
@ -1782,9 +1745,9 @@ public class DownloadService(
: "/Posts/Free";
bool isNew;
if (postKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
if (postKvp.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] parsed = postKVP.Value.Split(',');
string[] parsed = postKvp.Value.Split(',');
(string decryptionKey, DateTime lastModified)? drmInfo =
await GetDecryptionInfo(parsed[0], parsed[1], parsed[2], parsed[3],
parsed[4], parsed[5], "post", clientIdBlobMissing, devicePrivateKeyMissing);
@ -1793,8 +1756,8 @@ public class DownloadService(
continue;
}
isNew = await DownloadDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, postKVP.Key, "Posts",
isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, postKvp.Key, "Posts",
progressReporter, postPath + "/Videos", filenameFormat,
postInfo, mediaInfo, postInfo?.Author, users);
}
@ -1802,8 +1765,8 @@ public class DownloadService(
{
try
{
isNew = await DownloadMedia(postKVP.Value, path,
postKVP.Key, "Posts", progressReporter,
isNew = await DownloadMedia(postKvp.Value, path,
postKvp.Key, "Posts", progressReporter,
postPath, filenameFormat, postInfo, mediaInfo, postInfo?.Author, users);
}
catch
@ -1845,14 +1808,14 @@ public class DownloadService(
// Download preview messages
if (singlePaidMessageCollection.PreviewSingleMessages.Count > 0)
{
foreach (KeyValuePair<long, string> paidMessageKVP in singlePaidMessageCollection.PreviewSingleMessages)
foreach (KeyValuePair<long, string> paidMessageKvp in singlePaidMessageCollection.PreviewSingleMessages)
{
MessageEntities.Medium? mediaInfo =
singlePaidMessageCollection.PreviewSingleMessageMedia.FirstOrDefault(m =>
m.Id == paidMessageKVP.Key);
m.Id == paidMessageKvp.Key);
MessageEntities.SingleMessage? messageInfo =
singlePaidMessageCollection.SingleMessageObjects.FirstOrDefault(p =>
p.Media?.Any(m => m.Id == paidMessageKVP.Key) == true);
p.Media?.Any(m => m.Id == paidMessageKvp.Key) == true);
string filenameFormat = configService.CurrentConfig.GetCreatorFileNameFormatConfig(username)
.PaidMessageFileNameFormat ?? "";
string previewMsgPath = configService.CurrentConfig.FolderPerMessage && messageInfo != null &&
@ -1861,9 +1824,9 @@ public class DownloadService(
: "/Messages/Free";
bool isNew;
if (paidMessageKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
if (paidMessageKvp.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] parsed = paidMessageKVP.Value.Split(',');
string[] parsed = paidMessageKvp.Value.Split(',');
(string decryptionKey, DateTime lastModified)? drmInfo =
await GetDecryptionInfo(parsed[0], parsed[1], parsed[2], parsed[3],
parsed[4], parsed[5], "message", clientIdBlobMissing, devicePrivateKeyMissing);
@ -1872,15 +1835,15 @@ public class DownloadService(
continue;
}
isNew = await DownloadDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, paidMessageKVP.Key,
isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, paidMessageKvp.Key,
"Messages", progressReporter, previewMsgPath + "/Videos", filenameFormat,
messageInfo, mediaInfo, messageInfo?.FromUser, users);
}
else
{
isNew = await DownloadMedia(paidMessageKVP.Value, path,
paidMessageKVP.Key, "Messages", progressReporter,
isNew = await DownloadMedia(paidMessageKvp.Value, path,
paidMessageKvp.Key, "Messages", progressReporter,
previewMsgPath, filenameFormat, messageInfo, mediaInfo, messageInfo?.FromUser, users);
}
@ -1898,14 +1861,14 @@ public class DownloadService(
// Download actual paid messages
if (singlePaidMessageCollection.SingleMessages.Count > 0)
{
foreach (KeyValuePair<long, string> paidMessageKVP in singlePaidMessageCollection.SingleMessages)
foreach (KeyValuePair<long, string> paidMessageKvp in singlePaidMessageCollection.SingleMessages)
{
MessageEntities.Medium? mediaInfo =
singlePaidMessageCollection.SingleMessageMedia.FirstOrDefault(m =>
m.Id == paidMessageKVP.Key);
m.Id == paidMessageKvp.Key);
MessageEntities.SingleMessage? messageInfo =
singlePaidMessageCollection.SingleMessageObjects.FirstOrDefault(p =>
p.Media?.Any(m => m.Id == paidMessageKVP.Key) == true);
p.Media?.Any(m => m.Id == paidMessageKvp.Key) == true);
string filenameFormat = configService.CurrentConfig.GetCreatorFileNameFormatConfig(username)
.PaidMessageFileNameFormat ?? "";
string singlePaidMsgPath = configService.CurrentConfig.FolderPerPaidMessage &&
@ -1915,9 +1878,9 @@ public class DownloadService(
: "/Messages/Paid";
bool isNew;
if (paidMessageKVP.Value.Contains("cdn3.onlyfans.com/dash/files"))
if (paidMessageKvp.Value.Contains("cdn3.onlyfans.com/dash/files"))
{
string[] parsed = paidMessageKVP.Value.Split(',');
string[] parsed = paidMessageKvp.Value.Split(',');
(string decryptionKey, DateTime lastModified)? drmInfo =
await GetDecryptionInfo(parsed[0], parsed[1], parsed[2], parsed[3],
parsed[4], parsed[5], "message", clientIdBlobMissing, devicePrivateKeyMissing);
@ -1926,15 +1889,15 @@ public class DownloadService(
continue;
}
isNew = await DownloadDRMVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, paidMessageKVP.Key,
isNew = await DownloadDrmVideo(parsed[1], parsed[2], parsed[3], parsed[0],
drmInfo.Value.decryptionKey, path, drmInfo.Value.lastModified, paidMessageKvp.Key,
"Messages", progressReporter, singlePaidMsgPath + "/Videos", filenameFormat,
messageInfo, mediaInfo, messageInfo?.FromUser, users);
}
else
{
isNew = await DownloadMedia(paidMessageKVP.Value, path,
paidMessageKVP.Key, "Messages", progressReporter,
isNew = await DownloadMedia(paidMessageKvp.Value, path,
paidMessageKvp.Key, "Messages", progressReporter,
singlePaidMsgPath, filenameFormat, messageInfo, mediaInfo, messageInfo?.FromUser, users);
}

View File

@ -27,7 +27,7 @@ public class FileNameService(IAuthService authService) : IFileNameService
if (fileProperty != null && drmProperty != null && propertyName == "mediaCreatedAt")
{
string? mpdurl = GetNestedPropertyValue(media, "Files.Drm.Manifest.Dash") as string;
string? mpdUrl = GetNestedPropertyValue(media, "Files.Drm.Manifest.Dash") as string;
string? policy =
GetNestedPropertyValue(media, "Files.Drm.Signature.Dash.CloudFrontPolicy") as string;
string? signature =
@ -35,7 +35,7 @@ public class FileNameService(IAuthService authService) : IFileNameService
string? kvp =
GetNestedPropertyValue(media, "Files.Drm.Signature.Dash.CloudFrontKeyPairId") as string;
if (string.IsNullOrEmpty(mpdurl) || string.IsNullOrEmpty(policy) ||
if (string.IsNullOrEmpty(mpdUrl) || string.IsNullOrEmpty(policy) ||
string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(kvp) ||
authService.CurrentAuth == null)
{
@ -43,7 +43,7 @@ public class FileNameService(IAuthService authService) : IFileNameService
}
DateTime lastModified =
await DownloadService.GetDRMVideoLastModified(string.Join(",", mpdurl, policy, signature, kvp),
await DownloadService.GetDrmVideoLastModified(string.Join(",", mpdUrl, policy, signature, kvp),
authService.CurrentAuth);
values.Add(propertyName, lastModified.ToString("yyyy-MM-dd"));
continue;

View File

@ -9,27 +9,31 @@ using UserEntities = OF_DL.Models.Entities.Users;
namespace OF_DL.Services;
public interface IAPIService
public interface IApiService
{
Task<string> GetDecryptionKeyCDM(Dictionary<string, string> drmHeaders, string licenceURL, string pssh);
Task<DateTime> GetDRMMPDLastModified(string mpdUrl, string policy, string signature, string kvp);
Task<string> GetDRMMPDPSSH(string mpdUrl, string policy, string signature, string kvp);
Task<string> GetDecryptionKeyCdm(Dictionary<string, string> drmHeaders, string licenceUrl, string pssh);
Task<DateTime> GetDrmMpdLastModified(string mpdUrl, string policy, string signature, string kvp);
Task<string> GetDrmMpdPssh(string mpdUrl, string policy, string signature, string kvp);
Task<Dictionary<string, long>?> GetLists(string endpoint);
Task<List<string>?> GetListUsers(string endpoint);
Task<Dictionary<long, string>?> GetMedia(MediaType mediatype, string endpoint, string? username, string folder,
List<long> paid_post_ids);
Task<Dictionary<long, string>?> GetMedia(MediaType mediaType, string endpoint, string? username, string folder,
List<long> paidPostIds);
Task<PurchasedEntities.PaidPostCollection> GetPaidPosts(string endpoint, string folder, string username,
List<long> paid_post_ids,
List<long> paidPostIds,
IStatusReporter statusReporter);
Task<PostEntities.PostCollection> GetPosts(string endpoint, string folder, List<long> paid_post_ids,
Task<PostEntities.PostCollection> GetPosts(string endpoint, string folder, List<long> paidPostIds,
IStatusReporter statusReporter);
Task<PostEntities.SinglePostCollection> GetPost(string endpoint, string folder);
Task<StreamEntities.StreamsCollection> GetStreams(string endpoint, string folder, List<long> paid_post_ids,
Task<StreamEntities.StreamsCollection> GetStreams(string endpoint, string folder, List<long> paidPostIds,
IStatusReporter statusReporter);
Task<ArchivedEntities.ArchivedCollection> GetArchived(string endpoint, string folder,
@ -41,15 +45,21 @@ public interface IAPIService
IStatusReporter statusReporter);
Task<PurchasedEntities.SinglePaidMessageCollection> GetPaidMessage(string endpoint, string folder);
Task<Dictionary<string, long>> GetPurchasedTabUsers(string endpoint, Dictionary<string, long> users);
Task<List<PurchasedEntities.PurchasedTabCollection>> GetPurchasedTab(string endpoint, string folder,
Dictionary<string, long> users);
Task<UserEntities.User?> GetUserInfo(string endpoint);
Task<JObject?> GetUserInfoById(string endpoint);
Dictionary<string, string> GetDynamicHeaders(string path, string queryParam);
Task<Dictionary<string, long>?> GetActiveSubscriptions(string endpoint, bool includeRestrictedSubscriptions);
Task<Dictionary<string, long>?> GetExpiredSubscriptions(string endpoint, bool includeRestrictedSubscriptions);
Task<string> GetDecryptionKeyOFDL(Dictionary<string, string> drmHeaders, string licenceURL, string pssh);
Task<string> GetDecryptionKeyOfdl(Dictionary<string, string> drmHeaders, string licenceUrl, string pssh);
}

View File

@ -1,27 +0,0 @@
namespace OF_DL.Services;
public interface IDBService
{
Task AddMessage(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived,
DateTime created_at, long user_id);
Task AddPost(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived,
DateTime created_at);
Task AddStory(string folder, long post_id, string message_text, string price, bool is_paid, bool is_archived,
DateTime created_at);
Task CreateDB(string folder);
Task CreateUsersDB(Dictionary<string, long> users);
Task CheckUsername(KeyValuePair<string, long> user, string path);
Task AddMedia(string folder, long media_id, long post_id, string link, string? directory, string? filename,
long? size, string api_type, string media_type, bool preview, bool downloaded, DateTime? created_at);
Task UpdateMedia(string folder, long media_id, string api_type, string directory, string filename, long size,
bool downloaded, DateTime created_at);
Task<long> GetStoredFileSize(string folder, long media_id, string api_type);
Task<bool> CheckDownloaded(string folder, long media_id, string api_type);
Task<DateTime?> GetMostRecentPostDate(string folder);
}

View File

@ -0,0 +1,31 @@
namespace OF_DL.Services;
public interface IDbService
{
Task AddMessage(string folder, long postId, string messageText, string price, bool isPaid, bool isArchived,
DateTime createdAt, long userId);
Task AddPost(string folder, long postId, string messageText, string price, bool isPaid, bool isArchived,
DateTime createdAt);
Task AddStory(string folder, long postId, string messageText, string price, bool isPaid, bool isArchived,
DateTime createdAt);
Task CreateDb(string folder);
Task CreateUsersDb(Dictionary<string, long> users);
Task CheckUsername(KeyValuePair<string, long> user, string path);
Task AddMedia(string folder, long mediaId, long postId, string link, string? directory, string? filename,
long? size, string apiType, string mediaType, bool preview, bool downloaded, DateTime? createdAt);
Task UpdateMedia(string folder, long mediaId, string apiType, string directory, string filename, long size,
bool downloaded, DateTime createdAt);
Task<long> GetStoredFileSize(string folder, long mediaId, string apiType);
Task<bool> CheckDownloaded(string folder, long mediaId, string apiType);
Task<DateTime?> GetMostRecentPostDate(string folder);
}

View File

@ -11,22 +11,22 @@ public interface IDownloadService
{
Task<long> CalculateTotalFileSize(List<string> urls);
Task<bool> ProcessMediaDownload(string folder, long media_id, string api_type, string url, string path,
Task<bool> ProcessMediaDownload(string folder, long mediaId, string apiType, string url, string path,
string serverFileName, string resolvedFileName, string extension, IProgressReporter progressReporter);
Task<bool> DownloadMedia(string url, string folder, long media_id, string api_type,
Task<bool> DownloadMedia(string url, string folder, long mediaId, string apiType,
IProgressReporter progressReporter, string path,
string? filenameFormat, object? postInfo, object? postMedia,
object? author, Dictionary<string, long> users);
Task<bool> DownloadDRMVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long media_id, string api_type,
Task<bool> DownloadDrmVideo(string policy, string signature, string kvp, string url,
string decryptionKey, string folder, DateTime lastModified, long mediaId, string apiType,
IProgressReporter progressReporter, string path,
string? filenameFormat, object? postInfo, object? postMedia,
object? author, Dictionary<string, long> users);
Task<(string decryptionKey, DateTime lastModified)?> GetDecryptionInfo(
string mpdURL, string policy, string signature, string kvp,
string mpdUrl, string policy, string signature, string kvp,
string mediaId, string contentId, string drmType,
bool clientIdBlobMissing, bool devicePrivateKeyMissing);

View File

@ -6,6 +6,8 @@ namespace OF_DL.Services;
public interface ILoggingService
{
LoggingLevelSwitch LevelSwitch { get; }
void UpdateLoggingLevel(LoggingLevel newLevel);
LoggingLevel GetCurrentLoggingLevel();
}

View File

@ -221,7 +221,7 @@ public class StartupService(IConfigService configService, IAuthService authServi
return File.Exists(path);
}
public static string? GetFullPath(string filename)
private static string? GetFullPath(string filename)
{
if (File.Exists(filename))
{
@ -229,15 +229,7 @@ public class StartupService(IConfigService configService, IAuthService authServi
}
string pathEnv = Environment.GetEnvironmentVariable("PATH") ?? "";
foreach (string path in pathEnv.Split(Path.PathSeparator))
{
string fullPath = Path.Combine(path, filename);
if (File.Exists(fullPath))
{
return fullPath;
}
}
return null;
return pathEnv.Split(Path.PathSeparator).Select(path => Path.Combine(path, filename))
.FirstOrDefault(File.Exists);
}
}

View File

@ -110,8 +110,8 @@ public class Program(IServiceProvider serviceProvider)
services.AddSingleton(loggingService);
services.AddSingleton(configService);
services.AddSingleton<IAuthService, AuthService>();
services.AddSingleton<IAPIService, ApiService>();
services.AddSingleton<IDBService, DBService>();
services.AddSingleton<IApiService, ApiService>();
services.AddSingleton<IDbService, DbService>();
services.AddSingleton<IDownloadService, DownloadService>();
services.AddSingleton<IFileNameService, FileNameService>();
services.AddSingleton<IStartupService, StartupService>();
@ -403,8 +403,8 @@ public class Program(IServiceProvider serviceProvider)
AnsiConsole.Markup($"[red]Folder for {Markup.Escape(username)} already created\n[/]");
}
IDBService dbService = serviceProvider.GetRequiredService<IDBService>();
await dbService.CreateDB(path);
IDbService dbService = serviceProvider.GetRequiredService<IDbService>();
await dbService.CreateDb(path);
await orchestrationService.DownloadSinglePostAsync(username, postId, path, users,
startupResult.ClientIdBlobMissing, startupResult.DevicePrivateKeyMissing, eventHandler);
@ -475,8 +475,8 @@ public class Program(IServiceProvider serviceProvider)
Log.Debug($"Folder for {username} already created");
}
IDBService dbService = serviceProvider.GetRequiredService<IDBService>();
await dbService.CreateDB(path);
IDbService dbService = serviceProvider.GetRequiredService<IDbService>();
await dbService.CreateDb(path);
await orchestrationService.DownloadSinglePaidMessageAsync(username, messageId, path, users,
startupResult.ClientIdBlobMissing, startupResult.DevicePrivateKeyMissing, eventHandler);
@ -488,7 +488,7 @@ public class Program(IServiceProvider serviceProvider)
{
IConfigService configService = serviceProvider.GetRequiredService<IConfigService>();
IAuthService authService = serviceProvider.GetRequiredService<IAuthService>();
IAPIService apiService = serviceProvider.GetRequiredService<IAPIService>();
IApiService apiService = serviceProvider.GetRequiredService<IApiService>();
ILoggingService loggingService = serviceProvider.GetRequiredService<ILoggingService>();
bool hasSelectedUsers = false;