Merge pull request #125 from Gabisonfire/add-pre-commit

Add pre commit
This commit is contained in:
iPromKnight
2024-03-08 18:22:42 +00:00
committed by GitHub
222 changed files with 544 additions and 526 deletions

View File

@@ -1,3 +1,3 @@
**/node_modules
**/npm-debug.log
**/.env
**/.env

View File

@@ -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

View File

@@ -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:**

View File

@@ -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:

2
.gitignore vendored
View File

@@ -410,4 +410,4 @@ src/producer/.run/
# Caddy logs
deployment/docker/optional_reverse_proxy/logs/**
!deployment/docker/optional_reverse_proxy/logs/.gitkeep
!deployment/docker/optional_reverse_proxy/logs/.gitkeep

19
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,19 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-added-large-files
- id: check-json
- id: check-toml
- id: check-xml
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- id: mixed-line-ending
args: [--fix=lf]
- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
hooks:
- id: codespell
exclude: ^src/node/consumer/test/
args: ["-L", "strem,chage"]

View File

@@ -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:
<blank>
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:<password here>@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:<password here>@<docker-ip>/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.

View File

@@ -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

View File

@@ -130,7 +130,7 @@ services:
ports:
- "7000:7000"
networks:
knightcrawler-network:
driver: bridge

View File

@@ -1 +1 @@
TOKEN=cloudflare-tunnel-token-here
TOKEN=cloudflare-tunnel-token-here

View File

@@ -5,4 +5,4 @@ providers:
folder: Dashboards
type: file
options:
path: /var/lib/grafana/dashboards
path: /var/lib/grafana/dashboards

View File

@@ -578,4 +578,4 @@
"uid": "knightcrawler-logs",
"version": 1,
"weekStart": ""
}
}

View File

@@ -9,4 +9,4 @@ datasources:
basicAuth: false
isDefault: false
version: 1
editable: true
editable: true

View File

@@ -32,4 +32,4 @@ schema_config:
schema: v11
index:
prefix: index_
period: 24h
period: 24h

View File

@@ -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

View File

@@ -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.
}

View File

@@ -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"
}
}
}
}

View File

@@ -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<Program>();
return configuration;
}
public static TConfiguration LoadConfigurationFromConfig<TConfiguration>(this IServiceCollection services, IConfiguration configuration, string sectionName)
where TConfiguration : class
{
var instance = configuration.GetSection(sectionName).Get<TConfiguration>();
ArgumentNullException.ThrowIfNull(instance, nameof(instance));
services.TryAddSingleton(instance);
return instance;
}
public static TConfiguration LoadConfigurationFromEnv<TConfiguration>(this IServiceCollection services)
where TConfiguration : class
{
var instance = Activator.CreateInstance<TConfiguration>();
ArgumentNullException.ThrowIfNull(instance, nameof(instance));
services.TryAddSingleton(instance);
return instance;
}
}
}

View File

@@ -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}";
}
}

View File

@@ -3,4 +3,4 @@ namespace Metadata.Extensions;
public static class JsonSerializerExtensions
{
public static string ToJson<T>(this T value) => JsonSerializer.Serialize(value);
}
}

View File

@@ -8,15 +8,15 @@ public static class ServiceCollectionExtensions
return services;
}
internal static IServiceCollection AddMongoDb(this IServiceCollection services)
{
services.LoadConfigurationFromEnv<MongoConfiguration>();
services.AddTransient<ImdbMongoDbService>();
return services;
}
internal static IServiceCollection AddJobSupport(this IServiceCollection services)
{
services.LoadConfigurationFromEnv<JobConfiguration>();
@@ -24,7 +24,7 @@ public static class ServiceCollectionExtensions
services.AddScheduler()
.AddTransient<DownloadImdbDataJob>()
.AddHostedService<JobScheduler>();
return services;
}
}
}

View File

@@ -13,7 +13,7 @@ internal static class WebApplicationBuilderExtensions
{
options.DefaultExecutionTimeout = 6.Hours();
});
return builder;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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";
}
}

View File

@@ -1,3 +1,3 @@
namespace Metadata.Features.DeleteDownloadedImdbData;
public record DeleteDownloadedImdbDataRequest(string FilePath);
public record DeleteDownloadedImdbDataRequest(string FilePath);

View File

@@ -5,9 +5,9 @@ public class DeleteDownloadedImdbDataRequestHandler(ILogger<DeleteDownloadedImdb
public Task Handle(DeleteDownloadedImdbDataRequest request, CancellationToken _)
{
logger.LogInformation("Deleting file {FilePath}", request.FilePath);
File.Delete(request.FilePath);
logger.LogInformation("File Deleted");
if (configuration.DownloadImdbOnce)
@@ -18,4 +18,4 @@ public class DeleteDownloadedImdbDataRequestHandler(ILogger<DeleteDownloadedImdb
return Task.CompletedTask;
}
}
}

View File

@@ -5,4 +5,4 @@ public class DownloadImdbDataJob(IMessageBus messageBus, JobConfiguration config
public override bool IsScheduelable => !configuration.DownloadImdbOnce && !string.IsNullOrEmpty(configuration.DownloadImdbCronSchedule);
public override string JobName => nameof(DownloadImdbDataJob);
public override async Task Invoke() => await messageBus.SendAsync(new GetImdbDataRequest());
}
}

View File

@@ -1,3 +1,3 @@
namespace Metadata.Features.DownloadImdbData;
public record GetImdbDataRequest;
public record GetImdbDataRequest;

View File

@@ -3,28 +3,28 @@ namespace Metadata.Features.DownloadImdbData;
public class GetImdbDataRequestHandler(IHttpClientFactory clientFactory, ILogger<GetImdbDataRequestHandler> logger)
{
private const string TitleBasicsFileName = "title.basics.tsv";
public async Task<ImportImdbDataRequest> 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);
}
}
}

View File

@@ -12,4 +12,4 @@ public class ImdbEntry
public string? EndYear { get; set; }
public string? RuntimeMinutes { get; set; }
public string? Genres { get; set; }
}
}

View File

@@ -8,7 +8,7 @@ public class ImdbMongoDbService
public ImdbMongoDbService(MongoConfiguration configuration, ILogger<ImdbMongoDbService> 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;
}
}
}
}

View File

@@ -1,3 +1,3 @@
namespace Metadata.Features.ImportImdbData;
public record ImportImdbDataRequest(string FilePath);
public record ImportImdbDataRequest(string FilePath);

View File

@@ -21,16 +21,16 @@ public class ImportImdbDataRequestHandler(ILogger<ImportImdbDataRequestHandler>
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<ImportImdbDataRequestHandler>
{
return;
}
var batch = new List<ImdbEntry>
{
movieData,
@@ -63,7 +63,7 @@ public class ImportImdbDataRequestHandler(ILogger<ImportImdbDataRequestHandler>
}
}
}, cancellationToken);
private static async Task ReadEntries(CsvReader csv, Channel<ImdbEntry, ImdbEntry> channel, CancellationToken cancellationToken)
{
while (await csv.ReadAsync())
@@ -80,13 +80,13 @@ public class ImportImdbDataRequestHandler(ILogger<ImportImdbDataRequestHandler>
RuntimeMinutes = csv.GetField(7),
Genres = csv.GetField(8),
};
if (cancellationToken.IsCancellationRequested)
{
return;
}
await channel.Writer.WriteAsync(movieData, cancellationToken);
}
}
}
}

View File

@@ -5,6 +5,6 @@ public abstract class BaseJob : IMetadataJob
public abstract bool IsScheduelable { get; }
public abstract string JobName { get; }
public abstract Task Invoke();
}
}

View File

@@ -4,4 +4,4 @@ public interface IMetadataJob : IInvocable
{
bool IsScheduelable { get; }
string JobName { get; }
}
}

View File

@@ -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<ImdbMongoDbService>();
if (!mongoDbService.IsDatabaseInitialized())
{
throw new InvalidOperationException("MongoDb is not initialized");
}
var jobConfigurations = scope.ServiceProvider.GetRequiredService<JobConfiguration>();
var downloadJob = scope.ServiceProvider.GetRequiredService<DownloadImdbDataJob>();
@@ -20,15 +20,15 @@ public class JobScheduler(IServiceProvider serviceProvider) : IHostedService
{
return downloadJob.Invoke();
}
var scheduler = scope.ServiceProvider.GetRequiredService<IScheduler>();
scheduler.Schedule<DownloadImdbDataJob>()
.Cron(jobConfigurations.DownloadImdbCronSchedule)
.PreventOverlapping(nameof(downloadJob.JobName));
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
}

View File

@@ -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 * *";
}
}

View File

@@ -4,4 +4,4 @@ public static class HttpClients
{
public const string ImdbDataClientName = "imdb-data";
public const string ImdbClientBaseAddress = "https://datasets.imdbws.com/";
}
}

View File

@@ -10,4 +10,4 @@ builder.Services
var host = builder.Build();
await host.RunAsync();
await host.RunAsync();

View File

@@ -1 +1 @@
*.ts
*.ts

View File

@@ -36,4 +36,4 @@ module.exports = {
"one-var": ["error", { uninitialized: "consecutive" }],
"prefer-destructuring": "warn",
},
};
};

View File

@@ -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
docker push ippexdeploymentscr.azurecr.io/dave/stremio-addon-jackett:latest

View File

@@ -18,4 +18,4 @@
"typeRoots": ["node_modules/@types", "src/@types"]
},
"exclude": ["node_modules"]
}
}

View File

@@ -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))))

View File

@@ -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;
};
};

View File

@@ -38,4 +38,4 @@ export const searchJackett = async (query) => {
}
return sortedResults;
};
};

View File

@@ -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 { };
}
};
};

View File

@@ -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 {};
}
};
};

View File

@@ -22,4 +22,4 @@ export function parseConfiguration(configuration) {
.map(value => keysToUppercase.includes(key) ? value.toUpperCase() : value.toLowerCase()))
return configValues;
}
}

View File

@@ -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 => `<option value="${option.key}">${option.description}</option>`)
.join('\n');
return `
<!DOCTYPE html>
<html style="background-image: url(${background});">
@@ -239,54 +239,54 @@ export default function landingTemplate(manifest, config = {}) {
<h2 class="description">${manifest.description || ''}</h2>
<div class="separator"></div>
<label class="label" id="iLimitLabel" for="iLimit">Max results per quality:</label>
<input type="text" inputmode="numeric" pattern="[0-9]*" id="iLimit" onchange="generateInstallLink()" class="input" placeholder="All results">
<label class="label" for="iDebridProviders">Debrid provider:</label>
<select id="iDebridProviders" class="input" onchange="debridProvidersChange()">
<option value="none" selected>None</option>
${debridProvidersHTML}
</select>
<div id="dRealDebrid">
<label class="label" for="iRealDebrid">RealDebrid API Key (Find it <a href='https://real-debrid.com/apitoken' target="_blank">here</a>):</label>
<input type="text" id="iRealDebrid" onchange="generateInstallLink()" class="input">
</div>
<div id="dAllDebrid">
<label class="label" for="iAllDebrid">AllDebrid API Key (Create it <a href='https://alldebrid.com/apikeys' target="_blank">here</a>):</label>
<input type="text" id="iAllDebrid" onchange="generateInstallLink()" class="input">
</div>
<div id="dPremiumize">
<label class="label" for="iPremiumize">Premiumize API Key (Find it <a href='https://www.premiumize.me/account' target="_blank">here</a>):</label>
<input type="text" id="iPremiumize" onchange="generateInstallLink()" class="input">
</div>
<div id="dDebridLink">
<label class="label" for="iDebridLink">DebridLink API Key (Find it <a href='https://debrid-link.fr/webapp/apikey' target="_blank">here</a>):</label>
<input type="text" id="iDebridLink" onchange="generateInstallLink()" class="input">
</div>
<div id="dOffcloud">
<label class="label" for="iOffcloud">Offcloud API Key (Find it <a href='https://offcloud.com/#/account' target="_blank">here</a>):</label>
<input type="text" id="iOffcloud" onchange="generateInstallLink()" class="input">
</div>
<div id="dPutio">
<label class="label" for="iPutio">Put.io ClientId and Token (Create new OAuth App <a href='https://app.put.io/oauth' target="_blank">here</a>):</label>
<input type="text" id="iPutioClientId" placeholder="ClientId" onchange="generateInstallLink()" class="input">
<input type="text" id="iPutioToken" placeholder="Token" onchange="generateInstallLink()" class="input">
</div>
<div id="dDebridOptions">
<label class="label" for="iDebridOptions">Debrid options:</label>
<select id="iDebridOptions" class="input" onchange="generateInstallLink()" name="debridOptions[]" multiple="multiple">
${debridOptionsHTML}
</select>
</div>
<div class="separator"></div>
<a id="installLink" class="install-link" href="#">
@@ -295,7 +295,7 @@ export default function landingTemplate(manifest, config = {}) {
<div class="contact">
<p>Or paste into Stremio search bar after clicking install</p>
</div>
<div class="separator"></div>
</div>
<script type="text/javascript">
@@ -304,7 +304,7 @@ export default function landingTemplate(manifest, config = {}) {
const isTvAgent = /\\b(?:tv|wv)\\b/i.test(navigator.userAgent)
const isDesktopMedia = window.matchMedia("(pointer:fine)").matches;
if (isDesktopMedia && !isTvMedia && !isTvAgent) {
$('#iDebridOptions').multiselect({
$('#iDebridOptions').multiselect({
nonSelectedText: 'None',
buttonTextAlignment: 'left',
onChange: () => generateInstallLink()
@@ -325,7 +325,7 @@ export default function landingTemplate(manifest, config = {}) {
generateInstallLink();
debridProvidersChange();
});
function debridProvidersChange() {
const provider = $('#iDebridProviders').val()
$('#dDebridOptions').toggle(provider !== 'none');
@@ -336,10 +336,10 @@ export default function landingTemplate(manifest, config = {}) {
$('#dOffcloud').toggle(provider === '${MochOptions.offcloud.key}');
$('#dPutio').toggle(provider === '${MochOptions.putio.key}');
}
function generateInstallLink() {
const limitValue = $('#iLimit').val() || '';
const debridOptionsValue = $('#iDebridOptions').val().join(',') || '';
const realDebridValue = $('#iRealDebrid').val() || '';
const allDebridValue = $('#iAllDebrid').val() || '';
@@ -348,9 +348,9 @@ export default function landingTemplate(manifest, config = {}) {
const offcloudValue = $('#iOffcloud').val() || ''
const putioClientIdValue = $('#iPutioClientId').val() || '';
const putioTokenValue = $('#iPutioToken').val() || '';
const limit = /^[1-9][0-9]{0,2}$/.test(limitValue) && limitValue;
const debridOptions = debridOptionsValue.length && debridOptionsValue.trim();
const realDebrid = realDebridValue.length && realDebridValue.trim();
const premiumize = premiumizeValue.length && premiumizeValue.trim();
@@ -361,7 +361,7 @@ export default function landingTemplate(manifest, config = {}) {
let configurationValue = [
['limit', limit],
['${DebridOptions.key}', debridOptions],
['${DebridOptions.key}', debridOptions],
['${MochOptions.realdebrid.key}', realDebrid],
['${MochOptions.premiumize.key}', premiumize],
['${MochOptions.alldebrid.key}', allDebrid],

View File

@@ -56,4 +56,4 @@ export function getSources(magnetInfo) {
}
const trackers = Array.isArray(magnetInfo.announce) ? magnetInfo.announce : magnetInfo.announce.split(',');
return trackers.map(tracker => `tracker:${tracker}`).concat(`dht:${magnetInfo.infohash}`);
}
}

View File

@@ -39,4 +39,4 @@ export const cacheConfig = {
STALE_REVALIDATE_AGE: parseInt(process.env.STALE_REVALIDATE_AGE) || 4 * 60 * 60, // 4 hours
STALE_ERROR_AGE: parseInt(process.env.STALE_ERROR_AGE) || 7 * 24 * 60 * 60, // 7 days
GLOBAL_KEY_PREFIX: process.env.GLOBAL_KEY_PREFIX || 'jackettio-addon',
}
}

View File

@@ -31,7 +31,7 @@ export function toStreamInfo(record, type) {
const behaviorHints = bingeGroup ? { bingeGroup } : undefined;
const magnetInfo = decode(record.magneturl)
return cleanOutputObject({
name: name,
title: title,
@@ -61,7 +61,7 @@ function formatSize(size) {
function getBingeGroupParts(record, sameInfo, quality, torrentInfo, fileInfo, type) {
if (type === Type.MOVIE) {
return [quality];
} else if (sameInfo) {
return [quality];
}

View File

@@ -3,4 +3,4 @@ export const Type = {
SERIES: 'series',
ANIME: 'anime',
OTHER: 'other'
};
};

View File

@@ -13,4 +13,4 @@ export function isStaticUrl(url) {
return Object.values(staticVideoUrls).some(videoUrl => url?.endsWith(videoUrl));
}
export default staticVideoUrls
export default staticVideoUrls

View File

@@ -1 +1 @@
*.ts
*.ts

View File

@@ -36,4 +36,4 @@ module.exports = {
"one-var": ["error", { uninitialized: "consecutive" }],
"prefer-destructuring": "warn",
},
};
};

View File

@@ -11,4 +11,4 @@ module.exports = {
},
},
],
};
};

View File

@@ -65,4 +65,4 @@ try {
} catch (e) {
console.log(e);
process.exit(1);
}
}

View File

@@ -18,4 +18,4 @@
"typeRoots": ["node_modules/@types", "src/@types"]
},
"exclude": ["node_modules"]
}
}

View File

@@ -26,7 +26,7 @@ builder.defineStreamHandler((args) => {
if (!args.id.match(/tt\d+/i) && !args.id.match(/kitsu:\d+/i)) {
return Promise.resolve({ streams: [] });
}
return cacheWrapStream(args.id, () => limiter.schedule(() =>
streamHandler(args)
.then(records => records

View File

@@ -25,4 +25,4 @@ export function parseConfiguration(configuration) {
.map(value => keysToUppercase.includes(key) ? value.toUpperCase() : value.toLowerCase()))
return configValues;
}
}

View File

@@ -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);
}
`;
@@ -228,7 +228,7 @@ export default function landingTemplate(manifest, config = {}) {
.join('\n');
const stylizedTypes = manifest.types
.map(t => t[0].toUpperCase() + t.slice(1) + (t !== 'series' ? 's' : ''));
return `
<!DOCTYPE html>
<html style="background-image: url(${background});">
@@ -264,73 +264,73 @@ export default function landingTemplate(manifest, config = {}) {
</ul>
<div class="separator"></div>
<label class="label" for="iSort">Sorting:</label>
<select id="iSort" class="input" onchange="sortModeChange()">
${sortOptionsHTML}
</select>
<label class="label" for="iLanguages">Priority foreign language:</label>
<select id="iLanguages" class="input" onchange="generateInstallLink()" name="languages[]" multiple="multiple" title="Streams with the selected dubs/subs language will be shown on the top">
${languagesOptionsHTML}
</select>
<label class="label" for="iQualityFilter">Exclude qualities/resolutions:</label>
<select id="iQualityFilter" class="input" onchange="generateInstallLink()" name="qualityFilters[]" multiple="multiple">
${qualityFiltersHTML}
</select>
<label class="label" id="iLimitLabel" for="iLimit">Max results per quality:</label>
<input type="text" inputmode="numeric" pattern="[0-9]*" id="iLimit" onchange="generateInstallLink()" class="input" placeholder="All results">
<label class="label" id="iSizeFilterLabel" for="iSizeFilter">Video size limit:</label>
<input type="text" pattern="([0-9.]*(?:MB|GB),?)+" id="iSizeFilter" onchange="generateInstallLink()" class="input" placeholder="No limit" title="Returned videos cannot exceed this size, use comma to have different size for movies and series. Examples: 5GB ; 800MB ; 10GB,2GB">
<label class="label" for="iDebridProviders">Debrid provider:</label>
<select id="iDebridProviders" class="input" onchange="debridProvidersChange()">
<option value="none" selected>None</option>
${debridProvidersHTML}
</select>
<div id="dRealDebrid">
<label class="label" for="iRealDebrid">RealDebrid API Key (Find it <a href='https://real-debrid.com/apitoken' target="_blank">here</a>):</label>
<input type="text" id="iRealDebrid" onchange="generateInstallLink()" class="input">
</div>
<div id="dAllDebrid">
<label class="label" for="iAllDebrid">AllDebrid API Key (Create it <a href='https://alldebrid.com/apikeys' target="_blank">here</a>):</label>
<input type="text" id="iAllDebrid" onchange="generateInstallLink()" class="input">
</div>
<div id="dPremiumize">
<label class="label" for="iPremiumize">Premiumize API Key (Find it <a href='https://www.premiumize.me/account' target="_blank">here</a>):</label>
<input type="text" id="iPremiumize" onchange="generateInstallLink()" class="input">
</div>
<div id="dDebridLink">
<label class="label" for="iDebridLink">DebridLink API Key (Find it <a href='https://debrid-link.fr/webapp/apikey' target="_blank">here</a>):</label>
<input type="text" id="iDebridLink" onchange="generateInstallLink()" class="input">
</div>
<div id="dOffcloud">
<label class="label" for="iOffcloud">Offcloud API Key (Find it <a href='https://offcloud.com/#/account' target="_blank">here</a>):</label>
<input type="text" id="iOffcloud" onchange="generateInstallLink()" class="input">
</div>
<div id="dPutio">
<label class="label" for="iPutio">Put.io ClientId and Token (Create new OAuth App <a href='https://app.put.io/oauth' target="_blank">here</a>):</label>
<input type="text" id="iPutioClientId" placeholder="ClientId" onchange="generateInstallLink()" class="input">
<input type="text" id="iPutioToken" placeholder="Token" onchange="generateInstallLink()" class="input">
</div>
<div id="dDebridOptions">
<label class="label" for="iDebridOptions">Debrid options:</label>
<select id="iDebridOptions" class="input" onchange="generateInstallLink()" name="debridOptions[]" multiple="multiple">
${debridOptionsHTML}
</select>
</div>
<div class="separator"></div>
<a id="installLink" class="install-link" href="#">
@@ -339,7 +339,7 @@ export default function landingTemplate(manifest, config = {}) {
<div class="contact">
<p>Or paste into Stremio search bar after clicking install</p>
</div>
<div class="separator"></div>
</div>
<script type="text/javascript">
@@ -348,19 +348,19 @@ export default function landingTemplate(manifest, config = {}) {
const isTvAgent = /\\b(?:tv|wv)\\b/i.test(navigator.userAgent)
const isDesktopMedia = window.matchMedia("(pointer:fine)").matches;
if (isDesktopMedia && !isTvMedia && !isTvAgent) {
$('#iLanguages').multiselect({
$('#iLanguages').multiselect({
nonSelectedText: 'None',
buttonTextAlignment: 'left',
onChange: () => generateInstallLink()
});
$('#iLanguages').multiselect('select', [${languages.map(language => '"' + language + '"')}]);
$('#iQualityFilter').multiselect({
$('#iQualityFilter').multiselect({
nonSelectedText: 'None',
buttonTextAlignment: 'left',
onChange: () => generateInstallLink()
});
$('#iQualityFilter').multiselect('select', [${qualityFilters.map(filter => '"' + filter + '"')}]);
$('#iDebridOptions').multiselect({
$('#iDebridOptions').multiselect({
nonSelectedText: 'None',
buttonTextAlignment: 'left',
onChange: () => generateInstallLink()
@@ -385,7 +385,7 @@ export default function landingTemplate(manifest, config = {}) {
generateInstallLink();
debridProvidersChange();
});
function sortModeChange() {
if (['${SortOptions.options.seeders.key}', '${SortOptions.options.size.key}'].includes($('#iSort').val())) {
$("#iLimitLabel").text("Max results:");
@@ -394,7 +394,7 @@ export default function landingTemplate(manifest, config = {}) {
}
generateInstallLink();
}
function debridProvidersChange() {
const provider = $('#iDebridProviders').val()
$('#dDebridOptions').toggle(provider !== 'none');
@@ -405,14 +405,14 @@ export default function landingTemplate(manifest, config = {}) {
$('#dOffcloud').toggle(provider === '${MochOptions.offcloud.key}');
$('#dPutio').toggle(provider === '${MochOptions.putio.key}');
}
function generateInstallLink() {
const qualityFilterValue = $('#iQualityFilter').val().join(',') || '';
const sortValue = $('#iSort').val() || '';
const languagesValue = $('#iLanguages').val().join(',') || [];
const limitValue = $('#iLimit').val() || '';
const sizeFilterValue = $('#iSizeFilter').val() || '';
const debridOptionsValue = $('#iDebridOptions').val().join(',') || '';
const realDebridValue = $('#iRealDebrid').val() || '';
const allDebridValue = $('#iAllDebrid').val() || '';
@@ -421,13 +421,13 @@ export default function landingTemplate(manifest, config = {}) {
const offcloudValue = $('#iOffcloud').val() || ''
const putioClientIdValue = $('#iPutioClientId').val() || '';
const putioTokenValue = $('#iPutioToken').val() || '';
const qualityFilters = qualityFilterValue.length && qualityFilterValue;
const sort = sortValue !== '${SortOptions.options.qualitySeeders.key}' && sortValue;
const languages = languagesValue.length && languagesValue;
const limit = /^[1-9][0-9]{0,2}$/.test(limitValue) && limitValue;
const sizeFilter = sizeFilterValue.length && sizeFilterValue;
const debridOptions = debridOptionsValue.length && debridOptionsValue.trim();
const realDebrid = realDebridValue.length && realDebridValue.trim();
const premiumize = premiumizeValue.length && premiumizeValue.trim();
@@ -442,7 +442,7 @@ export default function landingTemplate(manifest, config = {}) {
['${QualityFilter.key}', qualityFilters],
['limit', limit],
['${SizeFilter.key}', sizeFilter],
['${DebridOptions.key}', debridOptions],
['${DebridOptions.key}', debridOptions],
['${MochOptions.realdebrid.key}', realDebrid],
['${MochOptions.premiumize.key}', premiumize],
['${MochOptions.alldebrid.key}', allDebrid],

View File

@@ -85,4 +85,4 @@ export function getSources(trackersInput, infoHash) {
function unique(array) {
return Array.from(new Set(array));
}
}

View File

@@ -36,7 +36,7 @@ export default function sortStreams(streams, config) {
function _sortStreams(streams, config) {
const limit = /^[1-9][0-9]*$/.test(config.limit) && parseInt(config.limit) || undefined;
return sortBySize(streams, limit);
}

View File

@@ -3,4 +3,4 @@ export const Type = {
SERIES: 'series',
ANIME: 'anime',
OTHER: 'other'
};
};

View File

@@ -13,4 +13,4 @@ export function isStaticUrl(url) {
return Object.values(staticVideoUrls).some(videoUrl => url?.endsWith(videoUrl));
}
export default staticVideoUrls
export default staticVideoUrls

View File

@@ -1,3 +1,3 @@
dist/
esbuild.ts
jest.config.ts
jest.config.ts

View File

@@ -81,4 +81,4 @@
}
}
]
}
}

View File

@@ -1 +1 @@
v20.10.0
v20.10.0

View File

@@ -44,4 +44,4 @@ try {
} catch (e) {
console.log(e);
process.exit(1);
}
}

View File

@@ -11,4 +11,4 @@ export default {
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
};
};

View File

@@ -1,4 +1,4 @@
export enum CacheType {
Memory = 'memory',
MongoDb = 'mongodb'
}
}

View File

@@ -2,4 +2,4 @@ export enum TorrentType {
Series = 'Series',
Movie = 'Movie',
Anime = 'anime',
}
}

View File

@@ -15,4 +15,4 @@ export const BooleanHelpers = {
throw new Error(`Invalid boolean value: '${value}'. Allowed values are 'true', 'false', 'yes', 'no', '1', or '0'.`);
}
}
}
}

View File

@@ -63,4 +63,4 @@ export const ExtensionHelpers = {
const extensionMatch = filename.match(/\.(\w{2,4})$/);
return extensionMatch !== null && extensions.includes(extensionMatch[1].toLowerCase());
}
}
}

View File

@@ -34,4 +34,4 @@ export const PromiseHelpers = {
return array.sort((a, b) => array.filter(v => v === a).length - array.filter(v => v === b).length).pop();
}
};
/* eslint-enable @typescript-eslint/no-explicit-any */
/* eslint-enable @typescript-eslint/no-explicit-any */

