├── .dockerignore ├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── Dockerfile ├── Dockerfile.debian-arm64 ├── LICENSE.md ├── Listrr.Tests ├── Helpers │ └── UserManagerHelpers.cs ├── Listrr.Tests.csproj └── Services │ ├── GitHubGraphServiceTests.cs │ └── TraktServiceTests.cs ├── Listrr.sln ├── Listrr ├── API │ └── GitHub │ │ ├── GitHubDonorReponse.cs │ │ ├── Node.cs │ │ ├── Sponsor.cs │ │ ├── SponsorShipAsMaintainer.cs │ │ ├── Tier.cs │ │ └── Viewer.cs ├── Areas │ └── Identity │ │ ├── IdentityHostingStartup.cs │ │ └── Pages │ │ ├── Account │ │ ├── AccessDenied.cshtml │ │ ├── AccessDenied.cshtml.cs │ │ ├── ConfirmEmail.cshtml │ │ ├── ConfirmEmail.cshtml.cs │ │ ├── ConfirmEmailChange.cshtml │ │ ├── ConfirmEmailChange.cshtml.cs │ │ ├── ExternalLogin.cshtml │ │ ├── ExternalLogin.cshtml.cs │ │ ├── ForgotPassword.cshtml │ │ ├── ForgotPassword.cshtml.cs │ │ ├── ForgotPasswordConfirmation.cshtml │ │ ├── ForgotPasswordConfirmation.cshtml.cs │ │ ├── Lockout.cshtml │ │ ├── Lockout.cshtml.cs │ │ ├── Login.cshtml │ │ ├── Login.cshtml.cs │ │ ├── LoginWith2fa.cshtml │ │ ├── LoginWith2fa.cshtml.cs │ │ ├── LoginWithRecoveryCode.cshtml │ │ ├── LoginWithRecoveryCode.cshtml.cs │ │ ├── Logout.cshtml │ │ ├── Logout.cshtml.cs │ │ ├── Manage │ │ │ ├── ChangePassword.cshtml │ │ │ ├── ChangePassword.cshtml.cs │ │ │ ├── DeletePersonalData.cshtml │ │ │ ├── DeletePersonalData.cshtml.cs │ │ │ ├── Disable2fa.cshtml │ │ │ ├── Disable2fa.cshtml.cs │ │ │ ├── DownloadPersonalData.cshtml │ │ │ ├── DownloadPersonalData.cshtml.cs │ │ │ ├── Email.cshtml │ │ │ ├── Email.cshtml.cs │ │ │ ├── EnableAuthenticator.cshtml │ │ │ ├── EnableAuthenticator.cshtml.cs │ │ │ ├── ExternalLogins.cshtml │ │ │ ├── ExternalLogins.cshtml.cs │ │ │ ├── GenerateRecoveryCodes.cshtml │ │ │ ├── GenerateRecoveryCodes.cshtml.cs │ │ │ ├── Index.cshtml │ │ │ ├── Index.cshtml.cs │ │ │ ├── ManageNavPages.cs │ │ │ ├── PersonalData.cshtml │ │ │ ├── PersonalData.cshtml.cs │ │ │ ├── ResetAuthenticator.cshtml │ │ │ ├── ResetAuthenticator.cshtml.cs │ │ │ ├── SetPassword.cshtml │ │ │ ├── SetPassword.cshtml.cs │ │ │ ├── ShowRecoveryCodes.cshtml │ │ │ ├── ShowRecoveryCodes.cshtml.cs │ │ │ ├── TwoFactorAuthentication.cshtml │ │ │ ├── TwoFactorAuthentication.cshtml.cs │ │ │ ├── _Layout.cshtml │ │ │ ├── _ManageNav.cshtml │ │ │ ├── _StatusMessage.cshtml │ │ │ └── _ViewImports.cshtml │ │ ├── Register.cshtml │ │ ├── Register.cshtml.cs │ │ ├── RegisterConfirmation.cshtml │ │ ├── RegisterConfirmation.cshtml.cs │ │ ├── ResetPassword.cshtml │ │ ├── ResetPassword.cshtml.cs │ │ ├── ResetPasswordConfirmation.cshtml │ │ ├── ResetPasswordConfirmation.cshtml.cs │ │ ├── _StatusMessage.cshtml │ │ └── _ViewImports.cshtml │ │ ├── Error.cshtml │ │ ├── Error.cshtml.cs │ │ ├── _ValidationScriptsPartial.cshtml │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml ├── AutoMapper │ └── DefaultProfile.cs ├── Comparer │ ├── TraktMovieComparer.cs │ └── TraktShowComparer.cs ├── Configuration │ ├── DiscordAPIConfiguration.cs │ ├── GithubAPIConfiguration.cs │ ├── HangfireConfiguration.cs │ ├── ILimitConfiguration.cs │ ├── LimitConfiguration.cs │ ├── LimitConfigurationList.cs │ ├── ListPaginationConfiguration.cs │ ├── NotificationConfiguration.cs │ ├── TraktAPIConfiguration.cs │ ├── UserMappingConfiguration.cs │ └── UserMappingConfigurationList.cs ├── Constants.cs ├── Controllers │ ├── DonorController.cs │ ├── HomeController.cs │ └── ListController.cs ├── Data │ ├── AppDbContext.cs │ ├── CountryCode.cs │ ├── CreatedAndUpdated.cs │ ├── DataProtectionDbContext.cs │ ├── IMDb │ │ └── IMDbRating.cs │ ├── LanguageCode.cs │ ├── Trakt │ │ ├── Filters │ │ │ ├── CertificationsMovieFilter.cs │ │ │ ├── CertificationsShowFilter.cs │ │ │ ├── CountriesCommonFilter.cs │ │ │ ├── GenresCommonFilter.cs │ │ │ ├── LanguagesCommonFilter.cs │ │ │ ├── NetworksShowFilter.cs │ │ │ ├── RatingsCommonFilter.cs │ │ │ ├── RuntimesCommonFilter.cs │ │ │ ├── StatusShowFilter.cs │ │ │ ├── TranslationsBasicFilter.cs │ │ │ └── YearsCommonFilter.cs │ │ ├── TraktList.cs │ │ ├── TraktMovieCertification.cs │ │ ├── TraktMovieGenre.cs │ │ ├── TraktShowCertification.cs │ │ ├── TraktShowGenre.cs │ │ ├── TraktShowNetwork.cs │ │ └── TraktShowStatus.cs │ └── User.cs ├── Exceptions │ └── RefreshTokenBadRequestException.cs ├── Extensions │ ├── AddHttpContextAccessorExtension.cs │ └── ListExtensions.cs ├── HangfireActivator.cs ├── Jobs │ ├── BackgroundJobs │ │ ├── IBackgroundJob.cs │ │ ├── ProcessMovieListBackgroundJob.cs │ │ └── ProcessShowListBackgroundJob.cs │ └── RecurringJobs │ │ ├── EnforceListLimitRecurringJob.cs │ │ ├── GetCountryCodesRecurringJob.cs │ │ ├── GetLanguageCodesRecurringJob.cs │ │ ├── GetMovieCertificationsRecurringJob.cs │ │ ├── GetMovieGenresRecurringJob.cs │ │ ├── GetShowCertificationsRecurringJob.cs │ │ ├── GetShowGenresRecurringJob.cs │ │ ├── GetShowNetworksRecurringJob.cs │ │ ├── GetShowStatusRecurringJob.cs │ │ ├── IMDb │ │ └── IMDbRatingsRecurringJob.cs │ │ ├── IRecurringJob.cs │ │ ├── ProcessDonorListsRecurringJob.cs │ │ ├── ProcessUserListsRecurringJob.cs │ │ ├── SetDonorsRecurringJob.cs │ │ └── UpdateAllListsRecurringJob.cs ├── Listrr.csproj ├── Migrations │ ├── 20210121180111_0.Designer.cs │ ├── 20210121180111_0.cs │ ├── 20210209153744_1.Designer.cs │ ├── 20210209153744_1.cs │ ├── AppDbContextModelSnapshot.cs │ └── DataProtectionDb │ │ ├── 20210121180408_Initial.Designer.cs │ │ ├── 20210121180408_Initial.cs │ │ └── DataProtectionDbContextModelSnapshot.cs ├── Models │ ├── CreateListViewModel.cs │ ├── CreateMovieListFileViewModel.cs │ ├── CreateMovieListViewModel.cs │ ├── CreateShowListFileViewModel.cs │ ├── CreateShowListViewModel.cs │ ├── DeleteListViewModel.cs │ ├── EditMovieListFileViewModel.cs │ ├── EditMovieListViewModel.cs │ ├── EditShowListFileViewModel.cs │ ├── EditShowListViewModel.cs │ ├── ErrorViewModel.cs │ ├── MyViewModel.cs │ ├── PaginationViewModel.cs │ └── WhyDonateViewModel.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Repositories │ ├── IIMDbRepository.cs │ ├── IMDbRepository.cs │ ├── ITraktCodeRepository.cs │ ├── ITraktListRepository.cs │ ├── ITraktMovieRepository.cs │ ├── ITraktShowRepository.cs │ ├── IUserRepository.cs │ ├── TraktCodeRepository.cs │ ├── TraktListRepository.cs │ ├── TraktMovieRepository.cs │ ├── TraktShowRepository.cs │ └── UserRepository.cs ├── Services │ ├── BackgroundJobQueueService.cs │ ├── GitHubGraphService.cs │ ├── IBackgroundJobQueueService.cs │ ├── IGitHubGraphService.cs │ ├── ITraktService.cs │ ├── IUserLimitService.cs │ ├── TraktService.cs │ └── UserLimitService.cs ├── Startup.cs ├── Views │ ├── Donor │ │ └── WhyDonate.cshtml │ ├── Home │ │ ├── Index.cshtml │ │ ├── Lists.cshtml │ │ └── Privacy.cshtml │ ├── List │ │ ├── Delete.cshtml │ │ ├── EditMovieList.cshtml │ │ ├── EditMovieListFile.cshtml │ │ ├── EditShowList.cshtml │ │ ├── EditShowListFile.cshtml │ │ ├── MovieList.cshtml │ │ ├── MovieListFile.cshtml │ │ ├── My.cshtml │ │ ├── ShowList.cshtml │ │ └── ShowListFile.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ ├── TraktBadGatewayException.cshtml │ │ ├── TraktServerUnavailableException.cshtml │ │ ├── _CreateByFileExplanationPartial.cshtml │ │ ├── _DonorPerkExplanationPartial.cshtml │ │ ├── _Layout.cshtml │ │ ├── _LimitNotification.cshtml │ │ ├── _LoginPartial.cshtml │ │ ├── _PaginationPartial.cshtml │ │ ├── _PreviewDonorOnlyPartial.cshtml │ │ ├── _QuotePartial.cshtml │ │ ├── _ReverseFiltersExplanationPartial.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml └── wwwroot │ ├── css │ ├── bootstrap.css │ ├── bootstrap.min.css │ ├── fontawesome.css │ ├── fontawesome.min.css │ ├── select2.css │ ├── select2.min.css │ └── site.css │ ├── favicon.ico │ ├── js │ ├── bootstrap.bundle.js │ ├── bootstrap.bundle.js.map │ ├── bootstrap.bundle.min.js │ ├── bootstrap.bundle.min.js.map │ ├── bootstrap.js │ ├── bootstrap.js.map │ ├── bootstrap.min.js │ ├── bootstrap.min.js.map │ ├── select2.js │ ├── select2.min.js │ └── site.js │ ├── lib │ ├── bootstrap │ │ ├── LICENSE │ │ └── dist │ │ │ ├── css │ │ │ ├── bootstrap-grid.css │ │ │ ├── bootstrap-grid.css.map │ │ │ ├── bootstrap-grid.min.css │ │ │ ├── bootstrap-grid.min.css.map │ │ │ ├── bootstrap-reboot.css │ │ │ ├── bootstrap-reboot.css.map │ │ │ ├── bootstrap-reboot.min.css │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ │ └── js │ │ │ ├── bootstrap.bundle.js │ │ │ ├── bootstrap.bundle.js.map │ │ │ ├── bootstrap.bundle.min.js │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.js.map │ │ │ ├── bootstrap.min.js │ │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ │ ├── LICENSE.txt │ │ ├── jquery.validate.unobtrusive.js │ │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ │ ├── LICENSE.md │ │ └── dist │ │ │ ├── additional-methods.js │ │ │ ├── additional-methods.min.js │ │ │ ├── jquery.validate.js │ │ │ └── jquery.validate.min.js │ └── jquery │ │ ├── LICENSE.txt │ │ └── dist │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map │ └── webfonts │ ├── fa-brands-400.eot │ ├── fa-brands-400.svg │ ├── fa-brands-400.ttf │ ├── fa-brands-400.woff │ ├── fa-brands-400.woff2 │ ├── fa-regular-400.eot │ ├── fa-regular-400.svg │ ├── fa-regular-400.ttf │ ├── fa-regular-400.woff │ ├── fa-regular-400.woff2 │ ├── fa-solid-900.eot │ ├── fa-solid-900.svg │ ├── fa-solid-900.ttf │ ├── fa-solid-900.woff │ └── fa-solid-900.woff2 └── README.md /.dockerignore: -------------------------------------------------------------------------------- 1 | .dockerignore 2 | .env 3 | .git 4 | .gitignore 5 | .vs 6 | .vscode 7 | */bin 8 | */obj 9 | **/.toolstarget -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: TheUltimateC0der 2 | patreon: listrrpro 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base 2 | WORKDIR /app 3 | EXPOSE 80 4 | 5 | FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build 6 | WORKDIR /src 7 | COPY ["Listrr/Listrr.csproj", "Listrr/"] 8 | RUN dotnet restore "Listrr/Listrr.csproj" 9 | COPY . . 10 | WORKDIR "/src/Listrr" 11 | RUN dotnet build "Listrr.csproj" -c Release -o /app/build 12 | 13 | FROM build AS publish 14 | RUN dotnet publish "Listrr.csproj" -c Release -o /app/publish 15 | 16 | FROM base AS final 17 | WORKDIR /app 18 | COPY --from=publish /app/publish . 19 | ENTRYPOINT ["dotnet", "Listrr.dll"] -------------------------------------------------------------------------------- /Dockerfile.debian-arm64: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build 2 | WORKDIR /source 3 | EXPOSE 80 4 | 5 | # copy csproj and restore as distinct layers 6 | COPY Listrr/Listrr.csproj . 7 | RUN dotnet restore "Listrr.csproj" -r linux-arm64 8 | 9 | # copy and publish app and libraries 10 | COPY Listrr/ . 11 | RUN dotnet publish -c release -o /app -r linux-arm64 --self-contained false --no-restore 12 | 13 | # final stage/image 14 | FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim-arm64v8 15 | WORKDIR /app 16 | COPY --from=build /app . 17 | ENTRYPOINT ["./dotnetapp"] -------------------------------------------------------------------------------- /Listrr.Tests/Listrr.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Listrr.Tests/Services/GitHubGraphServiceTests.cs: -------------------------------------------------------------------------------- 1 | using Listrr.Configuration; 2 | using Listrr.Services; 3 | 4 | using Moq; 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | 9 | using Xunit; 10 | 11 | namespace Listrr.Tests.Services 12 | { 13 | public class GitHubGraphServiceTests 14 | { 15 | [Theory] 16 | [MemberData(nameof(NullParameterData))] 17 | public void CreateGitHubGraphServiceWithNullParametersThowsNullReferenceException(GithubAPIConfiguration githubAPIConfiguration, LimitConfigurationList limitConfigurationList) 18 | { 19 | // Arrange 20 | // Act 21 | // Assert 22 | Assert.Throws(() => { new GitHubGraphService(githubAPIConfiguration, limitConfigurationList); }); 23 | } 24 | 25 | 26 | 27 | public static IEnumerable NullParameterData() 28 | { 29 | yield return new[] { (object)null, Mock.Of() }; 30 | yield return new[] { Mock.Of(), (object)null }; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Listrr.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29613.14 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Listrr", "Listrr\Listrr.csproj", "{B19F348F-E41B-460C-8514-D50B34226B96}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Listrr.Tests", "Listrr.Tests\Listrr.Tests.csproj", "{D318C1A1-114B-41A7-A3C5-79D5708AE835}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {B19F348F-E41B-460C-8514-D50B34226B96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {B19F348F-E41B-460C-8514-D50B34226B96}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {B19F348F-E41B-460C-8514-D50B34226B96}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {B19F348F-E41B-460C-8514-D50B34226B96}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {D318C1A1-114B-41A7-A3C5-79D5708AE835}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {D318C1A1-114B-41A7-A3C5-79D5708AE835}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {D318C1A1-114B-41A7-A3C5-79D5708AE835}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {D318C1A1-114B-41A7-A3C5-79D5708AE835}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {3615752E-8839-40CA-962B-548CCD2A4AD2} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Listrr/API/GitHub/GitHubDonorReponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Listrr.API.GitHub 4 | { 5 | public class GitHubDonorResponse 6 | { 7 | [JsonProperty("viewer")] 8 | public Viewer Viewer { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /Listrr/API/GitHub/Node.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Listrr.API.GitHub 4 | { 5 | public class Node 6 | { 7 | [JsonProperty("sponsor")] 8 | public Sponsor Sponsor { get; set; } 9 | 10 | [JsonProperty("tier")] 11 | public Tier Tier { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Listrr/API/GitHub/Sponsor.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Listrr.API.GitHub 4 | { 5 | public class Sponsor 6 | { 7 | [JsonProperty("login")] 8 | public string Login { get; set; } 9 | 10 | [JsonProperty("databaseId")] 11 | public long DatabaseId { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Listrr/API/GitHub/SponsorShipAsMaintainer.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Listrr.API.GitHub 4 | { 5 | public class SponsorshipsAsMaintainer 6 | { 7 | [JsonProperty("nodes")] 8 | public Node[] Nodes { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /Listrr/API/GitHub/Tier.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Listrr.API.GitHub 4 | { 5 | public class Tier 6 | { 7 | [JsonProperty("monthlyPriceInDollars")] 8 | public long MonthlyPriceInDollars { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /Listrr/API/GitHub/Viewer.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Listrr.API.GitHub 4 | { 5 | public class Viewer 6 | { 7 | [JsonProperty("sponsorshipsAsMaintainer")] 8 | public SponsorshipsAsMaintainer SponsorshipsAsMaintainer { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/IdentityHostingStartup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | 3 | [assembly: HostingStartup(typeof(Listrr.Areas.Identity.IdentityHostingStartup))] 4 | namespace Listrr.Areas.Identity 5 | { 6 | public class IdentityHostingStartup : IHostingStartup 7 | { 8 | public void Configure(IWebHostBuilder builder) 9 | { 10 | builder.ConfigureServices((context, services) => { 11 | }); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/AccessDenied.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AccessDeniedModel 3 | @{ 4 | ViewData["Title"] = "Access denied"; 5 | } 6 | 7 |
8 |

