diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..08fd618 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [Fazzani] \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index fbef5c3..8e954d5 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -10,8 +10,8 @@ _Put an `x` in the boxes that apply_ - [ ] Bugfix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] Documentation Update (if none of the other choices apply) -- [ ] CI/CD or unit tests improvements +- [ ] Documentation Update +- [ ] CI/CD or unit tests improvements (if none of the other choices apply) ## Further comments diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 75c50cb..c97bbcc 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -55,4 +55,10 @@ jobs: if: github.event_name == 'pull_request' with: recreate: true - path: code-coverage-results.md \ No newline at end of file + path: code-coverage-results.md + + - name: Run codacy-coverage-reporter + uses: codacy/codacy-coverage-reporter-action@v1.3.0 + with: + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + coverage-reports: coverage/**/coverage.cobertura.xml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 78e3e89..8931742 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,4 +38,9 @@ If you have a suggestion that would make this better, please fork the repo and c ```sh dotnet run ./src/Proxarr.Api/Proxarr.Api.csproj ``` -

back to top

\ No newline at end of file + +

+ + back to top + +

\ No newline at end of file diff --git a/README.md b/README.md index 90d8578..12d04c7 100644 --- a/README.md +++ b/README.md @@ -65,22 +65,22 @@ It uses TMDB to find out which streaming services are available in the selected * Acquire TMDB API KEY [How](https://dev.to/codexive_zech/streamlining-your-contribution-how-to-get-your-tmdb-api-key-for-ldbflix-contribution-52gf#:~:text=How%20to%20Obtain%20a%20TMDB%20API%20Key) -* Obtain SONARR/RADARR API KEY
- +* Obtain SONARR/RADARR API KEY +
### Installation with Docker compose 1. Prepare your [config.yml][config-yml] to fit your setup 2. On your Radarr/Sonarr instances we have to do some changes - - tag all indexers by the TAG_NAME defined in your [config.yml][config-yml] (`q` by default)
- tag indexers + - tag all indexers by the TAG_NAME defined in your [config.yml][config-yml] (`q` by default) +
tag indexers - specify Application URL: is essential because it is used by Proxarr to determine to which instance should return the response
Application Url config - - establish a Webhook connection between Sonarr/Radarr and Proxarr
- Application Url config
- _Note_ : Webhook URL is `http:///api/qualifier -3. Add the following to your docker-compose.yml (to be adapted according to your stack)
- [docker-compose.yml](docker-compose.yml) is an another full example of how to integrate Proxarr with Sonarr and Radarr. + - establish a Webhook connection between Sonarr/Radarr and Proxarr +
Application Url config +
_Note_ : Webhook URL is `http:///api/qualifier +3. Add the following to your docker-compose.yml (to be adapted according to your stack) +
[docker-compose.yml](docker-compose.yml) is an another full example of how to integrate Proxarr with Sonarr and Radarr. ```yaml proxarr: image: synker/proxarr:latest @@ -117,7 +117,11 @@ It uses TMDB to find out which streaming services are available in the selected docker run -itd --rm -e LOG_LEVEL=Debug -p 8880:8880 -v ${PWD}/config:/app/config --name proxarr synker/proxarr:latest ``` -

back to top

+

+ + back to top + +

### Watching providers configuration diff --git a/src/Proxarr.Api.Tests/RadarrServiceTests.cs b/src/Proxarr.Api.Tests/RadarrServiceTests.cs index 9a3c3cb..6af1f3c 100644 --- a/src/Proxarr.Api.Tests/RadarrServiceTests.cs +++ b/src/Proxarr.Api.Tests/RadarrServiceTests.cs @@ -61,7 +61,7 @@ public async Task Qualify_ShouldReturnNotFound_WhenMovieNotFoundIntoRadarr() }; var cancellationToken = new CancellationToken(); - _tmdbClientMock.Setup(x => x.GetMovieAsync(123, MovieMethods.WatchProviders, cancellationToken)) + _tmdbClientMock.Setup(x => x.GetMovieAsync(123, cancellationToken, MovieMethods.WatchProviders)) .ReturnsAsync(new TMDbLib.Objects.Movies.Movie { WatchProviders = new SingleResultContainer> { Results = [] } }); _radarrClientMock.Setup(x => x.MovieGET2Async(1, cancellationToken)) @@ -86,7 +86,7 @@ public async Task Qualify_ShouldReturnNotFound_WhenMovieNotFoundIntoTMDB() }; var cancellationToken = new CancellationToken(); - _tmdbClientMock.Setup(x => x.GetMovieAsync(123, MovieMethods.WatchProviders, cancellationToken)) + _tmdbClientMock.Setup(x => x.GetMovieAsync(123, cancellationToken, MovieMethods.WatchProviders)) .ReturnsAsync((TMDbLib.Objects.Movies.Movie)null); _radarrClientMock.Setup(x => x.MovieGET2Async(1, cancellationToken)) @@ -121,7 +121,7 @@ public async Task Qualify_ShouldUpdateTags_WhenMovieFound() var seriesResource = new MovieResource { Id = 1, Title = "Test Series", Tags = [] }; - _tmdbClientMock.Setup(x => x.GetMovieAsync(123, MovieMethods.WatchProviders, cancellationToken)) + _tmdbClientMock.Setup(x => x.GetMovieAsync(123, cancellationToken, MovieMethods.WatchProviders)) .ReturnsAsync(new TMDbLib.Objects.Movies.Movie { WatchProviders = watchProviders }); _radarrClientMock.Setup(x => x.MovieGET2Async(1, cancellationToken)) @@ -163,7 +163,7 @@ public async Task Qualify_Should_NotBeTagged_When_MatchedWatchProvider() var movieResource = new MovieResource { Id = 1, Title = "Test Series", Tags = [] }; - _tmdbClientMock.Setup(x => x.GetMovieAsync(123, MovieMethods.WatchProviders, cancellationToken)) + _tmdbClientMock.Setup(x => x.GetMovieAsync(123, cancellationToken, MovieMethods.WatchProviders)) .ReturnsAsync(new TMDbLib.Objects.Movies.Movie { WatchProviders = watchProviders }); _radarrClientMock.Setup(x => x.MovieGET2Async(1, cancellationToken)) @@ -210,7 +210,7 @@ public async Task Qualify_Should_BeTagged_When_MatchedWatchProvider() var movieResource = new MovieResource { Id = 1, Title = "Test Series", Tags = [] }; - _tmdbClientMock.Setup(x => x.GetMovieAsync(123, MovieMethods.WatchProviders, cancellationToken)) + _tmdbClientMock.Setup(x => x.GetMovieAsync(123, cancellationToken, MovieMethods.WatchProviders)) .ReturnsAsync(new TMDbLib.Objects.Movies.Movie { WatchProviders = watchProviders }); _radarrClientMock.Setup(x => x.MovieGET2Async(1, cancellationToken)) @@ -246,10 +246,9 @@ public async Task Qualify_Should_BeTagged_When_NoWatchProviders() }; var cancellationToken = new CancellationToken(); - var movieResource = new MovieResource { Id = 1, Title = "Test Series", Tags = [] }; - _tmdbClientMock.Setup(x => x.GetMovieAsync(123, MovieMethods.WatchProviders, cancellationToken)) + _tmdbClientMock.Setup(x => x.GetMovieAsync(123, cancellationToken, MovieMethods.WatchProviders)) .ReturnsAsync(new TMDbLib.Objects.Movies.Movie()); _radarrClientMock.Setup(x => x.MovieGET2Async(1, cancellationToken)) diff --git a/src/Proxarr.Api.Tests/SonarrServiceTests.cs b/src/Proxarr.Api.Tests/SonarrServiceTests.cs index d3db58e..6512ac9 100644 --- a/src/Proxarr.Api.Tests/SonarrServiceTests.cs +++ b/src/Proxarr.Api.Tests/SonarrServiceTests.cs @@ -60,7 +60,7 @@ public async Task Qualify_ShouldReturnNotFound_WhenSeriesNotFoundIntoSonarr() }; var cancellationToken = new CancellationToken(); - _tmdbClientMock.Setup(x => x.GetTvShowAsync(123, TvShowMethods.WatchProviders, null, null, cancellationToken)) + _tmdbClientMock.Setup(x => x.GetTvShowAsync(123, cancellationToken, TvShowMethods.WatchProviders)) .ReturnsAsync(new TvShow { WatchProviders = new SingleResultContainer> { Results = [] } }); _sonarrClientMock.Setup(x => x.SeriesGETAsync(1, false, cancellationToken)) @@ -85,7 +85,7 @@ public async Task Qualify_ShouldReturnNotFound_WhenSeriesNotFoundIntoTMDB() }; var cancellationToken = new CancellationToken(); - _tmdbClientMock.Setup(x => x.GetTvShowAsync(123, TvShowMethods.WatchProviders, null, null, cancellationToken)) + _tmdbClientMock.Setup(x => x.GetTvShowAsync(123, cancellationToken, TvShowMethods.WatchProviders)) .ReturnsAsync((TvShow)null); _sonarrClientMock.Setup(x => x.SeriesGETAsync(1, false, cancellationToken)) @@ -120,7 +120,7 @@ public async Task Qualify_ShouldUpdateTags_WhenSeriesFound() var seriesResource = new SeriesResource { Id = 1, Title = "Test Series", Tags = [] }; - _tmdbClientMock.Setup(x => x.GetTvShowAsync(123, TvShowMethods.WatchProviders, null, null, cancellationToken)) + _tmdbClientMock.Setup(x => x.GetTvShowAsync(123, cancellationToken, TvShowMethods.WatchProviders)) .ReturnsAsync(new TvShow { WatchProviders = watchProviders }); _sonarrClientMock.Setup(x => x.SeriesGETAsync(1, false, cancellationToken)) @@ -162,7 +162,7 @@ public async Task Qualify_Should_NotBeTagged_When_MatchedWatchProvider() var seriesResource = new SeriesResource { Id = 1, Title = "Test Series", Tags = [] }; - _tmdbClientMock.Setup(x => x.GetTvShowAsync(123, TvShowMethods.WatchProviders, null, null, cancellationToken)) + _tmdbClientMock.Setup(x => x.GetTvShowAsync(123, cancellationToken, TvShowMethods.WatchProviders)) .ReturnsAsync(new TvShow { WatchProviders = watchProviders }); _sonarrClientMock.Setup(x => x.SeriesGETAsync(1, false, cancellationToken)) @@ -208,7 +208,7 @@ public async Task Qualify_Should_BeTagged_When_MatchedWatchProvider() var seriesResource = new SeriesResource { Id = 1, Title = "Test Series", Tags = [] }; - _tmdbClientMock.Setup(x => x.GetTvShowAsync(123, TvShowMethods.WatchProviders, null, null, cancellationToken)) + _tmdbClientMock.Setup(x => x.GetTvShowAsync(123, cancellationToken, TvShowMethods.WatchProviders)) .ReturnsAsync(new TvShow { WatchProviders = watchProviders }); _sonarrClientMock.Setup(x => x.SeriesGETAsync(1, false, cancellationToken)) @@ -244,8 +244,8 @@ public async Task Qualify_Should_BeTagged_When_NoWatchProvider() var seriesResource = new SeriesResource { Id = 1, Title = "Test Series", Tags = [] }; - _tmdbClientMock.Setup(x => x.GetTvShowAsync(123, TvShowMethods.WatchProviders, null, null, cancellationToken)) - .ReturnsAsync(new TvShow ()); + _tmdbClientMock.Setup(x => x.GetTvShowAsync(123, cancellationToken, TvShowMethods.WatchProviders)) + .ReturnsAsync(new TvShow()); _sonarrClientMock.Setup(x => x.SeriesGETAsync(1, false, cancellationToken)) .ReturnsAsync(seriesResource); diff --git a/src/Proxarr.Api/Configuration/ClientConfiguration.cs b/src/Proxarr.Api/Configuration/ClientConfiguration.cs index cb86064..d112abb 100644 --- a/src/Proxarr.Api/Configuration/ClientConfiguration.cs +++ b/src/Proxarr.Api/Configuration/ClientConfiguration.cs @@ -5,7 +5,7 @@ namespace Proxarr.Api.Configuration [ExcludeFromCodeCoverage] public sealed class ClientConfiguration { - public const string SECTION_NAME = $"{AppConfiguration.SECTION_NAME}:Clients"; + public static string SECTION_NAME { get; } = $"{AppConfiguration.SECTION_NAME}:Clients"; /// /// Must be Sonarr or Radarr diff --git a/src/Proxarr.Api/Core/CronJobService.cs b/src/Proxarr.Api/Core/CronJobService.cs index b939351..ecd67c7 100644 --- a/src/Proxarr.Api/Core/CronJobService.cs +++ b/src/Proxarr.Api/Core/CronJobService.cs @@ -28,10 +28,15 @@ protected virtual async Task ScheduleJob(CancellationToken cancellationToken) while (!cancellationToken.IsCancellationRequested) { var next = _expression.GetNextOccurrence(DateTimeOffset.Now, timeZoneInfo); - if (!next.HasValue) continue; + + if (!next.HasValue) + { + continue; + } logger.LogInformation("{JobName}: scheduled next run at {NextRun}", GetType().Name, next.ToString()); var delay = next.Value - DateTimeOffset.Now; + if (delay.TotalMilliseconds <= 0) // prevent non-positive values from being passed into Timer { logger.LogInformation("{LoggerName}: scheduled next run is in the past. Moving to next.", GetType().Name); diff --git a/src/Proxarr.Api/Core/Http/BasicAuthenticationHandler.cs b/src/Proxarr.Api/Core/Http/BasicAuthenticationHandler.cs index e621f63..784e87f 100644 --- a/src/Proxarr.Api/Core/Http/BasicAuthenticationHandler.cs +++ b/src/Proxarr.Api/Core/Http/BasicAuthenticationHandler.cs @@ -13,7 +13,11 @@ namespace Proxarr.Api.Core.Http [ExcludeFromCodeCoverage] public class BasicAuthenticationDefaults { - public const string AuthenticationScheme = "Basic"; + protected BasicAuthenticationDefaults() + { + } + + public static string AuthenticationScheme { get; } = "Basic"; } [ExcludeFromCodeCoverage] diff --git a/src/Proxarr.Api/Core/TmdbProxy.cs b/src/Proxarr.Api/Core/TmdbProxy.cs index 96459ba..5bd77f9 100644 --- a/src/Proxarr.Api/Core/TmdbProxy.cs +++ b/src/Proxarr.Api/Core/TmdbProxy.cs @@ -7,8 +7,10 @@ namespace Proxarr.Api.Core { public interface ITmdbProxy { - Task GetMovieAsync(int movieId, MovieMethods extraMethods = MovieMethods.Undefined, CancellationToken cancellationToken = default); - Task GetTvShowAsync(int id, TvShowMethods extraMethods = TvShowMethods.Undefined, string language = null, string includeImageLanguage = null, CancellationToken cancellationToken = default); + Task GetMovieAsync(int movieId, MovieMethods extraMethods = MovieMethods.Undefined); + Task GetMovieAsync(int movieId, CancellationToken cancellationToken, MovieMethods extraMethods = MovieMethods.Undefined); + Task GetTvShowAsync(int id, TvShowMethods extraMethods = TvShowMethods.Undefined); + Task GetTvShowAsync(int id, CancellationToken cancellationToken, TvShowMethods extraMethods = TvShowMethods.Undefined); } [ExcludeFromCodeCoverage] @@ -22,12 +24,22 @@ public TmdbProxy(TMDbClient tMDbClient) _tMDbClient = tMDbClient; } - public Task GetTvShowAsync(int id, TvShowMethods extraMethods = TvShowMethods.Undefined, string language = null, string includeImageLanguage = null, CancellationToken cancellationToken = default) + public Task GetTvShowAsync(int id, TvShowMethods extraMethods = TvShowMethods.Undefined) { - return _tMDbClient.GetTvShowAsync(id, extraMethods, language, includeImageLanguage, cancellationToken); + return _tMDbClient.GetTvShowAsync(id, extraMethods, null, null, CancellationToken.None); } - public Task GetMovieAsync(int movieId, MovieMethods extraMethods = MovieMethods.Undefined, CancellationToken cancellationToken = default) + public Task GetTvShowAsync(int id, CancellationToken cancellationToken, TvShowMethods extraMethods = TvShowMethods.Undefined) + { + return _tMDbClient.GetTvShowAsync(id, extraMethods, null, null, cancellationToken); + } + + public Task GetMovieAsync(int movieId, MovieMethods extraMethods = MovieMethods.Undefined) + { + return _tMDbClient.GetMovieAsync(movieId, extraMethods, CancellationToken.None); + } + + public Task GetMovieAsync(int movieId, CancellationToken cancellationToken, MovieMethods extraMethods = MovieMethods.Undefined) { return _tMDbClient.GetMovieAsync(movieId, extraMethods, cancellationToken); } diff --git a/src/Proxarr.Api/Services/RadarrService.cs b/src/Proxarr.Api/Services/RadarrService.cs index 7c18515..620ebf8 100644 --- a/src/Proxarr.Api/Services/RadarrService.cs +++ b/src/Proxarr.Api/Services/RadarrService.cs @@ -61,7 +61,7 @@ public async Task Qualify(MovieAdded movieAdded, CancellationToken cance _logger.LogInformation("Qualifying movie {Title}", movieAdded.Movie.Title); var tmdbItem = await _tMDbClient - .GetMovieAsync(movieAdded.Movie.TmdbId, TMDbLib.Objects.Movies.MovieMethods.WatchProviders, cancellationToken) + .GetMovieAsync(movieAdded.Movie.TmdbId, cancellationToken, TMDbLib.Objects.Movies.MovieMethods.WatchProviders) .ConfigureAwait(false); if (tmdbItem != null) @@ -135,7 +135,7 @@ private async Task AddTag(MovieResource movieRadarr, { _logger.LogInformation("Adding tag {Tag} for {Title}", tag.Label, movieRadarr.Title); movieRadarr.Tags.Add(tag.Id); - updated = true; + return true; } return updated; diff --git a/src/Proxarr.Api/Services/SonarrService.cs b/src/Proxarr.Api/Services/SonarrService.cs index df31cf8..6a27b00 100644 --- a/src/Proxarr.Api/Services/SonarrService.cs +++ b/src/Proxarr.Api/Services/SonarrService.cs @@ -61,7 +61,7 @@ public async Task Qualify(TvAdded tvAdded, CancellationToken cancellatio _logger.LogInformation("Qualifying tv {Title}", tvAdded.Series.Title); var tmdbItem = await _tMDbClient - .GetTvShowAsync(tvAdded.Series.TmdbId, TMDbLib.Objects.TvShows.TvShowMethods.WatchProviders, cancellationToken: cancellationToken) + .GetTvShowAsync(tvAdded.Series.TmdbId, cancellationToken, TMDbLib.Objects.TvShows.TvShowMethods.WatchProviders) .ConfigureAwait(false); if (tmdbItem != null) @@ -135,7 +135,7 @@ private async Task AddTag(SeriesResource seriesSonarr, { _logger.LogInformation("Adding tag {Tag} for {Title}", tag.Label, seriesSonarr.Title); seriesSonarr.Tags.Add(tag.Id); - updated = true; + return true; } return updated;