View File

@@ -1,3 +1,3 @@
export interface ICacheOptions {
ttl: number;
}
}

View File

@@ -8,4 +8,4 @@ export interface ICacheService {
cacheTrackers: (method: CacheMethod) => Promise<any>;
}
/* eslint-enable @typescript-eslint/no-explicit-any */
/* eslint-enable @typescript-eslint/no-explicit-any */

View File

@@ -81,4 +81,4 @@ export interface ICinemetaLink {
export interface ICinemetaBehaviorHints {
defaultVideoId?: null;
hasScheduledVideos?: boolean;
}
}

View File

@@ -6,4 +6,4 @@ export interface ICommonVideoMetadata {
name?: string;
id?: string;
imdb_id?: string;
}
}

View File

@@ -12,4 +12,4 @@ export interface IIngestedRabbitTorrent {
export interface IIngestedRabbitMessage {
message: IIngestedRabbitTorrent;
}
}

View File

@@ -20,4 +20,4 @@ export interface IKitsuCatalogMetaData {
background: string;
trailers: IKitsuTrailer[];
links: IKitsuLink[];
}
}

View File

@@ -46,4 +46,4 @@ export interface IKitsuLink {
name?: string;
category?: string;
url?: string;
}
}

View File

@@ -9,4 +9,4 @@ export interface ILoggingService {
warn(message: string, ...args: any[]): void;
}
/* eslint-enable @typescript-eslint/no-explicit-any */
/* eslint-enable @typescript-eslint/no-explicit-any */

