Add more Environment Variables

This commit is contained in:
2025-09-18 01:46:53 +02:00
parent 33cb8a2ab3
commit d9a1923a3c
10 changed files with 67 additions and 58 deletions

View File

@@ -37,7 +37,7 @@ public class MangaConnectorController(MangaContext context) : Controller
[ProducesResponseType<string>(Status404NotFound, "text/plain")]
public Results<Ok<MangaConnector>, NotFound<string>> GetConnector(string MangaConnectorName)
{
if(Tranga.MangaConnectors.FirstOrDefault(c => c.Name.Equals(MangaConnectorName, StringComparison.InvariantCultureIgnoreCase)) is not { } connector)
if(!Tranga.TryGetMangaConnector(MangaConnectorName, out MangaConnectors.MangaConnector? connector))
return TypedResults.NotFound(nameof(MangaConnectorName));
return TypedResults.Ok(new MangaConnector(connector.Name, connector.Enabled, connector.IconUrl, connector.SupportedLanguages));
@@ -65,7 +65,6 @@ public class MangaConnectorController(MangaContext context) : Controller
[ProducesResponseType<List<MangaConnector>>(Status200OK, "application/json")]
public Ok<List<MangaConnector>> GetDisabledConnectors()
{
return TypedResults.Ok(Tranga.MangaConnectors
.Where(c => c.Enabled == false)
.Select(c => new MangaConnector(c.Name, c.Enabled, c.IconUrl, c.SupportedLanguages))
@@ -86,7 +85,7 @@ public class MangaConnectorController(MangaContext context) : Controller
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
public async Task<Results<Ok, NotFound<string>, InternalServerError<string>>> SetEnabled(string MangaConnectorName, bool Enabled)
{
if(Tranga.MangaConnectors.FirstOrDefault(c => c.Name.Equals(MangaConnectorName, StringComparison.InvariantCultureIgnoreCase)) is not { } connector)
if(!Tranga.TryGetMangaConnector(MangaConnectorName, out MangaConnectors.MangaConnector? connector))
return TypedResults.NotFound(nameof(MangaConnectorName));
connector.Enabled = Enabled;

View File

@@ -209,10 +209,10 @@ public class MangaController(MangaContext context) : Controller
string cache = CoverSize switch
{
MangaController.CoverSize.Small => TrangaSettings.coverImageCacheSmall,
MangaController.CoverSize.Medium => TrangaSettings.coverImageCacheMedium,
MangaController.CoverSize.Large => TrangaSettings.coverImageCacheLarge,
_ => TrangaSettings.coverImageCacheOriginal
MangaController.CoverSize.Small => TrangaSettings.CoverImageCacheSmall,
MangaController.CoverSize.Medium => TrangaSettings.CoverImageCacheMedium,
MangaController.CoverSize.Large => TrangaSettings.CoverImageCacheLarge,
_ => TrangaSettings.CoverImageCacheOriginal
};
if (await manga.GetCoverImage(cache, HttpContext.RequestAborted) is not { } data)

View File

@@ -47,7 +47,7 @@ public abstract class MangaConnector(string name, string[] supportedLanguages, s
//https?:\/\/[a-zA-Z0-9-]+\.([a-zA-Z0-9-]+\.[a-zA-Z0-9]+)\/(?:.+\/)*(.+\.([a-zA-Z]+)) for only second level domains
Match match = urlRex.Match(mangaId.Obj.CoverUrl);
string filename = $"{match.Groups[1].Value}-{mangaId.ObjId}.{mangaId.MangaConnectorName}.{match.Groups[3].Value}";
string saveImagePath = Path.Join(TrangaSettings.coverImageCacheOriginal, filename);
string saveImagePath = Path.Join(TrangaSettings.CoverImageCacheOriginal, filename);
if (File.Exists(saveImagePath))
return filename;
@@ -61,24 +61,24 @@ public abstract class MangaConnector(string name, string[] supportedLanguages, s
using MemoryStream ms = new();
coverResult.result.CopyTo(ms);
byte[] imageBytes = ms.ToArray();
Directory.CreateDirectory(TrangaSettings.coverImageCacheOriginal);
Directory.CreateDirectory(TrangaSettings.CoverImageCacheOriginal);
File.WriteAllBytes(saveImagePath, imageBytes);
using Image image = Image.Load(imageBytes);
Directory.CreateDirectory(TrangaSettings.coverImageCacheLarge);
Directory.CreateDirectory(TrangaSettings.CoverImageCacheLarge);
using Image large = image.Clone(x => x.Resize(new ResizeOptions
{ Size = Constants.ImageLgSize, Mode = ResizeMode.Max }));
large.SaveAsJpeg(Path.Join(TrangaSettings.coverImageCacheLarge, filename), new (){ Quality = 40 });
large.SaveAsJpeg(Path.Join(TrangaSettings.CoverImageCacheLarge, filename), new (){ Quality = 40 });
Directory.CreateDirectory(TrangaSettings.coverImageCacheMedium);
Directory.CreateDirectory(TrangaSettings.CoverImageCacheMedium);
using Image medium = image.Clone(x => x.Resize(new ResizeOptions
{ Size = Constants.ImageMdSize, Mode = ResizeMode.Max }));
medium.SaveAsJpeg(Path.Join(TrangaSettings.coverImageCacheMedium, filename), new (){ Quality = 40 });
medium.SaveAsJpeg(Path.Join(TrangaSettings.CoverImageCacheMedium, filename), new (){ Quality = 40 });
Directory.CreateDirectory(TrangaSettings.coverImageCacheSmall);
Directory.CreateDirectory(TrangaSettings.CoverImageCacheSmall);
using Image small = image.Clone(x => x.Resize(new ResizeOptions
{ Size = Constants.ImageSmSize, Mode = ResizeMode.Max }));
small.SaveAsJpeg(Path.Join(TrangaSettings.coverImageCacheSmall, filename), new (){ Quality = 40 });
small.SaveAsJpeg(Path.Join(TrangaSettings.CoverImageCacheSmall, filename), new (){ Quality = 40 });
}
catch (Exception e)
{

View File

@@ -109,7 +109,7 @@ using (IServiceScope scope = app.Services.CreateScope())
context.Database.Migrate();
if (!context.FileLibraries.Any())
context.FileLibraries.Add(new FileLibrary(Tranga.Settings.DownloadLocation, "Default FileLibrary"));
context.FileLibraries.Add(new (Tranga.Settings.DefaultDownloadLocation, "Default FileLibrary"));
await context.Sync(CancellationToken.None);
}
@@ -135,7 +135,7 @@ using (IServiceScope scope = app.Services.CreateScope())
await context.Sync(CancellationToken.None);
}
Tranga.SetServiceProvider(app.Services);
Tranga.ServiceProvider = app.Services;
Tranga.StartLogger(new FileInfo("Log4Net.config.xml"));
Tranga.AddDefaultWorkers();

View File

@@ -16,7 +16,7 @@ namespace API;
public static class Tranga
{
private static IServiceProvider? _serviceProvider;
internal static IServiceProvider? ServiceProvider { get; set; }
private static readonly ILog Log = LogManager.GetLogger(typeof(Tranga));
internal static readonly MetadataFetcher[] MetadataFetchers = [new MyAnimeList()];
@@ -57,11 +57,6 @@ public static class Tranga
AddWorker(CleanupMangaconnectorIdsWithoutConnector);
}
internal static void SetServiceProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
internal static bool TryGetMangaConnector(string name, [NotNullWhen(true)]out MangaConnector? mangaConnector)
{
mangaConnector =
@@ -82,12 +77,12 @@ public static class Tranga
private static void AddPeriodicWorker(BaseWorker worker, IPeriodic periodic)
{
Log.Debug($"Adding Periodic {worker}");
Task periodicTask = PeriodicTask(worker, periodic);
Task periodicTask = RefreshedPeriodicTask(worker, periodic);
PeriodicWorkers.TryAdd((worker as IPeriodic)!, periodicTask);
periodicTask.Start();
}
private static Task PeriodicTask(BaseWorker worker, IPeriodic periodic) => new (() =>
private static Task RefreshedPeriodicTask(BaseWorker worker, IPeriodic periodic) => new (() =>
{
Log.Debug($"Waiting {periodic.Interval} for next run of {worker}");
Thread.Sleep(periodic.Interval);
@@ -97,9 +92,12 @@ public static class Tranga
private static Action RefreshTask(BaseWorker worker, IPeriodic periodic) => () =>
{
if (worker.State < WorkerExecutionState.Created) //Failed
{
Log.Debug($"Task {worker} failed. Not refreshing.");
return;
}
Log.Debug($"Refreshing {worker}");
Task periodicTask = PeriodicTask(worker, periodic);
Task periodicTask = RefreshedPeriodicTask(worker, periodic);
PeriodicWorkers.AddOrUpdate((worker as IPeriodic)!, periodicTask, (_, _) => periodicTask);
periodicTask.Start();
};
@@ -113,15 +111,15 @@ public static class Tranga
private static readonly ConcurrentDictionary<BaseWorker, Task<BaseWorker[]>> RunningWorkers = new();
public static BaseWorker[] GetRunningWorkers() => RunningWorkers.Keys.ToArray();
internal static void StartWorker(BaseWorker worker, Action? callback = null)
internal static void StartWorker(BaseWorker worker, Action? finishedCallback = null)
{
Log.Debug($"Starting {worker}");
if (_serviceProvider is null)
if (ServiceProvider is null)
{
Log.Fatal("ServiceProvider is null");
return;
}
Action afterWorkCallback = AfterWork(worker, callback);
Action afterWorkCallback = DefaultAfterWork(worker, finishedCallback);
while (RunningWorkers.Count > Settings.MaxConcurrentWorkers)
{
@@ -131,23 +129,23 @@ public static class Tranga
if (worker is BaseWorkerWithContext<MangaContext> mangaContextWorker)
{
mangaContextWorker.SetScope(_serviceProvider.CreateScope());
mangaContextWorker.SetScope(ServiceProvider.CreateScope());
RunningWorkers.TryAdd(mangaContextWorker, mangaContextWorker.DoWork(afterWorkCallback));
}else if (worker is BaseWorkerWithContext<NotificationsContext> notificationContextWorker)
{
notificationContextWorker.SetScope(_serviceProvider.CreateScope());
notificationContextWorker.SetScope(ServiceProvider.CreateScope());
RunningWorkers.TryAdd(notificationContextWorker, notificationContextWorker.DoWork(afterWorkCallback));
}else if (worker is BaseWorkerWithContext<LibraryContext> libraryContextWorker)
{
libraryContextWorker.SetScope(_serviceProvider.CreateScope());
libraryContextWorker.SetScope(ServiceProvider.CreateScope());
RunningWorkers.TryAdd(libraryContextWorker, libraryContextWorker.DoWork(afterWorkCallback));
}else
RunningWorkers.TryAdd(worker, worker.DoWork(afterWorkCallback));
}
private static Action AfterWork(BaseWorker worker, Action? callback = null) => () =>
private static Action DefaultAfterWork(BaseWorker worker, Action? callback = null) => () =>
{
Log.Debug($"AfterWork {worker}");
Log.Debug($"DefaultAfterWork {worker}");
if (RunningWorkers.TryGetValue(worker, out Task<BaseWorker[]>? task))
{
Log.Debug($"Waiting for Children to exit {worker}");

View File

@@ -7,22 +7,19 @@ namespace API;
public struct TrangaSettings()
{
[JsonIgnore]
public static string workingDirectory => Path.Join(RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "/usr/share" : Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "tranga-api");
[JsonIgnore]
public static string settingsFilePath => Path.Join(workingDirectory, "settings.json");
[JsonIgnore] public static string coverImageCache => Path.Join(workingDirectory, "imageCache");
[JsonIgnore] public static string coverImageCacheOriginal => Path.Join(coverImageCache, "original");
[JsonIgnore] public static string coverImageCacheLarge => Path.Join(coverImageCache, "large");
[JsonIgnore] public static string coverImageCacheMedium => Path.Join(coverImageCache, "medium");
[JsonIgnore] public static string coverImageCacheSmall => Path.Join(coverImageCache, "small");
public string DownloadLocation => RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "/Manga" : Path.Join(Directory.GetCurrentDirectory(), "Manga");
[JsonIgnore]
internal static readonly string DefaultUserAgent = $"Tranga/2.0 ({Enum.GetName(Environment.OSVersion.Platform)}; {(Environment.Is64BitOperatingSystem ? "x64" : "")})";
[JsonIgnore] public static string WorkingDirectory => Path.Join(RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "/usr/share" : Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "tranga-api");
[JsonIgnore] public static string SettingsFilePath => Path.Join(WorkingDirectory, "settings.json");
[JsonIgnore] public static string CoverImageCache => Path.Join(WorkingDirectory, "imageCache");
[JsonIgnore] public static string CoverImageCacheOriginal => Path.Join(CoverImageCache, "original");
[JsonIgnore] public static string CoverImageCacheLarge => Path.Join(CoverImageCache, "large");
[JsonIgnore] public static string CoverImageCacheMedium => Path.Join(CoverImageCache, "medium");
[JsonIgnore] public static string CoverImageCacheSmall => Path.Join(CoverImageCache, "small");
public string DefaultDownloadLocation => Environment.GetEnvironmentVariable("DOWNLOAD_LOCATION") ?? (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "/Manga" : Path.Join(Directory.GetCurrentDirectory(), "Manga"));
[JsonIgnore] internal static readonly string DefaultUserAgent = $"Tranga/2.0 ({Enum.GetName(Environment.OSVersion.Platform)}; {(Environment.Is64BitOperatingSystem ? "x64" : "")})";
public string UserAgent { get; set; } = DefaultUserAgent;
public int ImageCompression{ get; set; } = 40;
public bool BlackWhiteImages { get; set; } = false;
public string FlareSolverrUrl { get; set; } = string.Empty;
public string FlareSolverrUrl { get; set; } = Environment.GetEnvironmentVariable("FLARESOLVERR_URL") ?? string.Empty;
/// <summary>
/// Placeholders:
/// %M Obj Name
@@ -53,20 +50,20 @@ public struct TrangaSettings()
public string DownloadLanguage { get; set; } = "en";
public int MaxConcurrentDownloads { get; set; } = 5;
public int MaxConcurrentDownloads { get; set; } = (int)Math.Max(Environment.ProcessorCount * 0.75, 1); // Minimum of 1 Tasks, maximum of 0.75 per Core
public int MaxConcurrentWorkers { get; set; } = 10;
public int MaxConcurrentWorkers { get; set; } = Math.Max(Environment.ProcessorCount, 4); // Minimum of 4 Tasks, maximum of 1 per Core
public static TrangaSettings Load()
{
if (!File.Exists(settingsFilePath))
if (!File.Exists(SettingsFilePath))
new TrangaSettings().Save();
return JsonConvert.DeserializeObject<TrangaSettings>(File.ReadAllText(settingsFilePath));
return JsonConvert.DeserializeObject<TrangaSettings>(File.ReadAllText(SettingsFilePath));
}
public void Save()
{
File.WriteAllText(settingsFilePath, JsonConvert.SerializeObject(this, Formatting.Indented));
File.WriteAllText(SettingsFilePath, JsonConvert.SerializeObject(this, Formatting.Indented));
}
public void SetUserAgent(string value)
@@ -122,4 +119,10 @@ public struct TrangaSettings()
this.MaxConcurrentDownloads = value;
Save();
}
public void SetMaxConcurrentWorkers(int value)
{
this.MaxConcurrentWorkers = value;
Save();
}
}

View File

@@ -207,7 +207,7 @@ public class DownloadChapterFromMangaconnectorWorker(MangaConnectorId<Chapter> c
return;
}
string fullCoverPath = Path.Join(TrangaSettings.coverImageCacheOriginal, coverFileNameInCache);
string fullCoverPath = Path.Join(TrangaSettings.CoverImageCacheOriginal, coverFileNameInCache);
string newFilePath = Path.Join(publicationFolder, $"cover.{Path.GetFileName(coverFileNameInCache).Split('.')[^1]}" );
File.Copy(fullCoverPath, newFilePath, true);
if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux))

View File

@@ -12,10 +12,10 @@ public class CleanupMangaCoversWorker(TimeSpan? interval = null, IEnumerable<Bas
{
Log.Info("Removing stale files...");
string[] usedFiles = DbContext.Mangas.Select(m => m.CoverFileNameInCache).Where(s => s != null).ToArray()!;
CleanupImageCache(usedFiles, TrangaSettings.coverImageCacheOriginal);
CleanupImageCache(usedFiles, TrangaSettings.coverImageCacheLarge);
CleanupImageCache(usedFiles, TrangaSettings.coverImageCacheMedium);
CleanupImageCache(usedFiles, TrangaSettings.coverImageCacheSmall);
CleanupImageCache(usedFiles, TrangaSettings.CoverImageCacheOriginal);
CleanupImageCache(usedFiles, TrangaSettings.CoverImageCacheLarge);
CleanupImageCache(usedFiles, TrangaSettings.CoverImageCacheMedium);
CleanupImageCache(usedFiles, TrangaSettings.CoverImageCacheSmall);
return new Task<BaseWorker[]>(() => []);
}

View File

@@ -18,7 +18,7 @@ public class CleanupMangaconnectorIdsWithoutConnector : BaseWorkerWithContext<Ma
.Where(mcId => connectorNames.Any(name => name == mcId.MangaConnectorName)).ToListAsync() is
{ Count: > 0 } list)
{
string filePath = Path.Join(TrangaSettings.workingDirectory, $"deletedManga-{DateTime.UtcNow.Ticks}.txt");
string filePath = Path.Join(TrangaSettings.WorkingDirectory, $"deletedManga-{DateTime.UtcNow.Ticks}.txt");
Log.Debug($"Writing deleted manga to {filePath}.");
await File.WriteAllLinesAsync(filePath, list.Select(id => string.Join('-', id.MangaConnectorName, id.IdOnConnectorSite, id.Obj.Name, id.WebsiteUrl)), CancellationToken);
}