294 lines
9.2 KiB
C#
294 lines
9.2 KiB
C#
using System.Runtime.InteropServices;
|
|
using Newtonsoft.Json;
|
|
using OF_DL.Entities;
|
|
using OF_DL.Exceptions;
|
|
using OF_DL.Helpers;
|
|
using Serilog;
|
|
|
|
namespace OF_DL;
|
|
|
|
public class AppCommon
|
|
{
|
|
private readonly Auth _auth;
|
|
private readonly Config _config;
|
|
private readonly bool _useCdrmProject;
|
|
|
|
private readonly IAPIHelper _apiHelper;
|
|
private readonly IDBHelper _dbHelper;
|
|
private readonly IDownloadHelper _downloadHelper;
|
|
|
|
private Dictionary<string, int> _activeSubscriptions = new();
|
|
private Dictionary<string, int> _expiredSubscriptions = new();
|
|
|
|
public AppCommon()
|
|
{
|
|
Log.Logger = new LoggerConfiguration()
|
|
.MinimumLevel.Debug()
|
|
.WriteTo.File("logs/OFDL.txt", rollingInterval: RollingInterval.Day)
|
|
.WriteTo.Console()
|
|
.CreateLogger();
|
|
|
|
VerifyOperatingSystemCompatibility();
|
|
_auth = GetAuth();
|
|
_config = GetConfig();
|
|
_useCdrmProject = !DetectDrmKeysPresence();
|
|
LoadFfmpeg();
|
|
|
|
_apiHelper = new APIHelper();
|
|
_dbHelper = new DBHelper();
|
|
_downloadHelper = new DownloadHelper();
|
|
}
|
|
|
|
private static void VerifyOperatingSystemCompatibility()
|
|
{
|
|
var os = Environment.OSVersion;
|
|
if (os.Platform != PlatformID.Win32NT || os.Version.Major >= 10) return;
|
|
|
|
var platform =
|
|
os.Platform switch
|
|
{
|
|
PlatformID.Win32NT => "Windows",
|
|
PlatformID.Unix => "Unix",
|
|
PlatformID.MacOSX => "macOS",
|
|
_ => "Unknown"
|
|
};
|
|
|
|
Log.Error($"Unsupported operating system: {platform} version {os.VersionString}");
|
|
throw new UnsupportedOperatingSystem(platform, os.VersionString);
|
|
}
|
|
|
|
private static Auth GetAuth()
|
|
{
|
|
if (File.Exists("auth.json"))
|
|
{
|
|
Log.Debug("auth.json located successfully");
|
|
var authJson = JsonConvert.DeserializeObject<Auth>(File.ReadAllText("auth.json"));
|
|
if (authJson != null)
|
|
{
|
|
return authJson;
|
|
}
|
|
|
|
Log.Error("auth.json is invalid");
|
|
throw new MalformedFileException("auth.json");
|
|
}
|
|
|
|
Log.Error("auth.json does not exist");
|
|
throw new MissingFileException("auth.json");
|
|
}
|
|
|
|
private static Config GetConfig()
|
|
{
|
|
if (File.Exists("config.json"))
|
|
{
|
|
Log.Debug("config.json located successfully");
|
|
var configJson = JsonConvert.DeserializeObject<Config>(File.ReadAllText("config.json"));
|
|
if (configJson != null)
|
|
{
|
|
return configJson;
|
|
}
|
|
|
|
Log.Error("config.json is invalid");
|
|
throw new MalformedFileException("config.json");
|
|
}
|
|
|
|
Log.Error("config.json does not exist");
|
|
throw new MissingFileException("config.json");
|
|
}
|
|
|
|
private void LoadFfmpeg()
|
|
{
|
|
var ffmpegFound = false;
|
|
var pathAutoDetected = false;
|
|
if (!string.IsNullOrEmpty(_config!.FFmpegPath) && ValidateFilePath(_config.FFmpegPath))
|
|
{
|
|
// FFmpeg path is set in config.json and is valid
|
|
ffmpegFound = true;
|
|
}
|
|
else if (!string.IsNullOrEmpty(_auth!.FFMPEG_PATH) && ValidateFilePath(_auth.FFMPEG_PATH))
|
|
{
|
|
// FFmpeg path is set in auth.json and is valid (config.json takes precedence and auth.json is only available for backward compatibility)
|
|
ffmpegFound = true;
|
|
_config.FFmpegPath = _auth.FFMPEG_PATH;
|
|
}
|
|
else if (string.IsNullOrEmpty(_config.FFmpegPath))
|
|
{
|
|
// FFmpeg path is not set in config.json, so we will try to locate it in the PATH or current directory
|
|
var ffmpegPath = GetFullPath("ffmpeg");
|
|
if (ffmpegPath != null)
|
|
{
|
|
// FFmpeg is found in the PATH or current directory
|
|
ffmpegFound = true;
|
|
pathAutoDetected = true;
|
|
_config.FFmpegPath = ffmpegPath;
|
|
}
|
|
else
|
|
{
|
|
// FFmpeg is not found in the PATH or current directory, so we will try to locate the windows executable
|
|
ffmpegPath = GetFullPath("ffmpeg.exe");
|
|
if (ffmpegPath != null)
|
|
{
|
|
// FFmpeg windows executable is found in the PATH or current directory
|
|
ffmpegFound = true;
|
|
pathAutoDetected = true;
|
|
_config.FFmpegPath = ffmpegPath;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ffmpegFound)
|
|
{
|
|
Log.Debug(
|
|
pathAutoDetected
|
|
? $"FFmpeg located successfully. Path auto-detected: {_config.FFmpegPath}"
|
|
: $"FFmpeg located successfully"
|
|
);
|
|
|
|
// Escape backslashes in the path for Windows
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && _config.FFmpegPath!.Contains(@":\") && !_config.FFmpegPath.Contains(@":\\"))
|
|
{
|
|
_config.FFmpegPath = _config.FFmpegPath.Replace(@"\", @"\\");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log.Error($"Cannot locate FFmpeg with path: {_config.FFmpegPath}");
|
|
throw new Exception("Cannot locate FFmpeg");
|
|
}
|
|
}
|
|
|
|
private static bool DetectDrmKeysPresence()
|
|
{
|
|
var clientIdBlobMissing = false;
|
|
var devicePrivateKeyMissing = false;
|
|
|
|
if (!File.Exists("cdm/devices/chrome_1610/device_client_id_blob"))
|
|
{
|
|
clientIdBlobMissing = true;
|
|
}
|
|
else
|
|
{
|
|
Log.Debug($"device_client_id_blob located successfully");
|
|
}
|
|
|
|
if (!File.Exists("cdm/devices/chrome_1610/device_private_key"))
|
|
{
|
|
devicePrivateKeyMissing = true;
|
|
}
|
|
else
|
|
{
|
|
Log.Debug($"device_private_key located successfully");
|
|
}
|
|
|
|
if (!clientIdBlobMissing && !devicePrivateKeyMissing)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Log.Information("device_client_id_blob and/or device_private_key missing, https://cdrm-project.com/ will be used instead for DRM protected videos");
|
|
return false;
|
|
}
|
|
|
|
private static bool ValidateFilePath(string path)
|
|
{
|
|
var invalidChars = System.IO.Path.GetInvalidPathChars();
|
|
var foundInvalidChars = path.Where(c => invalidChars.Contains(c)).ToArray();
|
|
|
|
if (foundInvalidChars.Length != 0)
|
|
{
|
|
Log.Information($"Invalid characters found in path {path}:[/] {string.Join(", ", foundInvalidChars)}");
|
|
return false;
|
|
}
|
|
|
|
if (File.Exists(path)) return true;
|
|
|
|
Log.Information(
|
|
Directory.Exists(path)
|
|
? $"The provided path {path} improperly points to a directory and not a file."
|
|
: $"The provided path {path} does not exist or is not accessible."
|
|
);
|
|
|
|
return false;
|
|
}
|
|
|
|
private static string? GetFullPath(string filename)
|
|
{
|
|
if (File.Exists(filename))
|
|
{
|
|
return Path.GetFullPath(filename);
|
|
}
|
|
|
|
var pathEnv = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
|
|
return pathEnv
|
|
.Split(Path.PathSeparator)
|
|
.Select(path => Path.Combine(path, filename))
|
|
.FirstOrDefault(File.Exists);
|
|
}
|
|
|
|
public async Task<User> GetUser()
|
|
{
|
|
var user = await _apiHelper.GetUserInfo("/users/me", _auth);
|
|
|
|
if (user is not { id: not null })
|
|
{
|
|
Log.Error("Authentication failed. Please check your credentials in auth.json");
|
|
throw new AuthenticationFailureException();
|
|
}
|
|
|
|
Log.Debug($"Logged in successfully as {user.name} {user.username}");
|
|
return user;
|
|
}
|
|
|
|
private async Task<Dictionary<string, int>> GetActiveSubscriptions()
|
|
{
|
|
if (_activeSubscriptions.Count > 0)
|
|
{
|
|
return _activeSubscriptions;
|
|
}
|
|
|
|
_activeSubscriptions = await _apiHelper.GetActiveSubscriptions("/subscriptions/subscribes", _auth, _config.IncludeRestrictedSubscriptions);
|
|
return _activeSubscriptions;
|
|
}
|
|
|
|
private async Task<Dictionary<string, int>> GetExpiredSubscriptions()
|
|
{
|
|
if (_expiredSubscriptions.Count > 0)
|
|
{
|
|
return _expiredSubscriptions;
|
|
}
|
|
|
|
_expiredSubscriptions = await _apiHelper.GetExpiredSubscriptions("/subscriptions/subscribes", _auth, _config.IncludeRestrictedSubscriptions);
|
|
return _expiredSubscriptions;
|
|
}
|
|
|
|
public async Task<Dictionary<string, int>> GetSubscriptions()
|
|
{
|
|
var subscriptions = new Dictionary<string, int>();
|
|
|
|
foreach (var (key, value) in await GetActiveSubscriptions())
|
|
{
|
|
subscriptions.Add(key, value);
|
|
}
|
|
|
|
if (_config.IncludeExpiredSubscriptions)
|
|
{
|
|
foreach (var (key, value) in await GetExpiredSubscriptions())
|
|
{
|
|
subscriptions.Add(key, value);
|
|
}
|
|
}
|
|
|
|
return subscriptions;
|
|
}
|
|
|
|
public async Task<Dictionary<string, int>> GetLists()
|
|
{
|
|
return await _apiHelper.GetLists("/lists", _auth);
|
|
}
|
|
|
|
public async Task CreateOrUpdateUsersDatabase()
|
|
{
|
|
var users = await GetSubscriptions();
|
|
await _dbHelper.CreateUsersDB(users);
|
|
}
|
|
}
|