View File

@@ -6,4 +6,4 @@ export interface IMetaDataQuery {
season?: number
episode?: number
id?: string | number
}
}

View File

@@ -12,4 +12,4 @@ export interface IMetadataResponse {
videos?: ICommonVideoMetadata[];
episodeCount?: number[];
totalCount?: number;
}
}

View File

@@ -11,4 +11,4 @@ export interface IMetadataService {
isEpisodeImdbId(imdbId: string | undefined): Promise<boolean>;
escapeTitle(title: string): string;
}
}

View File

@@ -33,4 +33,4 @@ export interface IParseTorrentTitleResult {
videoFile?: IFileAttributes;
folderName?: string;
fileName?: string;
}
}

View File

@@ -15,4 +15,4 @@ export interface IParsedTorrent extends IParseTorrentTitleResult {
seeders?: number;
torrentId?: string;
fileCollection?: ITorrentFileCollection;
}
}

View File

@@ -1,3 +1,3 @@
export interface IProcessTorrentsJob {
listenToQueue: () => Promise<void>;
}
}

View File

@@ -3,4 +3,4 @@ import {ITorrentFileCollection} from "@interfaces/torrent_file_collection";
export interface ITorrentDownloadService {
getTorrentFiles(torrent: IParsedTorrent, timeout: number): Promise<ITorrentFileCollection>;
}
}