@ViewData["Title"]

9 |

You do not have access to this resource.

10 |
11 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.RazorPages; 2 | 3 | namespace Listrr.Areas.Identity.Pages.Account 4 | { 5 | public class AccessDeniedModel : PageModel 6 | { 7 | public void OnGet() 8 | { 9 | 10 | } 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/ConfirmEmail.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ConfirmEmailModel 3 | @{ 4 | ViewData["Title"] = "Confirm email"; 5 | } 6 | 7 |

@ViewData["Title"]

8 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Threading.Tasks; 3 | 4 | using Listrr.Data; 5 | 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Identity; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.AspNetCore.Mvc.RazorPages; 10 | using Microsoft.AspNetCore.WebUtilities; 11 | 12 | namespace Listrr.Areas.Identity.Pages.Account 13 | { 14 | [AllowAnonymous] 15 | public class ConfirmEmailModel : PageModel 16 | { 17 | private readonly UserManager _userManager; 18 | 19 | public ConfirmEmailModel(UserManager userManager) 20 | { 21 | _userManager = userManager; 22 | } 23 | 24 | [TempData] 25 | public string StatusMessage { get; set; } 26 | 27 | public async Task OnGetAsync(string userId, string code) 28 | { 29 | return RedirectToAction("Index", "Home", new { Area = "" }); 30 | 31 | if (userId == null || code == null) 32 | { 33 | return RedirectToPage("/Index"); 34 | } 35 | 36 | var user = await _userManager.FindByIdAsync(userId); 37 | if (user == null) 38 | { 39 | return NotFound($"Unable to load user with ID '{userId}'."); 40 | } 41 | 42 | code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)); 43 | var result = await _userManager.ConfirmEmailAsync(user, code); 44 | StatusMessage = result.Succeeded ? "Thank you for confirming your email." : "Error confirming your email."; 45 | return Page(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ConfirmEmailChangeModel 3 | @{ 4 | ViewData["Title"] = "Confirm email change"; 5 | } 6 | 7 |

@ViewData["Title"]

8 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Threading.Tasks; 3 | 4 | using Listrr.Data; 5 | 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Identity; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.AspNetCore.Mvc.RazorPages; 10 | using Microsoft.AspNetCore.WebUtilities; 11 | 12 | namespace Listrr.Areas.Identity.Pages.Account 13 | { 14 | [AllowAnonymous] 15 | public class ConfirmEmailChangeModel : PageModel 16 | { 17 | private readonly UserManager _userManager; 18 | private readonly SignInManager _signInManager; 19 | 20 | public ConfirmEmailChangeModel(UserManager userManager, SignInManager signInManager) 21 | { 22 | _userManager = userManager; 23 | _signInManager = signInManager; 24 | } 25 | 26 | [TempData] 27 | public string StatusMessage { get; set; } 28 | 29 | public async Task OnGetAsync(string userId, string email, string code) 30 | { 31 | return RedirectToAction("Index", "Home", new { Area = "" }); 32 | 33 | if (userId == null || email == null || code == null) 34 | { 35 | return RedirectToPage("/Index"); 36 | } 37 | 38 | var user = await _userManager.FindByIdAsync(userId); 39 | if (user == null) 40 | { 41 | return NotFound($"Unable to load user with ID '{userId}'."); 42 | } 43 | 44 | code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)); 45 | var result = await _userManager.ChangeEmailAsync(user, email, code); 46 | if (!result.Succeeded) 47 | { 48 | StatusMessage = "Error changing email."; 49 | return Page(); 50 | } 51 | 52 | // In our UI email and user name are one and the same, so when we update the email 53 | // we need to update the user name. 54 | var setUserNameResult = await _userManager.SetUserNameAsync(user, email); 55 | if (!setUserNameResult.Succeeded) 56 | { 57 | StatusMessage = "Error changing user name."; 58 | return Page(); 59 | } 60 | 61 | await _signInManager.RefreshSignInAsync(user); 62 | StatusMessage = "Thank you for confirming your email change."; 63 | return Page(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/ExternalLogin.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ExternalLoginModel 3 | @{ 4 | ViewData["Title"] = "Register"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

Associate your @Model.LoginProvider account.

9 |
10 | 11 |

12 | You've successfully authenticated with @Model.LoginProvider. 13 | Please enter an email address for this site below and click the Register button to finish 14 | logging in. 15 |

16 | 17 |
18 |
19 |
20 |
21 |
22 | 23 | 24 | 25 |
26 | 27 |
28 |
29 |
30 | 31 | @section Scripts { 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/ForgotPassword.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ForgotPasswordModel 3 | @{ 4 | ViewData["Title"] = "Forgot your password?"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

Enter your email.

9 |
10 |
11 |
12 |
13 |
14 |
15 | 16 | 17 | 18 |
19 | 20 |
21 |
22 |
23 | 24 | @section Scripts { 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.Text; 3 | using System.Text.Encodings.Web; 4 | using System.Threading.Tasks; 5 | 6 | using Listrr.Data; 7 | 8 | using Microsoft.AspNetCore.Authorization; 9 | using Microsoft.AspNetCore.Identity; 10 | using Microsoft.AspNetCore.Identity.UI.Services; 11 | using Microsoft.AspNetCore.Mvc; 12 | using Microsoft.AspNetCore.Mvc.RazorPages; 13 | using Microsoft.AspNetCore.WebUtilities; 14 | 15 | namespace Listrr.Areas.Identity.Pages.Account 16 | { 17 | [AllowAnonymous] 18 | public class ForgotPasswordModel : PageModel 19 | { 20 | private readonly UserManager _userManager; 21 | private readonly IEmailSender _emailSender; 22 | 23 | public ForgotPasswordModel(UserManager userManager, IEmailSender emailSender) 24 | { 25 | _userManager = userManager; 26 | _emailSender = emailSender; 27 | } 28 | 29 | [BindProperty] 30 | public InputModel Input { get; set; } 31 | 32 | public class InputModel 33 | { 34 | [Required] 35 | [EmailAddress] 36 | public string Email { get; set; } 37 | } 38 | 39 | public async Task OnPostAsync() 40 | { 41 | return RedirectToAction("Index", "Home", new { Area = "" }); 42 | 43 | if (ModelState.IsValid) 44 | { 45 | var user = await _userManager.FindByEmailAsync(Input.Email); 46 | if (user == null || !(await _userManager.IsEmailConfirmedAsync(user))) 47 | { 48 | // Don't reveal that the user does not exist or is not confirmed 49 | return RedirectToPage("./ForgotPasswordConfirmation"); 50 | } 51 | 52 | // For more information on how to enable account confirmation and password reset please 53 | // visit https://go.microsoft.com/fwlink/?LinkID=532713 54 | var code = await _userManager.GeneratePasswordResetTokenAsync(user); 55 | code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); 56 | var callbackUrl = Url.Page( 57 | "/Account/ResetPassword", 58 | pageHandler: null, 59 | values: new { area = "Identity", code }, 60 | protocol: Request.Scheme); 61 | 62 | await _emailSender.SendEmailAsync( 63 | Input.Email, 64 | "Reset Password", 65 | $"Please reset your password by clicking here."); 66 | 67 | return RedirectToPage("./ForgotPasswordConfirmation"); 68 | } 69 | 70 | return Page(); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ForgotPasswordConfirmation 3 | @{ 4 | ViewData["Title"] = "Forgot password confirmation"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

9 | Please check your email to reset your password. 10 |

11 | 12 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.AspNetCore.Mvc.RazorPages; 4 | 5 | namespace Listrr.Areas.Identity.Pages.Account 6 | { 7 | [AllowAnonymous] 8 | public class ForgotPasswordConfirmation : PageModel 9 | { 10 | public IActionResult OnGet() 11 | { 12 | 13 | return RedirectToAction("Index", "Home", new { Area = "" }); 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Lockout.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model LockoutModel 3 | @{ 4 | ViewData["Title"] = "Locked out"; 5 | } 6 | 7 |
8 |

@ViewData["Title"]

9 |

This account has been locked out, please try again later.

10 |
11 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Lockout.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.AspNetCore.Mvc.RazorPages; 4 | 5 | namespace Listrr.Areas.Identity.Pages.Account 6 | { 7 | [AllowAnonymous] 8 | public class LockoutModel : PageModel 9 | { 10 | public IActionResult OnGet() 11 | { 12 | return RedirectToAction("Index", "Home", new { Area = "" }); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Login.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model LoginModel -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/LoginWith2fa.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model LoginWith2faModel 3 | @{ 4 | ViewData["Title"] = "Two-factor authentication"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |
9 |

Your login is protected with an authenticator app. Enter your authenticator code below.

10 |
11 |
12 |
13 | 14 |
15 |
16 | 17 | 18 | 19 |
20 |
21 |
22 | 26 |
27 |
28 |
29 | 30 |
31 |
32 |
33 |
34 |

35 | Don't have access to your authenticator device? You can 36 | log in with a recovery code. 37 |

38 | 39 | @section Scripts { 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model LoginWithRecoveryCodeModel 3 | @{ 4 | ViewData["Title"] = "Recovery code verification"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |
9 |

10 | You have requested to log in with a recovery code. This login will not be remembered until you provide 11 | an authenticator app code at log in or disable 2FA and log in again. 12 |

13 |
14 |
15 |
16 |
17 |
18 | 19 | 20 | 21 |
22 | 23 |
24 |
25 |
26 | 27 | @section Scripts { 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Logout.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model LogoutModel 3 | @{ 4 | ViewData["Title"] = "Log out"; 5 | } 6 | 7 |
8 |

@ViewData["Title"]

9 |

You have successfully logged out of the application.

10 |
-------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Logout.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | using Listrr.Data; 4 | 5 | using Microsoft.AspNetCore.Authorization; 6 | using Microsoft.AspNetCore.Identity; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.AspNetCore.Mvc.RazorPages; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace Listrr.Areas.Identity.Pages.Account 12 | { 13 | [AllowAnonymous] 14 | public class LogoutModel : PageModel 15 | { 16 | private readonly SignInManager _signInManager; 17 | private readonly ILogger _logger; 18 | 19 | public LogoutModel(SignInManager signInManager, ILogger logger) 20 | { 21 | _signInManager = signInManager; 22 | _logger = logger; 23 | } 24 | 25 | public void OnGet() 26 | { 27 | } 28 | 29 | public async Task OnPost(string returnUrl = null) 30 | { 31 | await _signInManager.SignOutAsync(); 32 | _logger.LogInformation("User logged out."); 33 | if (returnUrl != null) 34 | { 35 | return LocalRedirect(returnUrl); 36 | } 37 | else 38 | { 39 | return RedirectToPage(); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ChangePasswordModel 3 | @{ 4 | ViewData["Title"] = "Change password"; 5 | ViewData["ActivePage"] = ManageNavPages.ChangePassword; 6 | } 7 | 8 |

@ViewData["Title"]

9 | 10 |
11 |
12 |
13 |
14 |
15 | 16 | 17 | 18 |
19 |
20 | 21 | 22 | 23 |
24 |
25 | 26 | 27 | 28 |
29 | 30 |
31 |
32 |
33 | 34 | @section Scripts { 35 | 36 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model DeletePersonalDataModel 3 | @{ 4 | ViewData["Title"] = "Delete Personal Data"; 5 | ViewData["ActivePage"] = ManageNavPages.PersonalData; 6 | } 7 | 8 |

@ViewData["Title"]

9 | 10 | 15 | 16 |
17 |
18 |
19 | @if (Model.RequirePassword) 20 | { 21 |
22 | 23 | 24 | 25 |
26 | } 27 | 28 |
29 |
30 | 31 | @section Scripts { 32 | 33 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Threading.Tasks; 4 | 5 | using Listrr.Data; 6 | 7 | using Microsoft.AspNetCore.Identity; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.AspNetCore.Mvc.RazorPages; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace Listrr.Areas.Identity.Pages.Account.Manage 13 | { 14 | public class DeletePersonalDataModel : PageModel 15 | { 16 | private readonly UserManager _userManager; 17 | private readonly SignInManager _signInManager; 18 | private readonly ILogger _logger; 19 | 20 | public DeletePersonalDataModel( 21 | UserManager userManager, 22 | SignInManager signInManager, 23 | ILogger logger) 24 | { 25 | _userManager = userManager; 26 | _signInManager = signInManager; 27 | _logger = logger; 28 | } 29 | 30 | [BindProperty] 31 | public InputModel Input { get; set; } 32 | 33 | public class InputModel 34 | { 35 | [Required] 36 | [DataType(DataType.Password)] 37 | public string Password { get; set; } 38 | } 39 | 40 | public bool RequirePassword { get; set; } 41 | 42 | public async Task OnGet() 43 | { 44 | var user = await _userManager.GetUserAsync(User); 45 | if (user == null) 46 | { 47 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 48 | } 49 | 50 | RequirePassword = await _userManager.HasPasswordAsync(user); 51 | return Page(); 52 | } 53 | 54 | public async Task OnPostAsync() 55 | { 56 | var user = await _userManager.GetUserAsync(User); 57 | if (user == null) 58 | { 59 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 60 | } 61 | 62 | RequirePassword = await _userManager.HasPasswordAsync(user); 63 | if (RequirePassword) 64 | { 65 | if (!await _userManager.CheckPasswordAsync(user, Input.Password)) 66 | { 67 | ModelState.AddModelError(string.Empty, "Incorrect password."); 68 | return Page(); 69 | } 70 | } 71 | 72 | var result = await _userManager.DeleteAsync(user); 73 | var userId = await _userManager.GetUserIdAsync(user); 74 | if (!result.Succeeded) 75 | { 76 | throw new InvalidOperationException($"Unexpected error occurred deleting user with ID '{userId}'."); 77 | } 78 | 79 | await _signInManager.SignOutAsync(); 80 | 81 | _logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId); 82 | 83 | return Redirect("~/"); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Disable2faModel 3 | @{ 4 | ViewData["Title"] = "Disable two-factor authentication (2FA)"; 5 | ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; 6 | } 7 | 8 | 9 |

@ViewData["Title"]

10 | 11 | 20 | 21 |
22 |
23 | 24 |
25 |
26 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | using Listrr.Data; 5 | 6 | using Microsoft.AspNetCore.Identity; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.AspNetCore.Mvc.RazorPages; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace Listrr.Areas.Identity.Pages.Account.Manage 12 | { 13 | public class Disable2faModel : PageModel 14 | { 15 | private readonly UserManager _userManager; 16 | private readonly ILogger _logger; 17 | 18 | public Disable2faModel( 19 | UserManager userManager, 20 | ILogger logger) 21 | { 22 | _userManager = userManager; 23 | _logger = logger; 24 | } 25 | 26 | [TempData] 27 | public string StatusMessage { get; set; } 28 | 29 | public async Task OnGet() 30 | { 31 | return RedirectToAction("Index", "Home", new { Area = "" }); 32 | 33 | var user = await _userManager.GetUserAsync(User); 34 | if (user == null) 35 | { 36 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 37 | } 38 | 39 | if (!await _userManager.GetTwoFactorEnabledAsync(user)) 40 | { 41 | throw new InvalidOperationException($"Cannot disable 2FA for user with ID '{_userManager.GetUserId(User)}' as it's not currently enabled."); 42 | } 43 | 44 | return Page(); 45 | } 46 | 47 | public async Task OnPostAsync() 48 | { 49 | return RedirectToAction("Index", "Home", new { Area = "" }); 50 | 51 | var user = await _userManager.GetUserAsync(User); 52 | if (user == null) 53 | { 54 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 55 | } 56 | 57 | var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false); 58 | if (!disable2faResult.Succeeded) 59 | { 60 | throw new InvalidOperationException($"Unexpected error occurred disabling 2FA for user with ID '{_userManager.GetUserId(User)}'."); 61 | } 62 | 63 | _logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User)); 64 | StatusMessage = "2fa has been disabled. You can reenable 2fa when you setup an authenticator app"; 65 | return RedirectToPage("./TwoFactorAuthentication"); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model DownloadPersonalDataModel 3 | @{ 4 | ViewData["Title"] = "Download Your Data"; 5 | ViewData["ActivePage"] = ManageNavPages.PersonalData; 6 | } 7 | 8 |

@ViewData["Title"]

9 | 10 | @section Scripts { 11 | 12 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using Listrr.Data; 8 | 9 | using Microsoft.AspNetCore.Identity; 10 | using Microsoft.AspNetCore.Mvc; 11 | using Microsoft.AspNetCore.Mvc.RazorPages; 12 | using Microsoft.Extensions.Logging; 13 | 14 | using Newtonsoft.Json; 15 | 16 | namespace Listrr.Areas.Identity.Pages.Account.Manage 17 | { 18 | public class DownloadPersonalDataModel : PageModel 19 | { 20 | private readonly UserManager _userManager; 21 | private readonly ILogger _logger; 22 | 23 | public DownloadPersonalDataModel( 24 | UserManager userManager, 25 | ILogger logger) 26 | { 27 | _userManager = userManager; 28 | _logger = logger; 29 | } 30 | 31 | public async Task OnPostAsync() 32 | { 33 | var user = await _userManager.GetUserAsync(User); 34 | if (user == null) 35 | { 36 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 37 | } 38 | 39 | _logger.LogInformation("User with ID '{UserId}' asked for their personal data.", _userManager.GetUserId(User)); 40 | 41 | // Only include personal data for download 42 | var personalData = new Dictionary(); 43 | var personalDataProps = typeof(IdentityUser).GetProperties().Where( 44 | prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute))); 45 | foreach (var p in personalDataProps) 46 | { 47 | personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null"); 48 | } 49 | 50 | Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json"); 51 | return new FileContentResult(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(personalData)), "text/json"); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/Email.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model EmailModel 3 | @{ 4 | ViewData["Title"] = "Manage Email"; 5 | ViewData["ActivePage"] = ManageNavPages.Email; 6 | } 7 | 8 |

@ViewData["Title"]

9 | 10 |
11 |
12 |
13 |
14 |
15 | 16 | @if (Model.IsEmailConfirmed) 17 | { 18 |
19 | 20 |
21 | 22 |
23 |
24 | } 25 | else 26 | { 27 | 28 | 29 | } 30 |
31 |
32 | 33 | 34 | 35 |
36 | 37 |
38 |
39 |
40 | 41 | @section Scripts { 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model EnableAuthenticatorModel 3 | @{ 4 | ViewData["Title"] = "Configure authenticator app"; 5 | ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; 6 | } 7 | 8 | 9 |

@ViewData["Title"]

10 |
11 |

To use an authenticator app go through the following steps:

12 |
    13 |
  1. 14 |

    15 | Download a two-factor authenticator app like Microsoft Authenticator for 16 | Windows Phone, 17 | Android and 18 | iOS or 19 | Google Authenticator for 20 | Android and 21 | iOS. 22 |

    23 |
  2. 24 |
  3. 25 |

    Scan the QR Code or enter this key @Model.SharedKey into your two factor authenticator app. Spaces and casing do not matter.

    26 | 27 |
    28 |
    29 |
  4. 30 |
  5. 31 |

    32 | Once you have scanned the QR code or input the key above, your two factor authentication app will provide you 33 | with a unique code. Enter the code in the confirmation box below. 34 |

    35 |
    36 |
    37 |
    38 |
    39 | 40 | 41 | 42 |
    43 | 44 |
    45 |
    46 |
    47 |
    48 |
  6. 49 |
50 |
51 | 52 | @section Scripts { 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ExternalLoginsModel 3 | @{ 4 | ViewData["Title"] = "Manage your external logins"; 5 | ViewData["ActivePage"] = ManageNavPages.ExternalLogins; 6 | } 7 | 8 | 9 | @if (Model.CurrentLogins?.Count > 0) 10 | { 11 |

Registered Logins

12 | 13 | 14 | @foreach (var login in Model.CurrentLogins) 15 | { 16 | 17 | 18 | 34 | 35 | } 36 | 37 |
@login.ProviderDisplayName 19 | @if (Model.ShowRemoveButton) 20 | { 21 |
22 |
23 | 24 | 25 | 26 |
27 |
28 | } 29 | else 30 | { 31 | @:   32 | } 33 |
38 | } 39 | @if (Model.OtherLogins?.Count > 0) 40 | { 41 |

Add another service to log in.

42 |
43 | 53 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model GenerateRecoveryCodesModel 3 | @{ 4 | ViewData["Title"] = "Generate two-factor authentication (2FA) recovery codes"; 5 | ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; 6 | } 7 | 8 | 9 |

@ViewData["Title"]

10 | 23 |
24 |
25 | 26 |
27 |
-------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | using Listrr.Data; 6 | 7 | using Microsoft.AspNetCore.Identity; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.AspNetCore.Mvc.RazorPages; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace Listrr.Areas.Identity.Pages.Account.Manage 13 | { 14 | public class GenerateRecoveryCodesModel : PageModel 15 | { 16 | private readonly UserManager _userManager; 17 | private readonly ILogger _logger; 18 | 19 | public GenerateRecoveryCodesModel( 20 | UserManager userManager, 21 | ILogger logger) 22 | { 23 | _userManager = userManager; 24 | _logger = logger; 25 | } 26 | 27 | [TempData] 28 | public string[] RecoveryCodes { get; set; } 29 | 30 | [TempData] 31 | public string StatusMessage { get; set; } 32 | 33 | public async Task OnGetAsync() 34 | { 35 | return RedirectToAction("Index", "Home", new { Area = "" }); 36 | 37 | var user = await _userManager.GetUserAsync(User); 38 | if (user == null) 39 | { 40 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 41 | } 42 | 43 | var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user); 44 | if (!isTwoFactorEnabled) 45 | { 46 | var userId = await _userManager.GetUserIdAsync(user); 47 | throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' because they do not have 2FA enabled."); 48 | } 49 | 50 | return Page(); 51 | } 52 | 53 | public async Task OnPostAsync() 54 | { 55 | return RedirectToAction("Index", "Home", new { Area = "" }); 56 | 57 | var user = await _userManager.GetUserAsync(User); 58 | if (user == null) 59 | { 60 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 61 | } 62 | 63 | var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user); 64 | var userId = await _userManager.GetUserIdAsync(user); 65 | if (!isTwoFactorEnabled) 66 | { 67 | throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' as they do not have 2FA enabled."); 68 | } 69 | 70 | var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); 71 | RecoveryCodes = recoveryCodes.ToArray(); 72 | 73 | _logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", userId); 74 | StatusMessage = "You have generated new recovery codes."; 75 | return RedirectToPage("./ShowRecoveryCodes"); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model IndexModel 3 | @{ 4 | ViewData["Title"] = "Profile"; 5 | ViewData["ActivePage"] = ManageNavPages.Index; 6 | } 7 | 8 |

@ViewData["Title"]

9 | 10 |
11 |
12 |
13 |
14 |
15 | 16 | 17 |
18 | @*
19 | 20 | 21 | 22 |
23 | *@ 24 |
25 |
26 |
27 | 28 | @section Scripts { 29 | 30 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Microsoft.AspNetCore.Mvc.Rendering; 4 | 5 | namespace Listrr.Areas.Identity.Pages.Account.Manage 6 | { 7 | public static class ManageNavPages 8 | { 9 | public static string Index => "Index"; 10 | 11 | public static string Email => "Email"; 12 | 13 | public static string ChangePassword => "ChangePassword"; 14 | 15 | public static string ExternalLogins => "ExternalLogins"; 16 | 17 | public static string PersonalData => "PersonalData"; 18 | 19 | public static string TwoFactorAuthentication => "TwoFactorAuthentication"; 20 | 21 | public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index); 22 | 23 | public static string EmailNavClass(ViewContext viewContext) => PageNavClass(viewContext, Email); 24 | 25 | public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword); 26 | 27 | public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins); 28 | 29 | public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData); 30 | 31 | public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication); 32 | 33 | private static string PageNavClass(ViewContext viewContext, string page) 34 | { 35 | var activePage = viewContext.ViewData["ActivePage"] as string 36 | ?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName); 37 | return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model PersonalDataModel 3 | @{ 4 | ViewData["Title"] = "Personal Data"; 5 | ViewData["ActivePage"] = ManageNavPages.PersonalData; 6 | } 7 | 8 |

@ViewData["Title"]

9 | 10 |
11 |
12 |

Your account contains personal data that you have given us. This page allows you to download or delete that data.

13 |

14 | Deleting this data will permanently remove your account, and this cannot be recovered. 15 |

16 |
17 | 18 |
19 |

20 | Delete 21 |

22 |
23 |
24 | 25 | @section Scripts { 26 | 27 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | using Listrr.Data; 4 | 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace Listrr.Areas.Identity.Pages.Account.Manage 11 | { 12 | public class PersonalDataModel : PageModel 13 | { 14 | private readonly UserManager _userManager; 15 | private readonly ILogger _logger; 16 | 17 | public PersonalDataModel( 18 | UserManager userManager, 19 | ILogger logger) 20 | { 21 | _userManager = userManager; 22 | _logger = logger; 23 | } 24 | 25 | public async Task OnGet() 26 | { 27 | var user = await _userManager.GetUserAsync(User); 28 | if (user == null) 29 | { 30 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 31 | } 32 | 33 | return Page(); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ResetAuthenticatorModel 3 | @{ 4 | ViewData["Title"] = "Reset authenticator key"; 5 | ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; 6 | } 7 | 8 | 9 |

@ViewData["Title"]

10 | 20 |
21 |
22 | 23 |
24 |
-------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | using Listrr.Data; 4 | 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace Listrr.Areas.Identity.Pages.Account.Manage 11 | { 12 | public class ResetAuthenticatorModel : PageModel 13 | { 14 | UserManager _userManager; 15 | private readonly SignInManager _signInManager; 16 | ILogger _logger; 17 | 18 | public ResetAuthenticatorModel( 19 | UserManager userManager, 20 | SignInManager signInManager, 21 | ILogger logger) 22 | { 23 | _userManager = userManager; 24 | _signInManager = signInManager; 25 | _logger = logger; 26 | } 27 | 28 | [TempData] 29 | public string StatusMessage { get; set; } 30 | 31 | public async Task OnGet() 32 | { 33 | return RedirectToAction("Index", "Home", new { Area = "" }); 34 | 35 | var user = await _userManager.GetUserAsync(User); 36 | if (user == null) 37 | { 38 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 39 | } 40 | 41 | return Page(); 42 | } 43 | 44 | public async Task OnPostAsync() 45 | { 46 | return RedirectToAction("Index", "Home", new { Area = "" }); 47 | 48 | var user = await _userManager.GetUserAsync(User); 49 | if (user == null) 50 | { 51 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 52 | } 53 | 54 | await _userManager.SetTwoFactorEnabledAsync(user, false); 55 | await _userManager.ResetAuthenticatorKeyAsync(user); 56 | _logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", user.Id); 57 | 58 | await _signInManager.RefreshSignInAsync(user); 59 | StatusMessage = "Your authenticator app key has been reset, you will need to configure your authenticator app using the new key."; 60 | 61 | return RedirectToPage("./EnableAuthenticator"); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model SetPasswordModel 3 | @{ 4 | ViewData["Title"] = "Set password"; 5 | ViewData["ActivePage"] = ManageNavPages.ChangePassword; 6 | } 7 | 8 |

Set your password

9 | 10 |

11 | You do not have a local username/password for this site. Add a local 12 | account so you can log in without an external login. 13 |

14 |
15 |
16 |
17 |
18 |
19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | 27 |
28 | 29 |
30 |
31 |
32 | 33 | @section Scripts { 34 | 35 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ShowRecoveryCodesModel 3 | @{ 4 | ViewData["Title"] = "Recovery codes"; 5 | ViewData["ActivePage"] = "TwoFactorAuthentication"; 6 | } 7 | 8 | 9 |

@ViewData["Title"]

10 | 18 |
19 |
20 | @for (var row = 0; row < Model.RecoveryCodes.Length; row += 2) 21 | { 22 | @Model.RecoveryCodes[row] @Model.RecoveryCodes[row + 1]
23 | } 24 |
25 |
-------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | 4 | namespace Listrr.Areas.Identity.Pages.Account.Manage 5 | { 6 | public class ShowRecoveryCodesModel : PageModel 7 | { 8 | [TempData] 9 | public string[] RecoveryCodes { get; set; } 10 | 11 | [TempData] 12 | public string StatusMessage { get; set; } 13 | 14 | public IActionResult OnGet() 15 | { 16 | return RedirectToAction("Index", "Home", new { Area = "" }); 17 | 18 | if (RecoveryCodes == null || RecoveryCodes.Length == 0) 19 | { 20 | return RedirectToPage("./TwoFactorAuthentication"); 21 | } 22 | 23 | return Page(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model TwoFactorAuthenticationModel 3 | @{ 4 | ViewData["Title"] = "Two-factor authentication (2FA)"; 5 | ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; 6 | } 7 | 8 | 9 |

@ViewData["Title"]

10 | @if (Model.Is2faEnabled) 11 | { 12 | if (Model.RecoveryCodesLeft == 0) 13 | { 14 |
15 | You have no recovery codes left. 16 |

You must generate a new set of recovery codes before you can log in with a recovery code.

17 |
18 | } 19 | else if (Model.RecoveryCodesLeft == 1) 20 | { 21 |
22 | You have 1 recovery code left. 23 |

You can generate a new set of recovery codes.

24 |
25 | } 26 | else if (Model.RecoveryCodesLeft <= 3) 27 | { 28 |
29 | You have @Model.RecoveryCodesLeft recovery codes left. 30 |

You should generate a new set of recovery codes.

31 |
32 | } 33 | 34 | if (Model.IsMachineRemembered) 35 | { 36 |
37 | 38 |
39 | } 40 | Disable 2FA 41 | Reset recovery codes 42 | } 43 | 44 |
Authenticator app
45 | @if (!Model.HasAuthenticator) 46 | { 47 | Add authenticator app 48 | } 49 | else 50 | { 51 | Setup authenticator app 52 | Reset authenticator app 53 | } 54 | 55 | @section Scripts { 56 | 57 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | using Listrr.Data; 4 | 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace Listrr.Areas.Identity.Pages.Account.Manage 11 | { 12 | public class TwoFactorAuthenticationModel : PageModel 13 | { 14 | private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}"; 15 | 16 | private readonly UserManager _userManager; 17 | private readonly SignInManager _signInManager; 18 | private readonly ILogger _logger; 19 | 20 | public TwoFactorAuthenticationModel( 21 | UserManager userManager, 22 | SignInManager signInManager, 23 | ILogger logger) 24 | { 25 | _userManager = userManager; 26 | _signInManager = signInManager; 27 | _logger = logger; 28 | } 29 | 30 | public bool HasAuthenticator { get; set; } 31 | 32 | public int RecoveryCodesLeft { get; set; } 33 | 34 | [BindProperty] 35 | public bool Is2faEnabled { get; set; } 36 | 37 | public bool IsMachineRemembered { get; set; } 38 | 39 | [TempData] 40 | public string StatusMessage { get; set; } 41 | 42 | public async Task OnGet() 43 | { 44 | return RedirectToAction("Index", "Home", new { Area = "" }); 45 | 46 | var user = await _userManager.GetUserAsync(User); 47 | if (user == null) 48 | { 49 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 50 | } 51 | 52 | HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null; 53 | Is2faEnabled = await _userManager.GetTwoFactorEnabledAsync(user); 54 | IsMachineRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user); 55 | RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user); 56 | 57 | return Page(); 58 | } 59 | 60 | public async Task OnPost() 61 | { 62 | return RedirectToAction("Index", "Home", new { Area = "" }); 63 | 64 | var user = await _userManager.GetUserAsync(User); 65 | if (user == null) 66 | { 67 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); 68 | } 69 | 70 | await _signInManager.ForgetTwoFactorClientAsync(); 71 | StatusMessage = "The current browser has been forgotten. When you login again from this browser you will be prompted for your 2fa code."; 72 | return RedirectToPage(); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "/Areas/Identity/Pages/_Layout.cshtml"; 3 | } 4 | 5 |

Manage your account

6 | 7 |
8 |

Change your account settings

9 |
10 |
11 |
12 | 13 |
14 |
15 | @RenderBody() 16 |
17 |
18 |
19 | 20 | @section Scripts { 21 | @RenderSection("Scripts", required: false) 22 | } 23 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml: -------------------------------------------------------------------------------- 1 | @model string 2 | 3 | @if (!String.IsNullOrEmpty(Model)) 4 | { 5 | var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; 6 | 10 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Listrr.Areas.Identity.Pages.Account.Manage 2 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/Register.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model RegisterModel 3 | @{ 4 | ViewData["Title"] = "Register"; 5 | } 6 | 7 |

@ViewData["Title"]

8 | 9 |
10 |
11 |
12 |

Create a new account.

13 |
14 |
15 |
16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | 29 |
30 | 31 |
32 |
33 |
34 |
35 |

Use another service to register.

36 |
37 | @{ 38 | if ((Model.ExternalLogins?.Count ?? 0) == 0) 39 | { 40 |
41 |

42 | There are no external authentication services configured. See this article 43 | for details on setting up this ASP.NET application to support logging in via external services. 44 |

45 |
46 | } 47 | else 48 | { 49 |
50 |
51 |

52 | @foreach (var provider in Model.ExternalLogins) 53 | { 54 | 55 | } 56 |

57 |
58 |
59 | } 60 | } 61 |
62 |
63 |
64 | 65 | @section Scripts { 66 | 67 | } 68 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model RegisterConfirmationModel 3 | @{ 4 | ViewData["Title"] = "Register confirmation"; 5 | } 6 | 7 |

@ViewData["Title"]

8 | @{ 9 | if (@Model.DisplayConfirmAccountLink) 10 | { 11 |

12 | This app does not currently have a real email sender registered, see these docs for how to configure a real email sender. 13 | Normally this would be emailed: Click here to confirm your account 14 |

15 | } 16 | else 17 | { 18 |

19 | Please check your email to confirm your account. 20 |

21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Threading.Tasks; 3 | 4 | using Listrr.Data; 5 | 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Identity; 8 | using Microsoft.AspNetCore.Identity.UI.Services; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Microsoft.AspNetCore.Mvc.RazorPages; 11 | using Microsoft.AspNetCore.WebUtilities; 12 | 13 | namespace Listrr.Areas.Identity.Pages.Account 14 | { 15 | [AllowAnonymous] 16 | public class RegisterConfirmationModel : PageModel 17 | { 18 | private readonly UserManager _userManager; 19 | private readonly IEmailSender _sender; 20 | 21 | public RegisterConfirmationModel(UserManager userManager, IEmailSender sender) 22 | { 23 | _userManager = userManager; 24 | _sender = sender; 25 | } 26 | 27 | public string Email { get; set; } 28 | 29 | public bool DisplayConfirmAccountLink { get; set; } 30 | 31 | public string EmailConfirmationUrl { get; set; } 32 | 33 | public async Task OnGetAsync(string email) 34 | { 35 | return RedirectToAction("Index", "Home", new { Area = "" }); 36 | 37 | if (email == null) 38 | { 39 | return RedirectToPage("/Index"); 40 | } 41 | 42 | var user = await _userManager.FindByEmailAsync(email); 43 | if (user == null) 44 | { 45 | return NotFound($"Unable to load user with email '{email}'."); 46 | } 47 | 48 | Email = email; 49 | // Once you add a real email sender, you should remove this code that lets you confirm the account 50 | DisplayConfirmAccountLink = true; 51 | if (DisplayConfirmAccountLink) 52 | { 53 | var userId = await _userManager.GetUserIdAsync(user); 54 | var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); 55 | code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); 56 | EmailConfirmationUrl = Url.Page( 57 | "/Account/ConfirmEmail", 58 | pageHandler: null, 59 | values: new { area = "Identity", userId = userId, code = code }, 60 | protocol: Request.Scheme); 61 | } 62 | 63 | return Page(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/ResetPassword.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ResetPasswordModel 3 | @{ 4 | ViewData["Title"] = "Reset password"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

Reset your password.

9 |
10 |
11 |
12 |
13 |
14 | 15 |
16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | 29 |
30 | 31 |
32 |
33 |
34 | 35 | @section Scripts { 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ResetPasswordConfirmationModel 3 | @{ 4 | ViewData["Title"] = "Reset password confirmation"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

9 | Your password has been reset. Please click here to log in. 10 |

11 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.AspNetCore.Mvc.RazorPages; 4 | 5 | namespace Listrr.Areas.Identity.Pages.Account 6 | { 7 | [AllowAnonymous] 8 | public class ResetPasswordConfirmationModel : PageModel 9 | { 10 | public IActionResult OnGet() 11 | { 12 | return RedirectToAction("Index", "Home", new { Area = "" }); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/_StatusMessage.cshtml: -------------------------------------------------------------------------------- 1 | @model string 2 | 3 | @if (!String.IsNullOrEmpty(Model)) 4 | { 5 | var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; 6 | 10 | } 11 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Account/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Listrr.Areas.Identity.Pages.Account -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

13 | Request ID: @Model.RequestId 14 |

15 | } -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.Mvc.RazorPages; 6 | 7 | namespace Listrr.Areas.Identity.Pages 8 | { 9 | [AllowAnonymous] 10 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 11 | public class ErrorModel : PageModel 12 | { 13 | public string RequestId { get; set; } 14 | 15 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 16 | 17 | public void OnGet() 18 | { 19 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Identity 2 | @using Listrr.Areas.Identity 3 | @using Listrr.Areas.Identity.Pages 4 | @using Listrr.Data 5 | 6 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 7 | -------------------------------------------------------------------------------- /Listrr/Areas/Identity/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "/Views/Shared/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /Listrr/AutoMapper/DefaultProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | 3 | namespace Listrr.AutoMapper 4 | { 5 | public class DefaultProfile : Profile 6 | { 7 | 8 | 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /Listrr/Comparer/TraktMovieComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using TraktNet.Objects.Get.Movies; 5 | 6 | namespace Listrr.Comparer 7 | { 8 | public class TraktMovieComparer : IEqualityComparer 9 | { 10 | public bool Equals(ITraktMovie x, ITraktMovie y) 11 | { 12 | return x.Ids.Slug == y.Ids.Slug; 13 | } 14 | 15 | public int GetHashCode(ITraktMovie obj) 16 | { 17 | return Convert.ToInt32(obj.Ids.Trakt); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Listrr/Comparer/TraktShowComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using TraktNet.Objects.Get.Shows; 5 | 6 | namespace Listrr.Comparer 7 | { 8 | public class TraktShowComparer : IEqualityComparer 9 | { 10 | public bool Equals(ITraktShow x, ITraktShow y) 11 | { 12 | return x.Ids.Slug == y.Ids.Slug; 13 | } 14 | 15 | public int GetHashCode(ITraktShow obj) 16 | { 17 | return Convert.ToInt32(obj.Ids.Trakt); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Listrr/Configuration/DiscordAPIConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Listrr.Configuration 2 | { 3 | public class DiscordAPIConfiguration 4 | { 5 | public string ClientId { get; set; } 6 | 7 | public string ClientSecret { get; set; } 8 | 9 | public string Token { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /Listrr/Configuration/GithubAPIConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Listrr.Configuration 2 | { 3 | public class GithubAPIConfiguration 4 | { 5 | public string ClientId { get; set; } 6 | 7 | public string ClientSecret { get; set; } 8 | 9 | public string Token { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /Listrr/Configuration/HangfireConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Listrr.Configuration 2 | { 3 | public class HangFireConfiguration 4 | { 5 | public string Username { get; set; } 6 | 7 | public string Password { get; set; } 8 | 9 | public string DashboardPath { get; set; } 10 | 11 | public int Workers { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Listrr/Configuration/ILimitConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Listrr.Data; 2 | 3 | namespace Listrr.Configuration 4 | { 5 | public interface ILimitConfiguration 6 | { 7 | public int ListLimit { get; set; } 8 | 9 | public int Amount { get; set; } 10 | 11 | public bool ExclusionFilters { get; set; } 12 | 13 | public bool UpdateAfterEdit { get; set; } 14 | 15 | public bool UpdateManual { get; set; } 16 | 17 | public bool IMDbRatings { get; set; } 18 | 19 | public string QueueName { get; set; } 20 | 21 | public UserLevel Level { get; set; } 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /Listrr/Configuration/LimitConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Listrr.Data; 2 | 3 | namespace Listrr.Configuration 4 | { 5 | public class LimitConfiguration : ILimitConfiguration 6 | { 7 | public int ListLimit { get; set; } 8 | public int Amount { get; set; } 9 | public bool ExclusionFilters { get; set; } 10 | public bool UpdateAfterEdit { get; set; } 11 | public bool UpdateManual { get; set; } 12 | public bool ListsFromNames { get; set; } 13 | public string QueueName { get; set; } 14 | public UserLevel Level { get; set; } 15 | public bool IMDbRatings { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /Listrr/Configuration/LimitConfigurationList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Listrr.Configuration 4 | { 5 | public class LimitConfigurationList 6 | { 7 | public IList LimitConfigurations { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /Listrr/Configuration/ListPaginationConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Listrr.Configuration 2 | { 3 | public class ListPaginationConfiguration 4 | { 5 | 6 | public int PageSize { get; set; } 7 | 8 | } 9 | } -------------------------------------------------------------------------------- /Listrr/Configuration/NotificationConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Listrr.Configuration 2 | { 3 | public class NotificationConfiguration 4 | { 5 | public string Title { get; set; } 6 | 7 | public string Text { get; set; } 8 | 9 | public string Class { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /Listrr/Configuration/TraktAPIConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Listrr.Configuration 2 | { 3 | public class TraktAPIConfiguration 4 | { 5 | public int DelaySearch { get; set; } 6 | 7 | public int DelayList { get; set; } 8 | 9 | public int DelayIdSearch { get; set; } 10 | 11 | public string ClientId { get; set; } 12 | 13 | public string ClientSecret { get; set; } 14 | 15 | public uint? FetchLimitSearch { get; set; } 16 | 17 | public uint? FetchLimitList { get; set; } 18 | 19 | public int ChunkBy { get; set; } 20 | 21 | public int DelayRequeue { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /Listrr/Configuration/UserMappingConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Listrr.Data; 2 | 3 | namespace Listrr.Configuration 4 | { 5 | public class UserMappingConfiguration 6 | { 7 | public string User { get; set; } 8 | 9 | public UserLevel UserLevel { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /Listrr/Configuration/UserMappingConfigurationList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Listrr.Configuration 4 | { 5 | public class UserMappingConfigurationList 6 | { 7 | 8 | public IList UserMappingConfigurations { get; set; } 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Listrr/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Listrr 2 | { 3 | public static class Constants 4 | { 5 | public const string TOKEN_AccessToken = "access_token"; 6 | public const string TOKEN_RefreshToken = "refresh_token"; 7 | public const string TOKEN_ExpiresAt = "expires_at"; 8 | public const string TOKEN_LoginProvider = "Trakt"; 9 | 10 | public const string LIST_Description = "List created and maintained by https://listrr.pro"; 11 | 12 | public const string Trakt_Claim_Ids_Slug = "urn:trakt:ids:slug"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Listrr/Controllers/DonorController.cs: -------------------------------------------------------------------------------- 1 | using Listrr.Configuration; 2 | using Listrr.Data; 3 | using Listrr.Models; 4 | 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.EntityFrameworkCore; 7 | 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | 11 | namespace Listrr.Controllers 12 | { 13 | public class DonorController : Controller 14 | { 15 | private readonly AppDbContext _appDbContext; 16 | private readonly LimitConfigurationList _limitConfigurationList; 17 | 18 | public DonorController(AppDbContext appDbContext, LimitConfigurationList limitConfigurationList) 19 | { 20 | _appDbContext = appDbContext; 21 | _limitConfigurationList = limitConfigurationList; 22 | } 23 | 24 | 25 | 26 | //public IActionResult LinkGitHubAccount() 27 | //{ 28 | // //return redirectto 29 | //} 30 | 31 | public async Task WhyDonate() 32 | { 33 | var lists = await _appDbContext.TraktLists.Include(x => x.Owner).ToListAsync(); 34 | var listsWithExclusionFilters = lists.Where(x => 35 | x.ExclusionFilter_Certifications_Movie?.Certifications?.Length > 0 || 36 | x.ExclusionFilter_Certifications_Show?.Certifications?.Length > 0 || 37 | x.ExclusionFilter_Countries?.Languages?.Length > 0 || 38 | x.ExclusionFilter_Genres?.Genres?.Length > 0 || 39 | x.ExclusionFilter_Languages?.Languages?.Length > 0 || 40 | x.ExclusionFilter_Networks?.Networks?.Length > 0 || 41 | x.ExclusionFilter_Status?.Status?.Length > 0 || 42 | x.ExclusionFilter_Translations?.Translations?.Length > 0 43 | ).ToList(); 44 | 45 | var userConfiguration = _limitConfigurationList.LimitConfigurations.FirstOrDefault(x => x.Level == UserLevel.User); 46 | 47 | 48 | return View(new WhyDonateViewModel() 49 | { 50 | Lists = lists.Count, 51 | Users = await _appDbContext.Users.CountAsync(), 52 | ListsWithExclusionFilters = listsWithExclusionFilters.Count(), 53 | UsersWithExclusionFilters = listsWithExclusionFilters.Select(x => x.Owner).Distinct().Count(), 54 | UserListLimit = userConfiguration?.ListLimit ?? 0 55 | }); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Listrr/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Listrr.Configuration; 2 | using Listrr.Data.Trakt; 3 | using Listrr.Models; 4 | using Listrr.Repositories; 5 | 6 | using Microsoft.AspNetCore.Mvc; 7 | 8 | using System.Diagnostics; 9 | using System.Threading.Tasks; 10 | 11 | namespace Listrr.Controllers 12 | { 13 | public class HomeController : Controller 14 | { 15 | private readonly ITraktListRepository _traktListRepository; 16 | private readonly ListPaginationConfiguration _listPaginationConfiguration; 17 | 18 | public HomeController(ITraktListRepository traktListRepository, ListPaginationConfiguration listPaginationConfiguration) 19 | { 20 | _traktListRepository = traktListRepository; 21 | _listPaginationConfiguration = listPaginationConfiguration; 22 | } 23 | 24 | 25 | public IActionResult Index() 26 | { 27 | return View(); 28 | } 29 | 30 | public async Task Lists(int id) 31 | { 32 | var listCount = await _traktListRepository.Count(); 33 | var pageCount = (listCount + 9) / _listPaginationConfiguration.PageSize; 34 | 35 | if (id > pageCount) return RedirectToAction("Error"); 36 | if (id < 0) return RedirectToAction("Error"); 37 | 38 | var lists = await _traktListRepository.Get(_listPaginationConfiguration.PageSize, _listPaginationConfiguration.PageSize * id + (id != 0 ? 1 : 0)); 39 | 40 | return View(new PaginationViewModel 41 | { 42 | Items = lists, 43 | Pages = pageCount, 44 | Page = id 45 | }); 46 | } 47 | 48 | public IActionResult Privacy() 49 | { 50 | return View(); 51 | } 52 | 53 | 54 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 55 | public IActionResult Error() 56 | { 57 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 58 | } 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Listrr/Data/CountryCode.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | 4 | namespace Listrr.Data 5 | { 6 | public class CountryCode 7 | { 8 | public string Name { get; set; } 9 | 10 | public string Code { get; set; } 11 | } 12 | 13 | public class CountryCodeConfiguration : IEntityTypeConfiguration 14 | { 15 | public void Configure(EntityTypeBuilder builder) 16 | { 17 | builder 18 | .HasIndex(x => x.Code); 19 | 20 | builder 21 | .HasKey(x => new { x.Name, x.Code }); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Listrr/Data/CreatedAndUpdated.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Listrr.Data 4 | { 5 | public class CreatedAndUpdated 6 | { 7 | public DateTime Created { get; set; } 8 | public DateTime Updated { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /Listrr/Data/DataProtectionDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace Listrr.Data 5 | { 6 | public class DataProtectionDbContext : DbContext, IDataProtectionKeyContext 7 | { 8 | public DbSet DataProtectionKeys { get; set; } 9 | 10 | 11 | public DataProtectionDbContext(DbContextOptions options) : base(options) { } 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /Listrr/Data/IMDb/IMDbRating.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | 4 | namespace Listrr.Data.IMDb 5 | { 6 | public class IMDbRating : CreatedAndUpdated 7 | { 8 | public string IMDbId { get; set; } 9 | 10 | public int Rating { get; set; } 11 | 12 | public int Votes { get; set; } 13 | 14 | } 15 | 16 | public class IMDbRatingConfiguration : IEntityTypeConfiguration 17 | { 18 | public void Configure(EntityTypeBuilder builder) 19 | { 20 | builder 21 | .HasIndex(x => x.IMDbId) 22 | .IsUnique(); 23 | 24 | builder 25 | .HasKey(x => x.IMDbId); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Listrr/Data/LanguageCode.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | 4 | namespace Listrr.Data 5 | { 6 | public class LanguageCode 7 | { 8 | public string Name { get; set; } 9 | 10 | public string Code { get; set; } 11 | 12 | public string Description => $"{Code} ({Name})"; 13 | } 14 | 15 | public class LanguageCodeConfiguration : IEntityTypeConfiguration 16 | { 17 | public void Configure(EntityTypeBuilder builder) 18 | { 19 | builder 20 | .HasIndex(x => x.Code); 21 | 22 | builder 23 | .HasKey(x => new { x.Name, x.Code }); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/Filters/CertificationsMovieFilter.cs: -------------------------------------------------------------------------------- 1 | using Refit; 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Listrr.Data.Trakt.Filters 7 | { 8 | public class CertificationsMovieFilter 9 | { 10 | 11 | public CertificationsMovieFilter() 12 | { 13 | 14 | } 15 | 16 | public CertificationsMovieFilter(IEnumerable certifications) 17 | { 18 | this.Certifications = certifications?.ToArray(); 19 | } 20 | 21 | 22 | [AliasAs("certifications")] 23 | public string[] Certifications { get; set; } 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/Filters/CertificationsShowFilter.cs: -------------------------------------------------------------------------------- 1 | using Refit; 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Listrr.Data.Trakt.Filters 7 | { 8 | public class CertificationsShowFilter 9 | { 10 | public CertificationsShowFilter() 11 | { 12 | 13 | } 14 | 15 | public CertificationsShowFilter(IEnumerable certifications) 16 | { 17 | this.Certifications = certifications?.ToArray(); 18 | } 19 | 20 | 21 | [AliasAs("certifications")] 22 | public string[] Certifications { get; set; } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Listrr/Data/Trakt/Filters/CountriesCommonFilter.cs: -------------------------------------------------------------------------------- 1 | using Refit; 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Listrr.Data.Trakt.Filters 7 | { 8 | public class CountriesCommonFilter 9 | { 10 | 11 | public CountriesCommonFilter() 12 | { 13 | 14 | } 15 | 16 | public CountriesCommonFilter(IEnumerable countries) 17 | { 18 | this.Languages = countries?.ToArray(); 19 | } 20 | 21 | 22 | [AliasAs("languages")] 23 | public string[] Languages { get; set; } 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/Filters/GenresCommonFilter.cs: -------------------------------------------------------------------------------- 1 | using Refit; 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Listrr.Data.Trakt.Filters 7 | { 8 | public class GenresCommonFilter 9 | { 10 | 11 | public GenresCommonFilter() 12 | { 13 | 14 | } 15 | 16 | public GenresCommonFilter(IEnumerable genres) 17 | { 18 | this.Genres = genres?.ToArray(); 19 | } 20 | 21 | [AliasAs("genres")] 22 | public string[] Genres { get; set; } 23 | 24 | } 25 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/Filters/LanguagesCommonFilter.cs: -------------------------------------------------------------------------------- 1 | using Refit; 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Listrr.Data.Trakt.Filters 7 | { 8 | public class LanguagesCommonFilter 9 | { 10 | 11 | public LanguagesCommonFilter() 12 | { 13 | 14 | } 15 | 16 | public LanguagesCommonFilter(IEnumerable languages) 17 | { 18 | this.Languages = languages?.ToArray(); 19 | } 20 | 21 | 22 | [AliasAs("languages")] 23 | public string[] Languages { get; set; } 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/Filters/NetworksShowFilter.cs: -------------------------------------------------------------------------------- 1 | using Refit; 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Listrr.Data.Trakt.Filters 7 | { 8 | public class NetworksShowFilter 9 | { 10 | 11 | public NetworksShowFilter() 12 | { 13 | 14 | } 15 | 16 | public NetworksShowFilter(IEnumerable networks) 17 | { 18 | this.Networks = networks?.ToArray(); 19 | } 20 | 21 | [AliasAs("networks")] 22 | public string[] Networks { get; set; } 23 | 24 | } 25 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/Filters/RatingsCommonFilter.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Listrr.Data.Trakt.Filters 4 | { 5 | public class RatingsCommonFilter 6 | { 7 | 8 | [Range(0, 100)] 9 | [Display(Name = "Min rating", Prompt = "0")] 10 | public int From { get; set; } = 0; 11 | 12 | [Range(0, 100)] 13 | [Display(Name = "Max rating", Prompt = "100")] 14 | public int To { get; set; } = 0; 15 | 16 | [Range(0, int.MaxValue)] 17 | [Display(Name = "Min Votes", Prompt = "10")] 18 | public int Votes { get; set; } = 0; 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/Filters/RuntimesCommonFilter.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Listrr.Data.Trakt.Filters 4 | { 5 | public class RuntimesCommonFilter 6 | { 7 | 8 | [Display(Name = "Min runtime", Prompt = "30")] 9 | public int From { get; set; } 10 | 11 | [Display(Name = "Max runtime", Prompt = "200")] 12 | public int To { get; set; } 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/Filters/StatusShowFilter.cs: -------------------------------------------------------------------------------- 1 | using Refit; 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Listrr.Data.Trakt.Filters 7 | { 8 | public class StatusShowFilter 9 | { 10 | 11 | public StatusShowFilter() 12 | { 13 | 14 | } 15 | 16 | public StatusShowFilter(IEnumerable status) 17 | { 18 | this.Status = status?.ToArray(); 19 | } 20 | 21 | [AliasAs("status")] 22 | public string[] Status { get; set; } 23 | 24 | } 25 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/Filters/TranslationsBasicFilter.cs: -------------------------------------------------------------------------------- 1 | using Refit; 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Listrr.Data.Trakt.Filters 7 | { 8 | public class TranslationsBasicFilter 9 | { 10 | 11 | public TranslationsBasicFilter() 12 | { 13 | 14 | } 15 | 16 | public TranslationsBasicFilter(IEnumerable translations) 17 | { 18 | this.Translations = translations?.ToArray(); 19 | } 20 | 21 | 22 | [AliasAs("translations")] 23 | public string[] Translations { get; set; } 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/Filters/YearsCommonFilter.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Listrr.Data.Trakt.Filters 4 | { 5 | public class YearsCommonFilter 6 | { 7 | 8 | [Display(Name = "Min year", Prompt = "1990")] 9 | public int? From { get; set; } 10 | 11 | [Display(Name = "Max year", Prompt = "2018")] 12 | public int? To { get; set; } 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/TraktMovieCertification.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | 4 | namespace Listrr.Data.Trakt 5 | { 6 | public class TraktMovieCertification 7 | { 8 | public string Slug { get; set; } 9 | 10 | public string Description { get; set; } 11 | 12 | public string Name { get; set; } 13 | } 14 | 15 | public class TraktMovieCertificationConfiguration : IEntityTypeConfiguration 16 | { 17 | public void Configure(EntityTypeBuilder builder) 18 | { 19 | builder 20 | .HasIndex(x => x.Slug) 21 | .IsUnique(); 22 | 23 | builder 24 | .HasKey(x => x.Slug); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Listrr/Data/Trakt/TraktMovieGenre.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | 4 | namespace Listrr.Data.Trakt 5 | { 6 | public class TraktMovieGenre 7 | { 8 | public string Name { get; set; } 9 | 10 | public string Slug { get; set; } 11 | } 12 | 13 | public class TraktMovieGenreConfiguration : IEntityTypeConfiguration 14 | { 15 | public void Configure(EntityTypeBuilder builder) 16 | { 17 | builder 18 | .HasIndex(x => x.Slug) 19 | .IsUnique(); 20 | 21 | builder 22 | .HasKey(x => x.Slug); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/TraktShowCertification.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | 4 | namespace Listrr.Data.Trakt 5 | { 6 | public class TraktShowCertification 7 | { 8 | public string Slug { get; set; } 9 | 10 | public string Description { get; set; } 11 | 12 | public string Name { get; set; } 13 | } 14 | 15 | public class TraktShowCertificationConfiguration : IEntityTypeConfiguration 16 | { 17 | public void Configure(EntityTypeBuilder builder) 18 | { 19 | builder 20 | .HasIndex(x => x.Slug) 21 | .IsUnique(); 22 | 23 | builder 24 | .HasKey(x => x.Slug); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Listrr/Data/Trakt/TraktShowGenre.cs: -------------------------------------------------------------------------------- 1 |  2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 4 | 5 | namespace Listrr.Data.Trakt 6 | { 7 | public class TraktShowGenre 8 | { 9 | public string Name { get; set; } 10 | 11 | public string Slug { get; set; } 12 | } 13 | 14 | public class TraktShowGenreConfiguration : IEntityTypeConfiguration 15 | { 16 | public void Configure(EntityTypeBuilder builder) 17 | { 18 | builder 19 | .HasIndex(x => x.Slug) 20 | .IsUnique(); 21 | 22 | builder 23 | .HasKey(x => x.Slug); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/TraktShowNetwork.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | 4 | using Newtonsoft.Json; 5 | 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | 8 | namespace Listrr.Data.Trakt 9 | { 10 | public class TraktShowNetwork 11 | { 12 | [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 13 | [JsonIgnore] 14 | public string Id { get; set; } 15 | 16 | 17 | [JsonProperty("name")] 18 | public string Name { get; set; } 19 | } 20 | 21 | public class TraktShowNetworkConfiguration : IEntityTypeConfiguration 22 | { 23 | public void Configure(EntityTypeBuilder builder) 24 | { 25 | 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Listrr/Data/Trakt/TraktShowStatus.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 3 | 4 | using Newtonsoft.Json; 5 | 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | 8 | namespace Listrr.Data.Trakt 9 | { 10 | public class TraktShowStatus 11 | { 12 | [DatabaseGenerated(DatabaseGeneratedOption.None)] 13 | [JsonProperty("name")] 14 | public string Name { get; set; } 15 | } 16 | 17 | public class TraktShowStatusConfiguration : IEntityTypeConfiguration 18 | { 19 | public void Configure(EntityTypeBuilder builder) 20 | { 21 | builder 22 | .HasIndex(x => x.Name) 23 | .IsUnique(); 24 | 25 | builder 26 | .HasKey(x => x.Name); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Listrr/Data/User.cs: -------------------------------------------------------------------------------- 1 | using Listrr.Data.Trakt; 2 | 3 | using Microsoft.AspNetCore.Identity; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 6 | 7 | using System.Collections.Generic; 8 | using System.ComponentModel.DataAnnotations.Schema; 9 | 10 | namespace Listrr.Data 11 | { 12 | 13 | public enum UserLevel 14 | { 15 | User, 16 | Donor, 17 | DonorPlus, 18 | DonorPlusPlus, 19 | Developer 20 | } 21 | 22 | public class User : IdentityUser 23 | { 24 | 25 | [NotMapped] 26 | public bool IsDonor => Level != UserLevel.User; 27 | 28 | public UserLevel Level { get; set; } 29 | 30 | public IList TraktLists { get; set; } 31 | 32 | } 33 | 34 | public class UserConfiguration : IEntityTypeConfiguration 35 | { 36 | public void Configure(EntityTypeBuilder builder) 37 | { 38 | builder 39 | .Property(x => x.Level) 40 | .HasDefaultValue(UserLevel.User); 41 | 42 | builder 43 | .HasMany(x => x.TraktLists) 44 | .WithOne(x => x.Owner) 45 | .OnDelete(DeleteBehavior.Cascade); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Listrr/Exceptions/RefreshTokenBadRequestException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Listrr.Exceptions 4 | { 5 | public class RefreshTokenBadRequestException : Exception 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /Listrr/Extensions/AddHttpContextAccessorExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.DependencyInjection.Extensions; 6 | 7 | namespace Listrr.Extensions 8 | { 9 | public static class AddHttpContextAccessorExtension 10 | { 11 | 12 | public static IServiceCollection AddHttpContextAccessor(this IServiceCollection services) 13 | { 14 | if (services == null) 15 | { 16 | throw new ArgumentNullException(nameof(services)); 17 | } 18 | 19 | services.TryAddSingleton(); 20 | return services; 21 | } 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /Listrr/Extensions/ListExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Listrr.Extensions 5 | { 6 | public static class ListExtensions 7 | { 8 | public static List> ChunkBy(this List source, int chunkSize) 9 | { 10 | return source 11 | .Select((x, i) => new { Index = i, Value = x }) 12 | .GroupBy(x => x.Index / chunkSize) 13 | .Select(x => x.Select(v => v.Value).ToList()) 14 | .ToList(); 15 | } 16 | 17 | public static IEnumerable> ChunkBy(this IEnumerable source, int chunkSize) 18 | { 19 | return source 20 | .Select((x, i) => new { Index = i, Value = x }) 21 | .GroupBy(x => x.Index / chunkSize) 22 | .Select(x => x.Select(v => v.Value).ToList()) 23 | .ToList(); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Listrr/HangfireActivator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Hangfire; 4 | 5 | namespace Listrr 6 | { 7 | public class HangfireActivator : JobActivator 8 | { 9 | private readonly IServiceProvider _serviceProvider; 10 | 11 | public HangfireActivator(IServiceProvider serviceProvider) 12 | { 13 | _serviceProvider = serviceProvider; 14 | } 15 | 16 | public override object ActivateJob(Type type) 17 | { 18 | return _serviceProvider.GetService(type); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /Listrr/Jobs/BackgroundJobs/IBackgroundJob.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | using Hangfire.Server; 4 | 5 | namespace Listrr.Jobs.BackgroundJobs 6 | { 7 | public interface IBackgroundJob 8 | { 9 | Task Execute(PerformContext context); 10 | } 11 | 12 | 13 | public interface IBackgroundJob 14 | { 15 | Task Execute(T param, PerformContext context, bool queueNext = false, bool forceRefresh = false); 16 | 17 | Task ExecutePriorized(T param, PerformContext context, bool queueNext = false, bool forceRefresh = false); 18 | } 19 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/EnforceListLimitRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using Hangfire.Server; 3 | 4 | using Listrr.Configuration; 5 | using Listrr.Repositories; 6 | 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace Listrr.Jobs.RecurringJobs 11 | { 12 | [Queue("system")] 13 | public class EnforceListLimitRecurringJob : IRecurringJob 14 | { 15 | private readonly IUserRepository _userRepository; 16 | private readonly ITraktListRepository _traktRepository; 17 | 18 | private readonly LimitConfigurationList _limitConfigurationList; 19 | 20 | public EnforceListLimitRecurringJob(LimitConfigurationList limitConfigurationList, IUserRepository userRepository, ITraktListRepository traktRepository) 21 | { 22 | _limitConfigurationList = limitConfigurationList; 23 | _userRepository = userRepository; 24 | _traktRepository = traktRepository; 25 | } 26 | 27 | 28 | public async Task Execute(PerformContext context) 29 | { 30 | var users = await _userRepository.Get(); 31 | 32 | foreach (var user in users) 33 | { 34 | var limitConfig = _limitConfigurationList.LimitConfigurations.First(x => x.Level == user.Level); 35 | var lists = await _traktRepository.Get(user); 36 | 37 | if (lists.Count > limitConfig.ListLimit) 38 | { 39 | foreach (var traktList in lists.Where(x => x.Process)) 40 | { 41 | traktList.Process = false; 42 | 43 | await _traktRepository.Update(traktList); 44 | } 45 | } 46 | else 47 | { 48 | foreach (var traktList in lists.Where(x => x.Process == false)) 49 | { 50 | traktList.Process = true; 51 | 52 | await _traktRepository.Update(traktList); 53 | } 54 | } 55 | } 56 | 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/GetCountryCodesRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using Hangfire.Server; 3 | 4 | using Listrr.Data; 5 | using Listrr.Repositories; 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Globalization; 10 | using System.Linq; 11 | using System.Threading.Tasks; 12 | 13 | namespace Listrr.Jobs.RecurringJobs 14 | { 15 | 16 | [Queue("system")] 17 | public class GetCountryCodesRecurringJob : IRecurringJob 18 | { 19 | private readonly ITraktCodeRepository _traktCodeRepository; 20 | 21 | public GetCountryCodesRecurringJob(ITraktCodeRepository traktCodeRepository) 22 | { 23 | _traktCodeRepository = traktCodeRepository; 24 | } 25 | 26 | 27 | public async Task Execute(PerformContext context) 28 | { 29 | var countries = new List(); 30 | foreach (var culture in CultureInfo.GetCultures(CultureTypes.SpecificCultures)) 31 | { 32 | try 33 | { 34 | var country = new RegionInfo(culture.LCID); 35 | if (countries.All(p => p.Name != country.Name)) 36 | countries.Add(country); 37 | } 38 | catch (Exception e) 39 | { 40 | Console.WriteLine(e); 41 | } 42 | } 43 | 44 | var dbCountryCodes = await _traktCodeRepository.GetCountryCodes(); 45 | foreach (var regionInfo in countries) 46 | { 47 | var countryCode = dbCountryCodes.FirstOrDefault(x => x.Code == regionInfo.TwoLetterISORegionName.ToLower(CultureInfo.InvariantCulture)); 48 | 49 | if (countryCode != null) continue; 50 | if (regionInfo.TwoLetterISORegionName.Length != 2) continue; 51 | 52 | await _traktCodeRepository.CreateCountryCode( 53 | new CountryCode() 54 | { 55 | Code = regionInfo.TwoLetterISORegionName.ToLower(CultureInfo.InvariantCulture), 56 | Name = regionInfo.DisplayName 57 | } 58 | ); 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/GetLanguageCodesRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using Hangfire.Server; 3 | 4 | using Listrr.Data; 5 | using Listrr.Repositories; 6 | 7 | using System.Globalization; 8 | using System.Threading.Tasks; 9 | 10 | namespace Listrr.Jobs.RecurringJobs 11 | { 12 | 13 | [Queue("system")] 14 | public class GetLanguageCodesRecurringJob : IRecurringJob 15 | { 16 | private readonly ITraktCodeRepository _traktCodeRepository; 17 | 18 | public GetLanguageCodesRecurringJob(ITraktCodeRepository traktCodeRepository) 19 | { 20 | _traktCodeRepository = traktCodeRepository; 21 | } 22 | 23 | 24 | public async Task Execute(PerformContext context) 25 | { 26 | foreach (CultureInfo culture in CultureInfo.GetCultures(CultureTypes.NeutralCultures)) 27 | { 28 | var languageCode = _traktCodeRepository.GetLanguageCode(culture.NativeName); 29 | 30 | if (languageCode == null) 31 | { 32 | await _traktCodeRepository.CreateLanguageCode( 33 | new LanguageCode() 34 | { 35 | Code = culture.TwoLetterISOLanguageName, 36 | Name = culture.NativeName 37 | } 38 | ); 39 | } 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/GetMovieCertificationsRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using Hangfire.Server; 3 | 4 | using Listrr.Configuration; 5 | using Listrr.Data.Trakt; 6 | using Listrr.Repositories; 7 | 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | 11 | using TraktNet; 12 | 13 | namespace Listrr.Jobs.RecurringJobs 14 | { 15 | 16 | [Queue("system")] 17 | public class GetMovieCertificationsRecurringJob : IRecurringJob 18 | { 19 | private readonly TraktClient _traktClient; 20 | private readonly TraktAPIConfiguration _traktApiConfiguration; 21 | private readonly ITraktMovieRepository _traktMovieRepository; 22 | 23 | public GetMovieCertificationsRecurringJob(TraktAPIConfiguration traktApiConfiguration, ITraktMovieRepository traktMovieRepository) 24 | { 25 | _traktApiConfiguration = traktApiConfiguration; 26 | _traktMovieRepository = traktMovieRepository; 27 | 28 | _traktClient = new TraktClient(_traktApiConfiguration.ClientId, _traktApiConfiguration.ClientSecret); 29 | } 30 | 31 | public async Task Execute(PerformContext context) 32 | { 33 | var result = await _traktClient.Certifications.GetMovieCertificationsAsync(); 34 | 35 | if (result.IsSuccess) 36 | { 37 | var currentCertifications = await _traktMovieRepository.GetCertifications(); 38 | 39 | foreach (var traktCertification in result.Value.US) 40 | { 41 | var certification = currentCertifications.FirstOrDefault(x => x.Slug == traktCertification.Slug); 42 | 43 | if (certification == null) 44 | { 45 | await _traktMovieRepository.CreateCertification(new TraktMovieCertification() 46 | { 47 | Name = traktCertification.Name, 48 | Description = traktCertification.Description, 49 | Slug = traktCertification.Slug 50 | }); 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/GetMovieGenresRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using Hangfire.Server; 3 | 4 | using Listrr.Configuration; 5 | using Listrr.Data.Trakt; 6 | using Listrr.Repositories; 7 | 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | 11 | using TraktNet; 12 | 13 | namespace Listrr.Jobs.RecurringJobs 14 | { 15 | 16 | [Queue("system")] 17 | public class GetMovieGenresRecurringJob : IRecurringJob 18 | { 19 | private readonly TraktClient _traktClient; 20 | private readonly TraktAPIConfiguration _traktApiConfiguration; 21 | private readonly ITraktMovieRepository _traktMovieRepository; 22 | 23 | public GetMovieGenresRecurringJob(TraktAPIConfiguration traktApiConfiguration, ITraktMovieRepository traktMovieRepository) 24 | { 25 | _traktApiConfiguration = traktApiConfiguration; 26 | _traktMovieRepository = traktMovieRepository; 27 | 28 | _traktClient = new TraktClient(_traktApiConfiguration.ClientId, _traktApiConfiguration.ClientSecret); 29 | } 30 | 31 | 32 | public async Task Execute(PerformContext context) 33 | { 34 | var result = await _traktClient.Genres.GetMovieGenresAsync(); 35 | 36 | if (result.IsSuccess) 37 | { 38 | var currentGenres = await _traktMovieRepository.GetGenres(); 39 | 40 | foreach (var traktGenre in result.Value) 41 | { 42 | var currentGenre = currentGenres.FirstOrDefault(x => x.Slug == traktGenre.Slug); 43 | 44 | if (currentGenre == null) 45 | { 46 | await _traktMovieRepository.CreateGenre(new TraktMovieGenre() 47 | { 48 | Name = traktGenre.Name, 49 | Slug = traktGenre.Slug 50 | }); 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/GetShowCertificationsRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using Hangfire.Server; 3 | 4 | using Listrr.Configuration; 5 | using Listrr.Data.Trakt; 6 | using Listrr.Repositories; 7 | 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | 11 | using TraktNet; 12 | 13 | namespace Listrr.Jobs.RecurringJobs 14 | { 15 | 16 | [Queue("system")] 17 | public class GetShowCertificationsRecurringJob : IRecurringJob 18 | { 19 | 20 | private readonly TraktClient _traktClient; 21 | private readonly TraktAPIConfiguration _traktApiConfiguration; 22 | private readonly ITraktShowRepository _traktShowRepository; 23 | 24 | public GetShowCertificationsRecurringJob(TraktAPIConfiguration traktApiConfiguration, ITraktShowRepository traktShowRepository) 25 | { 26 | _traktApiConfiguration = traktApiConfiguration; 27 | _traktShowRepository = traktShowRepository; 28 | 29 | _traktClient = new TraktClient(_traktApiConfiguration.ClientId, _traktApiConfiguration.ClientSecret); 30 | } 31 | 32 | 33 | public async Task Execute(PerformContext context) 34 | { 35 | var result = await _traktClient.Certifications.GetShowCertificationsAsync(); 36 | 37 | if (result.IsSuccess) 38 | { 39 | var currentCertifications = await _traktShowRepository.GetCertifications(); 40 | 41 | foreach (var traktCertification in result.Value.US) 42 | { 43 | var currentCertification = currentCertifications.FirstOrDefault(x => x.Slug != traktCertification.Slug); 44 | 45 | if (currentCertification == null) 46 | { 47 | await _traktShowRepository.CreateCertification(new TraktShowCertification() 48 | { 49 | Name = traktCertification.Name, 50 | Description = traktCertification.Description, 51 | Slug = traktCertification.Slug 52 | }); 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/GetShowGenresRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using Hangfire.Server; 3 | 4 | using Listrr.Configuration; 5 | using Listrr.Data.Trakt; 6 | using Listrr.Repositories; 7 | 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | 11 | using TraktNet; 12 | 13 | namespace Listrr.Jobs.RecurringJobs 14 | { 15 | 16 | [Queue("system")] 17 | public class GetShowGenresRecurringJob : IRecurringJob 18 | { 19 | private readonly TraktClient _traktClient; 20 | private readonly TraktAPIConfiguration _traktApiConfiguration; 21 | private readonly ITraktShowRepository _traktShowRepository; 22 | 23 | public GetShowGenresRecurringJob(TraktAPIConfiguration traktApiConfiguration, ITraktShowRepository traktShowRepository) 24 | { 25 | _traktApiConfiguration = traktApiConfiguration; 26 | _traktShowRepository = traktShowRepository; 27 | 28 | _traktClient = new TraktClient(_traktApiConfiguration.ClientId, _traktApiConfiguration.ClientSecret); 29 | } 30 | 31 | 32 | public async Task Execute(PerformContext context) 33 | { 34 | var result = await _traktClient.Genres.GetShowGenresAsync(); 35 | 36 | if (result.IsSuccess) 37 | { 38 | var currentGenres = await _traktShowRepository.GetGenres(); 39 | 40 | foreach (var traktGenre in result.Value) 41 | { 42 | var currentGenre = currentGenres.FirstOrDefault(x => x.Slug != traktGenre.Slug); 43 | 44 | if (currentGenre == null) 45 | { 46 | await _traktShowRepository.CreateGenre(new TraktShowGenre() 47 | { 48 | Name = traktGenre.Name, 49 | Slug = traktGenre.Slug 50 | }); 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/GetShowNetworksRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using Hangfire.Server; 3 | 4 | using Listrr.Configuration; 5 | using Listrr.Data.Trakt; 6 | using Listrr.Repositories; 7 | 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | 11 | using TraktNet; 12 | 13 | namespace Listrr.Jobs.RecurringJobs 14 | { 15 | 16 | [Queue("system")] 17 | public class GetShowNetworksRecurringJob : IRecurringJob 18 | { 19 | private readonly TraktClient _traktClient; 20 | private readonly TraktAPIConfiguration _traktApiConfiguration; 21 | private readonly ITraktShowRepository _traktShowRepository; 22 | 23 | public GetShowNetworksRecurringJob(TraktAPIConfiguration traktApiConfiguration, ITraktShowRepository traktShowRepository) 24 | { 25 | _traktApiConfiguration = traktApiConfiguration; 26 | _traktShowRepository = traktShowRepository; 27 | 28 | _traktClient = new TraktClient(_traktApiConfiguration.ClientId, _traktApiConfiguration.ClientSecret); 29 | } 30 | 31 | 32 | public async Task Execute(PerformContext context) 33 | { 34 | var result = await _traktClient.Networks.GetNetworksAsync(); 35 | 36 | if (result.IsSuccess) 37 | { 38 | var currentNetworks = await _traktShowRepository.GetNetworks(); 39 | 40 | foreach (var traktNetwork in result.Value) 41 | { 42 | if (currentNetworks.All(x => x.Name != traktNetwork.Name)) 43 | { 44 | await _traktShowRepository.CreateNetwork(new TraktShowNetwork() 45 | { 46 | Name = traktNetwork.Name 47 | }); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/GetShowStatusRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using Hangfire.Server; 3 | 4 | using Listrr.Data.Trakt; 5 | using Listrr.Repositories; 6 | 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | 11 | namespace Listrr.Jobs.RecurringJobs 12 | { 13 | 14 | [Queue("system")] 15 | public class GetShowStatusRecurringJob : IRecurringJob 16 | { 17 | private readonly ITraktShowRepository _traktShowRepository; 18 | 19 | public GetShowStatusRecurringJob(ITraktShowRepository traktShowRepository) 20 | { 21 | _traktShowRepository = traktShowRepository; 22 | } 23 | 24 | 25 | public async Task Execute(PerformContext context) 26 | { 27 | var result = new List 28 | { 29 | TraktNet.Enums.TraktShowStatus.Ended.ObjectName, 30 | TraktNet.Enums.TraktShowStatus.InProduction.ObjectName, 31 | TraktNet.Enums.TraktShowStatus.ReturningSeries.ObjectName, 32 | TraktNet.Enums.TraktShowStatus.Canceled.ObjectName 33 | }; 34 | 35 | var currentStatuses = await _traktShowRepository.GetStatuses(); 36 | 37 | foreach (var traktStatus in result) 38 | { 39 | var currentStatus = currentStatuses.FirstOrDefault(x => x.Name == traktStatus); 40 | 41 | if (currentStatus == null) 42 | { 43 | await _traktShowRepository.CreateStatus(new TraktShowStatus() 44 | { 45 | Name = traktStatus 46 | }); 47 | } 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/IRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire.Server; 2 | 3 | using System.Threading.Tasks; 4 | 5 | namespace Listrr.Jobs.RecurringJobs 6 | { 7 | public interface IRecurringJob 8 | { 9 | Task Execute(PerformContext context); 10 | } 11 | 12 | 13 | public interface IRecurringJob 14 | { 15 | Task Execute(T param, PerformContext context); 16 | } 17 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/ProcessDonorListsRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using Hangfire.Server; 3 | 4 | using Listrr.Configuration; 5 | using Listrr.Data; 6 | using Listrr.Repositories; 7 | using Listrr.Services; 8 | 9 | using System.Linq; 10 | using System.Threading.Tasks; 11 | 12 | namespace Listrr.Jobs.RecurringJobs 13 | { 14 | 15 | [Queue("system")] 16 | public class ProcessDonorListsRecurringJob : IRecurringJob 17 | { 18 | private readonly ITraktListRepository _traktRepository; 19 | private readonly LimitConfigurationList _limitConfigurationList; 20 | private readonly IBackgroundJobQueueService _backgroundJobQueueService; 21 | 22 | public ProcessDonorListsRecurringJob(LimitConfigurationList limitConfigurationList, IBackgroundJobQueueService backgroundJobQueueService, ITraktListRepository traktRepository) 23 | { 24 | _limitConfigurationList = limitConfigurationList; 25 | _backgroundJobQueueService = backgroundJobQueueService; 26 | _traktRepository = traktRepository; 27 | } 28 | 29 | 30 | public async Task Execute(PerformContext context) 31 | { 32 | foreach (var limitConfiguration in _limitConfigurationList.LimitConfigurations.Where(x => x.Level != UserLevel.User)) 33 | { 34 | var lists = await _traktRepository.Get(limitConfiguration.Level); 35 | 36 | foreach (var traktList in lists) 37 | { 38 | _backgroundJobQueueService.Queue(traktList); 39 | } 40 | } 41 | } 42 | 43 | } 44 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/ProcessUserListsRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using Hangfire.Server; 3 | 4 | using Listrr.Data; 5 | using Listrr.Repositories; 6 | using Listrr.Services; 7 | 8 | using System.Threading.Tasks; 9 | 10 | namespace Listrr.Jobs.RecurringJobs 11 | { 12 | 13 | [Queue("system")] 14 | public class ProcessUserListsRecurringJob : IRecurringJob 15 | { 16 | private readonly ITraktListRepository _traktRepository; 17 | private readonly IBackgroundJobQueueService _backgroundJobQueueService; 18 | 19 | public ProcessUserListsRecurringJob(IBackgroundJobQueueService backgroundJobQueueService, ITraktListRepository traktRepository) 20 | { 21 | _backgroundJobQueueService = backgroundJobQueueService; 22 | _traktRepository = traktRepository; 23 | } 24 | 25 | 26 | public async Task Execute(PerformContext context) 27 | { 28 | var list = await _traktRepository.GetNextForUpdate(UserLevel.User); 29 | if (list != null) 30 | { 31 | _backgroundJobQueueService.Queue(list, true); 32 | } 33 | } 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/SetDonorsRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using Hangfire.Server; 3 | 4 | using Listrr.Configuration; 5 | using Listrr.Data; 6 | using Listrr.Services; 7 | 8 | using Microsoft.EntityFrameworkCore; 9 | 10 | using System.Linq; 11 | using System.Threading.Tasks; 12 | 13 | namespace Listrr.Jobs.RecurringJobs 14 | { 15 | 16 | [Queue("system")] 17 | public class SetDonorsRecurringJob : IRecurringJob 18 | { 19 | private readonly UserMappingConfigurationList _userMappingConfigurationList; 20 | private readonly IGitHubGraphService _gitHubGraphService; 21 | private readonly AppDbContext _appDbContext; 22 | 23 | public SetDonorsRecurringJob(IGitHubGraphService gitHubGraphService, AppDbContext appDbContext, UserMappingConfigurationList userMappingConfigurationList) 24 | { 25 | _gitHubGraphService = gitHubGraphService; 26 | _appDbContext = appDbContext; 27 | _userMappingConfigurationList = userMappingConfigurationList; 28 | } 29 | 30 | public async Task Execute(PerformContext context) 31 | { 32 | var newDonors = await _gitHubGraphService.GetDonor(); 33 | var currentDonors = await _appDbContext.Users.Where(x => x.Level != UserLevel.User).ToListAsync(); 34 | 35 | //Deactivate non donors 36 | foreach (var donor in currentDonors) 37 | { 38 | var login = await _appDbContext.UserLogins.FirstOrDefaultAsync(x => x.LoginProvider == "GitHub" && x.UserId == donor.Id); 39 | if (login == null) continue; 40 | if (newDonors.ContainsKey(login.ProviderKey)) continue; 41 | if (donor.Level == UserLevel.User) continue; 42 | 43 | donor.Level = UserLevel.User; 44 | 45 | await _appDbContext.SaveChangesAsync(); 46 | } 47 | 48 | //Activate donors 49 | foreach (var newDonor in newDonors) 50 | { 51 | var login = await _appDbContext.UserLogins.FirstOrDefaultAsync(x => x.LoginProvider == "GitHub" && x.ProviderKey == newDonor.Key); 52 | if (login == null) continue; 53 | 54 | var user = await _appDbContext.Users.FirstOrDefaultAsync(x => x.Id == login.UserId && x.Level != newDonor.Value.Level); 55 | if (user == null) continue; 56 | 57 | user.Level = newDonor.Value.Level; 58 | 59 | await _appDbContext.SaveChangesAsync(); 60 | } 61 | 62 | //Hand edited donors 63 | foreach (var userMappingConfiguration in _userMappingConfigurationList.UserMappingConfigurations) 64 | { 65 | var user = await _appDbContext.Users.FirstOrDefaultAsync(x => x.UserName == userMappingConfiguration.User && x.Level != userMappingConfiguration.UserLevel); 66 | if (user == null) continue; 67 | if (user.Level == userMappingConfiguration.UserLevel) continue; 68 | 69 | user.Level = userMappingConfiguration.UserLevel; 70 | 71 | await _appDbContext.SaveChangesAsync(); 72 | } 73 | } 74 | 75 | } 76 | } -------------------------------------------------------------------------------- /Listrr/Jobs/RecurringJobs/UpdateAllListsRecurringJob.cs: -------------------------------------------------------------------------------- 1 | using Hangfire; 2 | using Hangfire.Server; 3 | 4 | using Listrr.Repositories; 5 | using Listrr.Services; 6 | 7 | using System.Threading.Tasks; 8 | 9 | namespace Listrr.Jobs.RecurringJobs 10 | { 11 | 12 | [Queue("system")] 13 | public class UpdateAllListsRecurringJob : IRecurringJob 14 | { 15 | private readonly ITraktListRepository _traktRepository; 16 | private readonly IBackgroundJobQueueService _backgroundJobQueueService; 17 | 18 | public UpdateAllListsRecurringJob(IBackgroundJobQueueService backgroundJobQueueService, ITraktListRepository traktRepository) 19 | { 20 | _backgroundJobQueueService = backgroundJobQueueService; 21 | _traktRepository = traktRepository; 22 | } 23 | 24 | 25 | public async Task Execute(PerformContext context) 26 | { 27 | var lists = await _traktRepository.Get(); 28 | 29 | foreach (var traktList in lists) 30 | { 31 | _backgroundJobQueueService.Queue(traktList, false, true); 32 | } 33 | } 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /Listrr/Migrations/20210209153744_1.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | namespace Listrr.Migrations 4 | { 5 | public partial class _1 : Migration 6 | { 7 | protected override void Up(MigrationBuilder migrationBuilder) 8 | { 9 | migrationBuilder.AddColumn( 10 | name: "ItemsByNameReport", 11 | table: "TraktLists", 12 | type: "longtext CHARACTER SET utf8mb4", 13 | nullable: true); 14 | } 15 | 16 | protected override void Down(MigrationBuilder migrationBuilder) 17 | { 18 | migrationBuilder.DropColumn( 19 | name: "ItemsByNameReport", 20 | table: "TraktLists"); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Listrr/Migrations/DataProtectionDb/20210121180408_Initial.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using Listrr.Data; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Migrations; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | 8 | namespace Listrr.Migrations.DataProtectionDb 9 | { 10 | [DbContext(typeof(DataProtectionDbContext))] 11 | [Migration("20210121180408_Initial")] 12 | partial class Initial 13 | { 14 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 15 | { 16 | #pragma warning disable 612, 618 17 | modelBuilder 18 | .HasAnnotation("Relational:MaxIdentifierLength", 64) 19 | .HasAnnotation("ProductVersion", "5.0.2"); 20 | 21 | modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => 22 | { 23 | b.Property("Id") 24 | .ValueGeneratedOnAdd() 25 | .HasColumnType("int"); 26 | 27 | b.Property("FriendlyName") 28 | .HasColumnType("longtext CHARACTER SET utf8mb4"); 29 | 30 | b.Property("Xml") 31 | .HasColumnType("longtext CHARACTER SET utf8mb4"); 32 | 33 | b.HasKey("Id"); 34 | 35 | b.ToTable("DataProtectionKeys"); 36 | }); 37 | #pragma warning restore 612, 618 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Listrr/Migrations/DataProtectionDb/20210121180408_Initial.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | namespace Listrr.Migrations.DataProtectionDb 5 | { 6 | public partial class Initial : Migration 7 | { 8 | protected override void Up(MigrationBuilder migrationBuilder) 9 | { 10 | migrationBuilder.CreateTable( 11 | name: "DataProtectionKeys", 12 | columns: table => new 13 | { 14 | Id = table.Column(type: "int", nullable: false) 15 | .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), 16 | FriendlyName = table.Column(type: "longtext CHARACTER SET utf8mb4", nullable: true), 17 | Xml = table.Column(type: "longtext CHARACTER SET utf8mb4", nullable: true) 18 | }, 19 | constraints: table => 20 | { 21 | table.PrimaryKey("PK_DataProtectionKeys", x => x.Id); 22 | }); 23 | } 24 | 25 | protected override void Down(MigrationBuilder migrationBuilder) 26 | { 27 | migrationBuilder.DropTable( 28 | name: "DataProtectionKeys"); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Listrr/Migrations/DataProtectionDb/DataProtectionDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using Listrr.Data; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 6 | 7 | namespace Listrr.Migrations.DataProtectionDb 8 | { 9 | [DbContext(typeof(DataProtectionDbContext))] 10 | partial class DataProtectionDbContextModelSnapshot : ModelSnapshot 11 | { 12 | protected override void BuildModel(ModelBuilder modelBuilder) 13 | { 14 | #pragma warning disable 612, 618 15 | modelBuilder 16 | .HasAnnotation("Relational:MaxIdentifierLength", 64) 17 | .HasAnnotation("ProductVersion", "5.0.2"); 18 | 19 | modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => 20 | { 21 | b.Property("Id") 22 | .ValueGeneratedOnAdd() 23 | .HasColumnType("int"); 24 | 25 | b.Property("FriendlyName") 26 | .HasColumnType("longtext CHARACTER SET utf8mb4"); 27 | 28 | b.Property("Xml") 29 | .HasColumnType("longtext CHARACTER SET utf8mb4"); 30 | 31 | b.HasKey("Id"); 32 | 33 | b.ToTable("DataProtectionKeys"); 34 | }); 35 | #pragma warning restore 612, 618 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Listrr/Models/CreateMovieListFileViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Listrr.Models 4 | { 5 | public class CreateMovieListFileViewModel 6 | { 7 | [Required] 8 | [Display(Name = "List Name", Prompt = "List Name")] 9 | public string Name { get; set; } 10 | 11 | [Required] 12 | [Display(Name = "List Items", Prompt = "List Items")] 13 | public string ItemList { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /Listrr/Models/CreateMovieListViewModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Rendering; 2 | 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations; 5 | 6 | namespace Listrr.Models 7 | { 8 | public class CreateMovieListViewModel : CreateListViewModel 9 | { 10 | #region Common filter 11 | 12 | [Display(Name = "Genres", Prompt = "action,adventure")] 13 | public IEnumerable Filter_Genres { get; set; } 14 | public MultiSelectList Genres { get; set; } 15 | 16 | #endregion 17 | 18 | #region Reverse Common filter 19 | 20 | [Display(Name = "Genres", Prompt = "action,adventure")] 21 | public IEnumerable ExclusionFilter_Genres { get; set; } 22 | public MultiSelectList ExclusionGenres { get; set; } 23 | 24 | #endregion 25 | 26 | 27 | #region Movie filter 28 | 29 | [Display(Name = "Certifications", Prompt = "r,pg-13")] 30 | public IEnumerable Filter_Certifications { get; set; } 31 | 32 | public MultiSelectList Certifications { get; set; } 33 | 34 | #endregion 35 | 36 | #region Reverse Movie filter 37 | 38 | [Display(Name = "Certifications", Prompt = "r,pg-13")] 39 | public IEnumerable ExclusionFilter_Certifications { get; set; } 40 | public MultiSelectList ExclusionCertifications { get; set; } 41 | 42 | #endregion 43 | 44 | } 45 | } -------------------------------------------------------------------------------- /Listrr/Models/CreateShowListFileViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Listrr.Models 4 | { 5 | public class CreateShowListFileViewModel 6 | { 7 | [Required] 8 | [Display(Name = "List Name", Prompt = "List Name")] 9 | public string Name { get; set; } 10 | 11 | [Required] 12 | [Display(Name = "List Items", Prompt = "List Items")] 13 | public string ItemList { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /Listrr/Models/CreateShowListViewModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Rendering; 2 | 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations; 5 | 6 | namespace Listrr.Models 7 | { 8 | public class CreateShowListViewModel : CreateListViewModel 9 | { 10 | 11 | #region Common filter 12 | 13 | [Display(Name = "Genres", Prompt = "action,adventure")] 14 | public IEnumerable Filter_Genres { get; set; } 15 | public MultiSelectList Genres { get; set; } 16 | 17 | #endregion 18 | 19 | #region Exclusion Common filter 20 | 21 | [Display(Name = "Genres", Prompt = "action,adventure")] 22 | public IEnumerable ExclusionFilter_Genres { get; set; } 23 | public MultiSelectList ExclusionGenres { get; set; } 24 | 25 | #endregion 26 | 27 | 28 | #region Show filter 29 | 30 | [Display(Name = "Certifications", Prompt = "r,pg-13")] 31 | public IEnumerable Filter_Certifications { get; set; } 32 | public MultiSelectList Certifications { get; set; } 33 | 34 | 35 | [Display(Name = "Networks", Prompt = "netflix,amazon")] 36 | public IEnumerable Filter_Networks { get; set; } 37 | public MultiSelectList Networks { get; set; } 38 | 39 | 40 | [Display(Name = "Status", Prompt = "planned,in production")] 41 | public IEnumerable Filter_Status { get; set; } 42 | public MultiSelectList Status { get; set; } 43 | 44 | #endregion 45 | 46 | #region Exclusion Show filter 47 | 48 | [Display(Name = "Certifications", Prompt = "r,pg-13")] 49 | public IEnumerable ExclusionFilter_Certifications { get; set; } 50 | public MultiSelectList ExclusionCertifications { get; set; } 51 | 52 | 53 | [Display(Name = "Networks", Prompt = "netflix,amazon")] 54 | public IEnumerable ExclusionFilter_Networks { get; set; } 55 | public MultiSelectList ExclusionNetworks { get; set; } 56 | 57 | 58 | [Display(Name = "Status", Prompt = "planned,in production")] 59 | public IEnumerable ExclusionFilter_Status { get; set; } 60 | public MultiSelectList ExclusionStatus { get; set; } 61 | 62 | #endregion 63 | 64 | } 65 | } -------------------------------------------------------------------------------- /Listrr/Models/DeleteListViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace Listrr.Models 2 | { 3 | public class DeleteListViewModel 4 | { 5 | 6 | public uint Id { get; set; } 7 | 8 | public int? Items { get; set; } 9 | 10 | public string Name { get; set; } 11 | 12 | } 13 | } -------------------------------------------------------------------------------- /Listrr/Models/EditMovieListFileViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Listrr.Models 4 | { 5 | public class EditMovieListFileViewModel : CreateMovieListFileViewModel 6 | { 7 | [Required] 8 | public uint Id { get; set; } 9 | 10 | [Display(Name = "Report", Prompt = "Report")] 11 | public string Report { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Listrr/Models/EditMovieListViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Listrr.Models 4 | { 5 | public class EditMovieListViewModel : CreateMovieListViewModel 6 | { 7 | [Required] 8 | public uint Id { get; set; } 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /Listrr/Models/EditShowListFileViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Listrr.Models 4 | { 5 | public class EditShowListFileViewModel : CreateShowListFileViewModel 6 | { 7 | [Required] 8 | public uint Id { get; set; } 9 | 10 | [Display(Name = "Report", Prompt = "Report")] 11 | public string Report { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Listrr/Models/EditShowListViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Listrr.Models 4 | { 5 | public class EditShowListViewModel : CreateShowListViewModel 6 | { 7 | [Required] 8 | public uint Id { get; set; } 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /Listrr/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace Listrr.Models 2 | { 3 | public class ErrorViewModel 4 | { 5 | public string RequestId { get; set; } 6 | 7 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 8 | } 9 | } -------------------------------------------------------------------------------- /Listrr/Models/MyViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using Listrr.Data; 4 | using Listrr.Data.Trakt; 5 | 6 | namespace Listrr.Models 7 | { 8 | public class MyViewModel 9 | { 10 | 11 | public User User { get; set; } 12 | 13 | public IList TraktLists { get; set; } 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Listrr/Models/PaginationViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Listrr.Models 4 | { 5 | public class PaginationViewModel 6 | { 7 | public int Pages { get; set; } 8 | 9 | public int Page { get; set; } 10 | 11 | public IList Items { get; set; } 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /Listrr/Models/WhyDonateViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace Listrr.Models 2 | { 3 | public class WhyDonateViewModel 4 | { 5 | 6 | public int Lists { get; set; } 7 | 8 | public int Users { get; set; } 9 | 10 | public int ListsWithExclusionFilters { get; set; } 11 | 12 | public int UsersWithExclusionFilters { get; set; } 13 | 14 | public int UserListLimit { get; set; } 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /Listrr/Program.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.Hosting; 6 | 7 | namespace Listrr 8 | { 9 | public class Program 10 | { 11 | public static void Main(string[] args) 12 | { 13 | CreateHostBuilder(args).Build().Run(); 14 | } 15 | 16 | public static IHostBuilder CreateHostBuilder(string[] args) => 17 | Host.CreateDefaultBuilder(args) 18 | .ConfigureAppConfiguration((hostingContext, config) => 19 | { 20 | config.SetBasePath(Directory.GetCurrentDirectory()); 21 | config.AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true); 22 | config.AddEnvironmentVariables(); 23 | }) 24 | .ConfigureWebHostDefaults(webBuilder => 25 | { 26 | webBuilder.UseStartup(); 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Listrr/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:62713", 7 | "sslPort": 44373 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "Listrr": { 19 | "commandName": "Project", 20 | "environmentVariables": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 24 | }, 25 | "Docker": { 26 | "commandName": "Docker", 27 | "launchBrowser": true, 28 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 29 | "publishAllPorts": true, 30 | "useSSL": true 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Listrr/Repositories/IIMDbRepository.cs: -------------------------------------------------------------------------------- 1 | using Listrr.Data.IMDb; 2 | 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace Listrr.Repositories 7 | { 8 | public interface IIMDbRepository 9 | { 10 | Task Get(string imdbId); 11 | 12 | Task Create(IMDbRating model); 13 | Task CreateRange(ICollection model); 14 | 15 | 16 | Task Create(IEnumerable models); 17 | 18 | Task Update(IMDbRating model); 19 | 20 | Task Purge(); 21 | } 22 | } -------------------------------------------------------------------------------- /Listrr/Repositories/IMDbRepository.cs: -------------------------------------------------------------------------------- 1 |  2 | using Listrr.Data; 3 | using Listrr.Data.IMDb; 4 | 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | 10 | namespace Listrr.Repositories 11 | { 12 | public class IMDbRepository : IIMDbRepository 13 | { 14 | private readonly AppDbContext _appDbContext; 15 | 16 | public IMDbRepository(AppDbContext appDbContext) 17 | { 18 | _appDbContext = appDbContext; 19 | } 20 | 21 | public async Task Get(string imdbId) 22 | { 23 | return await _appDbContext.ImDbRatings.FirstOrDefaultAsync(x => x.IMDbId == imdbId); 24 | } 25 | 26 | public async Task Create(IMDbRating model) 27 | { 28 | var result = await _appDbContext.ImDbRatings.AddAsync(model); 29 | 30 | await _appDbContext.SaveChangesAsync(); 31 | 32 | return result.Entity; 33 | } 34 | 35 | public async Task CreateRange(ICollection model) 36 | { 37 | await _appDbContext.ImDbRatings.AddRangeAsync(model); 38 | await _appDbContext.SaveChangesAsync(); 39 | } 40 | 41 | public async Task Create(IEnumerable models) 42 | { 43 | await _appDbContext.ImDbRatings.AddRangeAsync(models); 44 | 45 | await _appDbContext.SaveChangesAsync(); 46 | } 47 | 48 | public async Task Update(IMDbRating model) 49 | { 50 | var result = _appDbContext.ImDbRatings.Update(model); 51 | 52 | await _appDbContext.SaveChangesAsync(); 53 | 54 | return result.Entity; 55 | } 56 | 57 | 58 | public async Task Purge() 59 | { 60 | await _appDbContext.Database.ExecuteSqlRawAsync($"TRUNCATE TABLE {nameof(AppDbContext.ImDbRatings)}"); 61 | } 62 | 63 | } 64 | } -------------------------------------------------------------------------------- /Listrr/Repositories/ITraktCodeRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using Listrr.Data; 5 | 6 | namespace Listrr.Repositories 7 | { 8 | public interface ITraktCodeRepository 9 | { 10 | 11 | Task CreateCountryCode(CountryCode model); 12 | Task GetCountryCode(string name); 13 | Task> GetCountryCodes(); 14 | 15 | Task CreateLanguageCode(LanguageCode model); 16 | Task GetLanguageCode(string name); 17 | Task> GetLanguageCodes(); 18 | 19 | } 20 | } -------------------------------------------------------------------------------- /Listrr/Repositories/ITraktListRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using Listrr.Data; 5 | using Listrr.Data.Trakt; 6 | 7 | using Microsoft.AspNetCore.Identity; 8 | 9 | namespace Listrr.Repositories 10 | { 11 | public interface ITraktListRepository 12 | { 13 | Task> Get(); 14 | Task> Get(int take, int skip); 15 | 16 | Task Get(uint id); 17 | Task> Get(IdentityUser user); 18 | Task> Get(UserLevel userClass); 19 | Task> Get(UserLevel userClass, int take); 20 | 21 | Task GetNextForUpdate(UserLevel userLevel); 22 | 23 | Task Count(); 24 | 25 | Task Create(TraktList model); 26 | Task Update(TraktList model); 27 | 28 | Task Delete(TraktList model); 29 | } 30 | } -------------------------------------------------------------------------------- /Listrr/Repositories/ITraktMovieRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using Listrr.Data.Trakt; 5 | 6 | namespace Listrr.Repositories 7 | { 8 | public interface ITraktMovieRepository 9 | { 10 | Task CreateCertification(TraktMovieCertification model); 11 | Task> GetCertifications(); 12 | 13 | Task CreateGenre(TraktMovieGenre model); 14 | Task> GetGenres(); 15 | } 16 | } -------------------------------------------------------------------------------- /Listrr/Repositories/ITraktShowRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using Listrr.Data.Trakt; 5 | 6 | namespace Listrr.Repositories 7 | { 8 | public interface ITraktShowRepository 9 | { 10 | Task CreateCertification(TraktShowCertification model); 11 | Task> GetCertifications(); 12 | 13 | Task CreateGenre(TraktShowGenre model); 14 | Task> GetGenres(); 15 | 16 | Task CreateNetwork(TraktShowNetwork model); 17 | Task> GetNetworks(); 18 | 19 | Task CreateStatus(TraktShowStatus model); 20 | Task> GetStatuses(); 21 | } 22 | } -------------------------------------------------------------------------------- /Listrr/Repositories/IUserRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using Listrr.Data; 5 | 6 | namespace Listrr.Repositories 7 | { 8 | public interface IUserRepository 9 | { 10 | 11 | Task> Get(); 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /Listrr/Repositories/TraktCodeRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | using Listrr.Data; 6 | 7 | using Microsoft.EntityFrameworkCore; 8 | 9 | namespace Listrr.Repositories 10 | { 11 | public class TraktCodeRepository : ITraktCodeRepository 12 | { 13 | private readonly AppDbContext _appDbContext; 14 | 15 | public TraktCodeRepository(AppDbContext appDbContext) 16 | { 17 | _appDbContext = appDbContext; 18 | } 19 | 20 | public async Task CreateCountryCode(CountryCode model) 21 | { 22 | _appDbContext.CountryCodes.Add(model); 23 | await _appDbContext.SaveChangesAsync(); 24 | 25 | return model; 26 | } 27 | 28 | public async Task CreateLanguageCode(LanguageCode model) 29 | { 30 | _appDbContext.LanguageCodes.Add(model); 31 | await _appDbContext.SaveChangesAsync(); 32 | 33 | return model; 34 | } 35 | 36 | public async Task GetCountryCode(string name) 37 | { 38 | return await _appDbContext.CountryCodes.FirstOrDefaultAsync(x => x.Name == name); 39 | } 40 | 41 | public async Task> GetCountryCodes() 42 | { 43 | return await _appDbContext.CountryCodes.OrderBy(x => x.Name).ToListAsync(); 44 | } 45 | 46 | public async Task GetLanguageCode(string name) 47 | { 48 | return await _appDbContext.LanguageCodes.FirstOrDefaultAsync(x => x.Name == name); 49 | } 50 | 51 | public async Task> GetLanguageCodes() 52 | { 53 | return await _appDbContext.LanguageCodes.OrderBy(x => x.Name).ToListAsync(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Listrr/Repositories/TraktMovieRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | using Listrr.Data; 6 | using Listrr.Data.Trakt; 7 | 8 | using Microsoft.EntityFrameworkCore; 9 | 10 | namespace Listrr.Repositories 11 | { 12 | public class TraktMovieRepository : ITraktMovieRepository 13 | { 14 | private readonly AppDbContext _appDbContext; 15 | 16 | public TraktMovieRepository(AppDbContext appDbContext) 17 | { 18 | _appDbContext = appDbContext; 19 | } 20 | 21 | public async Task CreateCertification(TraktMovieCertification model) 22 | { 23 | await _appDbContext.TraktMovieCertifications.AddAsync(model); 24 | await _appDbContext.SaveChangesAsync(); 25 | 26 | return model; 27 | } 28 | 29 | public async Task CreateGenre(TraktMovieGenre model) 30 | { 31 | await _appDbContext.TraktMovieGenres.AddAsync(model); 32 | await _appDbContext.SaveChangesAsync(); 33 | 34 | return model; 35 | } 36 | 37 | public async Task> GetCertifications() 38 | { 39 | return await _appDbContext.TraktMovieCertifications.OrderBy(x => x.Description).ToListAsync(); 40 | } 41 | 42 | public async Task> GetGenres() 43 | { 44 | return await _appDbContext.TraktMovieGenres.ToListAsync(); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /Listrr/Repositories/TraktShowRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | using Listrr.Data; 6 | using Listrr.Data.Trakt; 7 | 8 | using Microsoft.EntityFrameworkCore; 9 | 10 | namespace Listrr.Repositories 11 | { 12 | public class TraktShowRepository : ITraktShowRepository 13 | { 14 | private readonly AppDbContext _appDbContext; 15 | 16 | public TraktShowRepository(AppDbContext appDbContext) 17 | { 18 | _appDbContext = appDbContext; 19 | } 20 | 21 | public async Task CreateCertification(TraktShowCertification model) 22 | { 23 | await _appDbContext.TraktShowCertifications.AddAsync(model); 24 | await _appDbContext.SaveChangesAsync(); 25 | 26 | return model; 27 | } 28 | 29 | public async Task CreateGenre(TraktShowGenre model) 30 | { 31 | await _appDbContext.TraktShowGenres.AddAsync(model); 32 | await _appDbContext.SaveChangesAsync(); 33 | 34 | return model; 35 | } 36 | 37 | public async Task CreateNetwork(TraktShowNetwork model) 38 | { 39 | await _appDbContext.TraktShowNetworks.AddAsync(model); 40 | await _appDbContext.SaveChangesAsync(); 41 | 42 | return model; 43 | } 44 | 45 | public async Task CreateStatus(TraktShowStatus model) 46 | { 47 | await _appDbContext.TraktShowStatuses.AddAsync(model); 48 | await _appDbContext.SaveChangesAsync(); 49 | 50 | return model; 51 | } 52 | 53 | public async Task> GetCertifications() 54 | { 55 | return await _appDbContext.TraktShowCertifications.OrderBy(x => x.Description).ToListAsync(); 56 | } 57 | 58 | public async Task> GetGenres() 59 | { 60 | return await _appDbContext.TraktShowGenres.ToListAsync(); 61 | } 62 | 63 | public async Task> GetNetworks() 64 | { 65 | return await _appDbContext.TraktShowNetworks.OrderBy(x => x.Name).ToListAsync(); 66 | } 67 | 68 | public async Task> GetStatuses() 69 | { 70 | return await _appDbContext.TraktShowStatuses.OrderBy(x => x.Name).ToListAsync(); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Listrr/Repositories/UserRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using Listrr.Data; 5 | 6 | using Microsoft.EntityFrameworkCore; 7 | 8 | namespace Listrr.Repositories 9 | { 10 | public class UserRepository : IUserRepository 11 | { 12 | private readonly AppDbContext _appDbContext; 13 | 14 | public UserRepository(AppDbContext appDbContext) 15 | { 16 | _appDbContext = appDbContext; 17 | } 18 | 19 | public async Task> Get() 20 | { 21 | return await _appDbContext.Users.ToListAsync(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Listrr/Services/BackgroundJobQueueService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Hangfire; 4 | 5 | using Listrr.Data; 6 | using Listrr.Data.Trakt; 7 | using Listrr.Jobs.BackgroundJobs; 8 | 9 | namespace Listrr.Services 10 | { 11 | public class BackgroundJobQueueService : IBackgroundJobQueueService 12 | { 13 | public void Queue(TraktList list, bool queueNext = false, bool forceRefresh = false) 14 | { 15 | switch (list.Type) 16 | { 17 | case ListType.Movie: 18 | MovieList(list, queueNext, forceRefresh); 19 | 20 | break; 21 | case ListType.Show: 22 | ShowList(list, queueNext, forceRefresh); 23 | 24 | break; 25 | default: 26 | throw new ArgumentOutOfRangeException(); 27 | } 28 | } 29 | 30 | 31 | private void MovieList(TraktList traktList, bool queueNext = false, bool forceRefresh = false) 32 | { 33 | switch (traktList.Owner.Level) 34 | { 35 | case UserLevel.User: 36 | BackgroundJob.Enqueue(x => x.Execute(traktList.Id, null, queueNext, forceRefresh)); 37 | 38 | break; 39 | case UserLevel.Donor: 40 | case UserLevel.DonorPlus: 41 | case UserLevel.DonorPlusPlus: 42 | case UserLevel.Developer: 43 | BackgroundJob.Enqueue(x => x.ExecutePriorized(traktList.Id, null, queueNext, forceRefresh)); 44 | 45 | break; 46 | default: 47 | throw new ArgumentOutOfRangeException(nameof(traktList.Owner.Level), traktList.Owner.Level, null); 48 | } 49 | } 50 | 51 | private void ShowList(TraktList traktList, bool queueNext = false, bool forceRefresh = false) 52 | { 53 | switch (traktList.Owner.Level) 54 | { 55 | case UserLevel.User: 56 | BackgroundJob.Enqueue(x => x.Execute(traktList.Id, null, queueNext, forceRefresh)); 57 | 58 | break; 59 | case UserLevel.Donor: 60 | case UserLevel.DonorPlus: 61 | case UserLevel.DonorPlusPlus: 62 | case UserLevel.Developer: 63 | BackgroundJob.Enqueue(x => x.ExecutePriorized(traktList.Id, null, queueNext, forceRefresh)); 64 | 65 | break; 66 | default: 67 | throw new ArgumentOutOfRangeException(nameof(traktList.Owner.Level), traktList.Owner.Level, null); 68 | } 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /Listrr/Services/GitHubGraphService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using GraphQL.Client.Http; 7 | using GraphQL.Client.Serializer.Newtonsoft; 8 | 9 | using Listrr.API.GitHub; 10 | using Listrr.Configuration; 11 | 12 | using GraphQLRequest = GraphQL.GraphQLRequest; 13 | 14 | namespace Listrr.Services 15 | { 16 | public class GitHubGraphService : IGitHubGraphService 17 | { 18 | private readonly GithubAPIConfiguration _githubApiConfiguration; 19 | private readonly LimitConfigurationList _limitConfigurationList; 20 | 21 | public GitHubGraphService(GithubAPIConfiguration githubApiConfiguration, LimitConfigurationList limitConfigurationList) 22 | { 23 | _githubApiConfiguration = githubApiConfiguration ?? throw new ArgumentNullException(nameof(githubApiConfiguration)); 24 | _limitConfigurationList = limitConfigurationList ?? throw new ArgumentNullException(nameof(limitConfigurationList)); 25 | } 26 | 27 | 28 | public async Task> GetDonor() 29 | { 30 | var result = new Dictionary(); 31 | 32 | var donorRequest = new GraphQLRequest 33 | { 34 | Query = @" 35 | query { 36 | viewer { 37 | sponsorshipsAsMaintainer (includePrivate: true, first: 100) { 38 | nodes { 39 | sponsor { 40 | login, 41 | databaseId 42 | }, 43 | tier { 44 | monthlyPriceInDollars 45 | } 46 | } 47 | } 48 | } 49 | }" 50 | }; 51 | 52 | var graphqlClient = new GraphQLHttpClient("https://api.github.com/graphql", new NewtonsoftJsonSerializer()); 53 | graphqlClient.HttpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_githubApiConfiguration.Token}"); 54 | graphqlClient.HttpClient.DefaultRequestHeaders.Add("User-Agent", "listrr.pro graphql client"); 55 | 56 | var graphqlResponse = await graphqlClient.SendQueryAsync(donorRequest); 57 | 58 | foreach (var node in graphqlResponse.Data.Viewer.SponsorshipsAsMaintainer.Nodes) 59 | { 60 | var limitConfig = _limitConfigurationList.LimitConfigurations.FirstOrDefault(x => x.Amount == Convert.ToInt32(node.Tier.MonthlyPriceInDollars)); 61 | 62 | if (limitConfig != null) 63 | result.Add(node.Sponsor.DatabaseId.ToString(), limitConfig); 64 | } 65 | 66 | return result; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /Listrr/Services/IBackgroundJobQueueService.cs: -------------------------------------------------------------------------------- 1 | using Listrr.Data.Trakt; 2 | 3 | namespace Listrr.Services 4 | { 5 | public interface IBackgroundJobQueueService 6 | { 7 | 8 | void Queue(TraktList list, bool queueNext = false, bool forceRefresh = false); 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /Listrr/Services/IGitHubGraphService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using Listrr.Configuration; 5 | 6 | namespace Listrr.Services 7 | { 8 | public interface IGitHubGraphService 9 | { 10 | 11 | Task> GetDonor(); 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /Listrr/Services/ITraktService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using Listrr.Data.Trakt; 5 | 6 | using TraktNet.Objects.Get.Movies; 7 | using TraktNet.Objects.Get.Shows; 8 | 9 | namespace Listrr.Services 10 | { 11 | public interface ITraktService 12 | { 13 | 14 | Task Get(TraktList model); 15 | Task> GetMovies(TraktList model); 16 | Task MovieSearch(TraktList model, string name, int year); 17 | 18 | Task> GetShows(TraktList model); 19 | 20 | Task Create(TraktList model); 21 | Task Update(TraktList model); 22 | Task Delete(TraktList model); 23 | 24 | Task Exists(TraktList model); 25 | 26 | 27 | Task AddMovies(IList movies, TraktList list); 28 | Task RemoveMovies(IEnumerable movies, TraktList list); 29 | 30 | 31 | Task AddShows(IList shows, TraktList list); 32 | Task RemoveShows(IEnumerable shows, TraktList list); 33 | 34 | 35 | Task> MovieSearch(TraktList model); 36 | Task> ShowSearch(TraktList model); 37 | Task ShowSearch(TraktList model, string name, int year); 38 | 39 | } 40 | } -------------------------------------------------------------------------------- /Listrr/Services/IUserLimitService.cs: -------------------------------------------------------------------------------- 1 | using Listrr.Configuration; 2 | using Listrr.Data; 3 | 4 | namespace Listrr.Services 5 | { 6 | public interface IUserLimitService 7 | { 8 | LimitConfiguration Get(UserLevel userLevel); 9 | } 10 | } -------------------------------------------------------------------------------- /Listrr/Services/UserLimitService.cs: -------------------------------------------------------------------------------- 1 | using Listrr.Configuration; 2 | using Listrr.Data; 3 | 4 | using System.Linq; 5 | 6 | namespace Listrr.Services 7 | { 8 | public class UserLimitService : IUserLimitService 9 | { 10 | private readonly LimitConfigurationList _limitConfigurationList; 11 | 12 | public UserLimitService(LimitConfigurationList limitConfigurationList) 13 | { 14 | _limitConfigurationList = limitConfigurationList; 15 | } 16 | 17 | 18 | public LimitConfiguration Get(UserLevel userLevel) 19 | { 20 | return _limitConfigurationList.LimitConfigurations.FirstOrDefault(x => x.Level == userLevel); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Listrr/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model PaginationViewModel 2 | 3 | @{ 4 | ViewData["Title"] = $"Welcome"; 5 | } 6 | 7 |
8 |
9 |

Welcome to listrr.pro!

10 |

With listrr.pro, you can create personalized trakt.tv lists for tv shows and movies with a variety of different filters, and use them with your ultimate PLEX setup.

11 |
12 |
13 | 14 |
15 |
16 |
17 |

Community driven

18 |

19 | The community decides! 20 |

21 |

22 | This project is driven by the awesome ideas of our community. 23 |

24 |

25 | The project is also funded entirely through donations of awesome people inside our community. Check out our discord for joining us. 26 |

27 |
28 |
29 |

Automated updates

30 |

31 | All lists you create get updated completely automated every few days. 32 |

33 |

34 | As a donor, your lists get updated every 24 hours at night time, or you can update them manually. 35 |

36 |

37 | Check our Why Donate? page for more information about donor perks! 38 |

39 |
40 |
41 |

42 | Open Source 43 |

44 |

45 | We aim to be as transparent as possible with processing your data, thats why we decided to have all the source code available on GitHub for everyone to take a look onto how data gets processed. 46 |

47 |

48 | Take a look at our code to make yourself familiar how listrr.pro works. Contributions are also welcome! 49 |

50 |
51 |
52 |
-------------------------------------------------------------------------------- /Listrr/Views/Home/Lists.cshtml: -------------------------------------------------------------------------------- 1 | @model PaginationViewModel 2 | 3 | @{ 4 | ViewData["Title"] = $"User lists"; 5 | ViewData["Message"] = $"All lists that have been created by our users"; 6 | } 7 | 8 |
9 | 10 | @if (Model.Pages > 0) 11 | { 12 | @await Html.PartialAsync("_PaginationPartial") 13 | } 14 | 15 | @if (Model.Items.Count == 0) 16 | { 17 |
18 | 21 |
22 | } 23 | else 24 | { 25 |
26 | 27 | 28 | 29 | 32 | 35 | 38 | 41 | 44 | 45 | 46 | @foreach (var list in Model.Items) 47 | { 48 | 49 | 52 | 53 | 54 | 55 | @if (User.Identity.IsAuthenticated) 56 | { 57 | 60 | } 61 | else 62 | { 63 | 66 | } 67 | 68 | } 69 |
30 | Name 31 | 33 | Type 34 | 36 | Items 37 | 39 | Likes 40 | 42 | Action 43 |
50 | @list.Name 51 | @list.Type@list.Items@list.Likes 58 | Open on Trakt 59 | 64 | Sign in to show link 65 |
70 |
71 | } 72 | 73 | @if (Model.Pages > 0) 74 | { 75 | @await Html.PartialAsync("_PaginationPartial") 76 | } 77 |
-------------------------------------------------------------------------------- /Listrr/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /Listrr/Views/List/Delete.cshtml: -------------------------------------------------------------------------------- 1 | @model DeleteListViewModel 2 | @{ 3 | ViewData["Title"] = "Delete list"; 4 | ViewData["Message"] = "Are you sure?"; 5 | } 6 | 7 | 8 | 9 |
10 |
11 | 21 |
22 |
-------------------------------------------------------------------------------- /Listrr/Views/List/MovieListFile.cshtml: -------------------------------------------------------------------------------- 1 | @model CreateMovieListFileViewModel 2 | @inject UserManager userManager 3 | @inject IUserLimitService UserLimitService 4 | 5 | @{ 6 | ViewData["Title"] = "Movie list"; 7 | ViewData["Message"] = "Create a new list for movies from a list of movie names"; 8 | 9 | var user = await userManager.GetUserAsync(User); 10 | var userLimitConfiguration = UserLimitService.Get(user.Level); 11 | } 12 | 13 | 14 | 15 | @if (!userLimitConfiguration.ListsFromNames) 16 | { 17 | 18 | } 19 | 20 |
21 |
22 |
23 | 24 |
25 |
26 |
List settings
27 |
All information that is needed to create a list
28 | 29 |
30 | 31 |
32 |
33 |
34 | 35 | * Give your list a name that makes it easier to find after creating. 36 | 37 |
38 |
39 |
40 | 43 | 59 | 60 | Example of how your list should look like 61 |
62 |
63 |
64 |
65 | 66 |
67 | 68 | @if (userLimitConfiguration.ListsFromNames) 69 | { 70 | 71 | } 72 |
73 |
74 |
-------------------------------------------------------------------------------- /Listrr/Views/List/ShowListFile.cshtml: -------------------------------------------------------------------------------- 1 | @model CreateShowListFileViewModel 2 | @inject UserManager userManager 3 | @inject IUserLimitService UserLimitService 4 | 5 | @{ 6 | ViewData["Title"] = "Show list"; 7 | ViewData["Message"] = "Create a new list for shows from a list of show names"; 8 | 9 | var user = await userManager.GetUserAsync(User); 10 | var userLimitConfiguration = UserLimitService.Get(user.Level); 11 | } 12 | 13 | 14 | 15 | @if (!userLimitConfiguration.ListsFromNames) 16 | { 17 | 18 | } 19 | 20 |
21 |
22 |
23 | 24 |
25 |
26 |
List settings
27 |
All information that is needed to create a list
28 | 29 |
30 | 31 |
32 |
33 |
34 | 35 | * Give your list a name that makes it easier to find after creating. 36 | 37 |
38 |
39 |
40 | 43 | 52 | 53 | Example of how your list should look like 54 |
55 |
56 |
57 |
58 | 59 |
60 | 61 | @if (userLimitConfiguration.ListsFromNames) 62 | { 63 | 64 | } 65 |
66 |
67 |
-------------------------------------------------------------------------------- /Listrr/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |
7 |
8 | 17 |
18 |
-------------------------------------------------------------------------------- /Listrr/Views/Shared/TraktBadGatewayException.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Error"; 3 | } 4 | 5 |
6 |
7 | 11 |
12 |
13 | 14 | @await Html.PartialAsync("_QuotePartial") -------------------------------------------------------------------------------- /Listrr/Views/Shared/TraktServerUnavailableException.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Error"; 3 | } 4 | 5 |
6 |
7 | 11 |
12 |
13 | 14 | @await Html.PartialAsync("_QuotePartial") -------------------------------------------------------------------------------- /Listrr/Views/Shared/_CreateByFileExplanationPartial.cshtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | 6 |
7 |
-------------------------------------------------------------------------------- /Listrr/Views/Shared/_DonorPerkExplanationPartial.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |
3 | 8 |
9 |
-------------------------------------------------------------------------------- /Listrr/Views/Shared/_LimitNotification.cshtml: -------------------------------------------------------------------------------- 1 | @using Listrr.Repositories 2 | @inject LimitConfigurationList LimitConfigurationList 3 | @inject UserManager UserManager 4 | @inject ITraktListRepository TraktListRepository 5 | 6 | @{ 7 | var user = await UserManager.GetUserAsync(User); 8 | var lists = await TraktListRepository.Get(user); 9 | var limitConfig = LimitConfigurationList.LimitConfigurations.First(x => x.Level == user.Level); 10 | } 11 | 12 | @if (lists.Count > limitConfig.ListLimit) 13 | { 14 |
15 |
16 | 23 |
24 |
25 | } 26 | else if (lists.Count == limitConfig.ListLimit) 27 | { 28 |
29 |
30 | 39 |
40 |
41 | } 42 | else 43 | { 44 |
45 |
46 | 55 |
56 |
57 | } -------------------------------------------------------------------------------- /Listrr/Views/Shared/_LoginPartial.cshtml: -------------------------------------------------------------------------------- 1 | @inject SignInManager SignInManager 2 | 3 | -------------------------------------------------------------------------------- /Listrr/Views/Shared/_PaginationPartial.cshtml: -------------------------------------------------------------------------------- 1 | @model PaginationViewModel 2 | 3 |
4 | 80 |
-------------------------------------------------------------------------------- /Listrr/Views/Shared/_PreviewDonorOnlyPartial.cshtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | 9 |
10 |
-------------------------------------------------------------------------------- /Listrr/Views/Shared/_QuotePartial.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |
3 | 9 |
10 |
-------------------------------------------------------------------------------- /Listrr/Views/Shared/_ReverseFiltersExplanationPartial.cshtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | 6 |
7 |
-------------------------------------------------------------------------------- /Listrr/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /Listrr/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Listrr 2 | @using Listrr.Configuration 3 | @using Listrr.Models 4 | @using Listrr.Data 5 | @using Listrr.Data.Trakt 6 | @using Listrr.Services 7 | @using Microsoft.AspNetCore.Identity 8 | 9 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 10 | -------------------------------------------------------------------------------- /Listrr/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /Listrr/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | /* Provide sufficient contrast against white background */ 11 | a { 12 | color: #0366d6; 13 | } 14 | 15 | .btn-primary { 16 | color: #fff; 17 | background-color: #1b6ec2; 18 | border-color: #1861ac; 19 | } 20 | 21 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link { 22 | color: #fff; 23 | background-color: #1b6ec2; 24 | border-color: #1861ac; 25 | } 26 | 27 | /* Sticky footer styles 28 | -------------------------------------------------- */ 29 | html { 30 | font-size: 14px; 31 | } 32 | @media (min-width: 768px) { 33 | html { 34 | font-size: 16px; 35 | } 36 | } 37 | 38 | .border-top { 39 | border-top: 1px solid #e5e5e5; 40 | } 41 | .border-bottom { 42 | border-bottom: 1px solid #e5e5e5; 43 | } 44 | 45 | .box-shadow { 46 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 47 | } 48 | 49 | button.accept-policy { 50 | font-size: 1rem; 51 | line-height: inherit; 52 | } 53 | 54 | /* Sticky footer styles 55 | -------------------------------------------------- */ 56 | html { 57 | position: relative; 58 | min-height: 100%; 59 | } 60 | 61 | body { 62 | /* Margin bottom by footer height */ 63 | margin-bottom: 60px; 64 | } 65 | .footer { 66 | position: absolute; 67 | bottom: 0; 68 | width: 100%; 69 | white-space: nowrap; 70 | line-height: 60px; /* Vertically center the text there */ 71 | } 72 | 73 | 74 | 75 | .select2-container--default .select2-selection--multiple { 76 | background-color: black; 77 | } 78 | 79 | .select2-results__options { 80 | background: black; 81 | } 82 | 83 | .select2-container--default .select2-selection--multiple .select2-selection__choice { 84 | background-color: #444; 85 | border: 1px solid #aaa; 86 | } 87 | 88 | .select2-container--default .select2-results__option[aria-selected=true] { 89 | background-color: #444; 90 | } 91 | 92 | .select2-container .select2-search--inline .select2-search__field { 93 | color: #aaa; 94 | } -------------------------------------------------------------------------------- /Listrr/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheUltimateC0der/listrr/31fc23bf53ad47f6b825a90363e9e1b28b8d2145/Listrr/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Listrr/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | $(document).ready(function () { 6 | $('.select2').select2(); 7 | }); -------------------------------------------------------------------------------- /Listrr/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2018 Twitter, Inc. 4 | Copyright (c) 2011-2018 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Listrr/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Listrr/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Listrr/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /Listrr/wwwroot/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheUltimateC0der/listrr/31fc23bf53ad47f6b825a90363e9e1b28b8d2145/Listrr/wwwroot/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /Listrr/wwwroot/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheUltimateC0der/listrr/31fc23bf53ad47f6b825a90363e9e1b28b8d2145/Listrr/wwwroot/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /Listrr/wwwroot/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheUltimateC0der/listrr/31fc23bf53ad47f6b825a90363e9e1b28b8d2145/Listrr/wwwroot/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /Listrr/wwwroot/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheUltimateC0der/listrr/31fc23bf53ad47f6b825a90363e9e1b28b8d2145/Listrr/wwwroot/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /Listrr/wwwroot/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheUltimateC0der/listrr/31fc23bf53ad47f6b825a90363e9e1b28b8d2145/Listrr/wwwroot/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /Listrr/wwwroot/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheUltimateC0der/listrr/31fc23bf53ad47f6b825a90363e9e1b28b8d2145/Listrr/wwwroot/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /Listrr/wwwroot/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheUltimateC0der/listrr/31fc23bf53ad47f6b825a90363e9e1b28b8d2145/Listrr/wwwroot/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /Listrr/wwwroot/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheUltimateC0der/listrr/31fc23bf53ad47f6b825a90363e9e1b28b8d2145/Listrr/wwwroot/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /Listrr/wwwroot/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheUltimateC0der/listrr/31fc23bf53ad47f6b825a90363e9e1b28b8d2145/Listrr/wwwroot/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /Listrr/wwwroot/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheUltimateC0der/listrr/31fc23bf53ad47f6b825a90363e9e1b28b8d2145/Listrr/wwwroot/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /Listrr/wwwroot/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheUltimateC0der/listrr/31fc23bf53ad47f6b825a90363e9e1b28b8d2145/Listrr/wwwroot/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /Listrr/wwwroot/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheUltimateC0der/listrr/31fc23bf53ad47f6b825a90363e9e1b28b8d2145/Listrr/wwwroot/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What Listrr does 2 | Listrr creates lists for shows and movies based on your filters. The created lists get updated every 24 hours based on your filters, so Listrr will add all new items that match your filters, and will also remove all items that do not match your filter configuration anymore. 3 | 4 | [![Website: https://listrr.pro](https://img.shields.io/badge/Website-https%3A%2F%2Flistrr.pro-blue.svg?style=for-the-badge&colorB=177DC1&label=website)](https://listrr.pro) 5 | [![Discord: https://discord.gg/6PQseA7](https://img.shields.io/discord/632546249819553812?color=blue&label=Discord&style=for-the-badge)](https://discord.gg/6PQseA7) 6 | [![GitHub: Last commit date (develop)](https://img.shields.io/github/last-commit/TheUltimateC0der/Listrr/develop.svg?style=for-the-badge&colorB=177DC1)](https://github.com/TheUltimateC0der/Listrr/commits/develop) 7 |
8 | [![Docker Automated build: theultimatecoder/listrr.pro](https://img.shields.io/docker/cloud/automated/theultimatecoder/listrr.pro?color=blue&style=for-the-badge)](https://hub.docker.com/r/theultimatecoder/listrr.pro) 9 | [![Docker Build Status: theultimatecoder/listrr.pro](https://img.shields.io/docker/cloud/build/theultimatecoder/listrr.pro?color=blue&style=for-the-badge)](https://hub.docker.com/r/theultimatecoder/listrr.pro) 10 | [![Docker Pulls: theultimatecoder/listrr.pro](https://img.shields.io/docker/pulls/theultimatecoder/listrr.pro?color=blue&style=for-the-badge)](https://hub.docker.com/r/theultimatecoder/listrr.pro) 11 | 12 | # What can I use this for ? 13 | 14 | ## ![Radarr Logo](https://radarr.video/img/logo.png "Radarr") Radarr 15 | You can create lists with listrr and add them to your Radarr instance. Radarr automatically adds the items from the list. 16 | 17 | ## ![Sonarr Logo](https://sonarr.tv/img/logo.png "Sonarr") Sonarr 18 | You can create lists with listrr and add them to your Sonarr instance. Sonarr automatically adds the items from the list. 19 | 20 | ## Traktarr 21 | You can create lists with listrr and add them to your traktarr configuration. Traktarr automatically adds the items to your *arr instances. 22 | 23 | ## Python-PlexLibrary 24 | You can create libraries from your the trakt lists you created with Listrr. As your lists get updated every 24 hours, so does your libraries. --------------------------------------------------------------------------------