diff --git a/.dockerignore b/.dockerignore index 5538a38..5302b0e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,3 @@ **/node_modules **/npm-debug.log -**/.env \ No newline at end of file +**/.env diff --git a/.editorconfig b/.editorconfig index 9270194..03559e7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1236,11 +1236,11 @@ dotnet_naming_rule.unity_serialized_field_rule_1.style = lower_camel_case_style dotnet_naming_rule.unity_serialized_field_rule_1.symbols = unity_serialized_field_symbols_1 dotnet_naming_style.lower_camel_case_style.capitalization = camel_case dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = * -dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds = +dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds = dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance dotnet_naming_symbols.unity_serialized_field_symbols_1.applicable_accessibilities = * -dotnet_naming_symbols.unity_serialized_field_symbols_1.applicable_kinds = +dotnet_naming_symbols.unity_serialized_field_symbols_1.applicable_kinds = dotnet_naming_symbols.unity_serialized_field_symbols_1.resharper_applicable_kinds = unity_serialised_field dotnet_naming_symbols.unity_serialized_field_symbols_1.resharper_required_modifiers = instance dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none @@ -1264,7 +1264,7 @@ resharper_enforce_line_ending_style = true resharper_formatter_off_tag = @formatter:off resharper_formatter_on_tag = @formatter:on resharper_formatter_tags_enabled = true -resharper_instance_members_qualify_declared_in = +resharper_instance_members_qualify_declared_in = resharper_object_creation_when_type_not_evident = target_typed resharper_place_accessorholder_attribute_on_same_line = false resharper_place_expr_property_on_single_line = false diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 0f0494d..b928996 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -17,7 +17,7 @@ Steps to reproduce the behavior: A clear and concise description of what you expected to happen. **Logs** -Provide logs for all containers when this issue occurs. +Provide logs for all containers when this issue occurs. If the logs are short, make sure to triple backtick them, or use https://pastebin.com/ **Hardware:** diff --git a/.github/workflows/build_images.yaml b/.github/workflows/build_images.yaml index 10f9b6d..947a7ea 100644 --- a/.github/workflows/build_images.yaml +++ b/.github/workflows/build_images.yaml @@ -65,7 +65,7 @@ jobs: type=edge,branch=master,commit=${{ github.sha }} type=sha,commit=${{ github.sha }} type=raw,value=latest,enable={{is_default_branch}} - + - name: Build image for scanning ${{ matrix.image_name }} uses: docker/build-push-action@v5 with: diff --git a/.gitignore b/.gitignore index eeb3291..cb63146 100644 --- a/.gitignore +++ b/.gitignore @@ -410,4 +410,4 @@ src/producer/.run/ # Caddy logs deployment/docker/optional_reverse_proxy/logs/** -!deployment/docker/optional_reverse_proxy/logs/.gitkeep \ No newline at end of file +!deployment/docker/optional_reverse_proxy/logs/.gitkeep diff --git a/README.md b/README.md index 0f5f4d3..766c708 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ## Contents -> [!CAUTION] +> [!CAUTION] > Until we reach `v1.0.0`, please consider releases as alpha. > [!IMPORTANT] @@ -75,9 +75,9 @@ You can also increase `JOB_CONCURRENCY` from `5` to `10`. ### DebridMediaManager setup (optional) -There are some optional steps you should take to maximise the number of movies/tv shows we can find. +There are some optional steps you should take to maximise the number of movies/tv shows we can find. -We can search DebridMediaManager hash lists which are hosted on GitHub. This allows us to add hundreds of thousands of movies and tv shows, but it requires a Personal Access Token to be generated. The software only needs read access and only for public respositories. To generate one, please follow these steps: +We can search DebridMediaManager hash lists which are hosted on GitHub. This allows us to add hundreds of thousands of movies and tv shows, but it requires a Personal Access Token to be generated. The software only needs read access and only for public repositories. To generate one, please follow these steps: 1. Navigate to GitHub settings -> Developer Settings -> Personal access tokens -> Fine-grained tokens (click [here](https://github.com/settings/tokens?type=beta) for a direct link) 2. Press `Generate new token` @@ -89,8 +89,8 @@ We can search DebridMediaManager hash lists which are hosted on GitHub. This all 90 days Description: - Respository access - (checked) Public Repositories (read-only) + Repository access + (checked) Public Repositories (read-only) ``` 4. Click `Generate token` 5. Take the new token and add it to the bottom of the [.env](deployment/docker/.env) file @@ -109,7 +109,7 @@ Please choose which applies to you: You can use either a paid domain `your-domain.com` or a free reverse dns service like [DuckDNS](https://www.duckdns.org/) (you can [automate the update of your IP address](https://www.duckdns.org/install.jsp)). -Before continuing you need to open up port `80` and `443` in your firewall and configure any [port forwarding](https://portforward.com/) as necessary. You should not do this unless you understand the security implications. Please note that Knightcrawler and its contributors cannot be held responsible for any damage or loss of data from exposing your service publically. +Before continuing you need to open up port `80` and `443` in your firewall and configure any [port forwarding](https://portforward.com/) as necessary. You should not do this unless you understand the security implications. Please note that Knightcrawler and its contributors cannot be held responsible for any damage or loss of data from exposing your service publicly. You may find it safer to [use a tunnel/vpn](#i-will-be-using-a-tunnelvpn-cgnat-dont-want-to-open-ports-etc), but this will require the use of a paid domain or will not be accessible without being connected to your vpn. @@ -119,7 +119,7 @@ For this you can use a VPN like [Tailscale](https://tailscale.com/) which has it To use a Cloudflare tunnel you __will__ need a domain name. -Theres a sample compose for a Cloudflare tunnel [here](deployment/docker/example_cloudflare_tunnel/docker-compose.yml). +There's a sample compose for a Cloudflare tunnel [here](deployment/docker/example_cloudflare_tunnel/docker-compose.yml). If you are going to go this route, you will want to connect caddy to the cloudflare-tunnel network. It's all in Caddy's [docker-compose.yaml](deployment/docker/optional_reverse_proxy/docker-compose.yaml) you will just need to uncomment it. @@ -209,7 +209,7 @@ Here's how to set up and use Grafana and Prometheus for monitoring RabbitMQ: - You can use the following dashboard from Grafana's official library: [RabbitMQ Overview Dashboard](https://grafana.com/grafana/dashboards/10991-rabbitmq-overview/). - - You can alse use the following dashboard [PostgreSQL Database](https://grafana.com/grafana/dashboards/9628-postgresql-database/) to monitor Postgres metrics. + - You can also use the following dashboard [PostgreSQL Database](https://grafana.com/grafana/dashboards/9628-postgresql-database/) to monitor Postgres metrics. The Prometheus data source is already configured in Grafana, you just have to select it when importing the dashboard. @@ -252,7 +252,7 @@ with include drop, create tables, create indexes, reset sequences > [!NOTE] > If you have changed the default password for PostgreSQL (RECOMMENDED) please change it above accordingly -> +> > `postgresql://postgres:@postgres:5432/knightcrawler` Then run the following docker run command to import the database: @@ -295,7 +295,7 @@ with include drop, create tables, create indexes, reset sequences > [!NOTE] > If you have changed the default password for PostgreSQL (RECOMMENDED) please change it above accordingly -> +> > `postgresql://postgres:@/knightcrawler` > [!TIP] @@ -308,7 +308,7 @@ Then run `pgloader db.load`. ### Process the data we have imported -The data we have imported is not usable immediately. It is loaded into a new table called `items`. We need to move this data into the `ingested_torrents` table to be processed. The producer and atleast one consumer need to be running to process this data. +The data we have imported is not usable immediately. It is loaded into a new table called `items`. We need to move this data into the `ingested_torrents` table to be processed. The producer and at least one consumer need to be running to process this data. We are going to concatenate all of the different movie categories into one e.g. movies_uhd, movies_hd, movies_sd -> `movies`. This will give us a lot more data to work with. @@ -364,8 +364,8 @@ With the renaming of the project, you will have to change your database name in **With your existing stack still running**, run: ``` docker exec -it torrentio-selfhostio-postgres-1 psql -c " -SELECT pg_terminate_backend(pid) FROM pg_stat_activity -WHERE pid <> pg_backend_pid() AND datname = 'selfhostio'; +SELECT pg_terminate_backend(pid) FROM pg_stat_activity +WHERE pid <> pg_backend_pid() AND datname = 'selfhostio'; ALTER DATABASE selfhostio RENAME TO knightcrawler;" ``` Make sure your postgres container is named `torrentio-selfhostio-postgres-1`, otherwise, adjust accordingly. diff --git a/deployment/docker/.env.example b/deployment/docker/.env.example index 8ec6480..419e860 100644 --- a/deployment/docker/.env.example +++ b/deployment/docker/.env.example @@ -26,7 +26,7 @@ RABBITMQ_MAX_PUBLISH_BATCH_SIZE=500 RABBITMQ_PUBLISH_INTERVAL_IN_SECONDS=10 # Metadata -## Only used if DATA_ONCE is set to false. If true, the schedule is ignored +## Only used if DATA_ONCE is set to false. If true, the schedule is ignored METADATA_DOWNLOAD_IMDB_DATA_SCHEDULE="0 0 1 * *" ## If true, the metadata will be downloaded once and then the schedule will be ignored METADATA_DOWNLOAD_IMDB_DATA_ONCE=true diff --git a/deployment/docker/docker-compose.yaml b/deployment/docker/docker-compose.yaml index 570306b..228792b 100644 --- a/deployment/docker/docker-compose.yaml +++ b/deployment/docker/docker-compose.yaml @@ -130,7 +130,7 @@ services: ports: - "7000:7000" - + networks: knightcrawler-network: driver: bridge diff --git a/deployment/docker/example_cloudflare_tunnel/.env.example b/deployment/docker/example_cloudflare_tunnel/.env.example index cc023fe..2a2ef79 100644 --- a/deployment/docker/example_cloudflare_tunnel/.env.example +++ b/deployment/docker/example_cloudflare_tunnel/.env.example @@ -1 +1 @@ -TOKEN=cloudflare-tunnel-token-here \ No newline at end of file +TOKEN=cloudflare-tunnel-token-here diff --git a/deployment/docker/optional_metrics/config/grafana/dashboards/dashboards.yml b/deployment/docker/optional_metrics/config/grafana/dashboards/dashboards.yml index 131310a..2fa9046 100644 --- a/deployment/docker/optional_metrics/config/grafana/dashboards/dashboards.yml +++ b/deployment/docker/optional_metrics/config/grafana/dashboards/dashboards.yml @@ -5,4 +5,4 @@ providers: folder: Dashboards type: file options: - path: /var/lib/grafana/dashboards \ No newline at end of file + path: /var/lib/grafana/dashboards diff --git a/deployment/docker/optional_metrics/config/grafana/dashboards/logs.json b/deployment/docker/optional_metrics/config/grafana/dashboards/logs.json index 3b96f95..4b982c5 100644 --- a/deployment/docker/optional_metrics/config/grafana/dashboards/logs.json +++ b/deployment/docker/optional_metrics/config/grafana/dashboards/logs.json @@ -578,4 +578,4 @@ "uid": "knightcrawler-logs", "version": 1, "weekStart": "" -} \ No newline at end of file +} diff --git a/deployment/docker/optional_metrics/config/grafana/datasources/loki-datasource.yml b/deployment/docker/optional_metrics/config/grafana/datasources/loki-datasource.yml index a8ad746..d96bd06 100644 --- a/deployment/docker/optional_metrics/config/grafana/datasources/loki-datasource.yml +++ b/deployment/docker/optional_metrics/config/grafana/datasources/loki-datasource.yml @@ -9,4 +9,4 @@ datasources: basicAuth: false isDefault: false version: 1 - editable: true \ No newline at end of file + editable: true diff --git a/deployment/docker/optional_metrics/config/loki/config.yml b/deployment/docker/optional_metrics/config/loki/config.yml index c8efd11..b030f1c 100644 --- a/deployment/docker/optional_metrics/config/loki/config.yml +++ b/deployment/docker/optional_metrics/config/loki/config.yml @@ -32,4 +32,4 @@ schema_config: schema: v11 index: prefix: index_ - period: 24h \ No newline at end of file + period: 24h diff --git a/deployment/docker/optional_metrics/docker-compose.yml b/deployment/docker/optional_metrics/docker-compose.yml index c9c9c10..d209a55 100644 --- a/deployment/docker/optional_metrics/docker-compose.yml +++ b/deployment/docker/optional_metrics/docker-compose.yml @@ -7,7 +7,7 @@ networks: volumes: grafana-data: loki-data: - + services: prometheus: command: @@ -46,7 +46,7 @@ services: image: prometheuscommunity/postgres-exporter networks: - knightcrawler-network - + promtail: image: grafana/promtail:2.9.4 volumes: @@ -61,7 +61,7 @@ services: networks: - knightcrawler-network - + loki: command: '-config.file=/etc/loki/local-config.yml' depends_on: @@ -73,4 +73,3 @@ services: volumes: - ./config/loki/config.yml:/etc/loki/local-config.yml - loki-data:/loki - diff --git a/deployment/docker/optional_reverse_proxy/configs/Caddyfile b/deployment/docker/optional_reverse_proxy/configs/Caddyfile index 2d6051d..2d53b17 100644 --- a/deployment/docker/optional_reverse_proxy/configs/Caddyfile +++ b/deployment/docker/optional_reverse_proxy/configs/Caddyfile @@ -19,9 +19,9 @@ ## NOTE: I have dramatically lowered the above for testing. ## Once you have confirmed that everything works, start increasing the number ## the goal is to have HSTS set to one year with subdomains and preloading : - ## + ## # `Strict-Transport-Security: max-age=31536000; includeSubDomains; preload` - ## + ## ## Warning: You should ensure that you fully understand the implications ## of HSTS preloading before you include the directive in your policy and ## before you submit. It means that your entire domain and all subdomains, @@ -41,7 +41,7 @@ (cloudflare-tunnel-protection) { import ./snippets/cloudflare-replace-X-Forwarded-For trusted_proxies 172.17.0.0/16 # This needs to be your docker subnet - # I beleive this is what is configured by default. + # I believe this is what is configured by default. # If you can't make it work ask for my help on discord. } diff --git a/src/metadata/Configuration/logging.json b/src/metadata/Configuration/logging.json index fd48065..7a9b9b0 100644 --- a/src/metadata/Configuration/logging.json +++ b/src/metadata/Configuration/logging.json @@ -16,16 +16,16 @@ } }, "WriteTo": [ - { + { "Name": "Console", "Args": { "outputTemplate": "{Timestamp:HH:mm:ss} [{Level}] [{SourceContext}] {Message}{NewLine}{Exception}" } - } + } ], "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ], "Properties": { "Application": "Metadata" } } -} \ No newline at end of file +} diff --git a/src/metadata/Extensions/ConfigurationExtensions.cs b/src/metadata/Extensions/ConfigurationExtensions.cs index 5a68249..1c42085 100644 --- a/src/metadata/Extensions/ConfigurationExtensions.cs +++ b/src/metadata/Extensions/ConfigurationExtensions.cs @@ -4,41 +4,41 @@ public static class ConfigurationExtensions { private const string ConfigurationFolder = "Configuration"; private const string LoggingConfig = "logging.json"; - + public static IConfigurationBuilder AddServiceConfiguration(this IConfigurationBuilder configuration) { configuration.SetBasePath(Path.Combine(AppContext.BaseDirectory, ConfigurationFolder)); - + configuration.AddJsonFile(LoggingConfig, false, true); - + configuration.AddEnvironmentVariables(); configuration.AddUserSecrets(); - + return configuration; } - + public static TConfiguration LoadConfigurationFromConfig(this IServiceCollection services, IConfiguration configuration, string sectionName) where TConfiguration : class { var instance = configuration.GetSection(sectionName).Get(); - + ArgumentNullException.ThrowIfNull(instance, nameof(instance)); services.TryAddSingleton(instance); return instance; } - + public static TConfiguration LoadConfigurationFromEnv(this IServiceCollection services) where TConfiguration : class { var instance = Activator.CreateInstance(); - + ArgumentNullException.ThrowIfNull(instance, nameof(instance)); services.TryAddSingleton(instance); return instance; } -} \ No newline at end of file +} diff --git a/src/metadata/Extensions/EnvironmentExtensions.cs b/src/metadata/Extensions/EnvironmentExtensions.cs index 97f0ba0..d60e4a6 100644 --- a/src/metadata/Extensions/EnvironmentExtensions.cs +++ b/src/metadata/Extensions/EnvironmentExtensions.cs @@ -5,9 +5,9 @@ public static class EnvironmentExtensions public static bool GetEnvironmentVariableAsBool(this string prefix, string varName, bool fallback = false) { var fullVarName = GetFullVariableName(prefix, varName); - + var str = Environment.GetEnvironmentVariable(fullVarName); - + if (string.IsNullOrEmpty(str)) { return fallback; @@ -21,11 +21,11 @@ public static class EnvironmentExtensions _ => false, }; } - + public static int GetEnvironmentVariableAsInt(this string prefix, string varName, int fallback = 0) { var fullVarName = GetFullVariableName(prefix, varName); - + var str = Environment.GetEnvironmentVariable(fullVarName); if (string.IsNullOrEmpty(str)) @@ -35,11 +35,11 @@ public static class EnvironmentExtensions return int.TryParse(str, out var result) ? result : fallback; } - + public static string GetRequiredEnvironmentVariableAsString(this string prefix, string varName) { var fullVarName = GetFullVariableName(prefix, varName); - + var str = Environment.GetEnvironmentVariable(fullVarName); if (string.IsNullOrEmpty(str)) @@ -49,11 +49,11 @@ public static class EnvironmentExtensions return str; } - + public static string GetOptionalEnvironmentVariableAsString(this string prefix, string varName, string? fallback = null) { var fullVarName = GetFullVariableName(prefix, varName); - + var str = Environment.GetEnvironmentVariable(fullVarName); if (string.IsNullOrEmpty(str)) @@ -63,6 +63,6 @@ public static class EnvironmentExtensions return str; } - + private static string GetFullVariableName(string prefix, string varName) => $"{prefix}_{varName}"; -} \ No newline at end of file +} diff --git a/src/metadata/Extensions/JsonSerializerExtensions.cs b/src/metadata/Extensions/JsonSerializerExtensions.cs index 28149ce..5831bfc 100644 --- a/src/metadata/Extensions/JsonSerializerExtensions.cs +++ b/src/metadata/Extensions/JsonSerializerExtensions.cs @@ -3,4 +3,4 @@ namespace Metadata.Extensions; public static class JsonSerializerExtensions { public static string ToJson(this T value) => JsonSerializer.Serialize(value); -} \ No newline at end of file +} diff --git a/src/metadata/Extensions/ServiceCollectionExtensions.cs b/src/metadata/Extensions/ServiceCollectionExtensions.cs index e9f4f8f..ef92737 100644 --- a/src/metadata/Extensions/ServiceCollectionExtensions.cs +++ b/src/metadata/Extensions/ServiceCollectionExtensions.cs @@ -8,15 +8,15 @@ public static class ServiceCollectionExtensions return services; } - + internal static IServiceCollection AddMongoDb(this IServiceCollection services) { services.LoadConfigurationFromEnv(); services.AddTransient(); - + return services; } - + internal static IServiceCollection AddJobSupport(this IServiceCollection services) { services.LoadConfigurationFromEnv(); @@ -24,7 +24,7 @@ public static class ServiceCollectionExtensions services.AddScheduler() .AddTransient() .AddHostedService(); - + return services; } -} \ No newline at end of file +} diff --git a/src/metadata/Extensions/WebApplicationBuilderExtensions.cs b/src/metadata/Extensions/WebApplicationBuilderExtensions.cs index 6fddbd8..41d869d 100644 --- a/src/metadata/Extensions/WebApplicationBuilderExtensions.cs +++ b/src/metadata/Extensions/WebApplicationBuilderExtensions.cs @@ -13,7 +13,7 @@ internal static class WebApplicationBuilderExtensions { options.DefaultExecutionTimeout = 6.Hours(); }); - + return builder; } -} \ No newline at end of file +} diff --git a/src/metadata/Features/Configuration/JobConfiguration.cs b/src/metadata/Features/Configuration/JobConfiguration.cs index 090ba56..06a576c 100644 --- a/src/metadata/Features/Configuration/JobConfiguration.cs +++ b/src/metadata/Features/Configuration/JobConfiguration.cs @@ -6,8 +6,8 @@ public class JobConfiguration private const string DownloadImdbDataVariable = "DOWNLOAD_IMDB_DATA_SCHEDULE"; private const string DownloadImdbDataOnceVariable = "DOWNLOAD_IMDB_DATA_ONCE"; private const string InsertBatchSizeVariable = "INSERT_BATCH_SIZE"; - + public int InsertBatchSize { get; init; } = Prefix.GetEnvironmentVariableAsInt(InsertBatchSizeVariable, 25_000); public string DownloadImdbCronSchedule { get; init; } = Prefix.GetOptionalEnvironmentVariableAsString(DownloadImdbDataVariable, CronExpressions.EveryHour); public bool DownloadImdbOnce { get; init; } = Prefix.GetEnvironmentVariableAsBool(DownloadImdbDataOnceVariable); -} \ No newline at end of file +} diff --git a/src/metadata/Features/Configuration/MongoConfiguration.cs b/src/metadata/Features/Configuration/MongoConfiguration.cs index 278a78c..0b844a2 100644 --- a/src/metadata/Features/Configuration/MongoConfiguration.cs +++ b/src/metadata/Features/Configuration/MongoConfiguration.cs @@ -8,13 +8,13 @@ public class MongoConfiguration private const string DbVariable = "DB"; private const string UsernameVariable = "USER"; private const string PasswordVariable = "PASSWORD"; - + private string Host { get; init; } = Prefix.GetRequiredEnvironmentVariableAsString(HostVariable); private int Port { get; init; } = Prefix.GetEnvironmentVariableAsInt(PortVariable, 27017); private string Username { get; init; } = Prefix.GetRequiredEnvironmentVariableAsString(UsernameVariable); private string Password { get; init; } = Prefix.GetRequiredEnvironmentVariableAsString(PasswordVariable); public string DbName { get; init; } = Prefix.GetRequiredEnvironmentVariableAsString(DbVariable); - + public string ConnectionString => $"mongodb://{Username}:{Password}@{Host}:{Port}/{DbName}?tls=false&directConnection=true&authSource=admin"; -} \ No newline at end of file +} diff --git a/src/metadata/Features/DeleteDownloadedImdbData/DeleteDownloadedImdbDataRequest.cs b/src/metadata/Features/DeleteDownloadedImdbData/DeleteDownloadedImdbDataRequest.cs index d505021..01d6fd1 100644 --- a/src/metadata/Features/DeleteDownloadedImdbData/DeleteDownloadedImdbDataRequest.cs +++ b/src/metadata/Features/DeleteDownloadedImdbData/DeleteDownloadedImdbDataRequest.cs @@ -1,3 +1,3 @@ namespace Metadata.Features.DeleteDownloadedImdbData; -public record DeleteDownloadedImdbDataRequest(string FilePath); \ No newline at end of file +public record DeleteDownloadedImdbDataRequest(string FilePath); diff --git a/src/metadata/Features/DeleteDownloadedImdbData/DeleteDownloadedImdbDataRequestHandler.cs b/src/metadata/Features/DeleteDownloadedImdbData/DeleteDownloadedImdbDataRequestHandler.cs index bc6a4fe..bed5947 100644 --- a/src/metadata/Features/DeleteDownloadedImdbData/DeleteDownloadedImdbDataRequestHandler.cs +++ b/src/metadata/Features/DeleteDownloadedImdbData/DeleteDownloadedImdbDataRequestHandler.cs @@ -5,9 +5,9 @@ public class DeleteDownloadedImdbDataRequestHandler(ILogger !configuration.DownloadImdbOnce && !string.IsNullOrEmpty(configuration.DownloadImdbCronSchedule); public override string JobName => nameof(DownloadImdbDataJob); public override async Task Invoke() => await messageBus.SendAsync(new GetImdbDataRequest()); -} \ No newline at end of file +} diff --git a/src/metadata/Features/DownloadImdbData/GetImdbDataRequest.cs b/src/metadata/Features/DownloadImdbData/GetImdbDataRequest.cs index 6b4077b..84bab1d 100644 --- a/src/metadata/Features/DownloadImdbData/GetImdbDataRequest.cs +++ b/src/metadata/Features/DownloadImdbData/GetImdbDataRequest.cs @@ -1,3 +1,3 @@ namespace Metadata.Features.DownloadImdbData; -public record GetImdbDataRequest; \ No newline at end of file +public record GetImdbDataRequest; diff --git a/src/metadata/Features/DownloadImdbData/GetImdbDataRequestHandler.cs b/src/metadata/Features/DownloadImdbData/GetImdbDataRequestHandler.cs index 1b0420c..5f485b2 100644 --- a/src/metadata/Features/DownloadImdbData/GetImdbDataRequestHandler.cs +++ b/src/metadata/Features/DownloadImdbData/GetImdbDataRequestHandler.cs @@ -3,28 +3,28 @@ namespace Metadata.Features.DownloadImdbData; public class GetImdbDataRequestHandler(IHttpClientFactory clientFactory, ILogger logger) { private const string TitleBasicsFileName = "title.basics.tsv"; - + public async Task Handle(GetImdbDataRequest _, CancellationToken cancellationToken) { logger.LogInformation("Downloading IMDB data"); - + var client = clientFactory.CreateClient("imdb-data"); var response = await client.GetAsync($"{TitleBasicsFileName}.gz", cancellationToken); - + var tempFile = Path.Combine(Path.GetTempPath(), TitleBasicsFileName); - + response.EnsureSuccessStatusCode(); - + await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken); await using var gzipStream = new GZipStream(stream, CompressionMode.Decompress); await using var fileStream = File.Create(tempFile); - + await gzipStream.CopyToAsync(fileStream, cancellationToken); - + logger.LogInformation("Downloaded IMDB data to {TempFile}", tempFile); - + fileStream.Close(); return new(tempFile); } -} \ No newline at end of file +} diff --git a/src/metadata/Features/ImportImdbData/ImdbEntry.cs b/src/metadata/Features/ImportImdbData/ImdbEntry.cs index 8e0608e..7d1ba90 100644 --- a/src/metadata/Features/ImportImdbData/ImdbEntry.cs +++ b/src/metadata/Features/ImportImdbData/ImdbEntry.cs @@ -12,4 +12,4 @@ public class ImdbEntry public string? EndYear { get; set; } public string? RuntimeMinutes { get; set; } public string? Genres { get; set; } -} \ No newline at end of file +} diff --git a/src/metadata/Features/ImportImdbData/ImdbMongoDbService.cs b/src/metadata/Features/ImportImdbData/ImdbMongoDbService.cs index e1ed1d3..415d1e1 100644 --- a/src/metadata/Features/ImportImdbData/ImdbMongoDbService.cs +++ b/src/metadata/Features/ImportImdbData/ImdbMongoDbService.cs @@ -8,7 +8,7 @@ public class ImdbMongoDbService public ImdbMongoDbService(MongoConfiguration configuration, ILogger logger) { _logger = logger; - + var client = new MongoClient(configuration.ConnectionString); var database = client.GetDatabase(configuration.DbName); @@ -37,7 +37,7 @@ public class ImdbMongoDbService await _imdbCollection.BulkWriteAsync(operations); } - + public bool IsDatabaseInitialized() { try @@ -61,4 +61,4 @@ public class ImdbMongoDbService return false; } } -} \ No newline at end of file +} diff --git a/src/metadata/Features/ImportImdbData/ImportImdbDataRequest.cs b/src/metadata/Features/ImportImdbData/ImportImdbDataRequest.cs index 751d092..7d50247 100644 --- a/src/metadata/Features/ImportImdbData/ImportImdbDataRequest.cs +++ b/src/metadata/Features/ImportImdbData/ImportImdbDataRequest.cs @@ -1,3 +1,3 @@ namespace Metadata.Features.ImportImdbData; -public record ImportImdbDataRequest(string FilePath); \ No newline at end of file +public record ImportImdbDataRequest(string FilePath); diff --git a/src/metadata/Features/ImportImdbData/ImportImdbDataRequestHandler.cs b/src/metadata/Features/ImportImdbData/ImportImdbDataRequestHandler.cs index f429b72..cb1c0d1 100644 --- a/src/metadata/Features/ImportImdbData/ImportImdbDataRequestHandler.cs +++ b/src/metadata/Features/ImportImdbData/ImportImdbDataRequestHandler.cs @@ -21,16 +21,16 @@ public class ImportImdbDataRequestHandler(ILogger FullMode = BoundedChannelFullMode.Wait, }); - + // Skip the header row await csv.ReadAsync(); - + var batchInsertTask = CreateBatchOfEntries(channel, cancellationToken); await ReadEntries(csv, channel, cancellationToken); channel.Writer.Complete(); - + await batchInsertTask; return new(request.FilePath); @@ -45,7 +45,7 @@ public class ImportImdbDataRequestHandler(ILogger { return; } - + var batch = new List { movieData, @@ -63,7 +63,7 @@ public class ImportImdbDataRequestHandler(ILogger } } }, cancellationToken); - + private static async Task ReadEntries(CsvReader csv, Channel channel, CancellationToken cancellationToken) { while (await csv.ReadAsync()) @@ -80,13 +80,13 @@ public class ImportImdbDataRequestHandler(ILogger RuntimeMinutes = csv.GetField(7), Genres = csv.GetField(8), }; - + if (cancellationToken.IsCancellationRequested) { return; } - + await channel.Writer.WriteAsync(movieData, cancellationToken); } } -} \ No newline at end of file +} diff --git a/src/metadata/Features/Jobs/BaseJob.cs b/src/metadata/Features/Jobs/BaseJob.cs index 83e6986..6d5ad7a 100644 --- a/src/metadata/Features/Jobs/BaseJob.cs +++ b/src/metadata/Features/Jobs/BaseJob.cs @@ -5,6 +5,6 @@ public abstract class BaseJob : IMetadataJob public abstract bool IsScheduelable { get; } public abstract string JobName { get; } - + public abstract Task Invoke(); -} \ No newline at end of file +} diff --git a/src/metadata/Features/Jobs/IMetadataJob.cs b/src/metadata/Features/Jobs/IMetadataJob.cs index f5147e8..8e7d2b7 100644 --- a/src/metadata/Features/Jobs/IMetadataJob.cs +++ b/src/metadata/Features/Jobs/IMetadataJob.cs @@ -4,4 +4,4 @@ public interface IMetadataJob : IInvocable { bool IsScheduelable { get; } string JobName { get; } -} \ No newline at end of file +} diff --git a/src/metadata/Features/Jobs/JobScheduler.cs b/src/metadata/Features/Jobs/JobScheduler.cs index 059fc4f..02c5120 100644 --- a/src/metadata/Features/Jobs/JobScheduler.cs +++ b/src/metadata/Features/Jobs/JobScheduler.cs @@ -5,14 +5,14 @@ public class JobScheduler(IServiceProvider serviceProvider) : IHostedService public Task StartAsync(CancellationToken cancellationToken) { using var scope = serviceProvider.CreateAsyncScope(); - + var mongoDbService = scope.ServiceProvider.GetRequiredService(); - + if (!mongoDbService.IsDatabaseInitialized()) { throw new InvalidOperationException("MongoDb is not initialized"); } - + var jobConfigurations = scope.ServiceProvider.GetRequiredService(); var downloadJob = scope.ServiceProvider.GetRequiredService(); @@ -20,15 +20,15 @@ public class JobScheduler(IServiceProvider serviceProvider) : IHostedService { return downloadJob.Invoke(); } - + var scheduler = scope.ServiceProvider.GetRequiredService(); - + scheduler.Schedule() .Cron(jobConfigurations.DownloadImdbCronSchedule) .PreventOverlapping(nameof(downloadJob.JobName)); - + return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; -} \ No newline at end of file +} diff --git a/src/metadata/Features/Literals/CronExpressions.cs b/src/metadata/Features/Literals/CronExpressions.cs index c80bca1..0d48544 100644 --- a/src/metadata/Features/Literals/CronExpressions.cs +++ b/src/metadata/Features/Literals/CronExpressions.cs @@ -6,4 +6,4 @@ public static class CronExpressions public const string EveryDay = "0 0 0 * *"; public const string EveryWeek = "0 0 * * 0"; public const string EveryMonth = "0 0 0 * *"; -} \ No newline at end of file +} diff --git a/src/metadata/Features/Literals/HttpClients.cs b/src/metadata/Features/Literals/HttpClients.cs index 923a2cf..b1eb4f2 100644 --- a/src/metadata/Features/Literals/HttpClients.cs +++ b/src/metadata/Features/Literals/HttpClients.cs @@ -4,4 +4,4 @@ public static class HttpClients { public const string ImdbDataClientName = "imdb-data"; public const string ImdbClientBaseAddress = "https://datasets.imdbws.com/"; -} \ No newline at end of file +} diff --git a/src/metadata/Program.cs b/src/metadata/Program.cs index c1635f5..8444cba 100644 --- a/src/metadata/Program.cs +++ b/src/metadata/Program.cs @@ -10,4 +10,4 @@ builder.Services var host = builder.Build(); -await host.RunAsync(); \ No newline at end of file +await host.RunAsync(); diff --git a/src/node/addon-jackett/.eslintignore b/src/node/addon-jackett/.eslintignore index b0a155e..6461dee 100644 --- a/src/node/addon-jackett/.eslintignore +++ b/src/node/addon-jackett/.eslintignore @@ -1 +1 @@ -*.ts \ No newline at end of file +*.ts diff --git a/src/node/addon-jackett/.eslintrc.cjs b/src/node/addon-jackett/.eslintrc.cjs index 5aac833..f460262 100644 --- a/src/node/addon-jackett/.eslintrc.cjs +++ b/src/node/addon-jackett/.eslintrc.cjs @@ -36,4 +36,4 @@ module.exports = { "one-var": ["error", { uninitialized: "consecutive" }], "prefer-destructuring": "warn", }, -}; \ No newline at end of file +}; diff --git a/src/node/addon-jackett/build.sh b/src/node/addon-jackett/build.sh index c06bbb3..1209495 100644 --- a/src/node/addon-jackett/build.sh +++ b/src/node/addon-jackett/build.sh @@ -1,4 +1,4 @@ #!/bin/bash docker build -t ippexdeploymentscr.azurecr.io/dave/stremio-addon-jackett:latest . --platform linux/amd64 -docker push ippexdeploymentscr.azurecr.io/dave/stremio-addon-jackett:latest \ No newline at end of file +docker push ippexdeploymentscr.azurecr.io/dave/stremio-addon-jackett:latest diff --git a/src/node/addon-jackett/jsconfig.json b/src/node/addon-jackett/jsconfig.json index 6b1c407..cff0187 100644 --- a/src/node/addon-jackett/jsconfig.json +++ b/src/node/addon-jackett/jsconfig.json @@ -18,4 +18,4 @@ "typeRoots": ["node_modules/@types", "src/@types"] }, "exclude": ["node_modules"] -} \ No newline at end of file +} diff --git a/src/node/addon-jackett/src/addon.js b/src/node/addon-jackett/src/addon.js index 77d650e..a4db235 100644 --- a/src/node/addon-jackett/src/addon.js +++ b/src/node/addon-jackett/src/addon.js @@ -22,12 +22,12 @@ builder.defineStreamHandler((args) => { if (!args.id.match(/tt\d+/i) && !args.id.match(/kitsu:\d+/i)) { return Promise.resolve({ streams: [] }); } - + if (processConfig.DEBUG) { console.log(`Incoming stream ${args.id} request`) console.log('args', args); } - + return cacheWrapStream(args.id, () => limiter.schedule(() => streamHandler(args) .then(records => records.map(record => toStreamInfo(record, args.type)))) diff --git a/src/node/addon-jackett/src/jackett/jacketParser.js b/src/node/addon-jackett/src/jackett/jacketParser.js index b955551..790f994 100644 --- a/src/node/addon-jackett/src/jackett/jacketParser.js +++ b/src/node/addon-jackett/src/jackett/jacketParser.js @@ -111,7 +111,7 @@ export const transformData = async (data, query) => { console.log("Transforming data for query " + data); let results = []; - + const parsedData = await parseString(data); if (!parsedData.rss.channel[0]?.item) { @@ -126,7 +126,7 @@ export const transformData = async (data, query) => { [torznabDataItem.$.name]: torznabDataItem.$.value, }) ); - + if (torznabData.infohash) { const [title, pubDate, category, size] = [rssItem.title[0], rssItem.pubDate[0], rssItem.category[0], rssItem.size[0]]; @@ -148,4 +148,4 @@ export const transformData = async (data, query) => { return results; -}; \ No newline at end of file +}; diff --git a/src/node/addon-jackett/src/jackett/jackett.js b/src/node/addon-jackett/src/jackett/jackett.js index 0522dcb..85d94a6 100644 --- a/src/node/addon-jackett/src/jackett/jackett.js +++ b/src/node/addon-jackett/src/jackett/jackett.js @@ -38,4 +38,4 @@ export const searchJackett = async (query) => { } return sortedResults; -}; \ No newline at end of file +}; diff --git a/src/node/addon-jackett/src/jackett/jackettQueries.js b/src/node/addon-jackett/src/jackett/jackettQueries.js index eed8338..c794b30 100644 --- a/src/node/addon-jackett/src/jackett/jackettQueries.js +++ b/src/node/addon-jackett/src/jackett/jackettQueries.js @@ -18,7 +18,7 @@ const getMovieSearchQueries = (cleanName, year) => { const getSeriesSearchQueries = (cleanName, year, season, episode) => { return { seriesByEpisode: seriesByEpisode(cleanName, season, episode), - }; + }; } export const jackettSearchQueries = (cleanName, type, year, season, episode) => { @@ -27,8 +27,8 @@ export const jackettSearchQueries = (cleanName, type, year, season, episode) => return getMovieSearchQueries(cleanName, year); case Type.SERIES: return getSeriesSearchQueries(cleanName, year, season, episode); - + default: return { }; } -}; \ No newline at end of file +}; diff --git a/src/node/addon-jackett/src/lib/cinemetaProvider.js b/src/node/addon-jackett/src/lib/cinemetaProvider.js index faf38a4..dc524c4 100644 --- a/src/node/addon-jackett/src/lib/cinemetaProvider.js +++ b/src/node/addon-jackett/src/lib/cinemetaProvider.js @@ -8,7 +8,7 @@ const cinemetaUri = cinemetaConfig.URI; export const getMetaData = (args) => { const [imdbId] = args.id.split(':'); const {type} = args; - + return cacheWrapImdbMetaData(args.id, () => getInfoForImdbId(imdbId, type)); } @@ -20,7 +20,7 @@ const getInfoForImdbId = async (imdbId, type) => { console.log(`Getting info for ${imdbId} of type ${type}`); console.log(`Request URI: ${requestUri}`); } - + try { const { data: response } = await axios.get(requestUri, options); return response.meta; @@ -28,4 +28,4 @@ const getInfoForImdbId = async (imdbId, type) => { console.log(error); return {}; } -}; \ No newline at end of file +}; diff --git a/src/node/addon-jackett/src/lib/configuration.js b/src/node/addon-jackett/src/lib/configuration.js index 8b89123..79d4647 100644 --- a/src/node/addon-jackett/src/lib/configuration.js +++ b/src/node/addon-jackett/src/lib/configuration.js @@ -22,4 +22,4 @@ export function parseConfiguration(configuration) { .map(value => keysToUppercase.includes(key) ? value.toUpperCase() : value.toLowerCase())) return configValues; -} \ No newline at end of file +} diff --git a/src/node/addon-jackett/src/lib/landingTemplate.js b/src/node/addon-jackett/src/lib/landingTemplate.js index befdc70..7aa4e16 100644 --- a/src/node/addon-jackett/src/lib/landingTemplate.js +++ b/src/node/addon-jackett/src/lib/landingTemplate.js @@ -180,7 +180,7 @@ a.install-link { } .input:focus, .btn:focus { - outline: none; + outline: none; box-shadow: 0 0 0 2pt rgb(30, 144, 255, 0.7); } `; @@ -210,7 +210,7 @@ export default function landingTemplate(manifest, config = {}) { const debridOptionsHTML = Object.values(DebridOptions.options) .map(option => ``) .join('\n'); - + return ` @@ -239,54 +239,54 @@ export default function landingTemplate(manifest, config = {}) {

${manifest.description || ''}

- + - + - +
- +
- +
- + - +
- +
- +
- +
@@ -295,7 +295,7 @@ export default function landingTemplate(manifest, config = {}) {

Or paste into Stremio search bar after clicking install

- +