View File

@@ -15,4 +15,4 @@ export interface ITorrentEntriesService {
createTorrentContents(torrent: Torrent): Promise<void>;
updateTorrentSeeders(torrent: ITorrentAttributes): Promise<[number] | undefined>;
}
}

View File

@@ -6,4 +6,4 @@ export interface ITorrentFileCollection {
contents?: IContentAttributes[];
videos?: IFileAttributes[];
subtitles?: ISubtitleAttributes[];
}
}

View File

@@ -5,4 +5,4 @@ export interface ITorrentFileService {
parseTorrentFiles(torrent: IParsedTorrent): Promise<ITorrentFileCollection>;
isPackTorrent(torrent: IParsedTorrent): boolean;
}
}

View File

@@ -2,4 +2,4 @@ import {IIngestedTorrentAttributes} from "@repository/interfaces/ingested_torren
export interface ITorrentProcessingService {
processTorrentRecord(torrent: IIngestedTorrentAttributes): Promise<void>;
}
}

View File

@@ -2,4 +2,4 @@ import {ITorrentFileCollection} from "@interfaces/torrent_file_collection";
export interface ITorrentSubtitleService {
assignSubtitles(fileCollection: ITorrentFileCollection): ITorrentFileCollection;
}
}

View File

@@ -1,3 +1,3 @@
export interface ITrackerService {
getTrackers(): Promise<string[]>;
}
}

View File

@@ -60,4 +60,4 @@ export class ProcessTorrentsJob implements IProcessTorrentsJob {
this.logger.error('Failed to setup channel', error);
}
};
}
}

View File

@@ -12,4 +12,4 @@ export const cacheConfig = {
get MONGO_URI(): string {
return `mongodb://${this.MONGODB_USER}:${this.MONGODB_PASSWORD}@${this.MONGODB_HOST}:${this.MONGODB_PORT}/${this.MONGODB_DB}?authSource=admin`;
}
};
};

Some files were not shown because too many files have changed in this diff Show More