diff --git a/.github/workflows/coding-style.yml b/.github/workflows/coding-style.yml index eb14d7a6..c596b6d7 100644 --- a/.github/workflows/coding-style.yml +++ b/.github/workflows/coding-style.yml @@ -2,20 +2,6 @@ name: Coding Style on: [pull_request, workflow_dispatch] jobs: - back: - name: "Lint Back" - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./back - steps: - - uses: actions/checkout@v5 - - - name: Check coding style - run: | - dotnet tool restore - dotnet csharpier . --check - api: name: "Lint api" runs-on: ubuntu-latest diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a57cfa62..0dbcc976 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -19,16 +19,6 @@ jobs: fail-fast: false matrix: include: - - context: ./back - dockerfile: Dockerfile - label: back - image: ${{ github.repository_owner }}/kyoo_back - - - context: ./back - dockerfile: Dockerfile.migrations - label: migrations - image: ${{ github.repository_owner }}/kyoo_migrations - - context: ./api dockerfile: Dockerfile label: api diff --git a/back/.config/dotnet-tools.json b/back/.config/dotnet-tools.json deleted file mode 100644 index 4cdf7699..00000000 --- a/back/.config/dotnet-tools.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": 1, - "isRoot": true, - "tools": { - "dotnet-ef": { - "version": "8.0.21", - "commands": [ - "dotnet-ef" - ] - }, - "csharpier": { - "version": "0.28.2", - "commands": [ - "dotnet-csharpier" - ] - } - } -} \ No newline at end of file diff --git a/back/.dockerignore b/back/.dockerignore deleted file mode 100644 index 9f8aece3..00000000 --- a/back/.dockerignore +++ /dev/null @@ -1,17 +0,0 @@ -Dockerfile -Dockerfile.dev -Dockerfile.* -.dockerignore -.gitignore -docker-compose.yml -README.md -**/build -**/dist -**/bin -**/obj -out -docs -tests -front -video -nginx.conf.template diff --git a/back/.editorconfig b/back/.editorconfig deleted file mode 100644 index 30728be6..00000000 --- a/back/.editorconfig +++ /dev/null @@ -1,99 +0,0 @@ -root = false - -[*] -charset = utf-8 -end_of_line = lf -trim_trailing_whitespace = true -insert_final_newline = true -indent_style = tab -indent_size = tab -smart_tab = true - -[*.cs] -csharp_prefer_braces = false -dotnet_diagnostic.IDE0046.severity = none -dotnet_diagnostic.IDE0055.severity = none -dotnet_diagnostic.IDE0058.severity = none -dotnet_diagnostic.IDE0130.severity = none - -# Convert to file-scoped namespace -csharp_style_namespace_declarations = file_scoped:warning -# Sort using and Import directives with System.* appearing first -dotnet_sort_system_directives_first = true -csharp_using_directive_placement = outside_namespace:warning -# Avoid "this." if not necessary -dotnet_style_qualification_for_field = false:suggestion -dotnet_style_qualification_for_property = false:suggestion -dotnet_style_qualification_for_method = false:suggestion -dotnet_style_qualification_for_event = false:suggestion -# Use language keywords instead of framework type names for type references -dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion -dotnet_style_predefined_type_for_member_access = true:suggestion -# Suggest more modern language features when available -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_explicit_tuple_names = true:suggestion -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion -csharp_style_conditional_delegate_call = true:suggestion -dotnet_style_prefer_auto_properties = true -dotnet_style_prefer_conditional_expression_over_assignment = true -dotnet_style_prefer_conditional_expression_over_return = true -# Disable strange throw. -csharp_style_throw_expression = false:suggestion -# Forbid "var" everywhere -csharp_style_var_for_built_in_types = false:suggestion -csharp_style_var_when_type_is_apparent = false:suggestion -csharp_style_var_elsewhere = false:suggestion -# Prefer method-like constructs to have a block body -csharp_style_expression_bodied_methods = false:none -csharp_style_expression_bodied_constructors = false:none -csharp_style_expression_bodied_operators = false:none -# Prefer property-like constructs to have an expression-body -csharp_style_expression_bodied_properties = true:none -csharp_style_expression_bodied_indexers = true:none -csharp_style_expression_bodied_accessors = true:none -# Newline settings -csharp_new_line_before_open_brace = all -csharp_new_line_before_else = true -csharp_new_line_before_catch = true -csharp_new_line_before_finally = true -csharp_new_line_before_members_in_object_initializers = false -csharp_new_line_before_members_in_anonymous_types = true -# Indentation settings -csharp_indent_case_contents = true -csharp_indent_switch_labels = true -# Modifiers -dotnet_style_readonly_field = true:suggestion -dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion -# Naming style -dotnet_naming_symbols.privates.applicable_kinds = property,method,event,delegate -dotnet_naming_symbols.privates.applicable_accessibilities = private -dotnet_naming_style.underscore_pascal.capitalization = pascal_case -dotnet_naming_style.underscore_pascal.required_prefix = _ -dotnet_naming_rule.privates_with_underscore.symbols = privates -dotnet_naming_rule.privates_with_underscore.style = underscore_pascal -dotnet_naming_rule.privates_with_underscore.severity = warning -dotnet_diagnostic.IDE1006.severity = warning -# ReSharper properties -resharper_align_multiline_binary_expressions_chain = false -resharper_csharp_empty_block_style = together_same_line -resharper_indent_nested_foreach_stmt = true -resharper_indent_nested_for_stmt = true -resharper_indent_nested_while_stmt = true -resharper_keep_existing_embedded_arrangement = false -resharper_place_accessorholder_attribute_on_same_line = true -resharper_place_simple_embedded_statement_on_same_line = false -resharper_wrap_before_arrow_with_expressions = true -resharper_xmldoc_attribute_indent = align_by_first_attribute -resharper_xmldoc_indent_child_elements = RemoveIndent -resharper_xmldoc_indent_text = RemoveIndent -# Switch on enum -dotnet_diagnostic.CS8509.severity=error # missing switch case for named enum value -dotnet_diagnostic.CS8524.severity=none # missing switch case for unnamed enum value - -# Waiting for https://github.com/dotnet/roslyn/issues/44596 to get fixed. -# file_header_template = Kyoo - A portable and vast media library solution.\nCopyright (c) Kyoo.\n\nSee AUTHORS.md and LICENSE file in the project root for full license information.\n\nKyoo is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nKyoo is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Kyoo. If not, see . diff --git a/back/.env.example b/back/.env.example deleted file mode 100644 index 42beea2d..00000000 --- a/back/.env.example +++ /dev/null @@ -1,37 +0,0 @@ -# vi: ft=sh -# shellcheck disable=SC2034 - -# http route prefix (will listen to $KYOO_PREFIX/movie for example) -KYOO_PREFIX="" - - -# Optional authentication settings -# Set to true to disable login with password (OIDC auth must be configured) -# AUTHENTICATION_DISABLE_PASSWORD_LOGIN=true -# Set to true to disable the creation of new users (OIDC auth must be configured) -# AUTHENTICATION_DISABLE_USER_REGISTRATION=true - -# Postgres settings -# POSTGRES_URL=postgres://user:password@hostname:port/dbname?sslmode=verify-full&sslrootcert=/path/to/server.crt&sslcert=/path/to/client.crt&sslkey=/path/to/client.key -# The behavior of the below variables match what is documented here: -# https://www.postgresql.org/docs/current/libpq-envars.html -PGUSER=kyoo -PGPASSWORD=password -PGDB=kyooDB -PGSERVER=postgres -PGPORT=5432 -# PGOPTIONS=-c search_path=kyoo,public -# PGPASSFILE=/my/password # Takes precedence over PGPASSWORD. New line characters are not trimmed. -# PGSSLMODE=verify-full -# PGSSLROOTCERT=/my/serving.crt -# PGSSLCERT=/my/client.crt -# PGSSLKEY=/my/client.key - -# RabbitMQ settings -# Full list of options: https://www.rabbitmq.com/uri-spec.html, https://www.rabbitmq.com/docs/uri-query-parameters -# RABBITMQ_URL=amqps://user:password@rabbitmq-server:1234/vhost?cacertfile=/path/to/cacert.pem&certfile=/path/to/cert.pem&keyfile=/path/to/key.pem&verify=verify_peer&auth_mechanism=EXTERNAL -# These values override what is provided the the URL variable -RABBITMQ_DEFAULT_USER=guest -RABBITMQ_DEFAULT_PASS=guest -RABBITMQ_HOST=rabbitmq -RABBITMQ_PORT=5672 diff --git a/back/.gitignore b/back/.gitignore deleted file mode 100644 index 906935b3..00000000 --- a/back/.gitignore +++ /dev/null @@ -1,352 +0,0 @@ -out -libtranscoder.so -libtranscoder.dylib -transcoder.dll -kyoo_datadir - -video -.env - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- Backup*.rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb -/Kyoo/TheTVDB-Credentials.json - -.vscode -.netcoredbg_hist diff --git a/back/Dockerfile b/back/Dockerfile deleted file mode 100644 index 4bbc4f5b..00000000 --- a/back/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 as builder -ARG TARGETARCH -WORKDIR /kyoo - -COPY Kyoo.sln ./Kyoo.sln -COPY nuget.config ./nuget.config -COPY src/Directory.Build.props src/Directory.Build.props -COPY src/Kyoo.Authentication/Kyoo.Authentication.csproj src/Kyoo.Authentication/Kyoo.Authentication.csproj -COPY src/Kyoo.Abstractions/Kyoo.Abstractions.csproj src/Kyoo.Abstractions/Kyoo.Abstractions.csproj -COPY src/Kyoo.Core/Kyoo.Core.csproj src/Kyoo.Core/Kyoo.Core.csproj -COPY src/Kyoo.Postgresql/Kyoo.Postgresql.csproj src/Kyoo.Postgresql/Kyoo.Postgresql.csproj -COPY src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj -COPY src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj -COPY src/Kyoo.Swagger/Kyoo.Swagger.csproj src/Kyoo.Swagger/Kyoo.Swagger.csproj -RUN dotnet restore -a $TARGETARCH - -COPY . . -ARG VERSION -RUN dotnet publish -a $TARGETARCH --no-restore -c Release -o /app "-p:Version=${VERSION:-"0.0.0-dev"}" src/Kyoo.Core - -FROM mcr.microsoft.com/dotnet/aspnet:8.0 -RUN apt-get update && apt-get install -y curl -COPY --from=builder /app /app - -WORKDIR /app -EXPOSE 5000 -# The back can take a long time to start if meilisearch is initializing -HEALTHCHECK --interval=30s --retries=15 CMD curl --fail http://localhost:5000/health || exit -ENTRYPOINT ["/app/kyoo"] diff --git a/back/Dockerfile.dev b/back/Dockerfile.dev deleted file mode 100644 index b4428070..00000000 --- a/back/Dockerfile.dev +++ /dev/null @@ -1,22 +0,0 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0 -RUN apt-get update && apt-get install -y curl -WORKDIR /app - -COPY Kyoo.sln ./Kyoo.sln -COPY nuget.config ./nuget.config -COPY src/Directory.Build.props src/Directory.Build.props -COPY src/Kyoo.Authentication/Kyoo.Authentication.csproj src/Kyoo.Authentication/Kyoo.Authentication.csproj -COPY src/Kyoo.Abstractions/Kyoo.Abstractions.csproj src/Kyoo.Abstractions/Kyoo.Abstractions.csproj -COPY src/Kyoo.Core/Kyoo.Core.csproj src/Kyoo.Core/Kyoo.Core.csproj -COPY src/Kyoo.Postgresql/Kyoo.Postgresql.csproj src/Kyoo.Postgresql/Kyoo.Postgresql.csproj -COPY src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj -COPY src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj -COPY src/Kyoo.Swagger/Kyoo.Swagger.csproj src/Kyoo.Swagger/Kyoo.Swagger.csproj -RUN dotnet restore - -WORKDIR /app -EXPOSE 5000 -ENV DOTNET_USE_POLLING_FILE_WATCHER 1 -# HEALTHCHECK --interval=30s CMD curl --fail http://localhost:5000/health || exit -HEALTHCHECK CMD true -ENTRYPOINT ["dotnet", "watch", "--non-interactive", "run", "--no-restore", "--project", "/app/src/Kyoo.Core"] diff --git a/back/Dockerfile.migrations b/back/Dockerfile.migrations deleted file mode 100644 index 2278e642..00000000 --- a/back/Dockerfile.migrations +++ /dev/null @@ -1,30 +0,0 @@ -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 as builder -ARG TARGETARCH -WORKDIR /kyoo - -COPY .config/dotnet-tools.json .config/dotnet-tools.json -RUN dotnet tool restore - -COPY Kyoo.sln ./Kyoo.sln -COPY nuget.config ./nuget.config -COPY src/Directory.Build.props src/Directory.Build.props -COPY src/Kyoo.Authentication/Kyoo.Authentication.csproj src/Kyoo.Authentication/Kyoo.Authentication.csproj -COPY src/Kyoo.Abstractions/Kyoo.Abstractions.csproj src/Kyoo.Abstractions/Kyoo.Abstractions.csproj -COPY src/Kyoo.Core/Kyoo.Core.csproj src/Kyoo.Core/Kyoo.Core.csproj -COPY src/Kyoo.Postgresql/Kyoo.Postgresql.csproj src/Kyoo.Postgresql/Kyoo.Postgresql.csproj -COPY src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj -COPY src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj -COPY src/Kyoo.Swagger/Kyoo.Swagger.csproj src/Kyoo.Swagger/Kyoo.Swagger.csproj -RUN dotnet restore -a $TARGETARCH - -COPY . . -RUN dotnet build -RUN dotnet ef migrations bundle \ - --msbuildprojectextensionspath out/obj/Kyoo.Postgresql \ - --no-build --self-contained -r linux-${TARGETARCH} -f \ - -o /app/migrate -p src/Kyoo.Postgresql --verbose - -FROM mcr.microsoft.com/dotnet/runtime-deps:8.0 -COPY --from=builder /app/migrate /app/migrate - -ENTRYPOINT ["/app/migrate"] diff --git a/back/Kyoo.ruleset b/back/Kyoo.ruleset deleted file mode 100644 index 82ed916b..00000000 --- a/back/Kyoo.ruleset +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/back/Kyoo.sln b/back/Kyoo.sln deleted file mode 100644 index 1c1c9fd5..00000000 --- a/back/Kyoo.sln +++ /dev/null @@ -1,64 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kyoo.Core", "src\Kyoo.Core\Kyoo.Core.csproj", "{0F8275B6-C7DD-42DF-A168-755C81B1C329}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Abstractions", "src\Kyoo.Abstractions\Kyoo.Abstractions.csproj", "{BAB2CAE1-AC28-4509-AA3E-8DC75BD59220}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Postgresql", "src\Kyoo.Postgresql\Kyoo.Postgresql.csproj", "{3213C96D-0BF3-460B-A8B5-B9977229408A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Authentication", "src\Kyoo.Authentication\Kyoo.Authentication.csproj", "{7A841335-6523-47DB-9717-80AA7BD943FD}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Swagger", "src\Kyoo.Swagger\Kyoo.Swagger.csproj", "{7D1A7596-73F6-4D35-842E-A5AD9C620596}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Meilisearch", "src\Kyoo.Meilisearch\Kyoo.Meilisearch.csproj", "{F8E6018A-FD51-40EB-99FF-A26BA59F2762}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.RabbitMq", "src\Kyoo.RabbitMq\Kyoo.RabbitMq.csproj", "{B97AD4A8-E6E6-41CD-87DF-5F1326FD7198}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0F8275B6-C7DD-42DF-A168-755C81B1C329}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0F8275B6-C7DD-42DF-A168-755C81B1C329}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0F8275B6-C7DD-42DF-A168-755C81B1C329}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0F8275B6-C7DD-42DF-A168-755C81B1C329}.Release|Any CPU.Build.0 = Release|Any CPU - {BAB2CAE1-AC28-4509-AA3E-8DC75BD59220}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BAB2CAE1-AC28-4509-AA3E-8DC75BD59220}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BAB2CAE1-AC28-4509-AA3E-8DC75BD59220}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BAB2CAE1-AC28-4509-AA3E-8DC75BD59220}.Release|Any CPU.Build.0 = Release|Any CPU - {3213C96D-0BF3-460B-A8B5-B9977229408A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3213C96D-0BF3-460B-A8B5-B9977229408A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3213C96D-0BF3-460B-A8B5-B9977229408A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3213C96D-0BF3-460B-A8B5-B9977229408A}.Release|Any CPU.Build.0 = Release|Any CPU - {7A841335-6523-47DB-9717-80AA7BD943FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7A841335-6523-47DB-9717-80AA7BD943FD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7A841335-6523-47DB-9717-80AA7BD943FD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7A841335-6523-47DB-9717-80AA7BD943FD}.Release|Any CPU.Build.0 = Release|Any CPU - {6515380E-1E57-42DA-B6E3-E1C8A848818A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6515380E-1E57-42DA-B6E3-E1C8A848818A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6515380E-1E57-42DA-B6E3-E1C8A848818A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6515380E-1E57-42DA-B6E3-E1C8A848818A}.Release|Any CPU.Build.0 = Release|Any CPU - {2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Release|Any CPU.Build.0 = Release|Any CPU - {4FF1ECD9-6EEF-4440-B037-A661D78FB04D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4FF1ECD9-6EEF-4440-B037-A661D78FB04D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4FF1ECD9-6EEF-4440-B037-A661D78FB04D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4FF1ECD9-6EEF-4440-B037-A661D78FB04D}.Release|Any CPU.Build.0 = Release|Any CPU - {7D1A7596-73F6-4D35-842E-A5AD9C620596}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7D1A7596-73F6-4D35-842E-A5AD9C620596}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7D1A7596-73F6-4D35-842E-A5AD9C620596}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7D1A7596-73F6-4D35-842E-A5AD9C620596}.Release|Any CPU.Build.0 = Release|Any CPU - {F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Release|Any CPU.Build.0 = Release|Any CPU - {B97AD4A8-E6E6-41CD-87DF-5F1326FD7198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B97AD4A8-E6E6-41CD-87DF-5F1326FD7198}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B97AD4A8-E6E6-41CD-87DF-5F1326FD7198}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B97AD4A8-E6E6-41CD-87DF-5F1326FD7198}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/back/ef.rsp b/back/ef.rsp deleted file mode 100644 index de6fae14..00000000 --- a/back/ef.rsp +++ /dev/null @@ -1,4 +0,0 @@ ---project -src/Kyoo.Postgresql ---msbuildprojectextensionspath -out/obj/Kyoo.Postgresql diff --git a/back/icon.ico b/back/icon.ico deleted file mode 100644 index 8a6ef232..00000000 Binary files a/back/icon.ico and /dev/null differ diff --git a/back/nuget.config b/back/nuget.config deleted file mode 100644 index 36efeadd..00000000 --- a/back/nuget.config +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/back/shell.nix b/back/shell.nix deleted file mode 100644 index b4fd8ad0..00000000 --- a/back/shell.nix +++ /dev/null @@ -1,15 +0,0 @@ -{pkgs ? import {}}: let - dotnet = with pkgs.dotnetCorePackages; - combinePackages [ - sdk_8_0 - aspnetcore_8_0 - ]; -in - pkgs.mkShell { - packages = with pkgs; [ - dotnet - csharpier - ]; - - DOTNET_ROOT = "${dotnet}"; - } diff --git a/back/src/Directory.Build.props b/back/src/Directory.Build.props deleted file mode 100644 index 76832704..00000000 --- a/back/src/Directory.Build.props +++ /dev/null @@ -1,47 +0,0 @@ - - - net8.0 - default - enable - Kyoo - Kyoo - Copyright (c) Kyoo - true - GPL-3.0-or-later - true - - https://github.com/zoriya/Kyoo - git - true - https://github.com/zoriya/Kyoo - - 1.0.0 - true - snupkg - - $(MSBuildThisFileDirectory)../icon.ico - - true - - - - true - - - - $(MsBuildThisFileDirectory)/../out/obj/$(MSBuildProjectName) - $(MsBuildThisFileDirectory)/../out/bin/$(MSBuildProjectName) - - - - - - - - $(MSBuildThisFileDirectory)../Kyoo.ruleset - 1591;1305;8618;SYSLIB1045;CS1573 - - - - - diff --git a/back/src/Kyoo.Abstractions/.gitignore b/back/src/Kyoo.Abstractions/.gitignore deleted file mode 100644 index 8f7864db..00000000 --- a/back/src/Kyoo.Abstractions/.gitignore +++ /dev/null @@ -1,232 +0,0 @@ -## PROJECT CUSTOM IGNORES - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -build/ -bld/ -bin/ -Bin/ -obj/ -Obj/ - -# Visual Studio 2015 cache/options directory -.vs/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Microsoft Azure ApplicationInsights config file -ApplicationInsights.config - -# Windows Store app package directory -AppPackages/ -BundleArtifacts/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.pfx -*.publishsettings -orleans.codegen.cs - -/node_modules - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe - -# FAKE - F# Make -.fake/ diff --git a/back/src/Kyoo.Abstractions/Controllers/IIssueRepository.cs b/back/src/Kyoo.Abstractions/Controllers/IIssueRepository.cs deleted file mode 100644 index 831e50bb..00000000 --- a/back/src/Kyoo.Abstractions/Controllers/IIssueRepository.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Threading.Tasks; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Utils; - -namespace Kyoo.Abstractions.Controllers; - -public interface IIssueRepository -{ - Task> GetAll(Filter? filter = default); - - Task GetCount(Filter? filter = default); - - Task Upsert(Issue issue); - - Task DeleteAll(Filter? filter = default); -} diff --git a/back/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs b/back/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs deleted file mode 100644 index 8a1516b2..00000000 --- a/back/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using Kyoo.Abstractions.Models; - -namespace Kyoo.Abstractions.Controllers; - -/// -/// An interface to interact with the database. Every repository is mapped through here. -/// -public interface ILibraryManager -{ - IRepository Repository() - where T : IResource, IQuery; - - /// - /// The repository that handle libraries items (a wrapper around shows and collections). - /// - IRepository LibraryItems { get; } - - /// - /// The repository that handle new items. - /// - IRepository News { get; } - - /// - /// The repository that handle watched items. - /// - IWatchStatusRepository WatchStatus { get; } - - /// - /// The repository that handle collections. - /// - IRepository Collections { get; } - - /// - /// The repository that handle shows. - /// - IRepository Movies { get; } - - /// - /// The repository that handle shows. - /// - IRepository Shows { get; } - - /// - /// The repository that handle seasons. - /// - IRepository Seasons { get; } - - /// - /// The repository that handle episodes. - /// - IRepository Episodes { get; } - - /// - /// The repository that handle studios. - /// - IRepository Studios { get; } - - /// - /// The repository that handle users. - /// - IRepository Users { get; } -} diff --git a/back/src/Kyoo.Abstractions/Controllers/IPermissionValidator.cs b/back/src/Kyoo.Abstractions/Controllers/IPermissionValidator.cs deleted file mode 100644 index 4aa35625..00000000 --- a/back/src/Kyoo.Abstractions/Controllers/IPermissionValidator.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using Kyoo.Abstractions.Models.Permissions; -using Microsoft.AspNetCore.Mvc.Filters; - -namespace Kyoo.Abstractions.Controllers; - -/// -/// A service to validate permissions. -/// -public interface IPermissionValidator -{ - /// - /// Create an IAuthorizationFilter that will be used to validate permissions. - /// This can registered with any lifetime. - /// - /// The permission attribute to validate. - /// An authorization filter used to validate the permission. - IFilterMetadata Create(PermissionAttribute attribute); - - /// - /// Create an IAuthorizationFilter that will be used to validate permissions. - /// This can registered with any lifetime. - /// - /// - /// A partial attribute to validate. See . - /// - /// An authorization filter used to validate the permission. - IFilterMetadata Create(PartialPermissionAttribute attribute); -} diff --git a/back/src/Kyoo.Abstractions/Controllers/IRepository.cs b/back/src/Kyoo.Abstractions/Controllers/IRepository.cs deleted file mode 100644 index 4dbdd953..00000000 --- a/back/src/Kyoo.Abstractions/Controllers/IRepository.cs +++ /dev/null @@ -1,267 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Exceptions; -using Kyoo.Abstractions.Models.Utils; - -namespace Kyoo.Abstractions.Controllers; - -/// -/// A common repository for every resources. -/// -/// The resource's type that this repository manage. -public interface IRepository : IBaseRepository - where T : IResource, IQuery -{ - /// - /// The event handler type for all events of this repository. - /// - /// The resource created/modified/deleted - /// A representing the asynchronous operation. - public delegate Task ResourceEventHandler(T resource); - - /// - /// Get a resource from it's ID. - /// - /// The id of the resource - /// The related fields to include. - /// If the item could not be found. - /// The resource found - Task Get(Guid id, Include? include = default); - - /// - /// Get a resource from it's slug. - /// - /// The slug of the resource - /// The related fields to include. - /// If the item could not be found. - /// The resource found - Task Get(string slug, Include? include = default); - - /// - /// Get the first resource that match the predicate. - /// - /// A predicate to filter the resource. - /// The related fields to include. - /// A custom sort method to handle cases where multiples items match the filters. - /// Reverse the sort. - /// Select the first element after this id if it was in a list. - /// If the item could not be found. - /// The resource found - Task Get( - Filter filter, - Include? include = default, - Sort? sortBy = default, - bool reverse = false, - Guid? afterId = default - ); - - /// - /// Get a resource from it's ID or null if it is not found. - /// - /// The id of the resource - /// The related fields to include. - /// The resource found - Task GetOrDefault(Guid id, Include? include = default); - - /// - /// Get a resource from it's slug or null if it is not found. - /// - /// The slug of the resource - /// The related fields to include. - /// The resource found - Task GetOrDefault(string slug, Include? include = default); - - /// - /// Get the first resource that match the predicate or null if it is not found. - /// - /// A predicate to filter the resource. - /// The related fields to include. - /// A custom sort method to handle cases where multiples items match the filters. - /// Reverse the sort. - /// Select the first element after this id if it was in a list. - /// The resource found - Task GetOrDefault( - Filter? filter, - Include? include = default, - Sort? sortBy = default, - bool reverse = false, - Guid? afterId = default - ); - - /// - /// Search for resources with the database. - /// - /// The query string. - /// The related fields to include. - /// A list of resources found - Task> Search(string query, Include? include = default); - - /// - /// Get every resources that match all filters - /// - /// A filter predicate - /// Sort information about the query (sort by, sort order) - /// The related fields to include. - /// How pagination should be done (where to start and how many to return) - /// A list of resources that match every filters - Task> GetAll( - Filter? filter = null, - Sort? sort = default, - Include? include = default, - Pagination? limit = default - ); - - /// - /// Get the number of resources that match the filter's predicate. - /// - /// A filter predicate - /// How many resources matched that filter - Task GetCount(Filter? filter = null); - - /// - /// Map a list of ids to a list of items (keep the order). - /// - /// The list of items id. - /// The related fields to include. - /// A list of resources mapped from ids. - Task> FromIds(IList ids, Include? include = default); - - /// - /// Create a new resource. - /// - /// The item to register - /// The resource registers and completed by database's information (related items and so on) - Task Create(T obj); - - /// - /// Create a new resource if it does not exist already. If it does, the existing value is returned instead. - /// - /// The object to create - /// The newly created item or the existing value if it existed. - Task CreateIfNotExists(T obj); - - /// - /// Called when a resource has been created. - /// - static event ResourceEventHandler OnCreated; - - /// - /// Callback that should be called after a resource has been created. - /// - /// The resource newly created. - /// A representing the asynchronous operation. - protected static Task OnResourceCreated(T obj) => OnCreated?.Invoke(obj) ?? Task.CompletedTask; - - /// - /// Edit a resource and replace every property - /// - /// The resource to edit, it's ID can't change. - /// If the item is not found - /// The resource edited and completed by database's information (related items and so on) - Task Edit(T edited); - - /// - /// Edit only specific properties of a resource - /// - /// The id of the resource to edit - /// - /// A method that will be called when you need to update every properties that you want to - /// persist. - /// - /// If the item is not found - /// The resource edited and completed by database's information (related items and so on) - Task Patch(Guid id, Func patch); - - /// - /// Called when a resource has been edited. - /// - static event ResourceEventHandler OnEdited; - - /// - /// Callback that should be called after a resource has been edited. - /// - /// The resource newly edited. - /// A representing the asynchronous operation. - protected static Task OnResourceEdited(T obj) => OnEdited?.Invoke(obj) ?? Task.CompletedTask; - - /// - /// Delete a resource by it's ID - /// - /// The ID of the resource - /// If the item is not found - /// A representing the asynchronous operation. - Task Delete(Guid id); - - /// - /// Delete a resource by it's slug - /// - /// The slug of the resource - /// If the item is not found - /// A representing the asynchronous operation. - Task Delete(string slug); - - /// - /// Delete a resource - /// - /// The resource to delete - /// If the item is not found - /// A representing the asynchronous operation. - Task Delete(T obj); - - /// - /// Delete all resources that match the predicate. - /// - /// A predicate to filter resources to delete. Every resource that match this will be deleted. - /// A representing the asynchronous operation. - Task DeleteAll(Filter filter); - - /// - /// Called when a resource has been edited. - /// - static event ResourceEventHandler OnDeleted; - - /// - /// Callback that should be called after a resource has been deleted. - /// - /// The resource newly deleted. - /// A representing the asynchronous operation. - protected static Task OnResourceDeleted(T obj) => OnDeleted?.Invoke(obj) ?? Task.CompletedTask; -} - -/// -/// A base class for repositories. Every service implementing this will be handled by the . -/// -public interface IBaseRepository -{ - /// - /// The type for witch this repository is responsible or null if non applicable. - /// - Type RepositoryType { get; } -} - -public interface IUserRepository : IRepository -{ - Task GetByExternalId(string provider, string id); - Task AddExternalToken(Guid userId, string provider, ExternalToken token); - Task DeleteExternalToken(Guid userId, string provider); -} diff --git a/back/src/Kyoo.Abstractions/Controllers/IScanner.cs b/back/src/Kyoo.Abstractions/Controllers/IScanner.cs deleted file mode 100644 index 776131b5..00000000 --- a/back/src/Kyoo.Abstractions/Controllers/IScanner.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Threading.Tasks; - -namespace Kyoo.Abstractions.Controllers; - -public interface IScanner -{ - Task SendRescanRequest(); - Task SendRefreshRequest(string kind, Guid id); -} diff --git a/back/src/Kyoo.Abstractions/Controllers/ISearchManager.cs b/back/src/Kyoo.Abstractions/Controllers/ISearchManager.cs deleted file mode 100644 index d3985c6e..00000000 --- a/back/src/Kyoo.Abstractions/Controllers/ISearchManager.cs +++ /dev/null @@ -1,125 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Threading.Tasks; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Utils; - -namespace Kyoo.Abstractions.Controllers; - -/// -/// The service to search items. -/// -public interface ISearchManager -{ - /// - /// Search for items. - /// - /// The seach query. - /// Sort information about the query (sort by, sort order) - /// How pagination should be done (where to start and how many to return) - /// The related fields to include. - /// A list of resources that match every filters - public Task.SearchResult> SearchItems( - string? query, - Sort sortBy, - Filter? filter, - SearchPagination pagination, - Include? include = default - ); - - /// - /// Search for movies. - /// - /// The seach query. - /// Sort information about the query (sort by, sort order) - /// How pagination should be done (where to start and how many to return) - /// The related fields to include. - /// A list of resources that match every filters - public Task.SearchResult> SearchMovies( - string? query, - Sort sortBy, - Filter? filter, - SearchPagination pagination, - Include? include = default - ); - - /// - /// Search for shows. - /// - /// The seach query. - /// Sort information about the query (sort by, sort order) - /// How pagination should be done (where to start and how many to return) - /// The related fields to include. - /// A list of resources that match every filters - public Task.SearchResult> SearchShows( - string? query, - Sort sortBy, - Filter? filter, - SearchPagination pagination, - Include? include = default - ); - - /// - /// Search for collections. - /// - /// The seach query. - /// Sort information about the query (sort by, sort order) - /// How pagination should be done (where to start and how many to return) - /// The related fields to include. - /// A list of resources that match every filters - public Task.SearchResult> SearchCollections( - string? query, - Sort sortBy, - Filter? filter, - SearchPagination pagination, - Include? include = default - ); - - /// - /// Search for episodes. - /// - /// The seach query. - /// Sort information about the query (sort by, sort order) - /// How pagination should be done (where to start and how many to return) - /// The related fields to include. - /// A list of resources that match every filters - public Task.SearchResult> SearchEpisodes( - string? query, - Sort sortBy, - Filter? filter, - SearchPagination pagination, - Include? include = default - ); - - /// - /// Search for studios. - /// - /// The seach query. - /// Sort information about the query (sort by, sort order) - /// How pagination should be done (where to start and how many to return) - /// The related fields to include. - /// A list of resources that match every filters - public Task.SearchResult> SearchStudios( - string? query, - Sort sortBy, - Filter? filter, - SearchPagination pagination, - Include? include = default - ); -} diff --git a/back/src/Kyoo.Abstractions/Controllers/IThumbnailsManager.cs b/back/src/Kyoo.Abstractions/Controllers/IThumbnailsManager.cs deleted file mode 100644 index a1a37aa2..00000000 --- a/back/src/Kyoo.Abstractions/Controllers/IThumbnailsManager.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.IO; -using System.Threading.Tasks; -using Kyoo.Abstractions.Models; - -namespace Kyoo.Abstractions.Controllers; - -public interface IThumbnailsManager -{ - Task DownloadImages(T item) - where T : IThumbnails; - - Task DownloadImage(Image? image, string what); - - Task IsImageSaved(Guid imageId, ImageQuality quality); - - Task GetImage(Guid imageId, ImageQuality quality); - - Task DeleteImages(T item) - where T : IThumbnails; - - Task GetUserImage(Guid userId); - - Task SetUserImage(Guid userId, Stream? image); -} diff --git a/back/src/Kyoo.Abstractions/Controllers/IWatchStatusRepository.cs b/back/src/Kyoo.Abstractions/Controllers/IWatchStatusRepository.cs deleted file mode 100644 index 17ebe40c..00000000 --- a/back/src/Kyoo.Abstractions/Controllers/IWatchStatusRepository.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Utils; - -namespace Kyoo.Abstractions.Controllers; - -/// -/// A local repository to handle watched items -/// -public interface IWatchStatusRepository -{ - public delegate Task ResourceEventHandler(T resource); - - Task> GetAll( - Filter? filter = default, - Include? include = default, - Pagination? limit = default - ); - - Task GetMovieStatus(Guid movieId, Guid userId); - - Task SetMovieStatus( - Guid movieId, - Guid userId, - WatchStatus status, - int? watchedTime, - int? percent - ); - - static event ResourceEventHandler> OnMovieStatusChangedHandler; - protected static Task OnMovieStatusChanged(WatchStatus obj) => - OnMovieStatusChangedHandler?.Invoke(obj) ?? Task.CompletedTask; - - Task DeleteMovieStatus(Guid movieId, Guid userId); - - Task GetShowStatus(Guid showId, Guid userId); - - Task SetShowStatus(Guid showId, Guid userId, WatchStatus status); - - static event ResourceEventHandler> OnShowStatusChangedHandler; - protected static Task OnShowStatusChanged(WatchStatus obj) => - OnShowStatusChangedHandler?.Invoke(obj) ?? Task.CompletedTask; - - Task DeleteShowStatus(Guid showId, Guid userId); - - Task GetEpisodeStatus(Guid episodeId, Guid userId); - - /// Where the user has stopped watching. Only usable if Status - /// is - Task SetEpisodeStatus( - Guid episodeId, - Guid userId, - WatchStatus status, - int? watchedTime, - int? percent - ); - - static event ResourceEventHandler> OnEpisodeStatusChangedHandler; - protected static Task OnEpisodeStatusChanged(WatchStatus obj) => - OnEpisodeStatusChangedHandler?.Invoke(obj) ?? Task.CompletedTask; - - Task DeleteEpisodeStatus(Guid episodeId, Guid userId); -} diff --git a/back/src/Kyoo.Abstractions/Extensions.cs b/back/src/Kyoo.Abstractions/Extensions.cs deleted file mode 100644 index 42f4b0af..00000000 --- a/back/src/Kyoo.Abstractions/Extensions.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using Kyoo.Abstractions.Models.Exceptions; -using Kyoo.Authentication.Models; - -namespace Kyoo.Authentication; - -/// -/// Extension methods. -/// -public static class Extensions -{ - /// - /// Get the permissions of an user. - /// - /// The user - /// The list of permissions - public static ICollection GetPermissions(this ClaimsPrincipal user) - { - return user.Claims.FirstOrDefault(x => x.Type == Claims.Permissions)?.Value.Split(',') - ?? Array.Empty(); - } - - /// - /// Get the id of the current user or null if unlogged or invalid. - /// - /// The user. - /// The id of the user or null. - public static Guid? GetId(this ClaimsPrincipal user) - { - Claim? value = user.FindFirst(Claims.Id); - if (Guid.TryParse(value?.Value, out Guid id)) - return id; - return null; - } - - public static Guid GetIdOrThrow(this ClaimsPrincipal user) - { - Guid? ret = user.GetId(); - if (ret == null) - throw new UnauthorizedException(); - return ret.Value; - } -} diff --git a/back/src/Kyoo.Abstractions/Kyoo.Abstractions.csproj b/back/src/Kyoo.Abstractions/Kyoo.Abstractions.csproj deleted file mode 100644 index a7727ecc..00000000 --- a/back/src/Kyoo.Abstractions/Kyoo.Abstractions.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - Kyoo.Abstractions - Base package to create plugins for Kyoo. - Kyoo.Abstractions - - - - - - - - - - - - diff --git a/back/src/Kyoo.Abstractions/Models/Attributes/ApiDefinitionAttribute.cs b/back/src/Kyoo.Abstractions/Models/Attributes/ApiDefinitionAttribute.cs deleted file mode 100644 index 46014a69..00000000 --- a/back/src/Kyoo.Abstractions/Models/Attributes/ApiDefinitionAttribute.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; - -namespace Kyoo.Abstractions.Models.Attributes; - -/// -/// An attribute to specify on apis to specify it's documentation's name and category. -/// If this is applied on a method, the specified method will be exploded from the controller's page and be -/// included on the specified tag page. -/// -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] -public class ApiDefinitionAttribute : Attribute -{ - /// - /// The public name of this api. - /// - public string Name { get; } - - /// - /// The name of the group in witch this API is. You can also specify a custom sort order using the following - /// format: order:name. Everything before the first : will be removed but kept for - /// th alphabetical ordering. - /// - public string? Group { get; set; } - - /// - /// Create a new . - /// - /// The name of the api that will be used on the documentation page. - public ApiDefinitionAttribute(string name) - { - Name = name; - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Attributes/ComputedAttribute.cs b/back/src/Kyoo.Abstractions/Models/Attributes/ComputedAttribute.cs deleted file mode 100644 index 2fde7a84..00000000 --- a/back/src/Kyoo.Abstractions/Models/Attributes/ComputedAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; - -namespace Kyoo.Abstractions.Models.Attributes; - -/// -/// An attribute to inform that the property is computed automatically and can't be assigned manually. -/// -[AttributeUsage(AttributeTargets.Property)] -public class ComputedAttribute : NotMergeableAttribute { } diff --git a/back/src/Kyoo.Abstractions/Models/Attributes/LoadableRelationAttribute.cs b/back/src/Kyoo.Abstractions/Models/Attributes/LoadableRelationAttribute.cs deleted file mode 100644 index 8b40679e..00000000 --- a/back/src/Kyoo.Abstractions/Models/Attributes/LoadableRelationAttribute.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; - -namespace Kyoo.Abstractions.Models.Attributes; - -/// -/// The targeted relation can be loaded. -/// -[AttributeUsage(AttributeTargets.Property)] -public class LoadableRelationAttribute : Attribute -{ - /// - /// The name of the field containing the related resource's ID. - /// - public string? RelationID { get; } - - public string? Sql { get; set; } - - public string? On { get; set; } - - public string? Projected { get; set; } - - /// - /// Create a new . - /// - public LoadableRelationAttribute() { } - - /// - /// Create a new with a baking relationID field. - /// - /// The name of the RelationID field. - public LoadableRelationAttribute(string relationID) - { - RelationID = relationID; - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Attributes/NotMergeableAttribute.cs b/back/src/Kyoo.Abstractions/Models/Attributes/NotMergeableAttribute.cs deleted file mode 100644 index 138ec9a8..00000000 --- a/back/src/Kyoo.Abstractions/Models/Attributes/NotMergeableAttribute.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; - -namespace Kyoo.Abstractions.Models.Attributes; - -/// -/// Specify that a property can't be merged. -/// -[AttributeUsage(AttributeTargets.Property)] -public class NotMergeableAttribute : Attribute { } - -/// -/// An interface with a method called when this object is merged. -/// -public interface IOnMerge -{ - /// - /// This function is called after the object has been merged. - /// - /// The object that has been merged with this. - void OnMerge(object merged); -} diff --git a/back/src/Kyoo.Abstractions/Models/Attributes/OneOfAttribute.cs b/back/src/Kyoo.Abstractions/Models/Attributes/OneOfAttribute.cs deleted file mode 100644 index de064603..00000000 --- a/back/src/Kyoo.Abstractions/Models/Attributes/OneOfAttribute.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; - -namespace Kyoo.Abstractions.Models.Attributes; - -/// -/// An attribute to inform that this interface is a type union -/// -[AttributeUsage(AttributeTargets.Interface)] -public class OneOfAttribute : Attribute -{ - /// - /// The types this union concist of. - /// - public Type[] Types { get; set; } -} diff --git a/back/src/Kyoo.Abstractions/Models/Attributes/Permission/PartialPermissionAttribute.cs b/back/src/Kyoo.Abstractions/Models/Attributes/Permission/PartialPermissionAttribute.cs deleted file mode 100644 index ea300823..00000000 --- a/back/src/Kyoo.Abstractions/Models/Attributes/Permission/PartialPermissionAttribute.cs +++ /dev/null @@ -1,87 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using Kyoo.Abstractions.Controllers; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.DependencyInjection; - -namespace Kyoo.Abstractions.Models.Permissions; - -/// -/// Specify one part of a permissions needed for the API (the kind or the type). -/// -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] -public class PartialPermissionAttribute : Attribute, IFilterFactory -{ - /// - /// The needed permission type. - /// - public string? Type { get; } - - /// - /// The needed permission kind. - /// - public Kind? Kind { get; } - - /// - /// The group of this permission. - /// - public Group Group { get; set; } - - /// - /// Ask a permission to run an action. - /// - /// - /// With this attribute, you can only specify a type or a kind. - /// To have a valid permission attribute, you must specify the kind and the permission using two attributes. - /// Those attributes can be dispatched at different places (one on the class, one on the method for example). - /// If you don't put exactly two of those attributes, the permission attribute will be ill-formed and will - /// lead to unspecified behaviors. - /// - /// The type of the action - public PartialPermissionAttribute(string type) - { - Type = type.ToLower(); - } - - /// - /// Ask a permission to run an action. - /// - /// - /// With this attribute, you can only specify a type or a kind. - /// To have a valid permission attribute, you must specify the kind and the permission using two attributes. - /// Those attributes can be dispatched at different places (one on the class, one on the method for example). - /// If you don't put exactly two of those attributes, the permission attribute will be ill-formed and will - /// lead to unspecified behaviors. - /// - /// The kind of permission needed. - public PartialPermissionAttribute(Kind permission) - { - Kind = permission; - } - - /// - public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) - { - return serviceProvider.GetRequiredService().Create(this); - } - - /// - public bool IsReusable => true; -} diff --git a/back/src/Kyoo.Abstractions/Models/Attributes/Permission/PermissionAttribute.cs b/back/src/Kyoo.Abstractions/Models/Attributes/Permission/PermissionAttribute.cs deleted file mode 100644 index ba1ff743..00000000 --- a/back/src/Kyoo.Abstractions/Models/Attributes/Permission/PermissionAttribute.cs +++ /dev/null @@ -1,136 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using Kyoo.Abstractions.Controllers; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.DependencyInjection; - -namespace Kyoo.Abstractions.Models.Permissions; - -/// -/// The kind of permission needed. -/// -public enum Kind -{ - /// - /// Allow the user to read for this kind of data. - /// - Read, - - /// - /// Allow the user to write for this kind of data. - /// - Write, - - /// - /// Allow the user to create this kind of data. - /// - Create, - - /// - /// Allow the user to delete this kind of data. - /// - Delete, - - /// - /// Allow the user to play this file. - /// - Play, -} - -/// -/// The group of the permission. -/// -public enum Group -{ - /// - /// Default group indicating no value. - /// - None, - - /// - /// Allow all operations on basic items types. - /// - Overall, - - /// - /// Allow operation on sensitive items like libraries path, configurations and so on. - /// - Admin -} - -/// -/// Specify permissions needed for the API. -/// -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] -public class PermissionAttribute : Attribute, IFilterFactory -{ - /// - /// The needed permission as string. - /// - public string Type { get; } - - /// - /// The needed permission kind. - /// - public Kind Kind { get; } - - /// - /// The group of this permission. - /// - public Group Group { get; set; } - - /// - /// Ask a permission to run an action. - /// - /// - /// The type of the action - /// - /// - /// The kind of permission needed. - /// - /// - /// The group of this permission (allow grouped permission like overall.read - /// for all read permissions of this group). - /// - public PermissionAttribute(string type, Kind permission, Group group = Group.Overall) - { - Type = type.ToLower(); - Kind = permission; - Group = group; - } - - /// - public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) - { - return serviceProvider.GetRequiredService().Create(this); - } - - /// - public bool IsReusable => true; - - /// - /// Return this permission attribute as a string. - /// - /// The string representation. - public string AsPermissionString() - { - return Type; - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Attributes/Permission/UserOnlyAttribute.cs b/back/src/Kyoo.Abstractions/Models/Attributes/Permission/UserOnlyAttribute.cs deleted file mode 100644 index a50424a1..00000000 --- a/back/src/Kyoo.Abstractions/Models/Attributes/Permission/UserOnlyAttribute.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; - -namespace Kyoo.Abstractions.Models.Permissions; - -/// -/// The annotated route can only be accessed by a logged in user. -/// -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] -public class UserOnlyAttribute : Attribute -{ - // TODO: Implement a Filter Attribute to make this work. For now, this attribute is only useful as documentation. -} diff --git a/back/src/Kyoo.Abstractions/Models/Attributes/SqlFirstColumnAttribute.cs b/back/src/Kyoo.Abstractions/Models/Attributes/SqlFirstColumnAttribute.cs deleted file mode 100644 index e420a1f3..00000000 --- a/back/src/Kyoo.Abstractions/Models/Attributes/SqlFirstColumnAttribute.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using Kyoo.Utils; - -namespace Kyoo.Abstractions.Models.Attributes; - -[AttributeUsage(AttributeTargets.Class)] -public class SqlFirstColumnAttribute : Attribute -{ - /// - /// The name of the first column of the element. Used to split multiples - /// items on a single sql query. If not specified, it defaults to "Id". - /// - public string Name { get; set; } - - public SqlFirstColumnAttribute(string name) - { - Name = name.ToSnakeCase(); - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Exceptions/DuplicatedItemException.cs b/back/src/Kyoo.Abstractions/Models/Exceptions/DuplicatedItemException.cs deleted file mode 100644 index 19f5ece4..00000000 --- a/back/src/Kyoo.Abstractions/Models/Exceptions/DuplicatedItemException.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; - -namespace Kyoo.Abstractions.Models.Exceptions; - -/// -/// An exception raised when an item already exists in the database. -/// -[Serializable] -public class DuplicatedItemException(object? existing = null) - : Exception("Already exists in the database.") -{ - /// - /// The existing object. - /// - public object? Existing { get; } = existing; -} diff --git a/back/src/Kyoo.Abstractions/Models/Exceptions/ItemNotFoundException.cs b/back/src/Kyoo.Abstractions/Models/Exceptions/ItemNotFoundException.cs deleted file mode 100644 index ef708f84..00000000 --- a/back/src/Kyoo.Abstractions/Models/Exceptions/ItemNotFoundException.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; - -namespace Kyoo.Abstractions.Models.Exceptions; - -/// -/// An exception raised when an item could not be found. -/// -[Serializable] -public class ItemNotFoundException : Exception -{ - /// - /// Create a default with no message. - /// - public ItemNotFoundException() - : base("Item not found") { } - - /// - /// Create a new with a message - /// - /// The message of the exception - public ItemNotFoundException(string message) - : base(message) { } -} diff --git a/back/src/Kyoo.Abstractions/Models/Exceptions/UnauthorizedException.cs b/back/src/Kyoo.Abstractions/Models/Exceptions/UnauthorizedException.cs deleted file mode 100644 index 89f09d7a..00000000 --- a/back/src/Kyoo.Abstractions/Models/Exceptions/UnauthorizedException.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; - -namespace Kyoo.Abstractions.Models.Exceptions; - -[Serializable] -public class UnauthorizedException : Exception -{ - public UnauthorizedException() - : base("User not authenticated or token invalid.") { } - - public UnauthorizedException(string message) - : base(message) { } -} diff --git a/back/src/Kyoo.Abstractions/Models/Genre.cs b/back/src/Kyoo.Abstractions/Models/Genre.cs deleted file mode 100644 index e2e4f6a9..00000000 --- a/back/src/Kyoo.Abstractions/Models/Genre.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -namespace Kyoo.Abstractions.Models; - -/// -/// A genre that allow one to specify categories for shows. -/// -public enum Genre -{ - Action, - Adventure, - Animation, - Comedy, - Crime, - Documentary, - Drama, - Family, - Fantasy, - History, - Horror, - Music, - Mystery, - Romance, - ScienceFiction, - Thriller, - War, - Western, - Kids, - News, - Reality, - Soap, - Talk, - Politics, -} diff --git a/back/src/Kyoo.Abstractions/Models/ILibraryItem.cs b/back/src/Kyoo.Abstractions/Models/ILibraryItem.cs deleted file mode 100644 index 8b98aabd..00000000 --- a/back/src/Kyoo.Abstractions/Models/ILibraryItem.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models.Attributes; - -namespace Kyoo.Abstractions.Models; - -/// -/// A show, a movie or a collection. -/// -[OneOf(Types = new[] { typeof(Show), typeof(Movie), typeof(Collection) })] -public interface ILibraryItem : IResource, IThumbnails, IMetadata, IAddedDate, IQuery -{ - static Sort IQuery.DefaultSort => new Sort.By(nameof(Movie.Name)); -} diff --git a/back/src/Kyoo.Abstractions/Models/INews.cs b/back/src/Kyoo.Abstractions/Models/INews.cs deleted file mode 100644 index b5642f5d..00000000 --- a/back/src/Kyoo.Abstractions/Models/INews.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models.Attributes; - -namespace Kyoo.Abstractions.Models; - -/// -/// A show, a movie or a collection. -/// -[OneOf(Types = [typeof(Episode), typeof(Movie)])] -public interface INews : IResource, IThumbnails, IAddedDate, IQuery -{ - static Sort IQuery.DefaultSort => new Sort.By(nameof(AddedDate), true); -} diff --git a/back/src/Kyoo.Abstractions/Models/IWatchlist.cs b/back/src/Kyoo.Abstractions/Models/IWatchlist.cs deleted file mode 100644 index 03022701..00000000 --- a/back/src/Kyoo.Abstractions/Models/IWatchlist.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using Kyoo.Abstractions.Models.Attributes; - -namespace Kyoo.Abstractions.Models; - -/// -/// A watch list item. -/// -[OneOf(Types = new[] { typeof(Show), typeof(Movie) })] -public interface IWatchlist : IResource, IThumbnails, IMetadata, IAddedDate { } diff --git a/back/src/Kyoo.Abstractions/Models/Issues.cs b/back/src/Kyoo.Abstractions/Models/Issues.cs deleted file mode 100644 index 851dc29f..00000000 --- a/back/src/Kyoo.Abstractions/Models/Issues.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; - -namespace Kyoo.Abstractions.Models; - -/// -/// An issue that occured on kyoo. -/// -public class Issue : IAddedDate -{ - /// - /// The type of issue (for example, "Scanner" if this issue was created due to scanning error). - /// - public string Domain { get; set; } - - /// - /// Why this issue was caused? An unique cause that can be used to identify this issue. - /// For the scanner, a cause should be a video path. - /// - public string Cause { get; set; } - - /// - /// A human readable string explaining why this issue occured. - /// - public string Reason { get; set; } - - /// - /// Some extra data that could store domain-specific info. - /// - public Dictionary Extra { get; set; } = new(); - - /// - public DateTime AddedDate { get; set; } -} diff --git a/back/src/Kyoo.Abstractions/Models/MetadataID.cs b/back/src/Kyoo.Abstractions/Models/MetadataID.cs deleted file mode 100644 index ec384ec8..00000000 --- a/back/src/Kyoo.Abstractions/Models/MetadataID.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -namespace Kyoo.Abstractions.Models; - -/// -/// ID and link of an item on an external provider. -/// -public class MetadataId -{ - /// - /// The ID of the resource on the external provider. - /// - public string DataId { get; set; } - - /// - /// The URL of the resource on the external provider. - /// - public string? Link { get; set; } -} - -/// -/// ID informations about an episode. -/// -public class EpisodeId -{ - /// - /// The Id of the show on the metadata database. - /// - public string ShowId { get; set; } - - /// - /// The season number or null if absolute numbering is used in this database. - /// - public int? Season { get; set; } - - /// - /// The episode number or absolute number if Season is null. - /// - public int Episode { get; set; } - - /// - /// The URL of the resource on the external provider. - /// - public string? Link { get; set; } -} diff --git a/back/src/Kyoo.Abstractions/Models/Page.cs b/back/src/Kyoo.Abstractions/Models/Page.cs deleted file mode 100644 index 75c0f18e..00000000 --- a/back/src/Kyoo.Abstractions/Models/Page.cs +++ /dev/null @@ -1,105 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Linq; -using Kyoo.Utils; - -namespace Kyoo.Abstractions.Models; - -/// -/// A page of resource that contains information about the pagination of resources. -/// -/// The type of resource contained in this page. -public class Page - where T : IResource -{ - /// - /// The link of the current page. - /// - public string This { get; } - - /// - /// The link of the first page. - /// - public string First { get; } - - /// - /// The link of the previous page. - /// - public string? Previous { get; } - - /// - /// The link of the next page. - /// - public string? Next { get; } - - /// - /// The number of items in the current page. - /// - public int Count => Items.Count; - - /// - /// The list of items in the page. - /// - public ICollection Items { get; } - - /// - /// Create a new . - /// - /// The list of items in the page. - /// The link of the current page. - /// The link of the previous page. - /// The link of the next page. - /// The link of the first page. - public Page(ICollection items, string @this, string? previous, string? next, string first) - { - Items = items; - This = @this; - Previous = previous; - Next = next; - First = first; - } - - /// - /// Create a new and compute the urls. - /// - /// The list of items in the page. - /// The base url of the resources available from this page. - /// The list of query strings of the current page - /// The number of items requested for the current page. - public Page(ICollection items, string url, Dictionary query, int limit) - { - Items = items; - This = url + query.ToQueryString(); - if (items.Count > 0 && query.ContainsKey("afterID")) - { - query["afterID"] = items.First().Id.ToString(); - query["reverse"] = "true"; - Previous = url + query.ToQueryString(); - } - query.Remove("reverse"); - if (items.Count == limit && limit > 0) - { - query["afterID"] = items.Last().Id.ToString(); - Next = url + query.ToQueryString(); - } - query.Remove("afterID"); - First = url + query.ToQueryString(); - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Patch.cs b/back/src/Kyoo.Abstractions/Models/Patch.cs deleted file mode 100644 index 1eafe949..00000000 --- a/back/src/Kyoo.Abstractions/Models/Patch.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Text.Json; -using Kyoo.Abstractions.Models; - -namespace Kyoo.Models; - -public class Patch : Dictionary - where T : class, IResource -{ - public Guid? Id => this.GetValueOrDefault(nameof(IResource.Id))?.Deserialize(); - - public string? Slug => this.GetValueOrDefault(nameof(IResource.Slug))?.Deserialize(); - - public T Apply(T current) - { - foreach ((string property, JsonDocument value) in this) - { - PropertyInfo prop = typeof(T).GetProperty( - property, - BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance - )!; - prop.SetValue(current, value.Deserialize(prop.PropertyType)); - } - return current; - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Collection.cs b/back/src/Kyoo.Abstractions/Models/Resources/Collection.cs deleted file mode 100644 index caa6a13a..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/Collection.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text.Json.Serialization; -using Kyoo.Abstractions.Controllers; -using Kyoo.Utils; - -namespace Kyoo.Abstractions.Models; - -/// -/// A class representing collections of . -/// -public class Collection - : IQuery, - IResource, - IMetadata, - IThumbnails, - IAddedDate, - IRefreshable, - ILibraryItem -{ - public static Sort DefaultSort => new Sort.By(nameof(Collection.Name)); - - /// - public Guid Id { get; set; } - - /// - [MaxLength(256)] - public string Slug { get; set; } - - /// - /// The name of this collection. - /// - public string Name { get; set; } - - /// - /// The description of this collection. - /// - public string? Overview { get; set; } - - /// - public DateTime AddedDate { get; set; } - - /// - public Image? Poster { get; set; } - - /// - public Image? Thumbnail { get; set; } - - /// - public Image? Logo { get; set; } - - /// - /// The list of movies contained in this collection. - /// - [JsonIgnore] - public ICollection? Movies { get; set; } - - /// - /// The list of shows contained in this collection. - /// - [JsonIgnore] - public ICollection? Shows { get; set; } - - /// - public Dictionary ExternalId { get; set; } = new(); - - /// - public DateTime? NextMetadataRefresh { get; set; } - - public Collection() { } - - [JsonConstructor] - public Collection(string name) - { - if (name != null) - { - Slug = Utility.ToSlug(name); - Name = name; - } - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs deleted file mode 100644 index f32bf741..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs +++ /dev/null @@ -1,302 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text.Json.Serialization; -using System.Text.RegularExpressions; -using EntityFrameworkCore.Projectables; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models.Attributes; - -namespace Kyoo.Abstractions.Models; - -/// -/// A class to represent a single show's episode. -/// -public class Episode : IQuery, IResource, IThumbnails, IAddedDate, IRefreshable, INews -{ - // Use absolute numbers by default and fallback to season/episodes if it does not exists. - public static Sort DefaultSort => - new Sort.Conglomerate( - new Sort.By(x => x.AbsoluteNumber), - new Sort.By(x => x.SeasonNumber), - new Sort.By(x => x.EpisodeNumber) - ); - - /// - public Guid Id { get; set; } - - /// - [Computed] - [MaxLength(256)] - public string Slug - { - get - { - if (ShowSlug != null || Show?.Slug != null) - return GetSlug(ShowSlug ?? Show!.Slug, SeasonNumber, EpisodeNumber, AbsoluteNumber); - return GetSlug(ShowId.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber); - } - private set - { - Match match = Regex.Match(value, @"(?.+)-s(?\d+)e(?\d+)"); - - if (match.Success) - { - ShowSlug = match.Groups["show"].Value; - SeasonNumber = int.Parse(match.Groups["season"].Value); - EpisodeNumber = int.Parse(match.Groups["episode"].Value); - } - else - { - match = Regex.Match(value, @"(?.+)-(?\d+)"); - if (match.Success) - { - ShowSlug = match.Groups["show"].Value; - AbsoluteNumber = int.Parse(match.Groups["absolute"].Value); - } - else - ShowSlug = value; - SeasonNumber = null; - EpisodeNumber = null; - } - } - } - - /// - /// The slug of the Show that contain this episode. If this is not set, this episode is ill-formed. - /// - [JsonIgnore] - public string? ShowSlug { private get; set; } - - /// - /// The ID of the Show containing this episode. - /// - public Guid ShowId { get; set; } - - /// - /// The show that contains this episode. - /// - [LoadableRelation(nameof(ShowId))] - public Show? Show { get; set; } - - /// - /// The ID of the Season containing this episode. - /// - public Guid? SeasonId { get; set; } - - /// - /// The season that contains this episode. - /// - /// - /// This can be null if the season is unknown and the episode is only identified - /// by it's . - /// - [LoadableRelation(nameof(SeasonId))] - public Season? Season { get; set; } - - /// - /// The season in witch this episode is in. - /// - public int? SeasonNumber { get; set; } - - /// - /// The number of this episode in it's season. - /// - public int? EpisodeNumber { get; set; } - - /// - /// The absolute number of this episode. It's an episode number that is not reset to 1 after a new season. - /// - public int? AbsoluteNumber { get; set; } - - /// - /// The path of the video file for this episode. - /// - public string Path { get; set; } - - /// - /// The title of this episode. - /// - public string? Name { get; set; } - - /// - /// The overview of this episode. - /// - public string? Overview { get; set; } - - /// - /// How long is this episode? (in minutes) - /// - public int? Runtime { get; set; } - - /// - /// The release date of this episode. It can be null if unknown. - /// - public DateOnly? ReleaseDate { get; set; } - - /// - public DateTime AddedDate { get; set; } - - /// - public Image? Poster { get; set; } - - /// - public Image? Thumbnail { get; set; } - - /// - public Image? Logo { get; set; } - - /// - public Dictionary ExternalId { get; set; } = []; - - /// - public DateTime? NextMetadataRefresh { get; set; } - - /// - /// The previous episode that should be seen before viewing this one. - /// - [Projectable(UseMemberBody = nameof(_PreviousEpisode), OnlyOnInclude = true)] - [LoadableRelation( - // language=PostgreSQL - Sql = """ - select - pe.* -- Episode as pe - from - episodes as "pe" - where - pe.show_id = "this".show_id - and (pe.absolute_number < "this".absolute_number - or pe.season_number < "this".season_number - or (pe.season_number = "this".season_number - and e.episode_number < "this".episode_number)) - order by - pe.absolute_number desc nulls last, - pe.season_number desc, - pe.episode_number desc - limit 1 - """ - )] - public Episode? PreviousEpisode { get; set; } - - private Episode? _PreviousEpisode => - Show! - .Episodes!.OrderBy(x => x.AbsoluteNumber == null) - .ThenByDescending(x => x.AbsoluteNumber) - .ThenByDescending(x => x.SeasonNumber) - .ThenByDescending(x => x.EpisodeNumber) - .FirstOrDefault(x => - x.AbsoluteNumber < AbsoluteNumber - || x.SeasonNumber < SeasonNumber - || (x.SeasonNumber == SeasonNumber && x.EpisodeNumber < EpisodeNumber) - ); - - /// - /// The next episode to watch after this one. - /// - [Projectable(UseMemberBody = nameof(_NextEpisode), OnlyOnInclude = true)] - [LoadableRelation( - // language=PostgreSQL - Sql = """ - select - ne.* -- Episode as ne - from - episodes as "ne" - where - ne.show_id = "this".show_id - and (ne.absolute_number > "this".absolute_number - or ne.season_number > "this".season_number - or (ne.season_number = "this".season_number - and e.episode_number > "this".episode_number)) - order by - ne.absolute_number, - ne.season_number, - ne.episode_number - limit 1 - """ - )] - public Episode? NextEpisode { get; set; } - - private Episode? _NextEpisode => - Show! - .Episodes!.OrderBy(x => x.AbsoluteNumber) - .ThenBy(x => x.SeasonNumber) - .ThenBy(x => x.EpisodeNumber) - .FirstOrDefault(x => - x.AbsoluteNumber > AbsoluteNumber - || x.SeasonNumber > SeasonNumber - || (x.SeasonNumber == SeasonNumber && x.EpisodeNumber > EpisodeNumber) - ); - - [JsonIgnore] - public ICollection? Watched { get; set; } - - /// - /// Metadata of what an user as started/planned to watch. - /// - [Projectable(UseMemberBody = nameof(_WatchStatus), OnlyOnInclude = true)] - [LoadableRelation( - Sql = "episode_watch_status", - On = "episode_id = \"this\".id and \"relation\".user_id = [current_user]" - )] - public EpisodeWatchStatus? WatchStatus { get; set; } - - // There is a global query filter to filter by user so we just need to do single. - private EpisodeWatchStatus? _WatchStatus => Watched!.FirstOrDefault(); - - /// - /// Links to watch this episode. - /// - public VideoLinks Links => - new() { Direct = $"/episode/{Slug}/direct", Hls = $"/episode/{Slug}/master.m3u8", }; - - /// - /// Get the slug of an episode. - /// - /// The slug of the show. It can't be null. - /// - /// The season in which the episode is. - /// If this is a movie or if the episode should be referred by it's absolute number, set this to null. - /// - /// - /// The number of the episode in it's season. - /// If this is a movie or if the episode should be referred by it's absolute number, set this to null. - /// - /// - /// The absolute number of this show. - /// If you don't know it or this is a movie, use null - /// - /// The slug corresponding to the given arguments - public static string GetSlug( - string showSlug, - int? seasonNumber, - int? episodeNumber, - int? absoluteNumber = null - ) - { - return seasonNumber switch - { - null when absoluteNumber == null => showSlug, - null => $"{showSlug}-{absoluteNumber}", - _ => $"{showSlug}-s{seasonNumber}e{episodeNumber}" - }; - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IAddedDate.cs b/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IAddedDate.cs deleted file mode 100644 index 8b64b613..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IAddedDate.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; - -namespace Kyoo.Abstractions.Models; - -/// -/// An interface applied to resources. -/// -public interface IAddedDate -{ - /// - /// The date at which this resource was added to kyoo. - /// - public DateTime AddedDate { get; set; } -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IMetadata.cs b/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IMetadata.cs deleted file mode 100644 index db840cae..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IMetadata.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; - -namespace Kyoo.Abstractions.Models; - -/// -/// An interface applied to resources containing external metadata. -/// -public interface IMetadata -{ - /// - /// The link to metadata providers that this show has. See for more information. - /// - public Dictionary ExternalId { get; set; } -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IQuery.cs b/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IQuery.cs deleted file mode 100644 index 95634fa7..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IQuery.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using Kyoo.Abstractions.Controllers; - -namespace Kyoo.Abstractions.Models; - -public interface IQuery -{ - /// - /// The sorting that will be used when no user defined one is present. - /// - public static virtual Sort DefaultSort => throw new NotImplementedException(); -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IRefreshable.cs b/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IRefreshable.cs deleted file mode 100644 index 0a8acae3..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IRefreshable.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; - -namespace Kyoo.Abstractions.Models; - -public interface IRefreshable -{ - /// - /// The date of the next metadata refresh. Null if auto-refresh is disabled. - /// - public DateTime? NextMetadataRefresh { get; set; } - - public static DateTime ComputeNextRefreshDate(DateOnly airDate) - { - int days = DateOnly.FromDateTime(DateTime.UtcNow).DayNumber - airDate.DayNumber; - return days switch - { - <= 4 => DateTime.UtcNow.AddDays(1), - <= 21 => DateTime.UtcNow.AddDays(14), - _ => DateTime.UtcNow.AddMonths(2) - }; - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IResource.cs b/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IResource.cs deleted file mode 100644 index 87796456..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IResource.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.ComponentModel.DataAnnotations; -using Kyoo.Abstractions.Controllers; - -namespace Kyoo.Abstractions.Models; - -/// -/// An interface to represent a resource that can be retrieved from the database. -/// -public interface IResource : IQuery -{ - /// - /// A unique ID for this type of resource. This can't be changed and duplicates are not allowed. - /// - /// - /// You don't need to specify an ID manually when creating a new resource, - /// this field is automatically assigned by the . - /// - public Guid Id { get; set; } - - /// - /// A human-readable identifier that can be used instead of an ID. - /// A slug must be unique for a type of resource but it can be changed. - /// - /// - /// There is no setter for a slug since it can be computed from other fields. - /// For example, a season slug is {ShowSlug}-s{SeasonNumber}. - /// - [MaxLength(256)] - public string Slug { get; } -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IThumbnails.cs b/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IThumbnails.cs deleted file mode 100644 index 69fbca66..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IThumbnails.cs +++ /dev/null @@ -1,147 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.ComponentModel.DataAnnotations; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Kyoo.Abstractions.Models; - -/// -/// An interface representing items that contains images (like posters, thumbnails, logo, banners...) -/// -public interface IThumbnails -{ - /// - /// A poster is a 2/3 format image with the cover of the resource. - /// - public Image? Poster { get; set; } - - /// - /// A thumbnail is a 16/9 format image, it could ether be used as a background or as a preview but it usually - /// is not an official image. - /// - public Image? Thumbnail { get; set; } - - /// - /// A logo is a small image representing the resource. - /// - public Image? Logo { get; set; } -} - -[JsonConverter(typeof(ImageConvertor))] -public class Image -{ - /// - /// A unique identifier for the image. Used for proper http caches. - /// - public Guid Id { get; set; } - - /// - /// The original image from another server. - /// - public string Source { get; set; } - - /// - /// A hash to display as placeholder while the image is loading. - /// - [MaxLength(32)] - public string Blurhash { get; set; } - - /// - /// The url to access the image in low quality. - /// - public string Low => $"/thumbnails/{Id}?quality=low"; - - /// - /// The url to access the image in medium quality. - /// - public string Medium => $"/thumbnails/{Id}?quality=medium"; - - /// - /// The url to access the image in high quality. - /// - public string High => $"/thumbnails/{Id}?quality=high"; - - public Image() { } - - [JsonConstructor] - public Image(string source, string? blurhash = null) - { - Source = source; - Blurhash = blurhash ?? "000000"; - } - - // - public class ImageConvertor : JsonConverter - { - /// - public override Image? Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options - ) - { - if (reader.TokenType == JsonTokenType.String && reader.GetString() is string source) - return new Image(source); - using JsonDocument document = JsonDocument.ParseValue(ref reader); - string? src = document.RootElement.GetProperty("Source").GetString(); - string? blurhash = document.RootElement.GetProperty("Blurhash").GetString(); - Guid? id = document.RootElement.GetProperty("Id").GetGuid(); - return new Image(src ?? string.Empty, blurhash) { Id = id ?? Guid.Empty }; - } - - /// - public override void Write( - Utf8JsonWriter writer, - Image value, - JsonSerializerOptions options - ) - { - writer.WriteStartObject(); - writer.WriteString("source", value.Source); - writer.WriteString("blurhash", value.Blurhash); - writer.WriteString("low", value.Low); - writer.WriteString("medium", value.Medium); - writer.WriteString("high", value.High); - writer.WriteEndObject(); - } - } -} - -/// -/// The quality of an image -/// -public enum ImageQuality -{ - /// - /// Small - /// - Low, - - /// - /// Medium - /// - Medium, - - /// - /// Large - /// - High, -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/JwtToken.cs b/back/src/Kyoo.Abstractions/Models/Resources/JwtToken.cs deleted file mode 100644 index 1342dc07..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/JwtToken.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Text.Json.Serialization; - -namespace Kyoo.Abstractions.Models; - -/// -/// A container representing the response of a login or token refresh. -/// -/// -/// Initializes a new instance of the class. -/// -/// The access token used to authorize requests. -/// The refresh token to retrieve a new access token. -/// When the access token will expire. -public class JwtToken(string accessToken, string refreshToken, TimeSpan expireIn) -{ - /// - /// The type of this token (always a Bearer). - /// - [JsonPropertyName("token_type")] - public string TokenType => "Bearer"; - - /// - /// The access token used to authorize requests. - /// - [JsonPropertyName("access_token")] - public string AccessToken { get; set; } = accessToken; - - /// - /// The refresh token used to retrieve a new access/refresh token when the access token has expired. - /// - [JsonPropertyName("refresh_token")] - public string RefreshToken { get; set; } = refreshToken; - - /// - /// When the access token will expire. After this time, the refresh token should be used to retrieve. - /// a new token.cs - /// - [JsonPropertyName("expire_in")] - public TimeSpan ExpireIn => ExpireAt.Subtract(DateTime.UtcNow); - - /// - /// The exact date at which the access token will expire. - /// - [JsonPropertyName("expire_at")] - public DateTime ExpireAt { get; set; } = DateTime.UtcNow + expireIn; -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Movie.cs b/back/src/Kyoo.Abstractions/Models/Resources/Movie.cs deleted file mode 100644 index 18d49946..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/Movie.cs +++ /dev/null @@ -1,192 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Text.Json.Serialization; -using EntityFrameworkCore.Projectables; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Utils; - -namespace Kyoo.Abstractions.Models; - -/// -/// A series or a movie. -/// -public class Movie - : IQuery, - IResource, - IMetadata, - IThumbnails, - IAddedDate, - IRefreshable, - ILibraryItem, - INews, - IWatchlist -{ - public static Sort DefaultSort => new Sort.By(x => x.Name); - - /// - public Guid Id { get; set; } - - /// - [MaxLength(256)] - public string Slug { get; set; } - - /// - /// The title of this show. - /// - public string Name { get; set; } - - /// - /// A catchphrase for this movie. - /// - public string? Tagline { get; set; } - - /// - /// The list of alternative titles of this show. - /// - public string[] Aliases { get; set; } = Array.Empty(); - - /// - /// The path of the movie video file. - /// - public string Path { get; set; } - - /// - /// The summary of this show. - /// - public string? Overview { get; set; } - - /// - /// A list of tags that match this movie. - /// - public string[] Tags { get; set; } = []; - - /// - /// The list of genres (themes) this show has. - /// - public List Genres { get; set; } = []; - - /// - /// Is this show airing, not aired yet or finished? - /// - public Status Status { get; set; } - - /// - /// How well this item is rated? (from 0 to 100). - /// - public int Rating { get; set; } - - /// - /// How long is this movie? (in minutes) - /// - public int? Runtime { get; set; } - - /// - /// The date this movie aired. - /// - public DateOnly? AirDate { get; set; } - - /// - public DateTime AddedDate { get; set; } - - /// - public Image? Poster { get; set; } - - /// - public Image? Thumbnail { get; set; } - - /// - public Image? Logo { get; set; } - - [JsonIgnore] - [Column("air_date")] - public DateOnly? StartAir => AirDate; - - [JsonIgnore] - [Column("air_date")] - public DateOnly? EndAir => AirDate; - - /// - /// A video of a few minutes that tease the content. - /// - public string? Trailer { get; set; } - - /// - public Dictionary ExternalId { get; set; } = new(); - - /// - public DateTime? NextMetadataRefresh { get; set; } - - /// - /// The ID of the Studio that made this show. - /// - [JsonIgnore] - public Guid? StudioId { get; set; } - - /// - /// The Studio that made this show. - /// - [LoadableRelation(nameof(StudioId))] - public Studio? Studio { get; set; } - - /// - /// The list of collections that contains this show. - /// - [JsonIgnore] - public ICollection? Collections { get; set; } - - /// - /// Links to watch this movie. - /// - public VideoLinks Links => - new() { Direct = $"/movie/{Slug}/direct", Hls = $"/movie/{Slug}/master.m3u8", }; - - [JsonIgnore] - public ICollection? Watched { get; set; } - - /// - /// Metadata of what an user as started/planned to watch. - /// - [Projectable(UseMemberBody = nameof(_WatchStatus), OnlyOnInclude = true)] - [LoadableRelation( - Sql = "movie_watch_status", - On = "movie_id = \"this\".id and \"relation\".user_id = [current_user]" - )] - public MovieWatchStatus? WatchStatus { get; set; } - - // There is a global query filter to filter by user so we just need to do single. - private MovieWatchStatus? _WatchStatus => Watched!.FirstOrDefault(); - - public Movie() { } - - [JsonConstructor] - public Movie(string name) - { - if (name != null) - { - Slug = Utility.ToSlug(name); - Name = name; - } - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Season.cs b/back/src/Kyoo.Abstractions/Models/Resources/Season.cs deleted file mode 100644 index d94e6514..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/Season.cs +++ /dev/null @@ -1,151 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Text.Json.Serialization; -using System.Text.RegularExpressions; -using EntityFrameworkCore.Projectables; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models.Attributes; - -namespace Kyoo.Abstractions.Models; - -/// -/// A season of a . -/// -public class Season : IQuery, IResource, IMetadata, IThumbnails, IAddedDate, IRefreshable -{ - public static Sort DefaultSort => new Sort.By(x => x.SeasonNumber); - - /// - public Guid Id { get; set; } - - /// - [Computed] - [MaxLength(256)] - public string Slug - { - get - { - if (ShowSlug == null && Show == null) - return $"{ShowId}-s{SeasonNumber}"; - return $"{ShowSlug ?? Show?.Slug}-s{SeasonNumber}"; - } - private set - { - Match match = Regex.Match(value, @"(?.+)-s(?\d+)"); - - if (!match.Success) - throw new ArgumentException( - "Invalid season slug. Format: {showSlug}-s{seasonNumber}" - ); - ShowSlug = match.Groups["show"].Value; - SeasonNumber = int.Parse(match.Groups["season"].Value); - } - } - - /// - /// The slug of the Show that contain this episode. If this is not set, this season is ill-formed. - /// - [JsonIgnore] - public string? ShowSlug { private get; set; } - - /// - /// The ID of the Show containing this season. - /// - public Guid ShowId { get; set; } - - /// - /// The show that contains this season. - /// - [LoadableRelation(nameof(ShowId))] - public Show? Show { get; set; } - - /// - /// The number of this season. This can be set to 0 to indicate specials. - /// - public int SeasonNumber { get; set; } - - /// - /// The title of this season. - /// - public string? Name { get; set; } - - /// - /// A quick overview of this season. - /// - public string? Overview { get; set; } - - /// - /// The starting air date of this season. - /// - public DateOnly? StartDate { get; set; } - - /// - public DateTime AddedDate { get; set; } - - /// - /// The ending date of this season. - /// - public DateOnly? EndDate { get; set; } - - /// - public Image? Poster { get; set; } - - /// - public Image? Thumbnail { get; set; } - - /// - public Image? Logo { get; set; } - - /// - public Dictionary ExternalId { get; set; } = new(); - - /// - public DateTime? NextMetadataRefresh { get; set; } - - /// - /// The list of episodes that this season contains. - /// - [JsonIgnore] - public ICollection? Episodes { get; set; } - - /// - /// The number of episodes in this season. - /// - [Projectable(UseMemberBody = nameof(_EpisodesCount), OnlyOnInclude = true)] - [NotMapped] - [LoadableRelation( - // language=PostgreSQL - Projected = """ - ( - select - count(*)::int - from - episodes as e - where - e.season_id = id) as episodes_count - """ - )] - public int EpisodesCount { get; set; } - - private int _EpisodesCount => Episodes!.Count; -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Show.cs b/back/src/Kyoo.Abstractions/Models/Resources/Show.cs deleted file mode 100644 index b3af184f..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/Show.cs +++ /dev/null @@ -1,283 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Text.Json.Serialization; -using EntityFrameworkCore.Projectables; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Utils; - -namespace Kyoo.Abstractions.Models; - -/// -/// A series or a movie. -/// -public class Show - : IQuery, - IResource, - IMetadata, - IOnMerge, - IThumbnails, - IAddedDate, - IRefreshable, - ILibraryItem, - IWatchlist -{ - public static Sort DefaultSort => new Sort.By(x => x.Name); - - /// - public Guid Id { get; set; } - - /// - [MaxLength(256)] - public string Slug { get; set; } - - /// - /// The title of this show. - /// - public string Name { get; set; } - - /// - /// A catchphrase for this show. - /// - public string? Tagline { get; set; } - - /// - /// The list of alternative titles of this show. - /// - public List Aliases { get; set; } = new(); - - /// - /// The summary of this show. - /// - public string? Overview { get; set; } - - /// - /// A list of tags that match this movie. - /// - public List Tags { get; set; } = new(); - - /// - /// The list of genres (themes) this show has. - /// - public List Genres { get; set; } = new(); - - /// - /// Is this show airing, not aired yet or finished? - /// - public Status Status { get; set; } - - /// - /// How well this item is rated? (from 0 to 100). - /// - public int Rating { get; set; } - - /// - /// The date this show started airing. It can be null if this is unknown. - /// - public DateOnly? StartAir { get; set; } - - /// - /// The date this show finished airing. - /// It can also be null if this is unknown. - /// - public DateOnly? EndAir { get; set; } - - /// - public DateTime AddedDate { get; set; } - - /// - public Image? Poster { get; set; } - - /// - public Image? Thumbnail { get; set; } - - /// - public Image? Logo { get; set; } - - /// - /// A video of a few minutes that tease the content. - /// - public string? Trailer { get; set; } - - [JsonIgnore] - [Column("start_air")] - public DateOnly? AirDate => StartAir; - - /// - public Dictionary ExternalId { get; set; } = new(); - - /// - public DateTime? NextMetadataRefresh { get; set; } - - /// - /// The ID of the Studio that made this show. - /// - public Guid? StudioId { get; set; } - - /// - /// The Studio that made this show. - /// - [LoadableRelation(nameof(StudioId))] - public Studio? Studio { get; set; } - - /// - /// The different seasons in this show. If this is a movie, this list is always null or empty. - /// - [JsonIgnore] - public ICollection? Seasons { get; set; } - - /// - /// The list of episodes in this show. - /// If this is a movie, there will be a unique episode (with the seasonNumber and episodeNumber set to null). - /// Having an episode is necessary to store metadata and tracks. - /// - [JsonIgnore] - public ICollection? Episodes { get; set; } - - /// - /// The list of collections that contains this show. - /// - [JsonIgnore] - public ICollection? Collections { get; set; } - - /// - /// The first episode of this show. - /// - [Projectable(UseMemberBody = nameof(_FirstEpisode), OnlyOnInclude = true)] - [LoadableRelation( - // language=PostgreSQL - Sql = """ - select - fe.* -- Episode as fe - from ( - select - e.*, - row_number() over (partition by e.show_id order by e.absolute_number, e.season_number, e.episode_number) as number - from - episodes as e) as "fe" - where - fe.number <= 1 - """, - On = "show_id = \"this\".id" - )] - public Episode? FirstEpisode { get; set; } - - private Episode? _FirstEpisode => - Episodes! - .OrderBy(x => x.AbsoluteNumber) - .ThenBy(x => x.SeasonNumber) - .ThenBy(x => x.EpisodeNumber) - .FirstOrDefault(); - - /// - /// The number of episodes in this show. - /// - [Projectable(UseMemberBody = nameof(_EpisodesCount), OnlyOnInclude = true)] - [NotMapped] - [LoadableRelation( - // language=PostgreSQL - Projected = """ - ( - select - count(*)::int - from - episodes as e - where - e.show_id = "this".id) as episodes_count - """ - )] - public int EpisodesCount { get; set; } - - private int _EpisodesCount => Episodes!.Count; - - [JsonIgnore] - public ICollection? Watched { get; set; } - - /// - /// Metadata of what an user as started/planned to watch. - /// - [Projectable(UseMemberBody = nameof(_WatchStatus), OnlyOnInclude = true)] - [LoadableRelation( - Sql = "show_watch_status", - On = "show_id = \"this\".id and \"relation\".user_id = [current_user]" - )] - public ShowWatchStatus? WatchStatus { get; set; } - - // There is a global query filter to filter by user so we just need to do single. - private ShowWatchStatus? _WatchStatus => Watched!.FirstOrDefault(); - - /// - public void OnMerge(object merged) - { - if (Seasons != null) - { - foreach (Season season in Seasons) - season.Show = this; - } - - if (Episodes != null) - { - foreach (Episode episode in Episodes) - episode.Show = this; - } - } - - public Show() { } - - [JsonConstructor] - public Show(string name) - { - if (name != null) - { - Slug = Utility.ToSlug(name); - Name = name; - } - } -} - -/// -/// The enum containing show's status. -/// -public enum Status -{ - /// - /// The status of the show is not known. - /// - Unknown, - - /// - /// The show has finished airing. - /// - Finished, - - /// - /// The show is still actively airing. - /// - Airing, - - /// - /// This show has not aired yet but has been announced. - /// - Planned -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Studio.cs b/back/src/Kyoo.Abstractions/Models/Resources/Studio.cs deleted file mode 100644 index 9b6a5575..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/Studio.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text.Json.Serialization; -using Kyoo.Abstractions.Controllers; -using Kyoo.Utils; - -namespace Kyoo.Abstractions.Models; - -/// -/// A studio that make shows. -/// -public class Studio : IQuery, IResource, IMetadata -{ - public static Sort DefaultSort => new Sort.By(x => x.Name); - - /// - public Guid Id { get; set; } - - /// - [MaxLength(256)] - public string Slug { get; set; } - - /// - /// The name of this studio. - /// - public string Name { get; set; } - - /// - /// The list of shows that are made by this studio. - /// - [JsonIgnore] - public ICollection? Shows { get; set; } - - /// - /// The list of movies that are made by this studio. - /// - [JsonIgnore] - public ICollection? Movies { get; set; } - - /// - public Dictionary ExternalId { get; set; } = new(); - - /// - /// Create a new, empty, . - /// - public Studio() { } - - /// - /// Create a new with a specific name, the slug is calculated automatically. - /// - /// The name of the studio. - [JsonConstructor] - public Studio(string name) - { - if (name != null) - { - Slug = Utility.ToSlug(name); - Name = name; - } - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/User.cs b/back/src/Kyoo.Abstractions/Models/Resources/User.cs deleted file mode 100644 index 2dfffc70..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/User.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text.Json.Serialization; -using Kyoo.Abstractions.Controllers; -using Kyoo.Utils; - -namespace Kyoo.Abstractions.Models; - -/// -/// A single user of the app. -/// -public class User : IQuery, IResource, IAddedDate -{ - public static Sort DefaultSort => new Sort.By(x => x.Username); - - /// - public Guid Id { get; set; } - - /// - [MaxLength(256)] - public string Slug { get; set; } - - /// - /// A username displayed to the user. - /// - public string Username { get; set; } - - /// - /// The user email address. - /// - public string Email { get; set; } - - /// - /// The user password (hashed, it can't be read like that). The hashing format is implementation defined. - /// - [JsonIgnore] - public string? Password { get; set; } - - /// - /// Does the user can sign-in with a password or only via oidc? - /// - public bool HasPassword => Password != null; - - /// - /// The list of permissions of the user. The format of this is implementation dependent. - /// - public string[] Permissions { get; set; } = Array.Empty(); - - /// - public DateTime AddedDate { get; set; } - - /// - /// User settings - /// - public Dictionary Settings { get; set; } = new(); - - /// - /// User accounts on other services. - /// - public Dictionary ExternalId { get; set; } = new(); - - public User() { } - - [JsonConstructor] - public User(string username) - { - if (username != null) - { - Slug = Utility.ToSlug(username); - Username = username; - } - } -} - -public class ExternalToken -{ - /// - /// The id of this user on the external service. - /// - public string Id { get; set; } - - /// - /// The username on the external service. - /// - public string Username { get; set; } - - /// - /// The link to the user profile on this website. Null if it does not exist. - /// - public string? ProfileUrl { get; set; } - - /// - /// A jwt token used to interact with the service. - /// Do not forget to refresh it when using it if necessary. - /// - public JwtToken Token { get; set; } -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/WatchStatus.cs b/back/src/Kyoo.Abstractions/Models/Resources/WatchStatus.cs deleted file mode 100644 index d6576f5e..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/WatchStatus.cs +++ /dev/null @@ -1,279 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Text.Json.Serialization; -using Kyoo.Abstractions.Models.Attributes; - -namespace Kyoo.Abstractions.Models; - -/// -/// Has the user started watching, is it planned? -/// -public enum WatchStatus -{ - /// - /// The user has already watched this. - /// - Completed, - - /// - /// The user started watching this but has not finished. - /// - Watching, - - /// - /// The user does not plan to continue watching. - /// - Droped, - - /// - /// The user has not started watching this but plans to. - /// - Planned, - - /// - /// The watch status was deleted and can not be retrived again. - /// - Deleted, -} - -/// -/// Metadata of what an user as started/planned to watch. -/// -[SqlFirstColumn(nameof(UserId))] -public class MovieWatchStatus : IAddedDate -{ - /// - /// The ID of the user that started watching this episode. - /// - public Guid UserId { get; set; } - - /// - /// The user that started watching this episode. - /// - [JsonIgnore] - public User User { get; set; } - - /// - /// The ID of the movie started. - /// - public Guid MovieId { get; set; } - - /// - /// The started. - /// - [JsonIgnore] - public Movie Movie { get; set; } - - /// - public DateTime AddedDate { get; set; } - - /// - /// The date at which this item was played. - /// - public DateTime? PlayedDate { get; set; } - - /// - /// Has the user started watching, is it planned? - /// - public WatchStatus Status { get; set; } - - /// - /// Where the player has stopped watching the movie (in seconds). - /// - /// - /// Null if the status is not Watching. - /// - public int? WatchedTime { get; set; } - - /// - /// Where the player has stopped watching the movie (in percentage between 0 and 100). - /// - /// - /// Null if the status is not Watching. - /// - public int? WatchedPercent { get; set; } -} - -[SqlFirstColumn(nameof(UserId))] -public class EpisodeWatchStatus : IAddedDate -{ - /// - /// The ID of the user that started watching this episode. - /// - public Guid UserId { get; set; } - - /// - /// The user that started watching this episode. - /// - [JsonIgnore] - public User User { get; set; } - - /// - /// The ID of the episode started. - /// - public Guid? EpisodeId { get; set; } - - /// - /// The started. - /// - [JsonIgnore] - public Episode Episode { get; set; } - - /// - public DateTime AddedDate { get; set; } - - /// - /// The date at which this item was played. - /// - public DateTime? PlayedDate { get; set; } - - /// - /// Has the user started watching, is it planned? - /// - public WatchStatus Status { get; set; } - - /// - /// Where the player has stopped watching the episode (in seconds). - /// - /// - /// Null if the status is not Watching. - /// - public int? WatchedTime { get; set; } - - /// - /// Where the player has stopped watching the episode (in percentage between 0 and 100). - /// - /// - /// Null if the status is not Watching or if the next episode is not started. - /// - public int? WatchedPercent { get; set; } -} - -[SqlFirstColumn(nameof(UserId))] -public class ShowWatchStatus : IAddedDate -{ - /// - /// The ID of the user that started watching this episode. - /// - public Guid UserId { get; set; } - - /// - /// The user that started watching this episode. - /// - [JsonIgnore] - public User User { get; set; } - - /// - /// The ID of the show started. - /// - public Guid ShowId { get; set; } - - /// - /// The started. - /// - [JsonIgnore] - public Show Show { get; set; } - - /// - public DateTime AddedDate { get; set; } - - /// - /// The date at which this item was played. - /// - public DateTime? PlayedDate { get; set; } - - /// - /// Has the user started watching, is it planned? - /// - public WatchStatus Status { get; set; } - - /// - /// The number of episodes the user has not seen. - /// - public int UnseenEpisodesCount { get; set; } - - /// - /// The ID of the episode started. - /// - public Guid? NextEpisodeId { get; set; } - - /// - /// The next to watch. - /// - public Episode? NextEpisode { get; set; } - - /// - /// Where the player has stopped watching the episode (in seconds). - /// - /// - /// Null if the status is not Watching or if the next episode is not started. - /// - public int? WatchedTime { get; set; } - - /// - /// Where the player has stopped watching the episode (in percentage between 0 and 100). - /// - /// - /// Null if the status is not Watching or if the next episode is not started. - /// - public int? WatchedPercent { get; set; } -} - -public class WatchStatus : IAddedDate -{ - /// - /// Has the user started watching, is it planned? - /// - public required WatchStatus Status { get; set; } - - /// - public DateTime AddedDate { get; set; } - - /// - /// The date at which this item was played. - /// - public DateTime? PlayedDate { get; set; } - - /// - /// Where the player has stopped watching the episode (in seconds). - /// - /// - /// Null if the status is not Watching or if the next episode is not started. - /// - public int? WatchedTime { get; set; } - - /// - /// Where the player has stopped watching the episode (in percentage between 0 and 100). - /// - /// - /// Null if the status is not Watching or if the next episode is not started. - /// - public int? WatchedPercent { get; set; } - - /// - /// The user that started watching this episode. - /// - public required User User { get; set; } - - /// - /// The episode/show/movie whose status changed - /// - public required T Resource { get; set; } -} diff --git a/back/src/Kyoo.Abstractions/Models/SearchPage.cs b/back/src/Kyoo.Abstractions/Models/SearchPage.cs deleted file mode 100644 index 8ce2043a..00000000 --- a/back/src/Kyoo.Abstractions/Models/SearchPage.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; - -namespace Kyoo.Abstractions.Models; - -/// -/// Results of a search request. -/// -/// The search item's type. -public class SearchPage : Page - where T : IResource -{ - public SearchPage( - SearchResult result, - string @this, - string? previous, - string? next, - string first - ) - : base(result.Items, @this, previous, next, first) - { - Query = result.Query; - } - - /// - /// The query of the search request. - /// - public string? Query { get; init; } - - public class SearchResult - { - public string? Query { get; set; } - - public ICollection Items { get; set; } - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Claims.cs b/back/src/Kyoo.Abstractions/Models/Utils/Claims.cs deleted file mode 100644 index c8d8c3c7..00000000 --- a/back/src/Kyoo.Abstractions/Models/Utils/Claims.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -namespace Kyoo.Authentication.Models; - -/// -/// List of well known claims of kyoo -/// -public static class Claims -{ - /// - /// The id of the user - /// - public static string Id => "id"; - - /// - /// The name of the user - /// - public static string Name => "name"; - - /// - /// The email of the user. - /// - public static string Email => "email"; - - /// - /// The list of permissions that the user has. - /// - public static string Permissions => "permissions"; - - /// - /// The type of the token (either "access" or "refresh"). - /// - public static string Type => "type"; - - /// - /// A guid used to identify a specific refresh token. This is only useful for the server to revokate tokens. - /// - public static string Guid => "guid"; -} diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Constants.cs b/back/src/Kyoo.Abstractions/Models/Utils/Constants.cs deleted file mode 100644 index f12c44d5..00000000 --- a/back/src/Kyoo.Abstractions/Models/Utils/Constants.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using Kyoo.Abstractions.Models.Attributes; - -namespace Kyoo.Abstractions.Models.Utils; - -/// -/// A class containing constant numbers. -/// -public static class Constants -{ - /// - /// A property to use on a Microsoft.AspNet.MVC.Route.Order property to mark it as an alternative route - /// that won't be included on the swagger. - /// - public const int AlternativeRoute = 1; - - /// - /// A group name for . It should be used for endpoints used by users. - /// - public const string UsersGroup = "0:Users"; - - /// - /// A group name for . It should be used for main resources of kyoo. - /// - public const string ResourcesGroup = "1:Resources"; - - /// - /// A group name for . - /// It should be used for sub resources of kyoo that help define the main resources. - /// - public const string MetadataGroup = "2:Metadata"; - - /// - /// A group name for . It should be used for endpoints useful for playback. - /// - public const string WatchGroup = "3:Watch"; - - /// - /// A group name for . It should be used for endpoints used by admins. - /// - public const string AdminGroup = "4:Admin"; - public const string OtherGroup = "5:Other"; -} diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs b/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs deleted file mode 100644 index 4d832928..00000000 --- a/back/src/Kyoo.Abstractions/Models/Utils/Filter.cs +++ /dev/null @@ -1,369 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using Kyoo.Abstractions.Models.Attributes; -using Sprache; - -namespace Kyoo.Abstractions.Models.Utils; - -public static class ParseHelper -{ - public static Parser ErrorMessage(this Parser @this, string message) => - input => - { - IResult result = @this(input); - - return result.WasSuccessful - ? result - : Result.Failure(result.Remainder, message, result.Expectations); - }; - - public static Parser Error(string message) => - input => - { - return Result.Failure(input, message, Array.Empty()); - }; -} - -public abstract record Filter -{ - public static Filter? And(params Filter?[] filters) - { - return filters - .Where(x => x != null) - .Aggregate( - (Filter?)null, - (acc, filter) => - { - if (acc == null) - return filter; - return new Filter.And(acc, filter!); - } - ); - } - - public static Filter? Or(params Filter?[] filters) - { - return filters - .Where(x => x != null) - .Aggregate( - (Filter?)null, - (acc, filter) => - { - if (acc == null) - return filter; - return new Filter.Or(acc, filter!); - } - ); - } -} - -public abstract record Filter : Filter -{ - public record And(Filter First, Filter Second) : Filter; - - public record Or(Filter First, Filter Second) : Filter; - - public record Not(Filter Filter) : Filter; - - public record Eq(string Property, object? Value) : Filter; - - public record Ne(string Property, object? Value) : Filter; - - public record Gt(string Property, object Value) : Filter; - - public record Ge(string Property, object Value) : Filter; - - public record Lt(string Property, object Value) : Filter; - - public record Le(string Property, object Value) : Filter; - - public record Has(string Property, object Value) : Filter; - - /// - /// Internal filter used for keyset paginations to resume random sorts. - /// The pseudo sql is md5(seed || table.id) = md5(seed || 'hardCodedId') - /// - public record CmpRandom(string cmp, string Seed, Guid ReferenceId) : Filter; - - /// - /// Internal filter used only in EF with hard coded lamdas (used for relations). - /// - public record Lambda(Expression> Inner) : Filter; - - public static class FilterParsers - { - public static readonly Parser> Filter = Parse - .Ref(() => Bracket) - .Or(Parse.Ref(() => Not)) - .Or(Parse.Ref(() => Eq)) - .Or(Parse.Ref(() => Ne)) - .Or(Parse.Ref(() => Gt)) - .Or(Parse.Ref(() => Ge)) - .Or(Parse.Ref(() => Lt)) - .Or(Parse.Ref(() => Le)) - .Or(Parse.Ref(() => Has)); - - public static readonly Parser> CompleteFilter = Parse - .Ref(() => Or) - .Or(Parse.Ref(() => And)) - .Or(Filter); - - public static readonly Parser> Bracket = - from open in Parse.Char('(').Token() - from filter in CompleteFilter - from close in Parse.Char(')').Token() - select filter; - - public static readonly Parser> AndOperator = Parse - .IgnoreCase("and") - .Or(Parse.String("&&")) - .Token(); - - public static readonly Parser> OrOperator = Parse - .IgnoreCase("or") - .Or(Parse.String("||")) - .Token(); - - public static readonly Parser> And = Parse.ChainOperator( - AndOperator, - Filter, - (_, a, b) => new And(a, b) - ); - - public static readonly Parser> Or = Parse.ChainOperator( - OrOperator, - And.Or(Filter), - (_, a, b) => new Or(a, b) - ); - - public static readonly Parser> Not = - from not in Parse.IgnoreCase("not").Or(Parse.String("!")).Token() - from filter in CompleteFilter - select new Not(filter); - - private static Parser _GetValueParser(Type type) - { - Type? nullable = Nullable.GetUnderlyingType(type); - if (nullable != null) - { - return from value in _GetValueParser(nullable) select value; - } - - if (type == typeof(int)) - return Parse.Number.Select(x => int.Parse(x) as object); - - if (type == typeof(float)) - { - return from a in Parse.Number - from dot in Parse.Char('.') - from b in Parse.Number - select float.Parse($"{a}.{b}") as object; - } - - if (type == typeof(Guid)) - { - return from guid in Parse.Regex( - @"[({]?[a-fA-F0-9]{8}[-]?([a-fA-F0-9]{4}[-]?){3}[a-fA-F0-9]{12}[})]?", - "Guid" - ) - select Guid.Parse(guid) as object; - } - - if (type == typeof(string)) - { - return ( - from lq in Parse.Char('"').Or(Parse.Char('\'')) - from str in Parse.AnyChar.Where(x => x != lq).Many().Text() - from rq in Parse.Char(lq) - select str - ).Or(Parse.LetterOrDigit.Many().Text()); - } - - if (type.IsEnum) - { - return Parse - .LetterOrDigit.Many() - .Text() - .Then(x => - { - if (Enum.TryParse(type, x, true, out object? value)) - return Parse.Return(value); - return ParseHelper.Error($"Invalid enum value. Unexpected {x}"); - }); - } - - if (type == typeof(DateTime) || type == typeof(DateOnly)) - { - return from year in Parse.Digit.Repeat(4).Text().Select(int.Parse) - from yd in Parse.Char('-') - from month in Parse.Digit.Repeat(2).Text().Select(int.Parse) - from md in Parse.Char('-') - from day in Parse.Digit.Repeat(2).Text().Select(int.Parse) - select type == typeof(DateTime) - ? new DateTime(year, month, day) as object - : new DateOnly(year, month, day) as object; - } - - if (typeof(IEnumerable).IsAssignableFrom(type)) - return ParseHelper.Error( - "Can't filter a list with a default comparator, use the 'has' filter." - ); - return ParseHelper.Error("Unfilterable field found"); - } - - private static Parser> _GetOperationParser( - Parser op, - Func> apply, - Func>? customTypeParser = null - ) - { - Parser property = Parse.LetterOrDigit.AtLeastOnce().Text(); - - return property.Then(prop => - { - Type[] types = - typeof(T).GetCustomAttribute()?.Types ?? new[] { typeof(T) }; - - if (string.Equals(prop, "kind", StringComparison.OrdinalIgnoreCase)) - { - return from eq in op - from val in types - .Select(x => Parse.IgnoreCase(x.Name).Text()) - .Aggregate( - null as Parser, - (acc, x) => acc == null ? x : Parse.Or(acc, x) - ) - select apply("kind", val); - } - - PropertyInfo? propInfo = types - .Select(x => - x.GetProperty( - prop, - BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance - ) - ) - .FirstOrDefault(); - if (propInfo == null) - return ParseHelper.Error>($"The given filter '{prop}' is invalid."); - - Parser value = - customTypeParser != null - ? customTypeParser(propInfo.PropertyType) - : _GetValueParser(propInfo.PropertyType); - - return from eq in op - from val in value - select apply(propInfo.Name, val); - }); - } - - public static readonly Parser> Eq = _GetOperationParser( - Parse.IgnoreCase("eq").Or(Parse.String("=")).Token(), - (property, value) => new Eq(property, value), - (Type type) => - { - Type? inner = Nullable.GetUnderlyingType(type); - if (inner == null) - return _GetValueParser(type); - return Parse - .String("null") - .Token() - .Return((object?)null) - .Or(_GetValueParser(inner)); - } - ); - - public static readonly Parser> Ne = _GetOperationParser( - Parse.IgnoreCase("ne").Or(Parse.String("!=")).Token(), - (property, value) => new Ne(property, value), - (Type type) => - { - Type? inner = Nullable.GetUnderlyingType(type); - if (inner == null) - return _GetValueParser(type); - return Parse - .String("null") - .Token() - .Return((object?)null) - .Or(_GetValueParser(inner)); - } - ); - - public static readonly Parser> Gt = _GetOperationParser( - Parse.IgnoreCase("gt").Or(Parse.String(">")).Token(), - (property, value) => new Gt(property, value) - ); - - public static readonly Parser> Ge = _GetOperationParser( - Parse.IgnoreCase("ge").Or(Parse.IgnoreCase("gte")).Or(Parse.String(">=")).Token(), - (property, value) => new Ge(property, value) - ); - - public static readonly Parser> Lt = _GetOperationParser( - Parse.IgnoreCase("lt").Or(Parse.String("<")).Token(), - (property, value) => new Lt(property, value) - ); - - public static readonly Parser> Le = _GetOperationParser( - Parse.IgnoreCase("le").Or(Parse.IgnoreCase("lte")).Or(Parse.String("<=")).Token(), - (property, value) => new Le(property, value) - ); - - public static readonly Parser> Has = _GetOperationParser( - Parse.IgnoreCase("has").Token(), - (property, value) => new Has(property, value), - (Type type) => - { - if (typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string)) - return _GetValueParser( - type.GetElementType() ?? type.GenericTypeArguments.First() - ); - return ParseHelper.Error("Can't use 'has' on a non-list."); - } - ); - } - - public static Filter? From(string? filter) - { - if (filter == null) - return null; - - try - { - IResult> ret = FilterParsers.CompleteFilter.End().TryParse(filter); - if (ret.WasSuccessful) - return ret.Value; - throw new ValidationException( - $"Could not parse filter argument: {ret.Message}. Not parsed: {filter[ret.Remainder.Position..]}" - ); - } - catch (ParseException ex) - { - throw new ValidationException($"Could not parse filter argument: {ex.Message}."); - } - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Identifier.cs b/back/src/Kyoo.Abstractions/Models/Utils/Identifier.cs deleted file mode 100644 index f63a6afd..00000000 --- a/back/src/Kyoo.Abstractions/Models/Utils/Identifier.cs +++ /dev/null @@ -1,245 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Globalization; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; - -namespace Kyoo.Abstractions.Models.Utils; - -/// -/// A class that represent a resource. It is made to be used as a parameter in a query and not used somewhere else -/// on the application. -/// This class allow routes to be used via ether IDs or Slugs, this is suitable for every . -/// -[TypeConverter(typeof(IdentifierConvertor))] -public class Identifier -{ - /// - /// The ID of the resource or null if the slug is specified. - /// - private readonly Guid? _id; - - /// - /// The slug of the resource or null if the id is specified. - /// - private readonly string? _slug; - - /// - /// Create a new for the given id. - /// - /// The id of the resource. - public Identifier(Guid id) - { - _id = id; - } - - /// - /// Create a new for the given slug. - /// - /// The slug of the resource. - public Identifier(string slug) - { - _slug = slug; - } - - /// - /// Pattern match out of the identifier to a resource. - /// - /// The function to match the ID to a type . - /// The function to match the slug to a type . - /// The return type that will be converted to from an ID or a slug. - /// - /// The result of the or depending on the pattern. - /// - /// - /// Example usage: - /// - /// T ret = await identifier.Match( - /// id => _repository.GetOrDefault(id), - /// slug => _repository.GetOrDefault(slug) - /// ); - /// - /// - public T Match(Func idFunc, Func slugFunc) - { - return _id.HasValue ? idFunc(_id.Value) : slugFunc(_slug!); - } - - /// - /// Match a custom type to an identifier. This can be used for wrapped resources (see example for more details). - /// - /// An expression to retrieve an ID from the type . - /// An expression to retrieve a slug from the type . - /// The type to match against this identifier. - /// An expression to match the type to this identifier. - /// - /// - /// identifier.Matcher<Season>(x => x.ShowID, x => x.Show.Slug) - /// - /// - public Filter Matcher( - Expression> idGetter, - Expression> slugGetter - ) - { - ConstantExpression self = Expression.Constant(_id.HasValue ? _id.Value : _slug); - BinaryExpression equal = Expression.Equal( - _id.HasValue ? idGetter.Body : slugGetter.Body, - self - ); - ICollection parameters = _id.HasValue - ? idGetter.Parameters - : slugGetter.Parameters; - Expression> lambda = Expression.Lambda>(equal, parameters); - return new Filter.Lambda(lambda); - } - - /// - /// A matcher overload for nullable IDs. See - /// - /// for more details. - /// - /// An expression to retrieve an ID from the type . - /// An expression to retrieve a slug from the type . - /// The type to match against this identifier. - /// An expression to match the type to this identifier. - public Filter Matcher( - Expression> idGetter, - Expression> slugGetter - ) - { - ConstantExpression self = Expression.Constant(_id.HasValue ? _id.Value : _slug); - BinaryExpression equal = Expression.Equal( - _id.HasValue ? idGetter.Body : slugGetter.Body, - self - ); - ICollection parameters = _id.HasValue - ? idGetter.Parameters - : slugGetter.Parameters; - Expression> lambda = Expression.Lambda>(equal, parameters); - return new Filter.Lambda(lambda); - } - - /// - /// Return true if this match a resource. - /// - /// The resource to match - /// - /// true if the match this identifier, false otherwise. - /// - public bool IsSame(IResource resource) - { - return Match(id => resource.Id == id, slug => resource.Slug == slug); - } - - /// - /// Return a filter to get this match a given resource. - /// - /// The type of resource to match against. - /// - /// true if the given resource match this identifier, false otherwise. - /// - public Filter IsSame() - where T : IResource - { - return _id.HasValue ? new Filter.Eq("Id", _id.Value) : new Filter.Eq("Slug", _slug!); - } - - public bool Is(Guid uid) - { - return _id.HasValue && _id.Value == uid; - } - - public bool Is(string slug) - { - return !_id.HasValue && _slug == slug; - } - - private Expression> _IsSameExpression() - where T : IResource - { - return _id.HasValue ? x => x.Id == _id.Value : x => x.Slug == _slug; - } - - /// - /// Return an expression that return true if this is containing in a collection. - /// - /// An expression to retrieve the list to check. - /// The type that contain the list to check. - /// The type of resource to check this identifier against. - /// An expression to check if this is contained. - public Filter IsContainedIn(Expression?>> listGetter) - where T2 : IResource - { - MethodInfo method = typeof(Enumerable) - .GetMethods() - .Where(x => x.Name == nameof(Enumerable.Any)) - .FirstOrDefault(x => x.GetParameters().Length == 2)! - .MakeGenericMethod(typeof(T2)); - MethodCallExpression call = Expression.Call( - null, - method, - listGetter.Body, - _IsSameExpression() - ); - Expression> lambda = Expression.Lambda>( - call, - listGetter.Parameters - ); - return new Filter.Lambda(lambda); - } - - /// - public override string ToString() - { - return _id.HasValue ? _id.Value.ToString() : _slug!; - } - - /// - /// A custom used to convert int or strings to an . - /// - public class IdentifierConvertor : TypeConverter - { - /// - public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) - { - if (sourceType == typeof(Guid) || sourceType == typeof(string)) - return true; - return base.CanConvertFrom(context, sourceType); - } - - /// - public override object ConvertFrom( - ITypeDescriptorContext? context, - CultureInfo? culture, - object value - ) - { - if (value is Guid id) - return new Identifier(id); - if (value is not string slug) - return base.ConvertFrom(context, culture, value)!; - return Guid.TryParse(slug, out id) ? new Identifier(id) : new Identifier(slug); - } - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Include.cs b/back/src/Kyoo.Abstractions/Models/Utils/Include.cs deleted file mode 100644 index 78cf65c1..00000000 --- a/back/src/Kyoo.Abstractions/Models/Utils/Include.cs +++ /dev/null @@ -1,109 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Reflection; -using Kyoo.Abstractions.Models.Attributes; - -namespace Kyoo.Abstractions.Models.Utils; - -public class Include -{ - /// - /// The aditional fields to include in the result. - /// - public ICollection Metadatas { get; set; } = ArraySegment.Empty; - - public abstract record Metadata(string Name); - - public record SingleRelation(string Name, Type type, string RelationIdName) : Metadata(Name); - - public record CustomRelation(string Name, Type type, string Sql, string? On, Type Declaring) - : Metadata(Name); - - public record ProjectedRelation(string Name, string Sql) : Metadata(Name); -} - -/// -/// The aditional fields to include in the result. -/// -/// The type related to the new fields -public class Include : Include -{ - /// - /// The aditional fields names to include in the result. - /// - public ICollection Fields => Metadatas.Select(x => x.Name).ToList(); - - public Include() { } - - public Include(params string[] fields) - { - Type[] types = typeof(T).GetCustomAttribute()?.Types ?? new[] { typeof(T) }; - Metadatas = fields - .SelectMany(key => - { - var relations = types - .Select(x => - x.GetProperty( - key, - BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance - )! - ) - .Select(prop => - (prop, attr: prop?.GetCustomAttribute()!) - ) - .Where(x => x.prop != null && x.attr != null) - .ToList(); - if (!relations.Any()) - throw new ValidationException($"No loadable relation with the name {key}."); - return relations - .Select(x => - { - (PropertyInfo prop, LoadableRelationAttribute attr) = x; - - if (attr.RelationID != null) - return new SingleRelation(prop.Name, prop.PropertyType, attr.RelationID) - as Metadata; - if (attr.Sql != null) - return new CustomRelation( - prop.Name, - prop.PropertyType, - attr.Sql, - attr.On, - prop.DeclaringType! - ); - if (attr.Projected != null) - return new ProjectedRelation(prop.Name, attr.Projected); - throw new NotImplementedException(); - }) - .Distinct(); - }) - .ToArray(); - } - - public static Include From(string? fields) - { - if (string.IsNullOrEmpty(fields)) - return new Include(); - return new Include(fields.Split(',')); - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Pagination.cs b/back/src/Kyoo.Abstractions/Models/Utils/Pagination.cs deleted file mode 100644 index 66cfe74f..00000000 --- a/back/src/Kyoo.Abstractions/Models/Utils/Pagination.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; - -namespace Kyoo.Abstractions.Controllers; - -/// -/// Information about the pagination. How many items should be displayed and where to start. -/// -public class Pagination -{ - /// - /// The count of items to return. - /// - public int Limit { get; set; } - - /// - /// Where to start? Using the given sort. - /// - public Guid? AfterID { get; set; } - - /// - /// Should the previous page be returned instead of the next? - /// - public bool Reverse { get; set; } - - /// - /// Create a new with default values. - /// - public Pagination() - { - Limit = 50; - AfterID = null; - Reverse = false; - } - - /// - /// Create a new instance. - /// - /// Set the value - /// Set the value. If not specified, it will start from the start - /// Should the previous page be returned instead of the next? - public Pagination(int count, Guid? afterID = null, bool reverse = false) - { - Limit = count; - AfterID = afterID; - Reverse = reverse; - } - - /// - /// Implicitly create a new pagination from a limit number. - /// - /// Set the value - /// A new instance - public static implicit operator Pagination(int limit) => new(limit); -} diff --git a/back/src/Kyoo.Abstractions/Models/Utils/RequestError.cs b/back/src/Kyoo.Abstractions/Models/Utils/RequestError.cs deleted file mode 100644 index f3ea8205..00000000 --- a/back/src/Kyoo.Abstractions/Models/Utils/RequestError.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Linq; - -namespace Kyoo.Abstractions.Models.Utils; - -/// -/// The list of errors that where made in the request. -/// -public class RequestError -{ - /// - /// The list of errors that where made in the request. - /// - /// ["InvalidFilter: no field 'startYear' on a collection"] - public string[] Errors { get; set; } - - /// - /// Create a new with one error. - /// - /// The error to specify in the response. - public RequestError(string error) - { - if (error == null) - throw new ArgumentNullException(nameof(error)); - Errors = new[] { error }; - } - - /// - /// Create a new with multiple errors. - /// - /// The errors to specify in the response. - public RequestError(string[] errors) - { - if (errors == null || !errors.Any()) - throw new ArgumentException("Errors must be non null and not empty", nameof(errors)); - Errors = errors; - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Utils/SearchPagination.cs b/back/src/Kyoo.Abstractions/Models/Utils/SearchPagination.cs deleted file mode 100644 index 30002988..00000000 --- a/back/src/Kyoo.Abstractions/Models/Utils/SearchPagination.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -namespace Kyoo.Abstractions.Controllers; - -/// -/// Information about the pagination. How many items should be displayed and where to start. -/// -public class SearchPagination -{ - /// - /// The count of items to return. - /// - public int Limit { get; set; } = 50; - - /// - /// Where to start? How many items to skip? - /// - public int? Skip { get; set; } -} diff --git a/back/src/Kyoo.Abstractions/Models/Utils/Sort.cs b/back/src/Kyoo.Abstractions/Models/Utils/Sort.cs deleted file mode 100644 index 4bd4d447..00000000 --- a/back/src/Kyoo.Abstractions/Models/Utils/Sort.cs +++ /dev/null @@ -1,137 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Utils; - -namespace Kyoo.Abstractions.Controllers; - -public record Sort; - -/// -/// Information about how a query should be sorted. What factor should decide the sort and in which order. -/// -/// For witch type this sort applies -public record Sort : Sort - where T : IQuery -{ - /// - /// Sort by a specific key - /// - /// The sort keys. This members will be used to sort the results. - /// - /// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendant order. - /// - public record By(string Key, bool Desendant = false) : Sort - { - /// - /// Sort by a specific key - /// - /// The sort keys. This members will be used to sort the results. - /// - /// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendant order. - /// - public By(Expression> key, bool desendant = false) - : this(Utility.GetPropertyName(key), desendant) { } - } - - /// - /// Sort by multiple keys. - /// - /// The list of keys to sort by. - public record Conglomerate(params Sort[] List) : Sort; - - /// Sort randomly items - public record Random(uint Seed) : Sort - { - public Random() - : this(0) - { - uint seed = BitConverter.ToUInt32( - BitConverter.GetBytes(new System.Random().Next(int.MinValue, int.MaxValue)), - 0 - ); - Seed = seed; - } - } - - /// The default sort method for the given type. - public record Default : Sort - { - public void Deconstruct(out Sort value) - { - value = (Sort)T.DefaultSort; - } - } - - /// - /// Create a new instance from a key's name (case insensitive). - /// - /// A key name with an optional order specifier. Format: "key:asc", "key:desc" or "key". - /// The random seed. - /// An invalid key or sort specifier as been given. - /// A for the given string - public static Sort From(string? sortBy, uint seed) - { - if (string.IsNullOrEmpty(sortBy) || sortBy == "default") - return new Default(); - if (sortBy == "random") - return new Random(seed); - if (sortBy.Contains(',')) - return new Conglomerate(sortBy.Split(',').Select(x => From(x, seed)).ToArray()); - - if (sortBy.StartsWith("random:")) - { - if (uint.TryParse(sortBy["random:".Length..], out uint sseed)) - return new Random(sseed); - throw new ValidationException("Invalid random seed specified. Expected a number."); - } - - string key = sortBy.Contains(':') ? sortBy[..sortBy.IndexOf(':')] : sortBy; - string? order = sortBy.Contains(':') ? sortBy[(sortBy.IndexOf(':') + 1)..] : null; - bool desendant = order switch - { - "desc" => true, - "asc" => false, - null => false, - _ - => throw new ValidationException( - $"The sort order, if set, should be :asc or :desc but it was :{order}." - ) - }; - - Type[] types = typeof(T).GetCustomAttribute()?.Types ?? new[] { typeof(T) }; - PropertyInfo? property = types - .Select(x => - x.GetProperty( - key, - BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance - ) - ) - .FirstOrDefault(x => x != null); - if (property == null) - throw new ValidationException("The given sort key is not valid."); - return new By(property.Name, desendant); - } -} diff --git a/back/src/Kyoo.Abstractions/Models/VideoLinks.cs b/back/src/Kyoo.Abstractions/Models/VideoLinks.cs deleted file mode 100644 index 36998e96..00000000 --- a/back/src/Kyoo.Abstractions/Models/VideoLinks.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -namespace Kyoo.Abstractions.Models; - -/// -/// The links to see a movie or an episode. -/// -public class VideoLinks -{ - /// - /// The direct link to the unprocessed video (pristine quality). - /// - public string Direct { get; set; } - - /// - /// The link to an HLS master playlist containing all qualities available for this video. - /// - public string Hls { get; set; } -} diff --git a/back/src/Kyoo.Abstractions/Utility/ExpressionParameterReplacer.cs b/back/src/Kyoo.Abstractions/Utility/ExpressionParameterReplacer.cs deleted file mode 100644 index 9e248457..00000000 --- a/back/src/Kyoo.Abstractions/Utility/ExpressionParameterReplacer.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -namespace Kyoo.Utils; - -public sealed class ExpressionArgumentReplacer : ExpressionVisitor -{ - private readonly Dictionary _mapping; - - public ExpressionArgumentReplacer(Dictionary dict) - { - _mapping = dict; - } - - protected override Expression VisitParameter(ParameterExpression node) - { - if (_mapping.TryGetValue(node, out Expression? mappedArgument)) - return Visit(mappedArgument); - return base.VisitParameter(node); - } - - public static Expression ReplaceParams( - Expression expression, - IEnumerable epxParams, - params ParameterExpression[] param - ) - { - ExpressionArgumentReplacer replacer = - new(epxParams.Zip(param).ToDictionary(x => x.First, x => x.Second as Expression)); - return replacer.Visit(expression); - } -} diff --git a/back/src/Kyoo.Abstractions/Utility/JsonKindResolver.cs b/back/src/Kyoo.Abstractions/Utility/JsonKindResolver.cs deleted file mode 100644 index db72f7e2..00000000 --- a/back/src/Kyoo.Abstractions/Utility/JsonKindResolver.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text.Json; -using System.Text.Json.Serialization.Metadata; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using static System.Text.Json.JsonNamingPolicy; - -namespace Kyoo.Utils; - -public class JsonKindResolver : DefaultJsonTypeInfoResolver -{ - public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) - { - JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options); - - if (jsonTypeInfo.Type.GetCustomAttribute() != null) - { - jsonTypeInfo.PolymorphismOptions = new() - { - TypeDiscriminatorPropertyName = "kind", - IgnoreUnrecognizedTypeDiscriminators = true, - DerivedTypes = { }, - }; - IEnumerable derived = AppDomain - .CurrentDomain.GetAssemblies() - .SelectMany(s => s.GetTypes()) - .Where(p => type.IsAssignableFrom(p) && p.IsClass); - foreach (Type der in derived) - { - jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add( - new JsonDerivedType(der, CamelCase.ConvertName(der.Name)) - ); - } - } - else if ( - jsonTypeInfo.Type.IsAssignableTo(typeof(IResource)) - && jsonTypeInfo.Properties.All(x => x.Name != "kind") - ) - { - jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions - { - TypeDiscriminatorPropertyName = "kind", - IgnoreUnrecognizedTypeDiscriminators = true, - DerivedTypes = - { - new JsonDerivedType( - jsonTypeInfo.Type, - CamelCase.ConvertName(jsonTypeInfo.Type.Name) - ), - }, - }; - } - - return jsonTypeInfo; - } -} diff --git a/back/src/Kyoo.Abstractions/Utility/Utility.cs b/back/src/Kyoo.Abstractions/Utility/Utility.cs deleted file mode 100644 index 9e085457..00000000 --- a/back/src/Kyoo.Abstractions/Utility/Utility.cs +++ /dev/null @@ -1,212 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Text; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Text.RegularExpressions; - -namespace Kyoo.Utils; - -/// -/// A set of utility functions that can be used everywhere. -/// -public static class Utility -{ - public static readonly JsonSerializerOptions JsonOptions = - new() - { - TypeInfoResolver = new JsonKindResolver(), - Converters = { new JsonStringEnumConverter() }, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - }; - - /// - /// Convert a string to snake case. Stollen from - /// https://github.com/efcore/EFCore.NamingConventions/blob/main/EFCore.NamingConventions/Internal/SnakeCaseNameRewriter.cs - /// - /// The string to convert. - /// The string in snake case - public static string ToSnakeCase(this string name) - { - StringBuilder builder = new(name.Length + Math.Min(2, name.Length / 5)); - UnicodeCategory? previousCategory = default; - - for (int currentIndex = 0; currentIndex < name.Length; currentIndex++) - { - char currentChar = name[currentIndex]; - if (currentChar == '_') - { - builder.Append('_'); - previousCategory = null; - continue; - } - - UnicodeCategory currentCategory = char.GetUnicodeCategory(currentChar); - switch (currentCategory) - { - case UnicodeCategory.UppercaseLetter: - case UnicodeCategory.TitlecaseLetter: - if ( - previousCategory == UnicodeCategory.SpaceSeparator - || previousCategory == UnicodeCategory.LowercaseLetter - || ( - previousCategory != UnicodeCategory.DecimalDigitNumber - && previousCategory != null - && currentIndex > 0 - && currentIndex + 1 < name.Length - && char.IsLower(name[currentIndex + 1]) - ) - ) - { - builder.Append('_'); - } - - currentChar = char.ToLowerInvariant(currentChar); - break; - - case UnicodeCategory.LowercaseLetter: - case UnicodeCategory.DecimalDigitNumber: - if (previousCategory == UnicodeCategory.SpaceSeparator) - { - builder.Append('_'); - } - break; - - default: - if (previousCategory != null) - { - previousCategory = UnicodeCategory.SpaceSeparator; - } - continue; - } - - builder.Append(currentChar); - previousCategory = currentCategory; - } - - return builder.ToString(); - } - - /// - /// Is the lambda expression a member (like x => x.Body). - /// - /// The expression that should be checked - /// True if the expression is a member, false otherwise - public static bool IsPropertyExpression(LambdaExpression ex) - { - return ex.Body is MemberExpression - || ( - ex.Body.NodeType == ExpressionType.Convert - && ((UnaryExpression)ex.Body).Operand is MemberExpression - ); - } - - /// - /// Get the name of a property. Useful for selectors as members ex: Load(x => x.Shows) - /// - /// The expression - /// The name of the expression - /// If the expression is not a property, ArgumentException is thrown. - public static string GetPropertyName(LambdaExpression ex) - { - if (!IsPropertyExpression(ex)) - throw new ArgumentException($"{ex} is not a property expression."); - MemberExpression? member = - ex.Body.NodeType == ExpressionType.Convert - ? ((UnaryExpression)ex.Body).Operand as MemberExpression - : ex.Body as MemberExpression; - return member!.Member.Name; - } - - /// - /// Slugify a string (Replace spaces by -, Uniformize accents) - /// - /// The string to slugify - /// The slug version of the given string - public static string ToSlug(string str) - { - str = str.ToLowerInvariant(); - - string normalizedString = str.Normalize(NormalizationForm.FormD); - StringBuilder stringBuilder = new(); - foreach (char c in normalizedString) - { - UnicodeCategory unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c); - if (unicodeCategory != UnicodeCategory.NonSpacingMark) - stringBuilder.Append(c); - } - str = stringBuilder.ToString().Normalize(NormalizationForm.FormC); - - str = Regex.Replace(str, @"\s", "-", RegexOptions.Compiled); - str = Regex.Replace(str, @"[^\w\s\p{Pd}]", string.Empty, RegexOptions.Compiled); - str = str.Trim('-', '_'); - str = Regex.Replace(str, @"([-_]){2,}", "$1", RegexOptions.Compiled); - return str; - } - - /// - /// Return every in the inheritance tree of the parameter (interfaces are not returned) - /// - /// The starting type - /// A list of types - public static IEnumerable GetInheritanceTree(this Type self) - { - for (Type? type = self; type != null; type = type.BaseType) - yield return type; - } - - /// - /// Get the generic definition of . - /// For example, calling this function with List<string> and typeof(IEnumerable<>) will return IEnumerable<string> - /// - /// The type to check - /// The generic type to check against (Only generic types are supported like typeof(IEnumerable<>). - /// The generic definition of genericType that type inherit or null if type does not implement the generic type. - /// must be a generic type - public static Type? GetGenericDefinition(Type type, Type genericType) - { - if (!genericType.IsGenericType) - throw new ArgumentException($"{nameof(genericType)} is not a generic type."); - - IEnumerable types = genericType.IsInterface - ? type.GetInterfaces() - : type.GetInheritanceTree(); - return types - .Prepend(type) - .FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType); - } - - /// - /// Convert a dictionary to a query string. - /// - /// The list of query parameters. - /// A valid query string with all items in the dictionary. - public static string ToQueryString(this Dictionary query) - { - if (!query.Any()) - return string.Empty; - return "?" + string.Join('&', query.Select(x => $"{x.Key}={x.Value}")); - } -} diff --git a/back/src/Kyoo.Abstractions/Utility/Wrapper.cs b/back/src/Kyoo.Abstractions/Utility/Wrapper.cs deleted file mode 100644 index 1db9b0d8..00000000 --- a/back/src/Kyoo.Abstractions/Utility/Wrapper.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Data; -using Dapper; - -namespace Kyoo.Utils; - -// Only used due to https://github.com/DapperLib/Dapper/issues/332 -public class Wrapper -{ - public object Value { get; set; } - - public Wrapper(object value) - { - Value = value; - } - - public class Handler : SqlMapper.TypeHandler - { - public override Wrapper? Parse(object value) - { - throw new NotImplementedException("Wrapper should only be used to write"); - } - - public override void SetValue(IDbDataParameter parameter, Wrapper? value) - { - parameter.Value = value?.Value; - } - } -} diff --git a/back/src/Kyoo.Authentication/Attributes/DisableOnEnvVarAttribute.cs b/back/src/Kyoo.Authentication/Attributes/DisableOnEnvVarAttribute.cs deleted file mode 100644 index 50e2e67d..00000000 --- a/back/src/Kyoo.Authentication/Attributes/DisableOnEnvVarAttribute.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace Kyoo.Authentication.Attributes; - -/// -/// Disables the action if the specified environment variable is set to true. -/// -public class DisableOnEnvVarAttribute(string varName) : Attribute, IResourceFilter -{ - public void OnResourceExecuting(ResourceExecutingContext context) - { - var config = context.HttpContext.RequestServices.GetRequiredService(); - - if (config.GetValue(varName, false)) - context.Result = new Microsoft.AspNetCore.Mvc.NotFoundResult(); - } - - public void OnResourceExecuted(ResourceExecutedContext context) { } -} diff --git a/back/src/Kyoo.Authentication/AuthenticationModule.cs b/back/src/Kyoo.Authentication/AuthenticationModule.cs deleted file mode 100644 index 611a9eed..00000000 --- a/back/src/Kyoo.Authentication/AuthenticationModule.cs +++ /dev/null @@ -1,165 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Authentication.Models; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Primitives; -using Microsoft.IdentityModel.Tokens; -using Serilog; - -namespace Kyoo.Authentication; - -public static class AuthenticationModule -{ - public static void ConfigureAuthentication(this WebApplicationBuilder builder) - { - PermissionOption options = - new() - { - Default = builder - .Configuration.GetValue("UNLOGGED_PERMISSIONS", "")! - .Split(',') - .Where(x => x.Length > 0) - .ToArray(), - NewUser = builder - .Configuration.GetValue("DEFAULT_PERMISSIONS", "overall.read,overall.play")! - .Split(','), - RequireVerification = builder.Configuration.GetValue( - "REQUIRE_ACCOUNT_VERIFICATION", - true - ), - PublicUrl = - builder.Configuration.GetValue("PUBLIC_URL") - ?? "http://localhost:8901", - ApiKeys = builder.Configuration.GetValue("KYOO_APIKEYS", string.Empty)!.Split(','), - OIDC = builder - .Configuration.AsEnumerable() - .Where((pair) => pair.Key.StartsWith("OIDC_")) - .Aggregate( - new Dictionary(), - (acc, val) => - { - if (val.Value is null) - return acc; - if (val.Key.Split("_") is not ["OIDC", string provider, string key]) - { - Log.Error("Invalid oidc config value: {Key}", val.Key); - return acc; - } - provider = provider.ToLowerInvariant(); - key = key.ToLowerInvariant(); - - if (!acc.ContainsKey(provider)) - acc.Add(provider, new(provider)); - switch (key) - { - case "clientid": - acc[provider].ClientId = val.Value; - break; - case "secret": - acc[provider].Secret = val.Value; - break; - case "scope": - acc[provider].Scope = val.Value; - break; - case "authorization": - acc[provider].AuthorizationUrl = val.Value; - break; - case "token": - acc[provider].TokenUrl = val.Value; - break; - case "userinfo": - case "profile": - acc[provider].ProfileUrl = val.Value; - break; - case "name": - acc[provider].DisplayName = val.Value; - break; - case "logo": - acc[provider].LogoUrl = val.Value; - break; - case "clientauthmethod": - case "authmethod": - case "auth": - case "method": - if (!Enum.TryParse(val.Value, out AuthMethod method)) - { - Log.Error( - "Invalid AuthMethod value: {AuthMethod}. Ignoring.", - val.Value - ); - break; - } - acc[provider].ClientAuthMethod = method; - break; - default: - Log.Error("Invalid oidc config value: {Key}", key); - return acc; - } - return acc; - } - ), - }; - builder.Services.AddSingleton(options); - - byte[] secret = builder.Configuration.GetValue("AUTHENTICATION_SECRET")!; - builder.Services.AddSingleton(new AuthenticationOption() { Secret = secret }); - - builder - .Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddJwtBearer(options => - { - options.Events = new() - { - OnMessageReceived = (ctx) => - { - string prefix = "Bearer "; - if ( - ctx.Request.Headers.TryGetValue("Authorization", out StringValues val) - && val.ToString() is string auth - && auth.StartsWith(prefix) - ) - { - ctx.Token ??= auth[prefix.Length..]; - } - ctx.Token ??= ctx.Request.Cookies["X-Bearer"]; - return Task.CompletedTask; - } - }; - options.TokenValidationParameters = new TokenValidationParameters - { - ValidateIssuer = false, - ValidateAudience = false, - ValidateLifetime = true, - ValidateIssuerSigningKey = true, - IssuerSigningKey = new SymmetricSecurityKey(secret) - }; - }); - - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - } -} diff --git a/back/src/Kyoo.Authentication/Controllers/ITokenController.cs b/back/src/Kyoo.Authentication/Controllers/ITokenController.cs deleted file mode 100644 index b0599e0e..00000000 --- a/back/src/Kyoo.Authentication/Controllers/ITokenController.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Threading.Tasks; -using Kyoo.Abstractions.Models; -using Microsoft.IdentityModel.Tokens; - -namespace Kyoo.Authentication; - -/// -/// The service that controls jwt creation and validation. -/// -public interface ITokenController -{ - /// - /// Create a new access token for the given user. - /// - /// The user to create a token for. - /// When this token will expire. - /// A new, valid access token. - string CreateAccessToken(User user, out TimeSpan expireIn); - - /// - /// Create a new refresh token for the given user. - /// - /// The user to create a token for. - /// A new, valid refresh token. - Task CreateRefreshToken(User user); - - /// - /// Check if the given refresh token is valid and if it is, retrieve the id of the user this token belongs to. - /// - /// The refresh token to validate. - /// The given refresh token is not valid. - /// The id of the token's user. - Guid GetRefreshTokenUserID(string refreshToken); -} diff --git a/back/src/Kyoo.Authentication/Controllers/OidcController.cs b/back/src/Kyoo.Authentication/Controllers/OidcController.cs deleted file mode 100644 index b4fbbee9..00000000 --- a/back/src/Kyoo.Authentication/Controllers/OidcController.cs +++ /dev/null @@ -1,143 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Net.Http; -using System.Net.Http.Json; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Authentication.Models; -using Kyoo.Authentication.Models.DTO; - -namespace Kyoo.Authentication; - -public class OidcController( - IUserRepository users, - IHttpClientFactory clientFactory, - PermissionOption options -) -{ - private async Task<(User, ExternalToken)> _TranslateCode(string provider, string code) - { - OidcProvider prov = options.OIDC[provider]; - - HttpClient client = clientFactory.CreateClient(); - - Dictionary data = - new() - { - ["code"] = code, - ["redirect_uri"] = $"{options.PublicUrl.TrimEnd('/')}/api/auth/logged/{provider}", - ["grant_type"] = "authorization_code", - }; - - if (prov.ClientAuthMethod == AuthMethod.ClientSecretBasic) - { - string auth = Convert.ToBase64String( - Encoding.UTF8.GetBytes($"{prov.ClientId}:{prov.Secret}") - ); - client.DefaultRequestHeaders.Add("Authorization", $"Basic {auth}"); - } - else if (prov.ClientAuthMethod == AuthMethod.ClientSecretPost) - { - data["client_id"] = prov.ClientId; - data["client_secret"] = prov.Secret; - } - - HttpResponseMessage resp = prov.TokenUseJsonBody - ? await client.PostAsJsonAsync(prov.TokenUrl, data) - : await client.PostAsync(prov.TokenUrl, new FormUrlEncodedContent(data)); - if (!resp.IsSuccessStatusCode) - throw new ValidationException( - $"Invalid code or configuration. {resp.StatusCode}: {await resp.Content.ReadAsStringAsync()}" - ); - JwtToken? token = await resp.Content.ReadFromJsonAsync(); - if (token is null) - throw new ValidationException("Could not retrive token."); - - client.DefaultRequestHeaders.Remove("Authorization"); - client.DefaultRequestHeaders.Add("Authorization", $"{token.TokenType} {token.AccessToken}"); - Dictionary? extraHeaders = prov.GetExtraHeaders?.Invoke(prov); - if (extraHeaders is not null) - { - foreach ((string key, string value) in extraHeaders) - client.DefaultRequestHeaders.Add(key, value); - } - - JwtProfile? profile = await client.GetFromJsonAsync(prov.ProfileUrl); - if (profile is null || profile.Sub is null) - throw new ValidationException( - $"Missing sub on user object. Got: {JsonSerializer.Serialize(profile)}" - ); - ExternalToken extToken = - new() - { - Id = profile.Sub, - Token = token, - ProfileUrl = prov.GetProfileUrl?.Invoke(profile), - }; - User newUser = new(); - if (profile.Email is not null) - newUser.Email = profile.Email; - if (profile.Username is null) - { - throw new ValidationException( - $"Could not find a username for the user. You may need to add more scopes. Fields: {string.Join(',', profile.Extra)}" - ); - } - extToken.Username = profile.Username; - newUser.Username = profile.Username; - newUser.Slug = Utils.Utility.ToSlug(newUser.Username); - newUser.ExternalId.Add(provider, extToken); - return (newUser, extToken); - } - - public async Task LoginViaCode(string provider, string code) - { - (User newUser, ExternalToken extToken) = await _TranslateCode(provider, code); - User? user = await users.GetByExternalId(provider, extToken.Id); - if (user == null) - { - try - { - user = await users.Create(newUser); - } - catch - { - throw new ValidationException( - "A user already exists with the same username. If this is you, login via username and then link your account." - ); - } - } - return user; - } - - public async Task LinkAccountOrLogin(Guid userId, string provider, string code) - { - (_, ExternalToken extToken) = await _TranslateCode(provider, code); - User? user = await users.GetByExternalId(provider, extToken.Id); - if (user != null) - return user; - return await users.AddExternalToken(userId, provider, extToken); - } -} diff --git a/back/src/Kyoo.Authentication/Controllers/PermissionValidator.cs b/back/src/Kyoo.Authentication/Controllers/PermissionValidator.cs deleted file mode 100644 index 5b723163..00000000 --- a/back/src/Kyoo.Authentication/Controllers/PermissionValidator.cs +++ /dev/null @@ -1,284 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Authentication.Models; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Primitives; - -namespace Kyoo.Authentication; - -/// -/// A permission validator to validate permission with user Permission array -/// or the default array from the configurations if the user is not logged. -/// -public class PermissionValidator : IPermissionValidator -{ - /// - /// The permissions options to retrieve default permissions. - /// - private readonly PermissionOption _options; - - /// - /// Create a new factory with the given options. - /// - /// The option containing default values. - public PermissionValidator(PermissionOption options) - { - _options = options; - } - - /// - public IFilterMetadata Create(PermissionAttribute attribute) - { - return new PermissionValidatorFilter( - attribute.Type, - attribute.Kind, - attribute.Group, - _options - ); - } - - /// - public IFilterMetadata Create(PartialPermissionAttribute attribute) - { - return new PermissionValidatorFilter( - ((object?)attribute.Type ?? attribute.Kind)!, - attribute.Group, - _options - ); - } - - /// - /// The authorization filter used by . - /// - private class PermissionValidatorFilter : IAsyncAuthorizationFilter - { - /// - /// The permission to validate. - /// - private readonly string? _permission; - - /// - /// The kind of permission needed. - /// - private readonly Kind? _kind; - - /// - /// The group of he permission. - /// - private Group _group; - - /// - /// The permissions options to retrieve default permissions. - /// - private readonly PermissionOption _options; - - /// - /// Create a new permission validator with the given options. - /// - /// The permission to validate. - /// The kind of permission needed. - /// The group of the permission. - /// The option containing default values. - public PermissionValidatorFilter( - string permission, - Kind kind, - Group group, - PermissionOption options - ) - { - _permission = permission; - _kind = kind; - _group = group; - _options = options; - } - - /// - /// Create a new permission validator with the given options. - /// - /// The partial permission to validate. - /// The group of the permission. - /// The option containing default values. - public PermissionValidatorFilter(object partialInfo, Group? group, PermissionOption options) - { - switch (partialInfo) - { - case Kind kind: - _kind = kind; - break; - case string perm: - _permission = perm; - break; - default: - throw new ArgumentException( - $"{nameof(partialInfo)} can only be a permission string or a kind." - ); - } - - if (group is not null and not Group.None) - _group = group.Value; - _options = options; - } - - /// - public async Task OnAuthorizationAsync(AuthorizationFilterContext context) - { - string? permission = _permission; - Kind? kind = _kind; - - if (permission == null || kind == null) - { - if (context.HttpContext.Items["PermissionGroup"] is Group group and not Group.None) - _group = group; - else if (_group == Group.None) - _group = Group.Overall; - else - context.HttpContext.Items["PermissionGroup"] = _group; - - switch (context.HttpContext.Items["PermissionType"]) - { - case string perm: - permission = perm; - break; - case Kind kin: - kind = kin; - break; - case null when kind != null: - context.HttpContext.Items["PermissionType"] = kind; - return; - case null when permission != null: - context.HttpContext.Items["PermissionType"] = permission; - return; - default: - throw new ArgumentException( - "Multiple non-matching partial permission attribute " - + "are not supported." - ); - } - if (permission == null || kind == null) - { - throw new ArgumentException( - "The permission type or kind is still missing after two partial " - + "permission attributes, this is unsupported." - ); - } - } - - string permStr = $"{permission.ToLower()}.{kind.ToString()!.ToLower()}"; - string overallStr = $"{_group.ToString().ToLower()}.{kind.ToString()!.ToLower()}"; - AuthenticateResult res = _ApiKeyCheck(context); - if (res.None) - res = await _JwtCheck(context); - - if (res.Succeeded) - { - ICollection permissions = res.Principal.GetPermissions(); - if (permissions.All(x => x != permStr && x != overallStr)) - context.Result = _ErrorResult( - $"Missing permission {permStr} or {overallStr}", - StatusCodes.Status403Forbidden - ); - } - else if (res.None) - { - ICollection permissions = _options.Default ?? Array.Empty(); - if (permissions.All(x => x != permStr && x != overallStr)) - { - context.Result = _ErrorResult( - $"Unlogged user does not have permission {permStr} or {overallStr}", - StatusCodes.Status401Unauthorized - ); - } - } - else if (res.Failure != null) - context.Result = _ErrorResult(res.Failure.Message, StatusCodes.Status403Forbidden); - else - context.Result = _ErrorResult( - "Authentication panic", - StatusCodes.Status500InternalServerError - ); - } - - private AuthenticateResult _ApiKeyCheck(ActionContext context) - { - if ( - !context.HttpContext.Request.Headers.TryGetValue( - "X-API-Key", - out StringValues apiKey - ) - ) - return AuthenticateResult.NoResult(); - if (!_options.ApiKeys.Contains(apiKey!)) - return AuthenticateResult.Fail("Invalid API-Key."); - return AuthenticateResult.Success( - new AuthenticationTicket( - new ClaimsPrincipal( - new[] - { - new ClaimsIdentity( - new[] - { - // TODO: Make permission configurable, for now every APIKEY as all permissions. - new Claim( - Claims.Permissions, - string.Join(',', PermissionOption.Admin) - ) - } - ) - } - ), - "apikey" - ) - ); - } - - private async Task _JwtCheck(ActionContext context) - { - AuthenticateResult ret = await context.HttpContext.AuthenticateAsync( - JwtBearerDefaults.AuthenticationScheme - ); - // Change the failure message to make the API nice to use. - if (ret.Failure != null) - return AuthenticateResult.Fail("Invalid JWT token. The token may have expired."); - return ret; - } - } - - /// - /// Create a new action result with the given error message and error code. - /// - /// The error message. - /// The status code of the error. - /// The resulting error action. - private static IActionResult _ErrorResult(string error, int code) - { - return new ObjectResult(new RequestError(error)) { StatusCode = code }; - } -} diff --git a/back/src/Kyoo.Authentication/Controllers/TokenController.cs b/back/src/Kyoo.Authentication/Controllers/TokenController.cs deleted file mode 100644 index 818d65b3..00000000 --- a/back/src/Kyoo.Authentication/Controllers/TokenController.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using Kyoo.Abstractions.Models; -using Kyoo.Authentication.Models; -using Microsoft.IdentityModel.Tokens; - -namespace Kyoo.Authentication; - -public class TokenController(AuthenticationOption options) : ITokenController -{ - /// - public string CreateAccessToken(User user, out TimeSpan expireIn) - { - expireIn = new TimeSpan(1, 0, 0); - - SymmetricSecurityKey key = new(options.Secret); - SigningCredentials credential = new(key, SecurityAlgorithms.HmacSha256Signature); - string permissions = - user.Permissions != null ? string.Join(',', user.Permissions) : string.Empty; - List claims = - new() - { - new Claim(Claims.Id, user.Id.ToString()), - new Claim(Claims.Name, user.Username), - new Claim(Claims.Permissions, permissions), - new Claim(Claims.Type, "access") - }; - if (user.Email != null) - claims.Add(new Claim(Claims.Email, user.Email)); - JwtSecurityToken token = - new( - signingCredentials: credential, - claims: claims, - expires: DateTime.UtcNow.Add(expireIn) - ); - return new JwtSecurityTokenHandler().WriteToken(token); - } - - /// - public Task CreateRefreshToken(User user) - { - SymmetricSecurityKey key = new(options.Secret); - SigningCredentials credential = new(key, SecurityAlgorithms.HmacSha256Signature); - JwtSecurityToken token = - new( - signingCredentials: credential, - claims: new[] - { - new Claim(Claims.Id, user.Id.ToString()), - new Claim(Claims.Guid, Guid.NewGuid().ToString()), - new Claim(Claims.Type, "refresh") - }, - expires: DateTime.UtcNow.AddYears(1) - ); - // TODO: refresh keys are unique (thanks to the guid) but we could store them in DB to invalidate them if requested by the user. - return Task.FromResult(new JwtSecurityTokenHandler().WriteToken(token)); - } - - /// - public Guid GetRefreshTokenUserID(string refreshToken) - { - SymmetricSecurityKey key = new(options.Secret); - JwtSecurityTokenHandler tokenHandler = new(); - ClaimsPrincipal principal; - try - { - principal = tokenHandler.ValidateToken( - refreshToken, - new TokenValidationParameters - { - ValidateIssuer = false, - ValidateAudience = false, - ValidateIssuerSigningKey = true, - ValidateLifetime = true, - IssuerSigningKey = key - }, - out SecurityToken _ - ); - } - catch (Exception) - { - throw new SecurityTokenException("Invalid refresh token"); - } - - if (principal.Claims.First(x => x.Type == Claims.Type).Value != "refresh") - throw new SecurityTokenException( - "Invalid token type. The token should be a refresh token." - ); - Claim identifier = principal.Claims.First(x => x.Type == Claims.Id); - if (Guid.TryParse(identifier.Value, out Guid id)) - return id; - throw new SecurityTokenException("Token not associated to any user."); - } -} diff --git a/back/src/Kyoo.Authentication/Kyoo.Authentication.csproj b/back/src/Kyoo.Authentication/Kyoo.Authentication.csproj deleted file mode 100644 index 9c89f561..00000000 --- a/back/src/Kyoo.Authentication/Kyoo.Authentication.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/back/src/Kyoo.Authentication/Models/DTO/JwtProfile.cs b/back/src/Kyoo.Authentication/Models/DTO/JwtProfile.cs deleted file mode 100644 index c1de4fbb..00000000 --- a/back/src/Kyoo.Authentication/Models/DTO/JwtProfile.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -namespace Kyoo.Authentication.Models.DTO; - -public class JwtProfile -{ - public string? Sub { get; set; } - public string? Uid - { - set => Sub ??= value; - } - public string? Id - { - set => Sub ??= value; - } - public string? Guid - { - set => Sub ??= value; - } - - public string? Username { get; set; } - public string? Name - { - set => Username ??= value; - } - - public string? Email { get; set; } - - public JsonObject? Account - { - set - { - if (value is null) - return; - // simkl store their ids there. - Sub ??= value["id"]?.ToString(); - } - } - - public JsonObject? User - { - set - { - if (value is null) - return; - // trakt store their name there (they also store name but that's not the same). - Username ??= value["username"]?.ToString(); - // simkl store their name there. - Username ??= value["name"]?.ToString(); - - Sub ??= value["ids"]?["uuid"]?.ToString(); - } - } - - [JsonExtensionData] - public Dictionary Extra { get; set; } -} diff --git a/back/src/Kyoo.Authentication/Models/DTO/LoginRequest.cs b/back/src/Kyoo.Authentication/Models/DTO/LoginRequest.cs deleted file mode 100644 index c93730f6..00000000 --- a/back/src/Kyoo.Authentication/Models/DTO/LoginRequest.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -namespace Kyoo.Authentication.Models.DTO; - -/// -/// A model only used on login requests. -/// -public class LoginRequest -{ - /// - /// The user's username. - /// - public string Username { get; set; } - - /// - /// The user's password. - /// - public string Password { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The user's username. - /// The user's password. - public LoginRequest(string username, string password) - { - Username = username; - Password = password; - } -} diff --git a/back/src/Kyoo.Authentication/Models/DTO/PasswordResetRequest.cs b/back/src/Kyoo.Authentication/Models/DTO/PasswordResetRequest.cs deleted file mode 100644 index 5548dd7b..00000000 --- a/back/src/Kyoo.Authentication/Models/DTO/PasswordResetRequest.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.ComponentModel.DataAnnotations; - -namespace Kyoo.Authentication.Models.DTO; - -/// -/// A model only used on password resets. -/// -public class PasswordResetRequest -{ - /// - /// The old password - /// - public string? OldPassword { get; set; } - - /// - /// The new password - /// - [MinLength(4, ErrorMessage = "The password must have at least {1} characters")] - public string NewPassword { get; set; } -} diff --git a/back/src/Kyoo.Authentication/Models/DTO/RegisterRequest.cs b/back/src/Kyoo.Authentication/Models/DTO/RegisterRequest.cs deleted file mode 100644 index 0ee10211..00000000 --- a/back/src/Kyoo.Authentication/Models/DTO/RegisterRequest.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.ComponentModel.DataAnnotations; -using Kyoo.Abstractions.Models; -using Kyoo.Utils; -using BCryptNet = BCrypt.Net.BCrypt; - -namespace Kyoo.Authentication.Models.DTO; - -/// -/// A model only used on register requests. -/// -public class RegisterRequest -{ - /// - /// The user email address - /// - [EmailAddress(ErrorMessage = "The email must be a valid email address")] - public string Email { get; set; } - - /// - /// The user's username. - /// - [MinLength(4, ErrorMessage = "The username must have at least {1} characters")] - public string Username { get; set; } - - /// - /// The user's password. - /// - [MinLength(4, ErrorMessage = "The password must have at least {1} characters")] - public string Password { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The user email address. - /// The user's username. - /// The user's password. - public RegisterRequest(string email, string username, string password) - { - Email = email; - Username = username; - Password = password; - } - - /// - /// Convert this register request to a new class. - /// - /// A user representing this request. - public User ToUser() - { - return new User - { - Slug = Utility.ToSlug(Username), - Username = Username, - Password = BCryptNet.HashPassword(Password), - Email = Email, - }; - } -} diff --git a/back/src/Kyoo.Authentication/Models/DTO/ServerInfo.cs b/back/src/Kyoo.Authentication/Models/DTO/ServerInfo.cs deleted file mode 100644 index c7421a17..00000000 --- a/back/src/Kyoo.Authentication/Models/DTO/ServerInfo.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; - -namespace Kyoo.Authentication.Models; - -public class ServerInfo -{ - /// - /// The list of oidc providers configured for this instance of kyoo. - /// - public Dictionary Oidc { get; set; } - - /// - /// The url to reach the homepage of kyoo (add /api for the api). - /// - public string PublicUrl { get; set; } - - /// - /// True if guest accounts are allowed on this instance. - /// - public bool AllowGuests { get; set; } - - /// - /// True if new users needs to be verifed. - /// - public bool RequireVerification { get; set; } - - /// - /// The list of permissions available for the guest account. - /// - public List GuestPermissions { get; set; } - - /// - /// Check if kyoo's setup is finished. - /// - public SetupStep SetupStatus { get; set; } - - /// - /// True if password login is enabled on this instance. - /// - public bool PasswordLoginEnabled { get; set; } - - /// - /// True if registration is enabled on this instance. - /// - public bool RegistrationEnabled { get; set; } -} - -public class OidcInfo -{ - /// - /// The name of this oidc service. Human readable. - /// - public string DisplayName { get; set; } - - /// - /// A url returing a square logo for this provider. - /// - public string? LogoUrl { get; set; } -} - -/// -/// Check if kyoo's setup is finished. -/// -public enum SetupStep -{ - /// - /// No admin account exists, create an account before exposing kyoo to the internet! - /// - MissingAdminAccount, - - /// - /// No video was registered on kyoo, have you configured the rigth library path? - /// - NoVideoFound, - - /// - /// Setup finished! - /// - Done, -} diff --git a/back/src/Kyoo.Authentication/Models/Options/AuthenticationOption.cs b/back/src/Kyoo.Authentication/Models/Options/AuthenticationOption.cs deleted file mode 100644 index 74822e0d..00000000 --- a/back/src/Kyoo.Authentication/Models/Options/AuthenticationOption.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -namespace Kyoo.Authentication.Models; - -public class AuthenticationOption -{ - public byte[] Secret { get; set; } -} diff --git a/back/src/Kyoo.Authentication/Models/Options/PermissionOption.cs b/back/src/Kyoo.Authentication/Models/Options/PermissionOption.cs deleted file mode 100644 index 458003e3..00000000 --- a/back/src/Kyoo.Authentication/Models/Options/PermissionOption.cs +++ /dev/null @@ -1,180 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Authentication.Models.DTO; - -namespace Kyoo.Authentication.Models; - -/// -/// Permission options. -/// -public class PermissionOption -{ - /// - /// The path to get this option from the root configuration. - /// - public const string Path = "authentication:permissions"; - - /// - /// True if new users needs to be verifed. - /// - public bool RequireVerification { get; set; } - - /// - /// The default permissions that will be given to a non-connected user. - /// - public string[] Default { get; set; } = { "overall.read", "overall.play" }; - - /// - /// Permissions applied to a new user. - /// - public string[] NewUser { get; set; } = { "overall.read", "overall.play" }; - - public static string[] Admin => - Enum.GetNames() - .Where(x => x != nameof(Group.None)) - .SelectMany(group => - Enum.GetNames().Select(kind => $"{group}.{kind}".ToLowerInvariant()) - ) - .ToArray(); - - /// - /// The list of available ApiKeys. - /// - public string[] ApiKeys { get; set; } = Array.Empty(); - - public string PublicUrl { get; set; } - - public Dictionary OIDC { get; set; } -} - -public enum AuthMethod -{ - ClientSecretBasic, - ClientSecretPost, - None, -} - -public class OidcProvider -{ - public string DisplayName { get; set; } - public string? LogoUrl { get; set; } - public string AuthorizationUrl { get; set; } - public string TokenUrl { get; set; } - - /// - /// Some token endpoints do net respect the spec and require a json body instead of a form url encoded. - /// - public bool TokenUseJsonBody { get; set; } - - /// - /// The OIDC spec allows multiples ways of authorizing the client. - /// - public AuthMethod ClientAuthMethod { get; set; } = AuthMethod.ClientSecretBasic; - - public string ProfileUrl { get; set; } - public string? Scope { get; set; } - public string ClientId { get; set; } - public string Secret { get; set; } - - public Func? GetProfileUrl { get; init; } - public Func>? GetExtraHeaders { get; init; } - - public bool Enabled => - AuthorizationUrl != null - && TokenUrl != null - && ProfileUrl != null - && ClientId != null - && Secret != null; - - public OidcProvider(string provider) - { - DisplayName = provider; - if (KnownProviders?.ContainsKey(provider) == true) - { - DisplayName = KnownProviders[provider].DisplayName; - LogoUrl = KnownProviders[provider].LogoUrl; - AuthorizationUrl = KnownProviders[provider].AuthorizationUrl; - TokenUrl = KnownProviders[provider].TokenUrl; - ProfileUrl = KnownProviders[provider].ProfileUrl; - Scope = KnownProviders[provider].Scope; - ClientId = KnownProviders[provider].ClientId; - Secret = KnownProviders[provider].Secret; - TokenUseJsonBody = KnownProviders[provider].TokenUseJsonBody; - ClientAuthMethod = KnownProviders[provider].ClientAuthMethod; - GetProfileUrl = KnownProviders[provider].GetProfileUrl; - GetExtraHeaders = KnownProviders[provider].GetExtraHeaders; - } - } - - public static readonly Dictionary KnownProviders = - new() - { - ["google"] = new("google") - { - DisplayName = "Google", - LogoUrl = "https://logo.clearbit.com/google.com", - AuthorizationUrl = "https://accounts.google.com/o/oauth2/v2/auth", - TokenUrl = "https://oauth2.googleapis.com/token", - ProfileUrl = "https://openidconnect.googleapis.com/v1/userinfo", - Scope = "email profile", - }, - ["discord"] = new("discord") - { - DisplayName = "Discord", - LogoUrl = "https://logo.clearbit.com/discord.com", - AuthorizationUrl = "https://discord.com/oauth2/authorize", - TokenUrl = "https://discord.com/api/oauth2/token", - ProfileUrl = "https://discord.com/api/users/@me", - Scope = "email+identify", - }, - ["simkl"] = new("simkl") - { - DisplayName = "Simkl", - LogoUrl = "https://logo.clearbit.com/simkl.com", - AuthorizationUrl = "https://simkl.com/oauth/authorize", - TokenUrl = "https://api.simkl.com/oauth/token", - ProfileUrl = "https://api.simkl.com/users/settings", - // does not seems to have scopes - Scope = null, - TokenUseJsonBody = true, - ClientAuthMethod = AuthMethod.ClientSecretPost, - GetProfileUrl = (profile) => $"https://simkl.com/{profile.Sub}/dashboard/", - GetExtraHeaders = (OidcProvider self) => - new() { ["simkl-api-key"] = self.ClientId }, - }, - ["trakt"] = new("trakt") - { - DisplayName = "Trakt", - LogoUrl = "https://logo.clearbit.com/trakt.tv", - AuthorizationUrl = "https://api.trakt.tv/oauth/authorize", - TokenUrl = "https://api.trakt.tv/oauth/token", - ProfileUrl = "https://api.trakt.tv/users/settings", - // does not seems to have scopes - Scope = null, - TokenUseJsonBody = true, - GetProfileUrl = (profile) => $"https://trakt.tv/users/{profile.Username}", - GetExtraHeaders = (OidcProvider self) => - new() { ["trakt-api-key"] = self.ClientId, ["trakt-api-version"] = "2", }, - }, - }; -} diff --git a/back/src/Kyoo.Authentication/Views/AuthApi.cs b/back/src/Kyoo.Authentication/Views/AuthApi.cs deleted file mode 100644 index 6fa1f4e9..00000000 --- a/back/src/Kyoo.Authentication/Views/AuthApi.cs +++ /dev/null @@ -1,501 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Exceptions; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Authentication.Attributes; -using Kyoo.Authentication.Models; -using Kyoo.Authentication.Models.DTO; -using Kyoo.Models; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.IdentityModel.Tokens; -using static Kyoo.Abstractions.Models.Utils.Constants; -using BCryptNet = BCrypt.Net.BCrypt; - -namespace Kyoo.Authentication.Views; - -/// -/// Sign in, Sign up or refresh tokens. -/// -[ApiController] -[Route("auth")] -[ApiDefinition("Authentication", Group = UsersGroup)] -public class AuthApi( - IUserRepository users, - OidcController oidc, - ITokenController tokenController, - IThumbnailsManager thumbs, - PermissionOption options -) : ControllerBase -{ - /// - /// Create a new Forbidden result from an object. - /// - /// The json value to output on the response. - /// A new forbidden result with the given json object. - public static ObjectResult Forbid(object value) - { - return new ObjectResult(value) { StatusCode = StatusCodes.Status403Forbidden }; - } - - private static string _BuildUrl(string baseUrl, Dictionary queryParams) - { - char querySep = baseUrl.Contains('?') ? '&' : '?'; - foreach ((string key, string? val) in queryParams) - { - if (val is null) - continue; - baseUrl += $"{querySep}{key}={val}"; - querySep = '&'; - } - return baseUrl; - } - - /// - /// Oauth Login. - /// - /// - /// Login via a registered oauth provider. - /// - /// The provider code. - /// - /// A url where you will be redirected with the query params provider, code and error. It can be a deep link. - /// - /// A redirect to the provider's login page. - /// The provider is not register with this instance of kyoo. - [HttpGet("login/{provider}")] - [ProducesResponseType(StatusCodes.Status302Found)] - [ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(RequestError))] - public ActionResult LoginVia(string provider, [FromQuery] string redirectUrl) - { - if (!options.OIDC.ContainsKey(provider) || !options.OIDC[provider].Enabled) - { - return NotFound( - new RequestError( - $"Invalid provider. {provider} is not registered no this instance of kyoo." - ) - ); - } - OidcProvider prov = options.OIDC[provider]; - return Redirect( - _BuildUrl( - prov.AuthorizationUrl, - new() - { - ["response_type"] = "code", - ["client_id"] = prov.ClientId, - ["redirect_uri"] = - $"{options.PublicUrl.TrimEnd('/')}/api/auth/logged/{provider}", - ["scope"] = prov.Scope, - ["state"] = redirectUrl, - } - ) - ); - } - - /// - /// Oauth Code Redirect. - /// - /// - /// This route is not meant to be called manually, the user should be redirected automatically here - /// after a successful login on the /login/{provider} page. - /// - /// A redirect to the provider's login page. - /// The provider gave an error. - [HttpGet("logged/{provider}")] - [ProducesResponseType(StatusCodes.Status302Found)] - public ActionResult OauthCodeRedirect(string provider, string code, string state, string? error) - { - return Redirect( - _BuildUrl( - state, - new() - { - ["provider"] = provider, - ["code"] = code, - ["error"] = error, - } - ) - ); - } - - /// - /// Oauth callback - /// - /// - /// This route should be manually called by the page that got redirected to after a call to /login/{provider}. - /// - /// A jwt token - /// Bad provider or code - [HttpPost("callback/{provider}")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - public async Task> OauthCallback(string provider, string code) - { - if (!options.OIDC.ContainsKey(provider) || !options.OIDC[provider].Enabled) - { - return NotFound( - new RequestError( - $"Invalid provider. {provider} is not registered no this instance of kyoo." - ) - ); - } - if (code == null) - return BadRequest(new RequestError("Invalid code.")); - - Guid? userId = User.GetId(); - User user = userId.HasValue - ? await oidc.LinkAccountOrLogin(userId.Value, provider, code) - : await oidc.LoginViaCode(provider, code); - return new JwtToken( - tokenController.CreateAccessToken(user, out TimeSpan expireIn), - await tokenController.CreateRefreshToken(user), - expireIn - ); - } - - /// - /// Unlink account - /// - /// - /// Unlink your account from an external account. - /// - /// The provider code. - /// Your updated user account - [HttpDelete("login/{provider}")] - [ProducesResponseType(StatusCodes.Status200OK)] - [UserOnly] - public Task UnlinkAccount(string provider) - { - Guid id = User.GetIdOrThrow(); - return users.DeleteExternalToken(id, provider); - } - - /// - /// Login. - /// - /// - /// Login as a user and retrieve an access and a refresh token. - /// - /// The body of the request. - /// A new access and a refresh token. - /// The user and password does not match. - [HttpPost("login")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] - [DisableOnEnvVar("AUTHENTICATION_DISABLE_PASSWORD_LOGIN")] - public async Task> Login([FromBody] LoginRequest request) - { - User? user = await users.GetOrDefault( - new Filter.Eq(nameof(Abstractions.Models.User.Username), request.Username) - ); - if (user != null && user.Password == null) - return Forbid( - new RequestError( - "This account was registerd via oidc. Please login via oidc or add a password to your account in the settings first" - ) - ); - if (user == null || !BCryptNet.Verify(request.Password, user.Password)) - return Forbid(new RequestError("The user and password does not match.")); - - return new JwtToken( - tokenController.CreateAccessToken(user, out TimeSpan expireIn), - await tokenController.CreateRefreshToken(user), - expireIn - ); - } - - /// - /// Register. - /// - /// - /// Register a new user and get a new access/refresh token for this new user. - /// - /// The body of the request. - /// A new access and a refresh token. - /// The request is invalid. - /// A user already exists with this username or email address. - [HttpPost("register")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status409Conflict, Type = typeof(RequestError))] - [DisableOnEnvVar("AUTHENTICATION_DISABLE_USER_REGISTRATION")] - public async Task> Register([FromBody] RegisterRequest request) - { - try - { - User user = await users.Create(request.ToUser()); - return new JwtToken( - tokenController.CreateAccessToken(user, out TimeSpan expireIn), - await tokenController.CreateRefreshToken(user), - expireIn - ); - } - catch (DuplicatedItemException) - { - return Conflict(new RequestError("A user already exists with this username.")); - } - } - - /// - /// Refresh a token. - /// - /// - /// Refresh an access token using the given refresh token. A new access and refresh token are generated. - /// - /// A valid refresh token. - /// A new access and refresh token. - /// The given refresh token is invalid. - [HttpGet("refresh")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] - public async Task> Refresh([FromQuery] string token) - { - try - { - Guid userId = tokenController.GetRefreshTokenUserID(token); - User user = await users.Get(userId); - return new JwtToken( - tokenController.CreateAccessToken(user, out TimeSpan expireIn), - await tokenController.CreateRefreshToken(user), - expireIn - ); - } - catch (ItemNotFoundException) - { - return Forbid(new RequestError("Invalid refresh token.")); - } - catch (SecurityTokenException ex) - { - return Forbid(new RequestError(ex.Message)); - } - } - - /// - /// Reset your password - /// - /// - /// Change your password. - /// - /// The old and new password - /// Your account info. - /// The old password is invalid. - [HttpPost("password-reset")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] - public async Task> ResetPassword([FromBody] PasswordResetRequest request) - { - User user = await users.Get(User.GetIdOrThrow()); - if (user.HasPassword && !BCryptNet.Verify(request.OldPassword, user.Password)) - return Forbid(new RequestError("The old password is invalid.")); - return await users.Patch( - user.Id, - (user) => - { - user.Password = BCryptNet.HashPassword(request.NewPassword); - return user; - } - ); - } - - /// - /// Get authenticated user. - /// - /// - /// Get information about the currently authenticated user. This can also be used to ensure that you are - /// logged in. - /// - /// The currently authenticated user. - /// The user is not authenticated. - /// The given access token is invalid. - [HttpGet("me")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] - public async Task> GetMe() - { - try - { - return await users.Get(User.GetIdOrThrow()); - } - catch (ItemNotFoundException) - { - return Forbid(new RequestError("Invalid token")); - } - } - - /// - /// Edit self - /// - /// - /// Edit information about the currently authenticated user. - /// - /// The new data for the current user. - /// The currently authenticated user after modifications. - /// The user is not authenticated. - /// The given access token is invalid. - [HttpPut("me")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] - public async Task> EditMe(User user) - { - try - { - user.Id = User.GetIdOrThrow(); - return await users.Edit(user); - } - catch (ItemNotFoundException) - { - return Forbid(new RequestError("Invalid token")); - } - } - - /// - /// Patch self - /// - /// - /// Edit only provided informations about the currently authenticated user. - /// - /// The new data for the current user. - /// The currently authenticated user after modifications. - /// The user is not authenticated. - /// The given access token is invalid. - [HttpPatch("me")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] - public async Task> PatchMe([FromBody] Patch patch) - { - Guid userId = User.GetIdOrThrow(); - try - { - if (patch.Id.HasValue && patch.Id != userId) - throw new ArgumentException("Can't edit your user id."); - if (patch.ContainsKey(nameof(Abstractions.Models.User.Password))) - throw new ArgumentException( - "Can't edit your password via a PATCH. Use /auth/password-reset" - ); - return await users.Patch(userId, patch.Apply); - } - catch (ItemNotFoundException) - { - return Forbid(new RequestError("Invalid token")); - } - } - - /// - /// Delete account - /// - /// - /// Delete the current account. - /// - /// The user is not authenticated. - /// The given access token is invalid. - [HttpDelete("me")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] - public async Task> DeleteMe() - { - try - { - await users.Delete(User.GetIdOrThrow()); - return NoContent(); - } - catch (ItemNotFoundException) - { - return Forbid(new RequestError("Invalid token")); - } - } - - /// - /// Get profile picture - /// - /// - /// Get your profile picture - /// - /// The user is not authenticated. - /// The given access token is invalid. - [HttpGet("me/logo")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] - public async Task GetProfilePicture() - { - Stream img = await thumbs.GetUserImage(User.GetIdOrThrow()); - // Allow clients to cache the image for 6 month. - Response.Headers.CacheControl = $"public, max-age={60 * 60 * 24 * 31 * 6}"; - return File(img, "image/webp", true); - } - - /// - /// Set profile picture - /// - /// - /// Set your profile picture - /// - /// The user is not authenticated. - /// The given access token is invalid. - [HttpPost("me/logo")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] - public async Task SetProfilePicture(IFormFile picture) - { - if (picture == null || picture.Length == 0) - return BadRequest(); - await thumbs.SetUserImage(User.GetIdOrThrow(), picture.OpenReadStream()); - return NoContent(); - } - - /// - /// Delete profile picture - /// - /// - /// Delete your profile picture - /// - /// The user is not authenticated. - /// The given access token is invalid. - [HttpDelete("me/logo")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] - public async Task DeleteProfilePicture() - { - await thumbs.SetUserImage(User.GetIdOrThrow(), null); - return NoContent(); - } -} diff --git a/back/src/Kyoo.Core/.gitignore b/back/src/Kyoo.Core/.gitignore deleted file mode 100644 index 8f2daad7..00000000 --- a/back/src/Kyoo.Core/.gitignore +++ /dev/null @@ -1,234 +0,0 @@ -## PROJECT CUSTOM IGNORES -libtranscoder.so -wwwroot/ - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -build/ -bld/ -bin/ -Bin/ -obj/ -Obj/ - -# Visual Studio 2015 cache/options directory -.vs/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Microsoft Azure ApplicationInsights config file -ApplicationInsights.config - -# Windows Store app package directory -AppPackages/ -BundleArtifacts/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.pfx -*.publishsettings -orleans.codegen.cs - -/node_modules - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe - -# FAKE - F# Make -.fake/ diff --git a/back/src/Kyoo.Core/Controllers/Base64RouteConstraint.cs b/back/src/Kyoo.Core/Controllers/Base64RouteConstraint.cs deleted file mode 100644 index e69177a1..00000000 --- a/back/src/Kyoo.Core/Controllers/Base64RouteConstraint.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Text.RegularExpressions; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace Kyoo.Core.Controllers; - -public class Base64RouteConstraint : IRouteConstraint -{ - static Regex Base64Reg = new("^[-A-Za-z0-9+/]*={0,3}$"); - - /// - public bool Match( - HttpContext? httpContext, - IRouter? route, - string routeKey, - RouteValueDictionary values, - RouteDirection routeDirection - ) - { - return values.TryGetValue(routeKey, out object? val) - && val is string str - && Base64Reg.IsMatch(str); - } -} diff --git a/back/src/Kyoo.Core/Controllers/IdentifierRouteConstraint.cs b/back/src/Kyoo.Core/Controllers/IdentifierRouteConstraint.cs deleted file mode 100644 index a6ffd777..00000000 --- a/back/src/Kyoo.Core/Controllers/IdentifierRouteConstraint.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; - -namespace Kyoo.Core.Controllers; - -/// -/// The route constraint that goes with the . -/// -public class IdentifierRouteConstraint : IRouteConstraint -{ - /// - public bool Match( - HttpContext? httpContext, - IRouter? route, - string routeKey, - RouteValueDictionary values, - RouteDirection routeDirection - ) - { - return values.ContainsKey(routeKey); - } -} diff --git a/back/src/Kyoo.Core/Controllers/LibraryManager.cs b/back/src/Kyoo.Core/Controllers/LibraryManager.cs deleted file mode 100644 index c0763b19..00000000 --- a/back/src/Kyoo.Core/Controllers/LibraryManager.cs +++ /dev/null @@ -1,105 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Linq; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; - -namespace Kyoo.Core.Controllers; - -/// -/// An class to interact with the database. Every repository is mapped through here. -/// -public class LibraryManager : ILibraryManager -{ - private readonly IBaseRepository[] _repositories; - - public LibraryManager( - IRepository libraryItemRepository, - IRepository newsRepository, - IWatchStatusRepository watchStatusRepository, - IRepository collectionRepository, - IRepository movieRepository, - IRepository showRepository, - IRepository seasonRepository, - IRepository episodeRepository, - IRepository studioRepository, - IRepository userRepository - ) - { - LibraryItems = libraryItemRepository; - News = newsRepository; - WatchStatus = watchStatusRepository; - Collections = collectionRepository; - Movies = movieRepository; - Shows = showRepository; - Seasons = seasonRepository; - Episodes = episodeRepository; - Studios = studioRepository; - Users = userRepository; - - _repositories = - [ - LibraryItems, - News, - Collections, - Movies, - Shows, - Seasons, - Episodes, - Studios, - Users - ]; - } - - /// - public IRepository LibraryItems { get; } - - /// - public IRepository News { get; } - - /// - public IWatchStatusRepository WatchStatus { get; } - - /// - public IRepository Collections { get; } - - /// - public IRepository Movies { get; } - - /// - public IRepository Shows { get; } - - /// - public IRepository Seasons { get; } - - /// - public IRepository Episodes { get; } - - /// - public IRepository Studios { get; } - - /// - public IRepository Users { get; } - - public IRepository Repository() - where T : IResource, IQuery - { - return (IRepository)_repositories.First(x => x.RepositoryType == typeof(T)); - } -} diff --git a/back/src/Kyoo.Core/Controllers/MiscRepository.cs b/back/src/Kyoo.Core/Controllers/MiscRepository.cs deleted file mode 100644 index 28295623..00000000 --- a/back/src/Kyoo.Core/Controllers/MiscRepository.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Dapper; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Authentication.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using static System.Text.Json.JsonNamingPolicy; - -namespace Kyoo.Core.Controllers; - -public class MiscRepository( - DatabaseContext context, - DbConnection database, - IThumbnailsManager thumbnails -) -{ - public static async Task DownloadMissingImages(IServiceProvider services) - { - await using AsyncServiceScope scope = services.CreateAsyncScope(); - await scope.ServiceProvider.GetRequiredService().DownloadMissingImages(); - } - - private async Task> _GetAllImages() - { - string GetSql(string type) => - $""" - select poster from {type} - union all select thumbnail from {type} - union all select logo from {type} - """; - var queries = new string[] - { - "movies", - "collections", - "shows", - "seasons", - "episodes" - }.Select(x => GetSql(x)); - string sql = string.Join(" union all ", queries); - IEnumerable ret = await database.QueryAsync(sql); - return ret.ToArray() as Image[]; - } - - public async Task DownloadMissingImages() - { - ICollection images = await _GetAllImages(); - var tasks = images - .ToAsyncEnumerable() - .WhereAwait(async x => !await thumbnails.IsImageSaved(x.Id, ImageQuality.Low)) - .Select(x => thumbnails.DownloadImage(x, x.Id.ToString())) - .ToEnumerable(); - - // Chunk tasks to prevent http timouts - foreach (IEnumerable batch in tasks.Chunk(30)) - await Task.WhenAll(batch); - } - - public async Task> GetRegisteredPaths() - { - return await context - .Episodes.Select(x => x.Path) - .Concat(context.Movies.Select(x => x.Path)) - .ToListAsync(); - } - - public async Task DeletePath(string path, bool recurse) - { - // Make sure to include a path separator to prevents deletions from things like: - // DeletePath("/video/abc", true) -> /video/abdc (should not be deleted) - string dirPath = path.EndsWith("/") ? path : $"{path}/"; - - int count = await context - .Episodes.Where(x => x.Path == path || (recurse && x.Path.StartsWith(dirPath))) - .ExecuteDeleteAsync(); - count += await context - .Movies.Where(x => x.Path == path || (recurse && x.Path.StartsWith(dirPath))) - .ExecuteDeleteAsync(); - await context - .Issues.Where(x => - x.Domain == "scanner" - && (x.Cause == path || (recurse && x.Cause.StartsWith(dirPath))) - ) - .ExecuteDeleteAsync(); - return count; - } - - public async Task> GetRefreshableItems(DateTime end) - { - IQueryable GetItems() - where T : class, IResource, IRefreshable - { - return context - .Set() - .Select(x => new RefreshableItem - { - Kind = CamelCase.ConvertName(typeof(T).Name), - Id = x.Id, - RefreshDate = x.NextMetadataRefresh!.Value - }); - } - - return await GetItems() - .Concat(GetItems()) - .Concat(GetItems()) - .Concat(GetItems()) - .Concat(GetItems()) - .Where(x => x.RefreshDate <= end) - .OrderBy(x => x.RefreshDate) - .ToListAsync(); - } - - public async Task GetSetupStep() - { - bool hasUser = await context.Users.AnyAsync(); - if (!hasUser) - return SetupStep.MissingAdminAccount; - bool hasItem = await context.Movies.AnyAsync() || await context.Shows.AnyAsync(); - return hasItem ? SetupStep.Done : SetupStep.NoVideoFound; - } -} - -public class RefreshableItem -{ - public string Kind { get; set; } - - public Guid Id { get; set; } - - public DateTime RefreshDate { get; set; } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/CollectionRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/CollectionRepository.cs deleted file mode 100644 index 0a81a29b..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/CollectionRepository.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; - -namespace Kyoo.Core.Controllers; - -/// -/// A local repository to handle collections -/// -public class CollectionRepository(DatabaseContext database, IThumbnailsManager thumbnails) - : GenericRepository(database) -{ - /// - public override async Task> Search( - string query, - Include? include = default - ) - { - return await AddIncludes(Database.Collections, include) - .Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%")) - .Take(20) - .ToListAsync(); - } - - /// - protected override async Task Validate(Collection resource) - { - await base.Validate(resource); - - if (string.IsNullOrEmpty(resource.Name)) - throw new ArgumentException("The collection's name must be set and not empty"); - resource.NextMetadataRefresh ??= DateTime.UtcNow.AddMonths(2); - await thumbnails.DownloadImages(resource); - } - - public async Task AddMovie(Guid id, Guid movieId) - { - Database.AddLinks(id, movieId); - await Database.SaveChangesAsync(); - } - - public async Task AddShow(Guid id, Guid showId) - { - Database.AddLinks(id, showId); - await Database.SaveChangesAsync(); - } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/DapperHelper.cs b/back/src/Kyoo.Core/Controllers/Repositories/DapperHelper.cs deleted file mode 100644 index 19383169..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/DapperHelper.cs +++ /dev/null @@ -1,415 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using System.Data; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Dapper; -using InterpolatedSql.Dapper; -using InterpolatedSql.Dapper.SqlBuilders; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Authentication; -using Kyoo.Utils; -using Microsoft.AspNetCore.Http; - -namespace Kyoo.Core.Controllers; - -public static class DapperHelper -{ - public static SqlBuilder ProcessVariables(SqlBuilder sql, SqlVariableContext context) - { - int start = 0; - while ((start = sql.IndexOf("[", start, false)) != -1) - { - int end = sql.IndexOf("]", start, false); - if (end == -1) - throw new ArgumentException("Invalid sql variable substitue (missing ])"); - string var = sql.Format[(start + 1)..end]; - sql.Remove(start, end - start + 1); - sql.Insert(start, $"{context.ReadVar(var)}"); - } - - return sql; - } - - public static string Property(string key, Dictionary config) - { - if (key == "kind") - return "kind"; - string[] keys = config - .Where(x => !x.Key.StartsWith('_')) - // If first char is lower, assume manual sql instead of reflection. - .Where(x => char.IsLower(key.First()) || x.Value.GetProperty(key) != null) - .Select(x => - $"{x.Key}.{x.Value.GetProperty(key)?.GetCustomAttribute()?.Name ?? key.ToSnakeCase()}" - ) - .ToArray(); - if (keys.Length == 1) - return keys.First(); - return $"coalesce({string.Join(", ", keys)})"; - } - - public static string ProcessSort( - Sort sort, - bool reverse, - Dictionary config, - bool recurse = false - ) - where T : IQuery - { - string ret = sort switch - { - Sort.Default(var value) => ProcessSort(value, reverse, config, true), - Sort.By(string key, bool desc) - => $"{Property(key, config)} {(desc ^ reverse ? "desc" : "asc")}", - Sort.Random(var seed) - => $"md5('{seed}' || {Property("id", config)}) {(reverse ? "desc" : "asc")}", - Sort.Conglomerate(var list) - => string.Join(", ", list.Select(x => ProcessSort(x, reverse, config, true))), - _ => throw new SwitchExpressionException(), - }; - if (recurse) - return ret; - // always end query by an id sort. - return $"{ret}, {Property("id", config)} {(reverse ? "desc" : "asc")}"; - } - - public static ( - string projection, - string join, - List types, - Func, T> map - ) ProcessInclude(Include include, Dictionary config) - where T : class - { - int relation = 0; - List types = new(); - StringBuilder projection = new(); - StringBuilder join = new(); - - foreach (Include.Metadata metadata in include.Metadatas) - { - relation++; - switch (metadata) - { - case Include.SingleRelation(var name, var type, var rid): - string tableName = - type.GetCustomAttribute()?.Name - ?? $"{type.Name.ToSnakeCase()}s"; - types.Add(type); - projection.AppendLine($", r{relation}.* -- {type.Name} as r{relation}"); - join.Append( - $"\nleft join {tableName} as r{relation} on r{relation}.id = {Property(rid, config)}" - ); - break; - case Include.CustomRelation(var name, var type, var sql, var on, var declaring): - string owner = config.First(x => x.Value == declaring).Key; - string lateral = sql.Contains("\"this\"") ? " lateral" : string.Empty; - sql = sql.Replace("\"this\"", owner); - on = on?.Replace("\"this\"", owner)?.Replace("\"relation\"", $"r{relation}"); - if (sql.Any(char.IsWhiteSpace)) - sql = $"({sql})"; - types.Add(type); - projection.AppendLine($", r{relation}.*"); - join.Append($"\nleft join{lateral} {sql} as r{relation} on r{relation}.{on}"); - break; - case Include.ProjectedRelation: - continue; - default: - throw new NotImplementedException(); - } - } - - T Map(T item, IEnumerable relations) - { - IEnumerable metadatas = include - .Metadatas.Where(x => x is not Include.ProjectedRelation) - .Select(x => x.Name); - foreach ((string name, object? value) in metadatas.Zip(relations)) - { - if (value == null) - continue; - PropertyInfo? prop = item.GetType().GetProperty(name); - if (prop != null) - prop.SetValue(item, value); - } - return item; - } - - return (projection.ToString(), join.ToString(), types, Map); - } - - public static FormattableString ProcessFilter( - Filter filter, - Dictionary config - ) - { - FormattableString Format(string key, FormattableString op) - { - if (key == "kind") - { - string cases = string.Join( - '\n', - config - .Skip(1) - .Select(x => - $"when {x.Key}.id is not null then '{x.Value.Name.ToLowerInvariant()}'" - ) - ); - return $""" - case - {cases:raw} - else '{config.First().Value.Name.ToLowerInvariant():raw}' - end {op} - """; - } - - IEnumerable properties = config - .Where(x => !x.Key.StartsWith('_')) - // If first char is lower, assume manual sql instead of reflection. - .Where(x => char.IsLower(key.First()) || x.Value.GetProperty(key) != null) - .Select(x => - $"{x.Key}.{x.Value.GetProperty(key)?.GetCustomAttribute()?.Name ?? key.ToSnakeCase()}" - ); - - FormattableString ret = $"{properties.First():raw} {op}"; - foreach (string property in properties.Skip(1)) - ret = $"{ret} or {property:raw} {op}"; - return $"({ret})"; - } - - object P(object value) - { - if (value is Enum) - return new Wrapper(value); - return value; - } - - FormattableString Process(Filter fil) - { - return fil switch - { - Filter.And(var first, var second) => $"({Process(first)} and {Process(second)})", - Filter.Or(var first, var second) => $"({Process(first)} or {Process(second)})", - Filter.Not(var inner) => $"(not {Process(inner)})", - Filter.Eq(var property, var value) when value is null - => Format(property, $"is null"), - Filter.Ne(var property, var value) when value is null - => Format(property, $"is not null"), - Filter.Eq(var property, var value) => Format(property, $"= {P(value!)}"), - Filter.Ne(var property, var value) => Format(property, $"!= {P(value!)}"), - Filter.Gt(var property, var value) => Format(property, $"> {P(value)}"), - Filter.Ge(var property, var value) => Format(property, $">= {P(value)}"), - Filter.Lt(var property, var value) => Format(property, $"< {P(value)}"), - Filter.Le(var property, var value) => Format(property, $"> {P(value)}"), - Filter.Has(var property, var value) - => $"{P(value)} = any({Property(property, config):raw})", - Filter.CmpRandom(var op, var seed, var id) - => $"md5({seed} || coalesce({string.Join(", ", config.Select(x => $"{x.Key}.id")):raw})) {op:raw} md5({seed} || {id.ToString()})", - Filter.Lambda(var lambda) => throw new NotSupportedException(), - _ => throw new NotImplementedException(), - }; - } - return Process(filter); - } - - public static string ExpendProjections(Type type, string? prefix, Include include) - { - IEnumerable projections = include - .Metadatas.Select(x => (x as Include.ProjectedRelation)!) - .Where(x => x != null) - .Where(x => type.GetProperty(x.Name) != null) - .Select(x => x.Sql.Replace("\"this\".", prefix)); - return string.Join(", ", projections); - } - - public static async Task> Query( - this IDbConnection db, - FormattableString command, - Dictionary config, - Func, T> mapper, - Func> get, - SqlVariableContext context, - Include? include, - Filter? filter, - Sort? sort, - Pagination? limit - ) - where T : class, IResource, IQuery - { - SqlBuilder query = new(db, command); - - // Include handling - include ??= new(); - var (includeProjection, includeJoin, includeTypes, mapIncludes) = ProcessInclude( - include, - config - ); - query.Replace("/* includesJoin */", $"{includeJoin:raw}", out bool replaced); - if (!replaced) - query.AppendLiteral(includeJoin); - query.Replace("/* includes */", $"{includeProjection:raw}", out replaced); - if (!replaced) - throw new ArgumentException( - "Missing '/* includes */' placeholder in top level sql select to support includes." - ); - - // Handle pagination, orders and filter. - if (limit?.AfterID != null) - { - T reference = await get(limit.AfterID.Value); - Filter? keysetFilter = RepositoryHelper.KeysetPaginate( - sort, - reference, - !limit.Reverse - ); - filter = Filter.And(filter, keysetFilter); - } - if (filter != null) - { - FormattableString filterSql = ProcessFilter(filter, config); - query.Replace("/* where */", $"and {filterSql}", out replaced); - if (!replaced) - query += $"\nwhere {filterSql}"; - } - if (sort != null) - query += $"\norder by {ProcessSort(sort, limit?.Reverse ?? false, config):raw}"; - if (limit != null) - query += $"\nlimit {limit.Limit}"; - - ProcessVariables(query, context); - - // Build query and prepare to do the query/projections - IDapperSqlCommand cmd = query.Build(); - string sql = cmd.Sql; - List types = config.Select(x => x.Value).Concat(includeTypes).ToList(); - - // Expand projections on every types received. - sql = Regex.Replace( - sql, - @"(,?) -- (\w+)( as (\w+))?", - (match) => - { - string leadingComa = match.Groups[1].Value; - string type = match.Groups[2].Value; - string? prefix = match.Groups[4].Value; - prefix = !string.IsNullOrEmpty(prefix) ? $"{prefix}." : string.Empty; - - Type typeV = types.First(x => x.Name == type); - - // Only project top level items with explicit includes. - string? projection = config.Any(x => x.Value.Name == type) - ? ExpendProjections(typeV, prefix, include) - : null; - - if (string.IsNullOrEmpty(projection)) - return leadingComa; - return $", {projection}{leadingComa}"; - } - ); - - IEnumerable data = await db.QueryAsync( - sql, - types.ToArray(), - items => - { - return mapIncludes(mapper(items), items.Skip(config.Count)); - }, - ParametersDictionary.LoadFrom(cmd), - splitOn: string.Join( - ',', - types.Select(x => x.GetCustomAttribute()?.Name ?? "id") - ) - ); - if (limit?.Reverse == true) - data = data.Reverse(); - return data.ToList(); - } - - public static async Task QuerySingle( - this IDbConnection db, - FormattableString command, - Dictionary config, - Func, T> mapper, - SqlVariableContext context, - Include? include, - Filter? filter, - Sort? sort = null, - bool reverse = false, - Guid? afterId = default - ) - where T : class, IResource, IQuery - { - ICollection ret = await db.Query( - command, - config, - mapper, - get: null!, - context, - include, - filter, - sort, - new Pagination(1, afterId, reverse) - ); - return ret.FirstOrDefault(); - } - - public static async Task Count( - this IDbConnection db, - FormattableString command, - Dictionary config, - SqlVariableContext context, - Filter? filter - ) - where T : class, IResource - { - SqlBuilder query = new(db, command); - - if (filter != null) - query += ProcessFilter(filter, config); - ProcessVariables(query, context); - IDapperSqlCommand cmd = query.Build(); - - // language=postgreSQL - string sql = $"select count(*) from ({cmd.Sql}) as query"; - - return await db.QuerySingleAsync(sql, ParametersDictionary.LoadFrom(cmd)); - } -} - -public class SqlVariableContext(IHttpContextAccessor accessor) -{ - public object? ReadVar(string var) - { - return var switch - { - "current_user" => accessor.HttpContext?.User.GetId(), - _ => throw new ArgumentException($"Invalid sql variable name: {var}") - }; - } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/DapperRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/DapperRepository.cs deleted file mode 100644 index 7c37d79d..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/DapperRepository.cs +++ /dev/null @@ -1,221 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Exceptions; -using Kyoo.Abstractions.Models.Utils; - -namespace Kyoo.Core.Controllers; - -public abstract class DapperRepository : IRepository - where T : class, IResource, IQuery -{ - public Type RepositoryType => typeof(T); - - protected abstract FormattableString Sql { get; } - - protected abstract Dictionary Config { get; } - - protected abstract T Mapper(IList items); - - protected DbConnection Database { get; init; } - - protected SqlVariableContext Context { get; init; } - - public DapperRepository(DbConnection database, SqlVariableContext context) - { - Database = database; - Context = context; - } - - /// - public virtual async Task Get(Guid id, Include? include = default) - { - T? ret = await GetOrDefault(id, include); - if (ret == null) - throw new ItemNotFoundException($"No {typeof(T).Name} found with the id {id}"); - return ret; - } - - /// - public virtual async Task Get(string slug, Include? include = default) - { - T? ret = await GetOrDefault(slug, include); - if (ret == null) - throw new ItemNotFoundException($"No {typeof(T).Name} found with the slug {slug}"); - return ret; - } - - /// - public virtual async Task Get( - Filter? filter, - Include? include = default, - Sort? sortBy = default, - bool reverse = false, - Guid? afterId = default - ) - { - T? ret = await GetOrDefault(filter, include, sortBy, reverse, afterId); - if (ret == null) - throw new ItemNotFoundException($"No {typeof(T).Name} found with the given predicate."); - return ret; - } - - /// - public async Task> FromIds(IList ids, Include? include = null) - { - if (!ids.Any()) - return Array.Empty(); - return ( - await Database.Query( - Sql, - Config, - Mapper, - (id) => Get(id), - Context, - include, - Filter.Or(ids.Select(x => new Filter.Eq("id", x)).ToArray()), - sort: null, - limit: null - ) - ) - .OrderBy(x => ids.IndexOf(x.Id)) - .ToList(); - } - - /// - public Task GetOrDefault(Guid id, Include? include = null) - { - return Database.QuerySingle( - Sql, - Config, - Mapper, - Context, - include, - new Filter.Eq(nameof(IResource.Id), id) - ); - } - - /// - public Task GetOrDefault(string slug, Include? include = null) - { - if (slug == "random") - { - return Database.QuerySingle( - Sql, - Config, - Mapper, - Context, - include, - filter: null, - new Sort.Random() - ); - } - return Database.QuerySingle( - Sql, - Config, - Mapper, - Context, - include, - new Filter.Eq(nameof(IResource.Slug), slug) - ); - } - - /// - public virtual Task GetOrDefault( - Filter? filter, - Include? include = default, - Sort? sortBy = default, - bool reverse = false, - Guid? afterId = default - ) - { - return Database.QuerySingle( - Sql, - Config, - Mapper, - Context, - include, - filter, - sortBy, - reverse, - afterId - ); - } - - /// - public Task> GetAll( - Filter? filter = default, - Sort? sort = default, - Include? include = default, - Pagination? limit = default - ) - { - return Database.Query( - Sql, - Config, - Mapper, - (id) => Get(id), - Context, - include, - filter, - sort ?? new Sort.Default(), - limit ?? new() - ); - } - - /// - public Task GetCount(Filter? filter = null) - { - return Database.Count(Sql, Config, Context, filter); - } - - /// - public Task> Search(string query, Include? include = null) => - throw new NotImplementedException(); - - /// - public Task Create(T obj) => throw new NotImplementedException(); - - /// - public Task CreateIfNotExists(T obj) => throw new NotImplementedException(); - - /// - public Task Delete(Guid id) => throw new NotImplementedException(); - - /// - public Task Delete(string slug) => throw new NotImplementedException(); - - /// - public Task Delete(T obj) => throw new NotImplementedException(); - - /// - public Task DeleteAll(Filter filter) => throw new NotImplementedException(); - - /// - public Task Edit(T edited) => throw new NotImplementedException(); - - /// - public Task Patch(Guid id, Func patch) => throw new NotImplementedException(); -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/EfHelpers.cs b/back/src/Kyoo.Core/Controllers/Repositories/EfHelpers.cs deleted file mode 100644 index 76f0e731..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/EfHelpers.cs +++ /dev/null @@ -1,140 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Postgresql; -using Kyoo.Utils; - -namespace Kyoo.Core.Controllers; - -public static class EfHelpers -{ - public static Expression> ToEfLambda(this Filter? filter) - { - if (filter == null) - return x => true; - - ParameterExpression x = Expression.Parameter(typeof(T), "x"); - - Expression CmpRandomHandler(string cmp, string seed, Guid refId) - { - MethodInfo concat = typeof(string).GetMethod( - nameof(string.Concat), - new[] { typeof(string), typeof(string) } - )!; - Expression id = Expression.Call( - Expression.Property(x, "ID"), - nameof(Guid.ToString), - null - ); - Expression xrng = Expression.Call(concat, Expression.Constant(seed), id); - Expression left = Expression.Call( - typeof(DatabaseContext), - nameof(DatabaseContext.MD5), - null, - xrng - ); - Expression right = Expression.Call( - typeof(DatabaseContext), - nameof(DatabaseContext.MD5), - null, - Expression.Constant($"{seed}{refId}") - ); - return cmp switch - { - "=" => Expression.Equal(left, right), - "<" => Expression.GreaterThan(left, right), - ">" => Expression.LessThan(left, right), - _ => throw new NotImplementedException() - }; - } - - BinaryExpression StringCompatibleExpression( - Func operand, - string property, - object value - ) - { - var left = Expression.Property(x, property); - var right = Expression.Constant(value, ((PropertyInfo)left.Member).PropertyType); - if (left.Type != typeof(string)) - return operand(left, right); - MethodCallExpression call = Expression.Call( - typeof(string), - "Compare", - null, - left, - right - ); - return operand(call, Expression.Constant(0)); - } - - Expression Exp( - Func operand, - string property, - object? value - ) - { - var prop = Expression.Property(x, property); - var val = Expression.Constant(value, ((PropertyInfo)prop.Member).PropertyType); - return operand(prop, val); - } - - Expression Parse(Filter f) - { - return f switch - { - Filter.And(var first, var second) - => Expression.AndAlso(Parse(first), Parse(second)), - Filter.Or(var first, var second) - => Expression.OrElse(Parse(first), Parse(second)), - Filter.Not(var inner) => Expression.Not(Parse(inner)), - Filter.Eq(var property, var value) => Exp(Expression.Equal, property, value), - Filter.Ne(var property, var value) => Exp(Expression.NotEqual, property, value), - Filter.Gt(var property, var value) - => StringCompatibleExpression(Expression.GreaterThan, property, value), - Filter.Ge(var property, var value) - => StringCompatibleExpression(Expression.GreaterThanOrEqual, property, value), - Filter.Lt(var property, var value) - => StringCompatibleExpression(Expression.LessThan, property, value), - Filter.Le(var property, var value) - => StringCompatibleExpression(Expression.LessThanOrEqual, property, value), - Filter.Has(var property, var value) - => Expression.Call( - typeof(Enumerable), - "Contains", - new[] { value.GetType() }, - Expression.Property(x, property), - Expression.Constant(value) - ), - Filter.CmpRandom(var op, var seed, var refId) - => CmpRandomHandler(op, seed, refId), - Filter.Lambda(var lambda) - => ExpressionArgumentReplacer.ReplaceParams(lambda.Body, lambda.Parameters, x), - _ => throw new NotImplementedException(), - }; - } - - Expression body = Parse(filter); - return Expression.Lambda>(body, x); - } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs deleted file mode 100644 index 500972fa..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs +++ /dev/null @@ -1,140 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; - -namespace Kyoo.Core.Controllers; - -/// -/// A local repository to handle episodes. -/// -public class EpisodeRepository( - DatabaseContext database, - IRepository shows, - IThumbnailsManager thumbnails -) : GenericRepository(database) -{ - static EpisodeRepository() - { - // Edit episode slugs when the show's slug changes. - IRepository.OnEdited += async (show) => - { - await using AsyncServiceScope scope = CoreModule.Services.CreateAsyncScope(); - DatabaseContext database = scope.ServiceProvider.GetRequiredService(); - List episodes = await database - .Episodes.AsTracking() - .Where(x => x.ShowId == show.Id) - .ToListAsync(); - foreach (Episode ep in episodes) - { - ep.ShowSlug = show.Slug; - await database.SaveChangesAsync(); - await IRepository.OnResourceEdited(ep); - } - }; - } - - /// - public override async Task> Search( - string query, - Include? include = default - ) - { - return await AddIncludes(Database.Episodes, include) - .Where(x => EF.Functions.ILike(x.Name!, $"%{query}%")) - .Take(20) - .ToListAsync(); - } - - /// - protected override async Task Validate(Episode resource) - { - await base.Validate(resource); - resource.Show = null; - if (resource.ShowId == Guid.Empty) - throw new ValidationException("Missing show id"); - // This is storred in db so it needs to be set before every create/edit (and before events) - resource.ShowSlug = (await shows.Get(resource.ShowId)).Slug; - - resource.Season = null; - if (resource.SeasonId == null && resource.SeasonNumber != null) - { - resource.SeasonId = await Database - .Seasons.Where(x => - x.ShowId == resource.ShowId && x.SeasonNumber == resource.SeasonNumber - ) - .Select(x => x.Id) - .FirstOrDefaultAsync(); - } - - resource.NextMetadataRefresh ??= IRefreshable.ComputeNextRefreshDate( - resource.ReleaseDate ?? DateOnly.FromDateTime(resource.AddedDate) - ); - await thumbnails.DownloadImages(resource); - } - - /// - public override async Task Delete(Episode obj) - { - int epCount = await Database - .Episodes.Where(x => x.ShowId == obj.ShowId) - .Take(2) - .CountAsync(); - if (epCount == 1) - await shows.Delete(obj.ShowId); - else - await base.Delete(obj); - } - - /// - public override async Task DeleteAll(Filter filter) - { - ICollection items = await GetAll(filter); - Guid[] ids = items.Select(x => x.Id).ToArray(); - - await Database.Set().Where(x => ids.Contains(x.Id)).ExecuteDeleteAsync(); - foreach (Episode resource in items) - await IRepository.OnResourceDeleted(resource); - - Guid[] showIds = await Database - .Set() - .Where(filter.ToEfLambda()) - .Select(x => x.Show!) - .Where(x => !x.Episodes!.Any()) - .Select(x => x.Id) - .ToArrayAsync(); - - if (!showIds.Any()) - return; - - Filter[] showFilters = showIds - .Select(x => new Filter.Eq(nameof(Show.Id), x)) - .ToArray(); - await shows.DeleteAll(Filter.Or(showFilters)!); - } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/GenericRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/GenericRepository.cs deleted file mode 100644 index 36019ffd..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/GenericRepository.cs +++ /dev/null @@ -1,369 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Exceptions; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; - -namespace Kyoo.Core.Controllers; - -public abstract class GenericRepository(DatabaseContext database) : IRepository - where T : class, IResource, IQuery -{ - public DatabaseContext Database => database; - - /// - public Type RepositoryType => typeof(T); - - /// - /// Sort the given query. - /// - /// The query to sort. - /// How to sort the query. - /// The newly sorted query. - protected IOrderedQueryable Sort(IQueryable query, Sort? sortBy) - { - sortBy ??= new Sort.Default(); - - IOrderedQueryable _SortBy( - IQueryable qr, - Expression> sort, - bool desc, - bool then - ) - { - if (then && qr is IOrderedQueryable qro) - { - return desc ? qro.ThenByDescending(sort) : qro.ThenBy(sort); - } - return desc ? qr.OrderByDescending(sort) : qr.OrderBy(sort); - } - - IOrderedQueryable _Sort(IQueryable query, Sort sortBy, bool then) - { - switch (sortBy) - { - case Sort.Default(var value): - return _Sort(query, value, then); - case Sort.By(var key, var desc): - return _SortBy(query, x => EF.Property(x, key), desc, then); - case Sort.Random(var seed): - // NOTE: To edit this, don't forget to edit the random handiling inside the KeysetPaginate function - return _SortBy( - query, - x => DatabaseContext.MD5(seed + x.Id.ToString()), - false, - then - ); - case Sort.Conglomerate(var sorts): - IOrderedQueryable nQuery = _Sort(query, sorts.First(), false); - foreach (Sort sort in sorts.Skip(1)) - nQuery = _Sort(nQuery, sort, true); - return nQuery; - default: - // The language should not require me to do this... - throw new SwitchExpressionException(); - } - } - return _Sort(query, sortBy, false).ThenBy(x => x.Id); - } - - protected IQueryable AddIncludes(IQueryable query, Include? include) - { - if (include == null) - return query; - foreach (string field in include.Fields) - query = query.Include(field); - return query; - } - - protected virtual async Task GetWithTracking(Guid id) - { - T? ret = await Database.Set().AsTracking().FirstOrDefaultAsync(x => x.Id == id); - if (ret == null) - throw new ItemNotFoundException($"No {typeof(T).Name} found with the id {id}"); - return ret; - } - - /// - public virtual async Task Get(Guid id, Include? include = default) - { - T? ret = await GetOrDefault(id, include); - if (ret == null) - throw new ItemNotFoundException($"No {typeof(T).Name} found with the id {id}"); - return ret; - } - - /// - public virtual async Task Get(string slug, Include? include = default) - { - T? ret = await GetOrDefault(slug, include); - if (ret == null) - throw new ItemNotFoundException($"No {typeof(T).Name} found with the slug {slug}"); - return ret; - } - - /// - public virtual async Task Get( - Filter filter, - Include? include = default, - Sort? sortBy = default, - bool reverse = false, - Guid? afterId = default - ) - { - T? ret = await GetOrDefault(filter, include, sortBy, reverse, afterId); - if (ret == null) - throw new ItemNotFoundException($"No {typeof(T).Name} found with the given predicate."); - return ret; - } - - /// - public virtual Task GetOrDefault(Guid id, Include? include = default) - { - return AddIncludes(Database.Set(), include).FirstOrDefaultAsync(x => x.Id == id); - } - - /// - public virtual Task GetOrDefault(string slug, Include? include = default) - { - if (slug == "random") - { - return AddIncludes(Database.Set(), include) - .OrderBy(x => EF.Functions.Random()) - .FirstOrDefaultAsync(); - } - return AddIncludes(Database.Set(), include).FirstOrDefaultAsync(x => x.Slug == slug); - } - - /// - public virtual async Task GetOrDefault( - Filter? filter, - Include? include = default, - Sort? sortBy = default, - bool reverse = false, - Guid? afterId = default - ) - { - IQueryable query = await ApplyFilters( - Database.Set(), - filter, - sortBy, - new Pagination(1, afterId, reverse), - include - ); - return await query.FirstOrDefaultAsync(); - } - - /// - public virtual async Task> FromIds( - IList ids, - Include? include = default - ) - { - return ( - await AddIncludes(Database.Set(), include) - .Where(x => ids.Contains(x.Id)) - .ToListAsync() - ) - .OrderBy(x => ids.IndexOf(x.Id)) - .ToList(); - } - - /// - public abstract Task> Search(string query, Include? include = default); - - /// - public virtual async Task> GetAll( - Filter? filter = null, - Sort? sort = default, - Include? include = default, - Pagination? limit = default - ) - { - IQueryable query = await ApplyFilters(Database.Set(), filter, sort, limit, include); - return await query.ToListAsync(); - } - - /// - /// Apply filters to a query to ease sort, pagination and where queries for resources of this repository - /// - /// The base query to filter. - /// An expression to filter based on arbitrary conditions - /// The sort settings (sort order and sort by) - /// Pagination information (where to start and how many to get) - /// Related fields to also load with this query. - /// The filtered query - protected async Task> ApplyFilters( - IQueryable query, - Filter? filter = null, - Sort? sort = default, - Pagination? limit = default, - Include? include = default - ) - { - query = AddIncludes(query, include); - query = Sort(query, sort); - limit ??= new(); - - if (limit.AfterID != null) - { - T reference = await Get(limit.AfterID.Value); - Filter? keysetFilter = RepositoryHelper.KeysetPaginate( - sort, - reference, - !limit.Reverse - ); - filter = Filter.And(filter, keysetFilter); - } - if (filter != null) - query = query.Where(filter.ToEfLambda()); - - if (limit.Reverse) - query = query.Reverse(); - if (limit.Limit > 0) - query = query.Take(limit.Limit); - if (limit.Reverse) - query = query.Reverse(); - - return query; - } - - /// - public virtual Task GetCount(Filter? filter = null) - { - IQueryable query = Database.Set(); - if (filter != null) - query = query.Where(filter.ToEfLambda()); - return query.CountAsync(); - } - - /// - public virtual async Task Create(T obj) - { - await Validate(obj); - Database.Add(obj); - await Database.SaveChangesAsync(() => Get(obj.Slug)); - await IRepository.OnResourceCreated(obj); - return obj; - } - - /// - public virtual async Task CreateIfNotExists(T obj) - { - try - { - T? old = await GetOrDefault(obj.Slug); - if (old != null) - return old; - - return await Create(obj); - } - catch (DuplicatedItemException) - { - return await Get(obj.Slug); - } - } - - /// - public virtual async Task Edit(T edited) - { - await Validate(edited); - Database.Update(edited); - if (edited is IAddedDate date) - Database.Entry(date).Property(p => p.AddedDate).IsModified = false; - await Database.SaveChangesAsync(); - await IRepository.OnResourceEdited(edited); - return edited; - } - - /// - public virtual async Task Patch(Guid id, Func patch) - { - T resource = await GetWithTracking(id); - - resource = patch(resource); - if (resource is IAddedDate date) - Database.Entry(date).Property(p => p.AddedDate).IsModified = false; - - await Database.SaveChangesAsync(); - await IRepository.OnResourceEdited(resource); - return resource; - } - - /// - /// You can throw this if the resource is illegal and should not be saved. - /// - protected virtual Task Validate(T resource) - { - if ( - typeof(T).GetProperty(nameof(resource.Slug))!.GetCustomAttribute() - != null - ) - return Task.CompletedTask; - if (string.IsNullOrEmpty(resource.Slug)) - throw new ValidationException("Resource can't have null as a slug."); - if (resource.Slug == "random") - throw new ValidationException("Resources slug can't be the literal \"random\"."); - return Task.CompletedTask; - } - - /// - public virtual async Task Delete(Guid id) - { - T resource = await Get(id); - await Delete(resource); - } - - /// - public virtual async Task Delete(string slug) - { - T resource = await Get(slug); - await Delete(resource); - } - - /// - public virtual async Task Delete(T obj) - { - await Database.Set().Where(x => x.Id == obj.Id).ExecuteDeleteAsync(); - await IRepository.OnResourceDeleted(obj); - } - - /// - public virtual async Task DeleteAll(Filter filter) - { - ICollection items = await GetAll(filter); - Guid[] ids = items.Select(x => x.Id).ToArray(); - await Database.Set().Where(x => ids.Contains(x.Id)).ExecuteDeleteAsync(); - - foreach (T resource in items) - await IRepository.OnResourceDeleted(resource); - } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/IssueRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/IssueRepository.cs deleted file mode 100644 index f4cb64ae..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/IssueRepository.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; - -namespace Kyoo.Core.Controllers; - -public class IssueRepository(DatabaseContext database) : IIssueRepository -{ - public async Task> GetAll(Filter? filter = null) - { - return await database.Issues.Where(filter.ToEfLambda()).ToListAsync(); - } - - public Task GetCount(Filter? filter = null) - { - return database.Issues.Where(filter.ToEfLambda()).CountAsync(); - } - - public async Task Upsert(Issue issue) - { - issue.AddedDate = DateTime.UtcNow; - await database.Issues.Upsert(issue).RunAsync(); - return issue; - } - - public Task DeleteAll(Filter? filter = null) - { - return database.Issues.Where(filter.ToEfLambda()).ExecuteDeleteAsync(); - } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs deleted file mode 100644 index 27d6e844..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/LibraryItemRepository.cs +++ /dev/null @@ -1,124 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.IO; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Utils; - -namespace Kyoo.Core.Controllers; - -/// -/// A local repository to handle library items. -/// -public class LibraryItemRepository(DbConnection database, SqlVariableContext context) - : DapperRepository(database, context) -{ - // language=PostgreSQL - protected override FormattableString Sql => - $""" - select - s.*, -- Show as s - m.*, - c.* - /* includes */ - from - shows as s - full outer join ( - select - * -- Movie - from - movies) as m on false - full outer join( - select - c.* -- Collection as c - from - collections as c - left join link_collection_show as ls on ls.collection_id = c.id - left join link_collection_movie as lm on lm.collection_id = c.id - group by c.id - having count(*) > 1 - ) as c on false - """; - - protected override Dictionary Config => - new() - { - { "s", typeof(Show) }, - { "m", typeof(Movie) }, - { "c", typeof(Collection) } - }; - - protected override ILibraryItem Mapper(IList items) - { - if (items[0] is Show show && show.Id != Guid.Empty) - return show; - if (items[1] is Movie movie && movie.Id != Guid.Empty) - return movie; - if (items[2] is Collection collection && collection.Id != Guid.Empty) - return collection; - throw new InvalidDataException(); - } - - public async Task> GetAllOfCollection( - Guid collectionId, - Filter? filter = default, - Sort? sort = default, - Include? include = default, - Pagination? limit = default - ) - { - // language=PostgreSQL - FormattableString sql = $""" - select - s.*, - m.* - /* includes */ - from ( - select - * -- Show - from - shows - inner join link_collection_show as ls on ls.show_id = id and ls.collection_id = {collectionId} - ) as s - full outer join ( - select - * -- Movie - from - movies - inner join link_collection_movie as lm on lm.movie_id = id and lm.collection_id = {collectionId} - ) as m on false - """; - - return await Database.Query( - sql, - new() { { "s", typeof(Show) }, { "m", typeof(Movie) }, }, - Mapper, - (id) => Get(id), - Context, - include, - filter, - sort ?? new Sort.Default(), - limit ?? new() - ); - } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs deleted file mode 100644 index e33501e1..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/MovieRepository.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Exceptions; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; - -namespace Kyoo.Core.Controllers; - -public class MovieRepository( - DatabaseContext database, - IRepository studios, - IThumbnailsManager thumbnails -) : GenericRepository(database) -{ - /// - public override async Task> Search( - string query, - Include? include = default - ) - { - return await AddIncludes(Database.Movies, include) - .Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%")) - .Take(20) - .ToListAsync(); - } - - /// - public override Task Create(Movie obj) - { - try - { - return base.Create(obj); - } - catch (DuplicatedItemException ex) - when (ex.Existing is Movie existing - && existing.Slug == obj.Slug - && obj.AirDate is not null - && existing.AirDate?.Year != obj.AirDate?.Year - ) - { - obj.Slug = $"{obj.Slug}-{obj.AirDate!.Value.Year}"; - return base.Create(obj); - } - } - - /// - protected override async Task Validate(Movie resource) - { - await base.Validate(resource); - if (resource.Studio != null) - { - resource.StudioId = (await studios.CreateIfNotExists(resource.Studio)).Id; - resource.Studio = null; - } - resource.NextMetadataRefresh ??= IRefreshable.ComputeNextRefreshDate( - resource.AirDate ?? DateOnly.FromDateTime(resource.AddedDate) - ); - await thumbnails.DownloadImages(resource); - } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/NewsRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/NewsRepository.cs deleted file mode 100644 index c91c2eed..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/NewsRepository.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.IO; -using Kyoo.Abstractions.Models; - -namespace Kyoo.Core.Controllers; - -/// -/// A local repository to handle shows -/// -public class NewsRepository : DapperRepository -{ - // language=PostgreSQL - protected override FormattableString Sql => - $""" - select - e.*, -- Episode as e - m.* - /* includes */ - from - episodes as e - full outer join ( - select - * -- Movie - from - movies - ) as m on false - """; - - protected override Dictionary Config => - new() { { "e", typeof(Episode) }, { "m", typeof(Movie) }, }; - - protected override INews Mapper(IList items) - { - if (items[0] is Episode episode && episode.Id != Guid.Empty) - return episode; - if (items[1] is Movie movie && movie.Id != Guid.Empty) - return movie; - throw new InvalidDataException(); - } - - public NewsRepository(DbConnection database, SqlVariableContext context) - : base(database, context) { } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/RepositoryHelper.cs b/back/src/Kyoo.Core/Controllers/Repositories/RepositoryHelper.cs deleted file mode 100644 index cebb0c9d..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/RepositoryHelper.cs +++ /dev/null @@ -1,128 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Utils; - -namespace Kyoo.Core; - -public class RepositoryHelper -{ - private record SortIndicator(string Key, bool Desc, string? Seed); - - /// - /// Create a filter (where) expression on the query to skip everything before/after the referenceID. - /// The generalized expression for this in pseudocode is: - /// (x > a) OR - /// (x = a AND y > b) OR - /// (x = a AND y = b AND z > c) OR... - /// - /// Of course, this will be a bit more complex when ASC and DESC are mixed. - /// Assume x is ASC, y is DESC, and z is ASC: - /// (x > a) OR - /// (x = a AND y < b) OR - /// (x = a AND y = b AND z > c) OR... - /// - /// How items are sorted in the query - /// The reference item (the AfterID query) - /// True if the following page should be returned, false for the previous. - /// The type to paginate for. - /// An expression ready to be added to a Where close of a sorted query to handle the AfterID - public static Filter? KeysetPaginate(Sort? sort, T reference, bool next = true) - where T : class, IResource, IQuery - { - sort ??= new Sort.Default(); - - static IEnumerable GetSortsBy(Sort sort) - { - return sort switch - { - Sort.Default(var value) => GetSortsBy(value), - Sort.By @sortBy - => new[] { new SortIndicator(sortBy.Key, sortBy.Desendant, null) }, - Sort.Conglomerate(var list) => list.SelectMany(GetSortsBy), - Sort.Random(var seed) - => new[] { new SortIndicator("random", false, seed.ToString()) }, - _ => Array.Empty(), - }; - } - - // Don't forget that every sorts must end with a ID sort (to differentiate equalities). - IEnumerable sorts = GetSortsBy(sort) - .Append(new SortIndicator("Id", false, null)); - - Filter? ret = null; - List previousSteps = new(); - // TODO: Add an outer query >= for perf - // PERF: See https://use-the-index-luke.com/sql/partial-results/fetch-next-page#sb-equivalent-logic - foreach ((string key, bool desc, string? seed) in sorts) - { - object? value = reference.GetType().GetProperty(key)?.GetValue(reference); - // Comparing a value with null always return false so we short opt < > comparisons with null. - if (key != "random" && value == null) - { - previousSteps.Add(new SortIndicator(key, desc, seed)); - continue; - } - - // Create all the equality statements for previous sorts. - Filter? equals = null; - foreach ((string pKey, bool pDesc, string? pSeed) in previousSteps) - { - Filter pEquals = - pSeed == null - ? new Filter.Eq( - pKey, - reference.GetType().GetProperty(pKey)?.GetValue(reference) - ) - : new Filter.CmpRandom("=", pSeed, reference.Id); - equals = Filter.And(equals, pEquals); - } - - bool greaterThan = desc ^ next; - Func> comparer = greaterThan - ? (prop, val) => new Filter.Gt(prop, val) - : (prop, val) => new Filter.Lt(prop, val); - Filter last = - seed == null - ? comparer(key, value!) - : new Filter.CmpRandom(greaterThan ? ">" : "<", seed, reference.Id); - - if (key != "random") - { - Type[] types = - typeof(T).GetCustomAttribute()?.Types ?? new[] { typeof(T) }; - PropertyInfo property = types - .Select(x => x.GetProperty(key)!) - .First(x => x != null); - if (Nullable.GetUnderlyingType(property.PropertyType) != null) - last = new Filter.Or(last, new Filter.Eq(key, null)); - } - - ret = Filter.Or(ret, Filter.And(equals, last)); - previousSteps.Add(new SortIndicator(key, desc, seed)); - } - return ret; - } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs deleted file mode 100644 index a7f8233a..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/SeasonRepository.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; - -namespace Kyoo.Core.Controllers; - -public class SeasonRepository( - DatabaseContext database, - IRepository shows, - IThumbnailsManager thumbnails -) : GenericRepository(database) -{ - static SeasonRepository() - { - // Edit seasons slugs when the show's slug changes. - IRepository.OnEdited += async (show) => - { - await using AsyncServiceScope scope = CoreModule.Services.CreateAsyncScope(); - DatabaseContext database = scope.ServiceProvider.GetRequiredService(); - List seasons = await database - .Seasons.AsTracking() - .Where(x => x.ShowId == show.Id) - .ToListAsync(); - foreach (Season season in seasons) - { - season.ShowSlug = show.Slug; - await database.SaveChangesAsync(); - await IRepository.OnResourceEdited(season); - } - }; - } - - /// - public override async Task> Search( - string query, - Include? include = default - ) - { - return await AddIncludes(Database.Seasons, include) - .Where(x => EF.Functions.ILike(x.Name!, $"%{query}%")) - .Take(20) - .ToListAsync(); - } - - /// - protected override async Task Validate(Season resource) - { - await base.Validate(resource); - resource.Show = null; - if (resource.ShowId == Guid.Empty) - throw new ValidationException("Missing show id"); - // This is storred in db so it needs to be set before every create/edit (and before events) - resource.ShowSlug = (await shows.Get(resource.ShowId)).Slug; - resource.NextMetadataRefresh ??= IRefreshable.ComputeNextRefreshDate( - resource.StartDate ?? DateOnly.FromDateTime(resource.AddedDate) - ); - await thumbnails.DownloadImages(resource); - } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs deleted file mode 100644 index 51c9f76c..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/ShowRepository.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Exceptions; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; - -namespace Kyoo.Core.Controllers; - -public class ShowRepository( - DatabaseContext database, - IRepository studios, - IThumbnailsManager thumbnails -) : GenericRepository(database) -{ - /// - public override async Task> Search( - string query, - Include? include = default - ) - { - return await AddIncludes(Database.Shows, include) - .Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%")) - .Take(20) - .ToListAsync(); - } - - /// - public override Task Create(Show obj) - { - try - { - return base.Create(obj); - } - catch (DuplicatedItemException ex) - when (ex.Existing is Show existing - && existing.Slug == obj.Slug - && obj.StartAir is not null - && existing.StartAir?.Year != obj.StartAir?.Year - ) - { - obj.Slug = $"{obj.Slug}-{obj.AirDate!.Value.Year}"; - return base.Create(obj); - } - } - - /// - protected override async Task Validate(Show resource) - { - await base.Validate(resource); - if (resource.Studio != null) - { - resource.StudioId = (await studios.CreateIfNotExists(resource.Studio)).Id; - resource.Studio = null; - } - resource.NextMetadataRefresh ??= IRefreshable.ComputeNextRefreshDate( - resource.StartAir ?? DateOnly.FromDateTime(resource.AddedDate) - ); - await thumbnails.DownloadImages(resource); - } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/StudioRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/StudioRepository.cs deleted file mode 100644 index 91aba67f..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/StudioRepository.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; - -namespace Kyoo.Core.Controllers; - -/// -/// A local repository to handle studios -/// -public class StudioRepository(DatabaseContext database) : GenericRepository(database) -{ - /// - public override async Task> Search( - string query, - Include? include = default - ) - { - return await AddIncludes(Database.Studios, include) - .Where(x => EF.Functions.ILike(x.Name, $"%{query}%")) - .Take(20) - .ToListAsync(); - } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs deleted file mode 100644 index c98d8eb0..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/UserRepository.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Authentication.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; - -namespace Kyoo.Core.Controllers; - -/// -/// A repository for users. -/// -/// -/// Create a new -/// -public class UserRepository( - DatabaseContext database, - DbConnection db, - SqlVariableContext context, - PermissionOption options -) : GenericRepository(database), IUserRepository -{ - /// - public override async Task> Search( - string query, - Include? include = default - ) - { - return await AddIncludes(Database.Users, include) - .Where(x => EF.Functions.ILike(x.Username, $"%{query}%")) - .Take(20) - .ToListAsync(); - } - - /// - public override async Task Create(User obj) - { - // If no users exists, the new one will be an admin. Give it every permissions. - if (!await Database.Users.AnyAsync()) - obj.Permissions = PermissionOption.Admin; - else if (!options.RequireVerification) - obj.Permissions = options.NewUser; - else - obj.Permissions = Array.Empty(); - - return await base.Create(obj); - } - - public Task GetByExternalId(string provider, string id) - { - // language=PostgreSQL - return db.QuerySingle( - $""" - select - u.* -- User as u - /* includes */ - from - users as u - where - u.external_id->{provider}->>'Id' = {id} - """, - new() { ["u"] = typeof(User) }, - (items) => (items[0] as User)!, - context, - null, - null, - null - ); - } - - public async Task AddExternalToken(Guid userId, string provider, ExternalToken token) - { - User user = await GetWithTracking(userId); - user.ExternalId[provider] = token; - // without that, the change tracker does not find the modification. /shrug - Database.Entry(user).Property(x => x.ExternalId).IsModified = true; - await Database.SaveChangesAsync(); - return user; - } - - public async Task DeleteExternalToken(Guid userId, string provider) - { - User user = await GetWithTracking(userId); - user.ExternalId.Remove(provider); - // without that, the change tracker does not find the modification. /shrug - Database.Entry(user).Property(x => x.ExternalId).IsModified = true; - await Database.SaveChangesAsync(); - return user; - } -} diff --git a/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs deleted file mode 100644 index 3159cbfb..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs +++ /dev/null @@ -1,564 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Data.Common; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Exceptions; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; - -namespace Kyoo.Core.Controllers; - -public class WatchStatusRepository( - DatabaseContext database, - IRepository movies, - IRepository shows, - IRepository episodes, - IRepository users, - DbConnection db, - SqlVariableContext context -) : IWatchStatusRepository -{ - /// - /// If the watch percent is below this value, don't consider the item started. - /// - public const int MinWatchPercent = 5; - - /// - /// If the watch percent is higher than this value, consider the item completed. - /// - /// - /// This value is lower to account credits in movies that can last really long. - /// - public const int MaxWatchPercent = 90; - - // Those two are defined here because x => WatchingStatus.Watching complies to x => 1 - // but x => Watching compiles to x => Convert.ToInt(WatchingStatus.Watching) - // The second one can be converted to sql wherase the first can't (tries to compare WatchStatus with int). - private WatchStatus Watching = WatchStatus.Watching; - private WatchStatus Completed = WatchStatus.Completed; - private WatchStatus Planned = WatchStatus.Planned; - - static WatchStatusRepository() - { - IRepository.OnCreated += async (ep) => - { - await using AsyncServiceScope scope = CoreModule.Services.CreateAsyncScope(); - DatabaseContext db = scope.ServiceProvider.GetRequiredService(); - WatchStatusRepository repo = - scope.ServiceProvider.GetRequiredService(); - List users = await db - .ShowWatchStatus.IgnoreQueryFilters() - .Where(x => x.ShowId == ep.ShowId && x.Status == WatchStatus.Completed) - .Select(x => x.UserId) - .ToListAsync(); - foreach (Guid userId in users) - await repo._SetShowStatus( - ep.ShowId, - userId, - WatchStatus.Watching, - newEpisode: true - ); - }; - } - - // language=PostgreSQL - protected FormattableString Sql => - $""" - select - s.*, - swe.*, -- Episode as swe - m.* - /* includes */ - from ( - select - s.*, -- Show as s - sw.*, - sw.added_date as order, - sw.status as watch_status - from - shows as s - inner join show_watch_status as sw on sw.show_id = s.id - and sw.user_id = [current_user]) as s - full outer join ( - select - m.*, -- Movie as m - mw.*, - mw.added_date as order, - mw.status as watch_status - from - movies as m - inner join movie_watch_status as mw on mw.movie_id = m.id - and mw.user_id = [current_user]) as m on false - left join episodes as swe on swe.id = s.next_episode_id - /* includesJoin */ - where - (coalesce(s.watch_status, m.watch_status) = 'watching'::watch_status - or coalesce(s.watch_status, m.watch_status) = 'planned'::watch_status) - /* where */ - order by - coalesce(s.order, m.order) desc, - coalesce(s.id, m.id) asc - """; - - protected Dictionary Config => - new() - { - { "s", typeof(Show) }, - { "_sw", typeof(ShowWatchStatus) }, - { "_swe", typeof(Episode) }, - { "m", typeof(Movie) }, - { "_mw", typeof(MovieWatchStatus) }, - }; - - protected IWatchlist Mapper(IList items) - { - if (items[0] is Show show && show.Id != Guid.Empty) - { - show.WatchStatus = items[1] as ShowWatchStatus; - if (show.WatchStatus != null) - show.WatchStatus.NextEpisode = items[2] as Episode; - return show; - } - if (items[3] is Movie movie && movie.Id != Guid.Empty) - { - movie.WatchStatus = items[4] as MovieWatchStatus; - return movie; - } - throw new InvalidDataException(); - } - - /// - public virtual async Task Get(Guid id, Include? include = default) - { - IWatchlist? ret = await GetOrDefault(id, include); - if (ret == null) - throw new ItemNotFoundException($"No {nameof(IWatchlist)} found with the id {id}"); - return ret; - } - - /// - public Task GetOrDefault(Guid id, Include? include = null) - { - return db.QuerySingle( - Sql, - Config, - Mapper, - context, - include, - new Filter.Eq(nameof(IResource.Id), id) - ); - } - - /// - public async Task> GetAll( - Filter? filter = default, - Include? include = default, - Pagination? limit = default - ) - { - if (include != null) - include.Metadatas = include - .Metadatas.Where(x => x.Name != nameof(Show.WatchStatus)) - .ToList(); - - // We can't use the generic after id hanler since the sort depends on a relation. - if (limit?.AfterID != null) - { - dynamic cursor = await Get(limit.AfterID.Value); - filter = Filter.And( - filter, - Filter.Or( - new Filter.Lt("order", cursor.WatchStatus.AddedDate), - Filter.And( - new Filter.Eq("order", cursor.WatchStatus.AddedDate), - new Filter.Gt("Id", cursor.Id) - ) - ) - ); - limit.AfterID = null; - } - - return await db.Query( - Sql, - Config, - Mapper, - (id) => Get(id), - context, - include, - filter, - null, - limit ?? new() - ); - } - - /// - public Task GetMovieStatus(Guid movieId, Guid userId) - { - return database.MovieWatchStatus.FirstOrDefaultAsync(x => - x.MovieId == movieId && x.UserId == userId - ); - } - - /// - public async Task SetMovieStatus( - Guid movieId, - Guid userId, - WatchStatus status, - int? watchedTime, - int? percent - ) - { - Movie movie = await movies.Get(movieId); - - if (percent == null && watchedTime != null && movie.Runtime > 0) - percent = (int)Math.Round(watchedTime.Value / (movie.Runtime.Value * 60f) * 100f); - - if (percent < MinWatchPercent) - return null; - if (percent > MaxWatchPercent) - { - status = WatchStatus.Completed; - watchedTime = null; - percent = null; - } - - if (watchedTime.HasValue && status != WatchStatus.Watching) - throw new ValidationException( - "Can't have a watched time if the status is not watching." - ); - - if (watchedTime.HasValue != percent.HasValue) - throw new ValidationException( - "Can't specify watched time without specifing percent (or vise-versa)." - + "Percent could not be guessed since duration is unknown." - ); - - MovieWatchStatus ret = - new() - { - UserId = userId, - MovieId = movieId, - Status = status, - WatchedTime = watchedTime, - WatchedPercent = percent, - AddedDate = DateTime.UtcNow, - PlayedDate = status == WatchStatus.Completed ? DateTime.UtcNow : null, - }; - await database - .MovieWatchStatus.Upsert(ret) - .UpdateIf(x => status != Watching || x.Status != Completed) - .RunAsync(); - await IWatchStatusRepository.OnMovieStatusChanged( - new() - { - User = await users.Get(ret.UserId), - Resource = await movies.Get(ret.MovieId), - Status = ret.Status, - WatchedTime = ret.WatchedTime, - WatchedPercent = ret.WatchedPercent, - AddedDate = ret.AddedDate, - PlayedDate = ret.PlayedDate, - } - ); - return ret; - } - - /// - public async Task DeleteMovieStatus(Guid movieId, Guid userId) - { - await database - .MovieWatchStatus.Where(x => x.MovieId == movieId && x.UserId == userId) - .ExecuteDeleteAsync(); - await IWatchStatusRepository.OnMovieStatusChanged( - new() - { - User = await users.Get(userId), - Resource = await movies.Get(movieId), - AddedDate = DateTime.UtcNow, - Status = WatchStatus.Deleted, - } - ); - } - - /// - public Task GetShowStatus(Guid showId, Guid userId) - { - return database.ShowWatchStatus.FirstOrDefaultAsync(x => - x.ShowId == showId && x.UserId == userId - ); - } - - /// - public Task SetShowStatus(Guid showId, Guid userId, WatchStatus status) => - _SetShowStatus(showId, userId, status); - - private async Task _SetShowStatus( - Guid showId, - Guid userId, - WatchStatus status, - bool newEpisode = false, - bool skipStatusUpdate = false - ) - { - int unseenEpisodeCount = - status != WatchStatus.Completed - ? await database - .Episodes.Where(x => x.ShowId == showId) - .Where(x => - x.Watched!.First(x => x.UserId == userId)!.Status != WatchStatus.Completed - ) - .CountAsync() - : 0; - if (unseenEpisodeCount == 0) - status = WatchStatus.Completed; - - EpisodeWatchStatus? cursorWatchStatus = null; - Guid? nextEpisodeId = null; - if (status == WatchStatus.Watching) - { - var cursor = await database - .Episodes.IgnoreQueryFilters() - .Where(x => x.ShowId == showId) - .OrderByDescending(x => x.AbsoluteNumber) - .ThenByDescending(x => x.SeasonNumber) - .ThenByDescending(x => x.EpisodeNumber) - .Select(x => new - { - x.Id, - x.AbsoluteNumber, - x.SeasonNumber, - x.EpisodeNumber, - Status = x.Watched!.First(x => x.UserId == userId) - }) - .FirstOrDefaultAsync(x => - x.Status.Status == WatchStatus.Completed - || x.Status.Status == WatchStatus.Watching - ); - cursorWatchStatus = cursor?.Status; - nextEpisodeId = - cursor?.Status.Status == WatchStatus.Watching - ? cursor.Id - : await database - .Episodes.IgnoreQueryFilters() - .Where(x => x.ShowId == showId) - .OrderBy(x => x.AbsoluteNumber) - .ThenBy(x => x.SeasonNumber) - .ThenBy(x => x.EpisodeNumber) - .Where(x => - cursor == null - || x.AbsoluteNumber > cursor.AbsoluteNumber - || x.SeasonNumber > cursor.SeasonNumber - || ( - x.SeasonNumber == cursor.SeasonNumber - && x.EpisodeNumber > cursor.EpisodeNumber - ) - ) - .Select(x => new - { - x.Id, - Status = x.Watched!.FirstOrDefault(x => x.UserId == userId) - }) - .Where(x => x.Status == null || x.Status.Status != WatchStatus.Completed) - // The as Guid? is here to add the nullability status of the queryable. - // Without this, FirstOrDefault returns new Guid() when no result is found (which is 16 0s and invalid in sql). - .Select(x => x.Id as Guid?) - .FirstOrDefaultAsync(); - } - else if (status == WatchStatus.Completed) - { - List episodes = await database - .Episodes.Where(x => x.ShowId == showId) - .Select(x => x.Id) - .ToListAsync(); - await database - .EpisodeWatchStatus.UpsertRange( - episodes.Select(episodeId => new EpisodeWatchStatus - { - UserId = userId, - EpisodeId = episodeId, - Status = WatchStatus.Completed, - AddedDate = DateTime.UtcNow, - PlayedDate = DateTime.UtcNow - }) - ) - .UpdateIf(x => x.Status == Watching || x.Status == Planned) - .RunAsync(); - } - - ShowWatchStatus ret = - new() - { - UserId = userId, - ShowId = showId, - Status = status, - AddedDate = DateTime.UtcNow, - NextEpisodeId = nextEpisodeId, - WatchedTime = - cursorWatchStatus?.Status == WatchStatus.Watching - ? cursorWatchStatus.WatchedTime - : null, - WatchedPercent = - cursorWatchStatus?.Status == WatchStatus.Watching - ? cursorWatchStatus.WatchedPercent - : null, - UnseenEpisodesCount = unseenEpisodeCount, - PlayedDate = status == WatchStatus.Completed ? DateTime.UtcNow : null, - }; - await database - .ShowWatchStatus.Upsert(ret) - .UpdateIf(x => status != Watching || x.Status != Completed || newEpisode) - .RunAsync(); - if (!skipStatusUpdate) - { - await IWatchStatusRepository.OnShowStatusChanged( - new() - { - User = await users.Get(ret.UserId), - Resource = await shows.Get(ret.ShowId), - Status = ret.Status, - WatchedTime = ret.WatchedTime, - WatchedPercent = ret.WatchedPercent, - AddedDate = ret.AddedDate, - PlayedDate = ret.PlayedDate, - } - ); - } - return ret; - } - - /// - public async Task DeleteShowStatus(Guid showId, Guid userId) - { - await database - .ShowWatchStatus.IgnoreAutoIncludes() - .Where(x => x.ShowId == showId && x.UserId == userId) - .ExecuteDeleteAsync(); - await database - .EpisodeWatchStatus.Where(x => x.Episode.ShowId == showId && x.UserId == userId) - .ExecuteDeleteAsync(); - await IWatchStatusRepository.OnShowStatusChanged( - new() - { - User = await users.Get(userId), - Resource = await shows.Get(showId), - AddedDate = DateTime.UtcNow, - Status = WatchStatus.Deleted, - } - ); - } - - /// - public Task GetEpisodeStatus(Guid episodeId, Guid userId) - { - return database.EpisodeWatchStatus.FirstOrDefaultAsync(x => - x.EpisodeId == episodeId && x.UserId == userId - ); - } - - /// - public async Task SetEpisodeStatus( - Guid episodeId, - Guid userId, - WatchStatus status, - int? watchedTime, - int? percent - ) - { - Episode episode = await episodes.Get(episodeId); - - if (percent == null && watchedTime != null && episode.Runtime > 0) - percent = (int)Math.Round(watchedTime.Value / (episode.Runtime.Value * 60f) * 100f); - - if (percent < MinWatchPercent) - return null; - if (percent > MaxWatchPercent) - { - status = WatchStatus.Completed; - watchedTime = null; - percent = null; - } - - if (watchedTime.HasValue && status != WatchStatus.Watching) - throw new ValidationException( - "Can't have a watched time if the status is not watching." - ); - - if (watchedTime.HasValue != percent.HasValue) - throw new ValidationException( - "Can't specify watched time without specifing percent (or vise-versa)." - + "Percent could not be guessed since duration is unknown." - ); - - EpisodeWatchStatus ret = - new() - { - UserId = userId, - EpisodeId = episodeId, - Status = status, - WatchedTime = watchedTime, - WatchedPercent = percent, - AddedDate = DateTime.UtcNow, - PlayedDate = status == WatchStatus.Completed ? DateTime.UtcNow : null, - }; - await database - .EpisodeWatchStatus.Upsert(ret) - .UpdateIf(x => status != Watching || x.Status != Completed) - .RunAsync(); - await IWatchStatusRepository.OnEpisodeStatusChanged( - new() - { - User = await users.Get(ret.UserId), - Resource = await episodes.Get(episodeId, new(nameof(Episode.Show))), - Status = ret.Status, - WatchedTime = ret.WatchedTime, - WatchedPercent = ret.WatchedPercent, - AddedDate = ret.AddedDate, - PlayedDate = ret.PlayedDate, - } - ); - await _SetShowStatus(episode.ShowId, userId, WatchStatus.Watching, skipStatusUpdate: true); - return ret; - } - - /// - public async Task DeleteEpisodeStatus(Guid episodeId, Guid userId) - { - await database - .EpisodeWatchStatus.Where(x => x.EpisodeId == episodeId && x.UserId == userId) - .ExecuteDeleteAsync(); - await IWatchStatusRepository.OnEpisodeStatusChanged( - new() - { - User = await users.Get(userId), - Resource = await episodes.Get(episodeId, new(nameof(Episode.Show))), - AddedDate = DateTime.UtcNow, - Status = WatchStatus.Deleted, - } - ); - } -} diff --git a/back/src/Kyoo.Core/Controllers/ThumbnailsManager.cs b/back/src/Kyoo.Core/Controllers/ThumbnailsManager.cs deleted file mode 100644 index 211452ed..00000000 --- a/back/src/Kyoo.Core/Controllers/ThumbnailsManager.cs +++ /dev/null @@ -1,219 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; -using Blurhash.SkiaSharp; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Exceptions; -using Kyoo.Core.Storage; -using Microsoft.Extensions.Logging; -using SkiaSharp; -using SKSvg = SkiaSharp.Extended.Svg.SKSvg; - -namespace Kyoo.Core.Controllers; - -/// -/// Download images and retrieve the path of those images for a resource. -/// -public class ThumbnailsManager( - IHttpClientFactory clientFactory, - ILogger logger, - IStorage storage, - Lazy> users -) : IThumbnailsManager -{ - private async Task _SaveImage(SKBitmap bitmap, string path, int quality) - { - SKData data = bitmap.Encode(SKEncodedImageFormat.Webp, quality); - await using Stream reader = data.AsStream(); - await storage.Write(reader, path); - } - - private SKBitmap _SKBitmapFrom(Stream reader, bool isSvg) - { - if (isSvg) - { - SKSvg svg = new(); - svg.Load(reader); - SKBitmap bitmap = new((int)svg.CanvasSize.Width, (int)svg.CanvasSize.Height); - using SKCanvas canvas = new(bitmap); - canvas.DrawPicture(svg.Picture); - return bitmap; - } - - using SKCodec codec = SKCodec.Create(reader); - if (codec == null) - throw new NotSupportedException("Unsupported codec"); - - SKImageInfo info = codec.Info; - info.ColorType = SKColorType.Rgba8888; - return SKBitmap.Decode(codec, info); - } - - public async Task DownloadImage(Image? image, string what) - { - if (image == null) - return; - try - { - if (image.Id == Guid.Empty) - { - // Ensure stable ids to prevent duplicated images being stored on the fs. - using MD5 md5 = MD5.Create(); - image.Id = new Guid(md5.ComputeHash(Encoding.UTF8.GetBytes(image.Source))); - } - - logger.LogInformation("Downloading image {What}", what); - - HttpClient client = clientFactory.CreateClient(); - HttpResponseMessage response = await client.GetAsync(image.Source); - response.EnsureSuccessStatusCode(); - await using Stream reader = await response.Content.ReadAsStreamAsync(); - using SKBitmap original = _SKBitmapFrom( - reader, - isSvg: response.Content.Headers.ContentType?.MediaType?.Contains("svg") == true - ); - - using SKBitmap high = original.Resize( - new SKSizeI(original.Width, original.Height), - SKFilterQuality.High - ); - await _SaveImage(original, _GetImagePath(image.Id, ImageQuality.High), 90); - - using SKBitmap medium = high.Resize( - new SKSizeI((int)(high.Width / 1.5), (int)(high.Height / 1.5)), - SKFilterQuality.Medium - ); - await _SaveImage(medium, _GetImagePath(image.Id, ImageQuality.Medium), 75); - - using SKBitmap low = medium.Resize( - new SKSizeI(original.Width / 2, original.Height / 2), - SKFilterQuality.Low - ); - await _SaveImage(low, _GetImagePath(image.Id, ImageQuality.Low), 50); - - image.Blurhash = Blurhasher.Encode(low, 4, 3); - } - catch (Exception ex) - { - logger.LogError(ex, "{What} could not be downloaded", what); - } - } - - /// - public async Task DownloadImages(T item) - where T : IThumbnails - { - string name = item is IResource res ? res.Slug : "???"; - - await DownloadImage(item.Poster, $"The poster of {name}"); - await DownloadImage(item.Thumbnail, $"The thumbnail of {name}"); - await DownloadImage(item.Logo, $"The logo of {name}"); - } - - public async Task IsImageSaved(Guid imageId, ImageQuality quality) => - await storage.DoesExist(_GetImagePath(imageId, quality)); - - public async Task GetImage(Guid imageId, ImageQuality quality) - { - string path = _GetImagePath(imageId, quality); - if (await storage.DoesExist(path)) - return await storage.Read(path); - - throw new ItemNotFoundException(); - } - - /// - private string _GetImagePath(Guid imageId, ImageQuality quality) - { - return $"/metadata/{imageId}.{quality.ToString().ToLowerInvariant()}.webp"; - } - - /// - public Task DeleteImages(T item) - where T : IThumbnails - { - var imageDeletionTasks = new[] { item.Poster?.Id, item.Thumbnail?.Id, item.Logo?.Id } - .Where(x => x is not null) - .SelectMany(x => $"/metadata/{x}") - .SelectMany(x => - new[] - { - ImageQuality.High.ToString().ToLowerInvariant(), - ImageQuality.Medium.ToString().ToLowerInvariant(), - ImageQuality.Low.ToString().ToLowerInvariant(), - }.Select(quality => $"{x}.{quality}.webp") - ) - .Select(storage.Delete); - - return Task.WhenAll(imageDeletionTasks); - } - - public async Task GetUserImage(Guid userId) - { - var filePath = $"/metadata/user/{userId}.webp"; - if (await storage.DoesExist(filePath)) - return await storage.Read(filePath); - - User user = await users.Value.Get(userId); - if (user.Email == null) - throw new ItemNotFoundException(); - using MD5 md5 = MD5.Create(); - string hash = Convert - .ToHexString(md5.ComputeHash(Encoding.ASCII.GetBytes(user.Email))) - .ToLower(); - try - { - HttpClient client = clientFactory.CreateClient(); - HttpResponseMessage response = await client.GetAsync( - $"https://www.gravatar.com/avatar/{hash}.jpg?d=404&s=250" - ); - response.EnsureSuccessStatusCode(); - return await response.Content.ReadAsStreamAsync(); - } - catch - { - throw new ItemNotFoundException(); - } - } - - public async Task SetUserImage(Guid userId, Stream? image) - { - var filePath = $"/metadata/user/{userId}.webp"; - if (image == null) - { - if (await storage.DoesExist(filePath)) - await storage.Delete(filePath); - return; - } - - using SKCodec codec = SKCodec.Create(image); - SKImageInfo info = codec.Info; - info.ColorType = SKColorType.Rgba8888; - using SKBitmap original = SKBitmap.Decode(codec, info); - using SKBitmap ret = original.Resize(new SKSizeI(250, 250), SKFilterQuality.High); - await _SaveImage(ret, filePath, 75); - } -} diff --git a/back/src/Kyoo.Core/CoreModule.cs b/back/src/Kyoo.Core/CoreModule.cs deleted file mode 100644 index a8d61b96..00000000 --- a/back/src/Kyoo.Core/CoreModule.cs +++ /dev/null @@ -1,94 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Linq; -using Amazon.S3; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Core.Controllers; -using Kyoo.Core.Storage; -using Kyoo.Postgresql; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace Kyoo.Core; - -public static class CoreModule -{ - /// - /// A service provider to access services in static context (in events for example). - /// - /// Don't forget to create a scope. - public static IServiceProvider Services { get; set; } - - public static void AddRepository(this IServiceCollection services) - where T : IResource - where TRepo : class, IRepository - { - services.AddScoped(); - services.AddScoped>(x => x.GetRequiredService()); - services.AddScoped>>(x => new(() => x.GetRequiredService())); - } - - public static void ConfigureKyoo(this WebApplicationBuilder builder) - { - builder._AddStorage(); - - builder.Services.AddScoped(); - builder.Services.AddScoped(); - - builder.Services.AddRepository(); - builder.Services.AddRepository(); - builder.Services.AddRepository(); - builder.Services.AddRepository(); - builder.Services.AddRepository(); - builder.Services.AddRepository(); - builder.Services.AddRepository(); - builder.Services.AddRepository(); - builder.Services.AddRepository(); - builder.Services.AddScoped(x => x.GetRequiredService()); - builder.Services.AddScoped(); - builder.Services.AddScoped(x => - x.GetRequiredService() - ); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - } - - private static void _AddStorage(this WebApplicationBuilder builder) - { - var shouldUseS3 = !string.IsNullOrEmpty( - builder.Configuration.GetValue(S3Storage.S3BucketEnvironmentVariable) - ); - - if (!shouldUseS3) - { - builder.Services.AddScoped(); - return; - } - - // Configuration (credentials, endpoint, etc.) are done via standard AWS env vars - // See https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-netcore.html - builder.Services.AddDefaultAWSOptions(builder.Configuration.GetAWSOptions()); - builder.Services.AddAWSService(); - builder.Services.AddScoped(); - } -} diff --git a/back/src/Kyoo.Core/ExceptionFilter.cs b/back/src/Kyoo.Core/ExceptionFilter.cs deleted file mode 100644 index 77ea1027..00000000 --- a/back/src/Kyoo.Core/ExceptionFilter.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.ComponentModel.DataAnnotations; -using Kyoo.Abstractions.Models.Exceptions; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Logging; - -namespace Kyoo.Core; - -/// -/// A middleware to handle errors globally. -/// -/// -/// Initializes a new instance of the class. -/// -/// The logger used to log errors. -public class ExceptionFilter(ILogger logger) : IExceptionFilter -{ - /// - public void OnException(ExceptionContext context) - { - switch (context.Exception) - { - case ValidationException ex: - context.Result = new BadRequestObjectResult(new RequestError(ex.Message)); - break; - case ItemNotFoundException ex: - context.Result = new NotFoundObjectResult(new RequestError(ex.Message)); - break; - case DuplicatedItemException ex when ex.Existing is not null: - context.Result = new ConflictObjectResult(ex.Existing); - break; - case DuplicatedItemException: - // Should not happen but if it does, it is better than returning a 409 with no body since clients expect json content - context.Result = new ConflictObjectResult(new RequestError("Duplicated item")); - break; - case UnauthorizedException ex: - context.Result = new UnauthorizedObjectResult(new RequestError(ex.Message)); - break; - case Exception ex: - logger.LogError(ex, "Unhandled error"); - context.Result = new ServerErrorObjectResult( - new RequestError("Internal Server Error") - ); - break; - } - } - - /// - public class ServerErrorObjectResult : ObjectResult - { - /// - /// Initializes a new instance of the class. - /// - /// The object to return. - public ServerErrorObjectResult(object value) - : base(value) - { - StatusCode = StatusCodes.Status500InternalServerError; - } - } -} diff --git a/back/src/Kyoo.Core/Extensions/ServiceExtensions.cs b/back/src/Kyoo.Core/Extensions/ServiceExtensions.cs deleted file mode 100644 index 5dd49309..00000000 --- a/back/src/Kyoo.Core/Extensions/ServiceExtensions.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Linq; -using System.Text.Json; -using System.Text.Json.Serialization; -using AspNetCore.Proxy; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Authentication; -using Kyoo.Core.Api; -using Kyoo.Core.Controllers; -using Kyoo.Utils; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; - -namespace Kyoo.Core.Extensions; - -public static class ServiceExtensions -{ - public static void ConfigureMvc(this IServiceCollection services) - { - services.AddHttpContextAccessor(); - - services - .AddMvcCore(options => - { - options.Filters.Add(); - options.ModelBinderProviders.Insert(0, new SortBinder.Provider()); - options.ModelBinderProviders.Insert(0, new IncludeBinder.Provider()); - options.ModelBinderProviders.Insert(0, new FilterBinder.Provider()); - }) - .AddApplicationPart(typeof(CoreModule).Assembly) - .AddApplicationPart(typeof(AuthenticationModule).Assembly) - .AddJsonOptions(x => - { - x.JsonSerializerOptions.TypeInfoResolver = new JsonKindResolver() - { - Modifiers = { IncludeBinder.HandleLoadableFields } - }; - x.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); - x.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; - }) - .AddDataAnnotations() - .AddControllersAsServices() - .AddApiExplorer() - .ConfigureApiBehaviorOptions(options => - { - options.SuppressMapClientErrors = true; - options.InvalidModelStateResponseFactory = ctx => - { - string[] errors = ctx - .ModelState.SelectMany(x => x.Value!.Errors) - .Select(x => x.ErrorMessage) - .ToArray(); - return new BadRequestObjectResult(new RequestError(errors)); - }; - }); - - services.Configure(x => - { - x.ConstraintMap.Add("id", typeof(IdentifierRouteConstraint)); - x.ConstraintMap.Add("base64", typeof(Base64RouteConstraint)); - }); - - services.AddResponseCompression(x => - { - x.EnableForHttps = true; - }); - - services.AddProxies(); - services.AddHttpClient(); - } -} diff --git a/back/src/Kyoo.Core/Kyoo.Core.csproj b/back/src/Kyoo.Core/Kyoo.Core.csproj deleted file mode 100644 index bb7faf30..00000000 --- a/back/src/Kyoo.Core/Kyoo.Core.csproj +++ /dev/null @@ -1,44 +0,0 @@ - - - Kyoo.Core - Kyoo.Core - Exe - kyoo - - 50 - - - - - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - diff --git a/back/src/Kyoo.Core/Program.cs b/back/src/Kyoo.Core/Program.cs deleted file mode 100644 index 9233e8cb..00000000 --- a/back/src/Kyoo.Core/Program.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using Kyoo.Authentication; -using Kyoo.Core; -using Kyoo.Core.Controllers; -using Kyoo.Core.Extensions; -using Kyoo.Meiliseach; -using Kyoo.Postgresql; -using Kyoo.RabbitMq; -using Kyoo.Swagger; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Serilog; -using Serilog.Events; -using Serilog.Templates; -using Serilog.Templates.Themes; - -#if DEBUG -const string EnvironmentName = "Development"; -#else -const string EnvironmentName = "Production"; -#endif - -WebApplicationBuilder builder = WebApplication.CreateBuilder( - new WebApplicationOptions() - { - Args = args, - EnvironmentName = EnvironmentName, - ApplicationName = "Kyoo", - ContentRootPath = AppDomain.CurrentDomain.BaseDirectory, - } -); -builder.WebHost.UseKestrel(opt => -{ - opt.AddServerHeader = false; -}); - -const string template = - "[{@t:HH:mm:ss} {@l:u3} {Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1), 25} " - + "({@i:D10})] {@m}{#if not EndsWith(@m, '\n')}\n{#end}{@x}"; -Log.Logger = new LoggerConfiguration() - .MinimumLevel.Warning() - .MinimumLevel.Override("Kyoo", LogEventLevel.Verbose) - .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Verbose) - .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Fatal) - .WriteTo.Console(new ExpressionTemplate(template, theme: TemplateTheme.Code)) - .Enrich.WithThreadId() - .Enrich.FromLogContext() - .CreateLogger(); -AppDomain.CurrentDomain.ProcessExit += (_, _) => Log.CloseAndFlush(); -AppDomain.CurrentDomain.UnhandledException += (_, ex) => - Log.Fatal(ex.ExceptionObject as Exception, "Unhandled exception"); -builder.Host.UseSerilog(); - -builder.Services.ConfigureMvc(); -builder.Services.ConfigureOpenApi(); - -// configure postgres first to allow other services to depend on db config -builder.ConfigurePostgres(); -builder.ConfigureKyoo(); -builder.ConfigureAuthentication(); -builder.ConfigureMeilisearch(); -builder.ConfigureRabbitMq(); - -WebApplication app = builder.Build(); -CoreModule.Services = app.Services; - -app.UsePathBase(new PathString(builder.Configuration.GetValue("KYOO_PREFIX", ""))); -app.UseHsts(); -app.UseKyooOpenApi(); -app.UseResponseCompression(); -app.UseRouting(); -app.UseAuthentication(); -app.MapControllers(); - -// Activate services that always run in the background -app.Services.GetRequiredService(); -app.Services.GetRequiredService(); - -// Only run sync on the main instance -if (app.Configuration.GetValue("RUN_MIGRATIONS", true)) -{ - await using (AsyncServiceScope scope = app.Services.CreateAsyncScope()) - { - await MeilisearchModule.Initialize(app.Services); - } - - // The methods takes care of creating a scope and will download images on the background. - _ = MeilisearchModule.SyncDatabase(app.Services); - _ = MiscRepository.DownloadMissingImages(app.Services); -} - -await app.RunAsync(Environment.GetEnvironmentVariable("KYOO_BIND_URL") ?? "http://*:5000"); diff --git a/back/src/Kyoo.Core/Storage/FileStorage.cs b/back/src/Kyoo.Core/Storage/FileStorage.cs deleted file mode 100644 index 45687bd6..00000000 --- a/back/src/Kyoo.Core/Storage/FileStorage.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.IO; -using System.Threading.Tasks; - -namespace Kyoo.Core.Storage; - -/// -/// File-backed storage. -/// -public class FileStorage : IStorage -{ - public Task DoesExist(string path) => Task.FromResult(File.Exists(path)); - - public async Task Read(string path) => - await Task.FromResult(File.Open(path, FileMode.Open, FileAccess.Read)); - - public async Task Write(Stream reader, string path) - { - Directory.CreateDirectory( - Path.GetDirectoryName(path) ?? throw new InvalidOperationException() - ); - await using Stream file = File.Create(path); - await reader.CopyToAsync(file); - } - - public Task Delete(string path) - { - if (File.Exists(path)) - File.Delete(path); - - return Task.CompletedTask; - } -} diff --git a/back/src/Kyoo.Core/Storage/IStorage.cs b/back/src/Kyoo.Core/Storage/IStorage.cs deleted file mode 100644 index 2253661c..00000000 --- a/back/src/Kyoo.Core/Storage/IStorage.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.IO; -using System.Threading.Tasks; - -namespace Kyoo.Core.Storage; - -/// -/// Interface for storage operations. -/// -public interface IStorage -{ - Task DoesExist(string path); - Task Read(string path); - Task Write(Stream stream, string path); - Task Delete(string path); -} diff --git a/back/src/Kyoo.Core/Storage/S3Storage.cs b/back/src/Kyoo.Core/Storage/S3Storage.cs deleted file mode 100644 index 5c78c69d..00000000 --- a/back/src/Kyoo.Core/Storage/S3Storage.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.IO; -using System.Threading.Tasks; -using Amazon.S3; -using Microsoft.Extensions.Configuration; - -namespace Kyoo.Core.Storage; - -/// -/// S3-backed storage. -/// -public class S3Storage(IAmazonS3 s3Client, IConfiguration configuration) : IStorage -{ - public const string S3BucketEnvironmentVariable = "S3_BUCKET_NAME"; - - public Task DoesExist(string path) - { - return s3Client - .GetObjectMetadataAsync(_GetBucketName(), path) - .ContinueWith(t => - { - if (t.IsFaulted) - return false; - - return t.Result.HttpStatusCode == System.Net.HttpStatusCode.OK; - }); - } - - public async Task Read(string path) - { - var response = await s3Client.GetObjectAsync(_GetBucketName(), path); - return response.ResponseStream; - } - - public Task Write(Stream reader, string path) - { - return s3Client.PutObjectAsync( - new Amazon.S3.Model.PutObjectRequest - { - BucketName = _GetBucketName(), - Key = path, - InputStream = reader - } - ); - } - - public Task Delete(string path) - { - return s3Client.DeleteObjectAsync(_GetBucketName(), path); - } - - private string _GetBucketName() - { - var bucketName = configuration.GetValue(S3BucketEnvironmentVariable); - if (string.IsNullOrEmpty(bucketName)) - throw new InvalidOperationException("S3 bucket name is not configured."); - - return bucketName; - } -} diff --git a/back/src/Kyoo.Core/Views/Admin/Health.cs b/back/src/Kyoo.Core/Views/Admin/Health.cs deleted file mode 100644 index 3fa1d7aa..00000000 --- a/back/src/Kyoo.Core/Views/Admin/Health.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Threading.Tasks; -using Kyoo.Abstractions.Models.Attributes; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Diagnostics.HealthChecks; - -namespace Kyoo.Core.Api; - -/// -/// An API endpoint to check the health. -/// -[Route("health")] -[ApiController] -[ApiDefinition("Health")] -public class Health(HealthCheckService healthCheckService) : BaseApi -{ - /// - /// Check if the api is ready to accept requests. - /// - /// A status indicating the health of the api. - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)] - public async Task CheckHealth() - { - IHeaderDictionary headers = HttpContext.Response.Headers; - headers.CacheControl = "no-store, no-cache"; - headers.Pragma = "no-cache"; - headers.Expires = "Thu, 01 Jan 1970 00:00:00 GMT"; - - HealthReport result = await healthCheckService.CheckHealthAsync(); - return result.Status switch - { - HealthStatus.Healthy => Ok(new HealthResult("Healthy")), - HealthStatus.Unhealthy => Ok(new HealthResult("Unstable")), - HealthStatus.Degraded => StatusCode(StatusCodes.Status503ServiceUnavailable), - _ => StatusCode(StatusCodes.Status500InternalServerError), - }; - } - - /// - /// The result of a health operation. - /// - public record HealthResult(string Status); -} diff --git a/back/src/Kyoo.Core/Views/Admin/Misc.cs b/back/src/Kyoo.Core/Views/Admin/Misc.cs deleted file mode 100644 index edd53514..00000000 --- a/back/src/Kyoo.Core/Views/Admin/Misc.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Core.Controllers; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; - -namespace Kyoo.Core.Api; - -/// -/// Private APIs only used for other services. Can change at any time without notice. -/// -[ApiController] -[PartialPermission(nameof(Misc), Group = Group.Admin)] -public class Misc(MiscRepository repo) : BaseApi -{ - /// - /// List all registered paths. - /// - /// The list of paths known to Kyoo. - [HttpGet("/paths")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - public Task> GetAllPaths() - { - return repo.GetRegisteredPaths(); - } - - /// - /// Delete item at path. - /// - /// The path to delete. - /// - /// If true, the path will be considered as a directory and every children will be removed. - /// - /// Nothing - [HttpDelete("/paths")] - [PartialPermission(Kind.Delete)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task DeletePath( - [FromQuery] string path, - [FromQuery] bool recursive = false - ) - { - await repo.DeletePath(path, recursive); - return NoContent(); - } - - /// - /// Rescan library - /// - /// - /// Trigger a complete library rescan - /// - /// Nothing - [HttpPost("/rescan")] - [PartialPermission(Kind.Write)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task RescanLibrary([FromServices] IScanner scanner) - { - await scanner.SendRescanRequest(); - return NoContent(); - } - - /// - /// List items to refresh. - /// - /// The upper limit for the refresh date. - /// The items that should be refreshed before the given date - [HttpGet("/refreshables")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - public Task> GetAllPaths([FromQuery] DateTime? date) - { - return repo.GetRefreshableItems(date ?? DateTime.UtcNow); - } -} diff --git a/back/src/Kyoo.Core/Views/Content/ThumbnailsApi.cs b/back/src/Kyoo.Core/Views/Content/ThumbnailsApi.cs deleted file mode 100644 index 1693804d..00000000 --- a/back/src/Kyoo.Core/Views/Content/ThumbnailsApi.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.IO; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// Retrive images. -/// -[ApiController] -[Route("thumbnails")] -[Route("images", Order = AlternativeRoute)] -[Route("image", Order = AlternativeRoute)] -[Permission(nameof(Image), Kind.Read, Group = Group.Overall)] -[ApiDefinition("Images", Group = OtherGroup)] -public class ThumbnailsApi(IThumbnailsManager thumbs) : BaseApi -{ - /// - /// Get Image - /// - /// - /// Get an image from it's id. You can select a specefic quality. - /// - /// The ID of the image to retrive. - /// The quality of the image to retrieve. - /// The image asked. - /// - /// The image does not exists on kyoo. - /// - [HttpGet("{id:guid}")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetPoster(Guid id, [FromQuery] ImageQuality? quality) - { - quality ??= ImageQuality.High; - if (!await thumbs.IsImageSaved(id, quality.Value)) - return NotFound(); - - // Allow clients to cache the image for 6 month. - Response.Headers.CacheControl = $"public, max-age={60 * 60 * 24 * 31 * 6}"; - return File(await thumbs.GetImage(id, quality.Value), "image/webp", true); - } -} diff --git a/back/src/Kyoo.Core/Views/Content/VideoApi.cs b/back/src/Kyoo.Core/Views/Content/VideoApi.cs deleted file mode 100644 index aa1b9b0c..00000000 --- a/back/src/Kyoo.Core/Views/Content/VideoApi.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Threading.Tasks; -using AspNetCore.Proxy; -using AspNetCore.Proxy.Options; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// Private routes of the transcoder. -/// Url for these routes will be returned from /info or /master.m3u8 routes. -/// This should not be called manually -/// -[ApiController] -[Route("videos")] -[Route("video", Order = AlternativeRoute)] -[Permission("video", Kind.Read, Group = Group.Overall)] -[ApiDefinition("Video", Group = OtherGroup)] -public class VideoApi : Controller -{ - public static string TranscoderUrl = - Environment.GetEnvironmentVariable("TRANSCODER_URL") ?? "http://transcoder:7666"; - - private Task _Proxy(string route) - { - HttpProxyOptions proxyOptions = HttpProxyOptionsBuilder - .Instance.WithHandleFailure( - async (context, exception) => - { - context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; - await context.Response.WriteAsJsonAsync( - new RequestError("Service unavailable") - ); - } - ) - .Build(); - return this.HttpProxyAsync($"{TranscoderUrl}/{route}", proxyOptions); - } - - [HttpGet("{path:base64}/direct")] - [PartialPermission(Kind.Play)] - [ProducesResponseType(StatusCodes.Status206PartialContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetDirectStream(string path) - { - await _Proxy($"{path}/direct"); - } - - [HttpGet("{path:base64}/direct/{identifier}")] - [PartialPermission(Kind.Play)] - [ProducesResponseType(StatusCodes.Status206PartialContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetDirectStream(string path, string identifier) - { - await _Proxy($"{path}/direct/{identifier}"); - } - - [HttpGet("{path:base64}/master.m3u8")] - [PartialPermission(Kind.Play)] - [ProducesResponseType(StatusCodes.Status206PartialContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetMaster(string path) - { - await _Proxy($"{path}/master.m3u8"); - } - - [HttpGet("{path:base64}/{video:int}/{quality}/index.m3u8")] - [PartialPermission(Kind.Play)] - public async Task GetVideoIndex(string path, int video, string quality) - { - await _Proxy($"{path}/{video}/{quality}/index.m3u8"); - } - - [HttpGet("{path:base64}/{video:int}/{quality}/{segment}")] - [PartialPermission(Kind.Play)] - public async Task GetVideoSegment(string path, int video, string quality, string segment) - { - await _Proxy($"{path}/{video}/{quality}/{segment}"); - } - - [HttpGet("{path:base64}/audio/{audio}/index.m3u8")] - [PartialPermission(Kind.Play)] - public async Task GetAudioIndex(string path, string audio) - { - await _Proxy($"{path}/audio/{audio}/index.m3u8"); - } - - [HttpGet("{path:base64}/audio/{audio}/{segment}")] - [PartialPermission(Kind.Play)] - public async Task GetAudioSegment(string path, string audio, string segment) - { - await _Proxy($"{path}/audio/{audio}/{segment}"); - } - - [HttpGet("{path:base64}/attachment/{name}")] - [PartialPermission(Kind.Play)] - public async Task GetAttachment(string path, string name) - { - await _Proxy($"{path}/attachment/{name}"); - } - - [HttpGet("{path:base64}/subtitle/{name}")] - [PartialPermission(Kind.Play)] - public async Task GetSubtitle(string path, string name) - { - await _Proxy($"{path}/subtitle/{name}"); - } - - [HttpGet("{path:base64}/thumbnails.png")] - [PartialPermission(Kind.Read)] - public async Task GetThumbnails(string path) - { - await _Proxy($"{path}/thumbnails.png"); - } - - [HttpGet("{path:base64}/thumbnails.vtt")] - [PartialPermission(Kind.Read)] - public async Task GetThumbnailsVtt(string path) - { - await _Proxy($"{path}/thumbnails.vtt"); - } -} diff --git a/back/src/Kyoo.Core/Views/Helper/BaseApi.cs b/back/src/Kyoo.Core/Views/Helper/BaseApi.cs deleted file mode 100644 index 9d1c7fa7..00000000 --- a/back/src/Kyoo.Core/Views/Helper/BaseApi.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Utils; -using Microsoft.AspNetCore.Mvc; - -namespace Kyoo.Core.Api; - -/// -/// A common API containing custom methods to help inheritors. -/// -public abstract class BaseApi : ControllerBase -{ - /// - /// Construct and return a page from an api. - /// - /// The list of resources that should be included in the current page. - /// - /// The max number of items that should be present per page. This should be the same as in the request, - /// it is used to calculate if this is the last page and so on. - /// - /// The type of items on the page. - /// A Page representing the response. - protected Page Page(ICollection resources, int limit) - where TResult : IResource - { - Dictionary query = Request.Query.ToDictionary( - x => x.Key, - x => x.Value.ToString(), - StringComparer.InvariantCultureIgnoreCase - ); - - // If the query was sorted randomly, add the seed to the url to get reproducible links (next,prev,first...) - if (query.ContainsKey("sortBy")) - { - object seed = HttpContext.Items["seed"]!; - - query["sortBy"] = Regex.Replace(query["sortBy"], "random(?!:)", $"random:{seed}"); - } - return new Page(resources, Request.Path, query, limit); - } - - protected SearchPage SearchPage(SearchPage.SearchResult result) - where TResult : IResource - { - Dictionary query = Request.Query.ToDictionary( - x => x.Key, - x => x.Value.ToString(), - StringComparer.InvariantCultureIgnoreCase - ); - - string self = Request.Path + query.ToQueryString(); - string? previous = null; - string? next = null; - string first; - int limit = query.TryGetValue("limit", out string? limitStr) - ? int.Parse(limitStr) - : new SearchPagination().Limit; - int? skip = query.TryGetValue("skip", out string? skipStr) ? int.Parse(skipStr) : null; - - if (skip != null) - { - query["skip"] = Math.Max(0, skip.Value - limit).ToString(); - previous = Request.Path + query.ToQueryString(); - } - if (result.Items.Count == limit && limit > 0) - { - int newSkip = skip.HasValue ? skip.Value + limit : limit; - query["skip"] = newSkip.ToString(); - next = Request.Path + query.ToQueryString(); - } - - query.Remove("skip"); - first = Request.Path + query.ToQueryString(); - - return new SearchPage(result, self, previous, next, first); - } -} diff --git a/back/src/Kyoo.Core/Views/Helper/CrudApi.cs b/back/src/Kyoo.Core/Views/Helper/CrudApi.cs deleted file mode 100644 index 44e4447f..00000000 --- a/back/src/Kyoo.Core/Views/Helper/CrudApi.cs +++ /dev/null @@ -1,299 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Models; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; - -namespace Kyoo.Core.Api; - -/// -/// A base class to handle CRUD operations on a specific resource type . -/// -/// The type of resource to make CRUD apis for. -[ApiController] -public class CrudApi : BaseApi - where T : class, IResource, IQuery -{ - /// - /// The repository of the resource, used to retrieve, save and do operations on the baking store. - /// - protected IRepository Repository { get; } - - /// - /// Create a new using the given repository and base url. - /// - /// - /// The repository to use as a baking store for the type . - /// - public CrudApi(IRepository repository) - { - Repository = repository; - } - - /// - /// Get item - /// - /// - /// Get a specific resource via it's ID or it's slug. - /// - /// The ID or slug of the resource to retrieve. - /// The aditional fields to include in the result. - /// The retrieved resource. - /// A resource with the given ID or slug does not exist. - [HttpGet("{identifier:id}")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> Get(Identifier identifier, [FromQuery] Include? fields) - { - T? ret = await identifier.Match( - id => Repository.GetOrDefault(id, fields), - slug => Repository.GetOrDefault(slug, fields) - ); - if (ret == null) - return NotFound(); - return ret; - } - - /// - /// Get count - /// - /// - /// Get the number of resources that match the filters. - /// - /// A list of filters to respect. - /// How many resources matched that filter. - /// Invalid filters. - [HttpGet("count")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - public async Task> GetCount([FromQuery] Filter filter) - { - return await Repository.GetCount(filter); - } - - /// - /// Get all - /// - /// - /// Get all resources that match the given filter. - /// - /// Sort information about the query (sort by, sort order). - /// Filter the returned items. - /// How many items per page should be returned, where should the page start... - /// The aditional fields to include in the result. - /// A list of resources that match every filters. - /// Invalid filters or sort information. - [HttpGet] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - public async Task>> GetAll( - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] Pagination pagination, - [FromQuery] Include? fields - ) - { - ICollection resources = await Repository.GetAll(filter, sortBy, fields, pagination); - - return Page(resources, pagination.Limit); - } - - /// - /// Create new - /// - /// - /// Create a new item and store it. You may leave the ID unspecified, it will be filed by Kyoo. - /// - /// The resource to create. - /// The created resource. - /// The resource in the request body is invalid. - /// This item already exists (maybe a duplicated slug). - [HttpPost] - [PartialPermission(Kind.Create)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status409Conflict, Type = typeof(ActionResult<>))] - public virtual async Task> Create([FromBody] T resource) - { - return await Repository.Create(resource); - } - - /// - /// Edit - /// - /// - /// Edit an item. If the ID is specified it will be used to identify the resource. - /// If not, the slug will be used to identify it. - /// - /// The resource to edit. - /// The edited resource. - /// The resource in the request body is invalid. - /// No item found with the specified ID (or slug). - [HttpPut] - [PartialPermission(Kind.Write)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> Edit([FromBody] T resource) - { - if (resource.Id != Guid.Empty) - return await Repository.Edit(resource); - - T old = await Repository.Get(resource.Slug); - resource.Id = old.Id; - return await Repository.Edit(resource); - } - - /// - /// Edit - /// - /// - /// Edit an item. If the ID is specified it will be used to identify the resource. - /// If not, the slug will be used to identify it. - /// - /// The id or slug of the resource. - /// The resource to edit. - /// The edited resource. - /// The resource in the request body is invalid. - /// No item found with the specified ID (or slug). - [HttpPut("{identifier:id}")] - [PartialPermission(Kind.Write)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> Edit(Identifier identifier, [FromBody] T resource) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await Repository.Get(slug)).Id - ); - resource.Id = id; - return await Repository.Edit(resource); - } - - /// - /// Patch - /// - /// - /// Edit only specified properties of an item. If the ID is specified it will be used to identify the resource. - /// If not, the slug will be used to identify it. - /// - /// The resource to patch. - /// The edited resource. - /// The resource in the request body is invalid. - /// No item found with the specified ID (or slug). - [HttpPatch] - [PartialPermission(Kind.Write)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> Patch([FromBody] Patch patch) - { - if (patch.Id.HasValue) - return await Repository.Patch(patch.Id.Value, patch.Apply); - if (patch.Slug == null) - throw new ArgumentException( - "Either the Id or the slug of the resource has to be defined to edit it." - ); - - T old = await Repository.Get(patch.Slug); - return await Repository.Patch(old.Id, patch.Apply); - } - - /// - /// Patch - /// - /// - /// Edit only specified properties of an item. If the ID is specified it will be used to identify the resource. - /// If not, the slug will be used to identify it. - /// - /// The id or slug of the resource. - /// The resource to patch. - /// The edited resource. - /// The resource in the request body is invalid. - /// No item found with the specified ID (or slug). - [HttpPatch("{identifier:id}")] - [PartialPermission(Kind.Write)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> Patch(Identifier identifier, [FromBody] Patch patch) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await Repository.Get(slug)).Id - ); - if (patch.Id.HasValue && patch.Id.Value != id) - throw new ArgumentException("Can not edit id of a resource."); - return await Repository.Patch(id, patch.Apply); - } - - /// - /// Delete an item - /// - /// - /// Delete one item via it's ID or it's slug. - /// - /// The ID or slug of the resource to delete. - /// The item has successfully been deleted. - /// No item could be found with the given id or slug. - [HttpDelete("{identifier:id}")] - [PartialPermission(Kind.Delete)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task Delete(Identifier identifier) - { - await identifier.Match(id => Repository.Delete(id), slug => Repository.Delete(slug)); - return NoContent(); - } - - /// - /// Delete all where - /// - /// - /// Delete all items matching the given filters. If no filter is specified, delete all items. - /// - /// The list of filters. - /// The item(s) has successfully been deleted. - /// One or multiple filters are invalid. - [HttpDelete] - [PartialPermission(Kind.Delete)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - public async Task Delete([FromQuery] Filter filter) - { - if (filter == null) - return BadRequest( - new RequestError("Incule a filter to delete items, all items won't be deleted.") - ); - - await Repository.DeleteAll(filter); - return NoContent(); - } -} diff --git a/back/src/Kyoo.Core/Views/Helper/CrudThumbsApi.cs b/back/src/Kyoo.Core/Views/Helper/CrudThumbsApi.cs deleted file mode 100644 index bf8aa7ca..00000000 --- a/back/src/Kyoo.Core/Views/Helper/CrudThumbsApi.cs +++ /dev/null @@ -1,124 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -[ApiController] -public class CrudThumbsApi(IRepository repository) : CrudApi(repository) - where T : class, IResource, IThumbnails, IQuery -{ - private async Task _GetImage( - Identifier identifier, - string image, - ImageQuality? quality - ) - { - T? resource = await identifier.Match( - id => Repository.GetOrDefault(id), - slug => Repository.GetOrDefault(slug) - ); - if (resource == null) - return NotFound(); - - Image? img = image switch - { - "poster" => resource.Poster, - "thumbnail" => resource.Thumbnail, - "logo" => resource.Logo, - _ => throw new ArgumentException(nameof(image)), - }; - if (img is null) - return NotFound(); - - // TODO: Remove the /api and use a proxy rewrite instead. - return Redirect($"/api/thumbnails/{img.Id}"); - } - - /// - /// Get Poster - /// - /// - /// Get the poster for the specified item. - /// - /// The ID or slug of the resource to get the image for. - /// The quality of the image to retrieve. - /// The image asked. - /// - /// No item exist with the specific identifier or the image does not exists on kyoo. - /// - [HttpGet("{identifier:id}/poster")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status302Found)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public Task GetPoster(Identifier identifier, [FromQuery] ImageQuality? quality) - { - return _GetImage(identifier, "poster", quality); - } - - /// - /// Get Logo - /// - /// - /// Get the logo for the specified item. - /// - /// The ID or slug of the resource to get the image for. - /// The quality of the image to retrieve. - /// The image asked. - /// - /// No item exist with the specific identifier or the image does not exists on kyoo. - /// - [HttpGet("{identifier:id}/logo")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status302Found)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public Task GetLogo(Identifier identifier, [FromQuery] ImageQuality? quality) - { - return _GetImage(identifier, "logo", quality); - } - - /// - /// Get Thumbnail - /// - /// - /// Get the thumbnail for the specified item. - /// - /// The ID or slug of the resource to get the image for. - /// The quality of the image to retrieve. - /// The image asked. - /// - /// No item exist with the specific identifier or the image does not exists on kyoo. - /// - [HttpGet("{identifier:id}/thumbnail")] - [HttpGet("{identifier:id}/backdrop", Order = AlternativeRoute)] - [ProducesResponseType(StatusCodes.Status302Found)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public Task GetBackdrop(Identifier identifier, [FromQuery] ImageQuality? quality) - { - return _GetImage(identifier, "thumbnail", quality); - } -} diff --git a/back/src/Kyoo.Core/Views/Helper/FilterBinder.cs b/back/src/Kyoo.Core/Views/Helper/FilterBinder.cs deleted file mode 100644 index 3be01fa9..00000000 --- a/back/src/Kyoo.Core/Views/Helper/FilterBinder.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Reflection; -using System.Threading.Tasks; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; - -namespace Kyoo.Core.Api; - -public class FilterBinder : IModelBinder -{ - public Task BindModelAsync(ModelBindingContext bindingContext) - { - ValueProviderResult fields = bindingContext.ValueProvider.GetValue( - bindingContext.FieldName - ); - try - { - object? filter = bindingContext - .ModelType.GetMethod(nameof(Filter.From))! - .Invoke(null, new object?[] { fields.FirstValue }); - bindingContext.Result = ModelBindingResult.Success(filter); - return Task.CompletedTask; - } - catch (TargetInvocationException ex) - { - throw ex.InnerException!; - } - } - - public class Provider : IModelBinderProvider - { - public IModelBinder GetBinder(ModelBinderProviderContext context) - { - if (context.Metadata.ModelType.Name == "Filter`1") - { - return new BinderTypeModelBinder(typeof(FilterBinder)); - } - - return null!; - } - } -} diff --git a/back/src/Kyoo.Core/Views/Helper/IncludeBinder.cs b/back/src/Kyoo.Core/Views/Helper/IncludeBinder.cs deleted file mode 100644 index fd783946..00000000 --- a/back/src/Kyoo.Core/Views/Helper/IncludeBinder.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text.Json.Serialization.Metadata; -using System.Threading.Tasks; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; - -namespace Kyoo.Core.Api; - -public class IncludeBinder : IModelBinder -{ - private readonly Random _rng = new(); - - public Task BindModelAsync(ModelBindingContext bindingContext) - { - ValueProviderResult fields = bindingContext.ValueProvider.GetValue( - bindingContext.FieldName - ); - try - { - object include = bindingContext - .ModelType.GetMethod(nameof(Include.From))! - .Invoke(null, new object?[] { fields.FirstValue })!; - bindingContext.Result = ModelBindingResult.Success(include); - bindingContext.HttpContext.Items["fields"] = ((dynamic)include).Fields; - return Task.CompletedTask; - } - catch (TargetInvocationException ex) - { - throw ex.InnerException!; - } - } - - private static readonly IHttpContextAccessor _accessor = new HttpContextAccessor(); - - public static void HandleLoadableFields(JsonTypeInfo info) - { - foreach (JsonPropertyInfo prop in info.Properties) - { - object[] attributes = - prop.AttributeProvider?.GetCustomAttributes(typeof(LoadableRelationAttribute), true) - ?? []; - if (attributes.FirstOrDefault() is not LoadableRelationAttribute relation) - continue; - prop.ShouldSerialize = (_, _) => - { - if (_accessor?.HttpContext?.Items["fields"] is not ICollection fields) - return false; - return fields.Contains(prop.Name, StringComparer.InvariantCultureIgnoreCase); - }; - } - } - - public class Provider : IModelBinderProvider - { - public IModelBinder GetBinder(ModelBinderProviderContext context) - { - if (context.Metadata.ModelType.Name == "Include`1") - { - return new BinderTypeModelBinder(typeof(IncludeBinder)); - } - - return null!; - } - } -} diff --git a/back/src/Kyoo.Core/Views/Helper/SortBinder.cs b/back/src/Kyoo.Core/Views/Helper/SortBinder.cs deleted file mode 100644 index fb39e0b1..00000000 --- a/back/src/Kyoo.Core/Views/Helper/SortBinder.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Reflection; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; - -namespace Kyoo.Core.Api; - -public class SortBinder : IModelBinder -{ - private readonly Random _rng = new(); - - public Task BindModelAsync(ModelBindingContext bindingContext) - { - ValueProviderResult sortBy = bindingContext.ValueProvider.GetValue( - bindingContext.FieldName - ); - uint seed = BitConverter.ToUInt32( - BitConverter.GetBytes(_rng.Next(int.MinValue, int.MaxValue)), - 0 - ); - try - { - object sort = bindingContext - .ModelType.GetMethod(nameof(Sort.From))! - .Invoke(null, [sortBy.FirstValue, seed])!; - bindingContext.Result = ModelBindingResult.Success(sort); - bindingContext.HttpContext.Items["seed"] = seed; - return Task.CompletedTask; - } - catch (TargetInvocationException ex) - { - throw ex.InnerException!; - } - } - - public class Provider : IModelBinderProvider - { - public IModelBinder GetBinder(ModelBinderProviderContext context) - { - if (context.Metadata.ModelType.Name == "Sort`1") - { - return new BinderTypeModelBinder(typeof(SortBinder)); - } - - return null!; - } - } -} diff --git a/back/src/Kyoo.Core/Views/Helper/Transcoder.cs b/back/src/Kyoo.Core/Views/Helper/Transcoder.cs deleted file mode 100644 index 6a7ceb84..00000000 --- a/back/src/Kyoo.Core/Views/Helper/Transcoder.cs +++ /dev/null @@ -1,150 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Text; -using System.Threading.Tasks; -using AspNetCore.Proxy; -using AspNetCore.Proxy.Options; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.WebUtilities; - -namespace Kyoo.Core.Api; - -public abstract class TranscoderApi(IRepository repository) : CrudThumbsApi(repository) - where T : class, IResource, IThumbnails, IQuery -{ - private Task _Proxy(string route) - { - HttpProxyOptions proxyOptions = HttpProxyOptionsBuilder - .Instance.WithHandleFailure( - async (context, exception) => - { - context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; - await context.Response.WriteAsJsonAsync( - new RequestError("Service unavailable") - ); - } - ) - .Build(); - return this.HttpProxyAsync($"{VideoApi.TranscoderUrl}/{route}", proxyOptions); - } - - protected abstract Task GetPath(Identifier identifier); - - private async Task _GetPath64(Identifier identifier) - { - string path = await GetPath(identifier); - return WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(path)); - } - - /// - /// Direct stream - /// - /// - /// Retrieve the raw video stream, in the same container as the one on the server. No transcoding or - /// transmuxing is done. - /// - /// The ID or slug of the . - /// The video file of this episode. - /// No episode with the given ID or slug could be found. - [HttpGet("{identifier:id}/direct")] - [PartialPermission(Kind.Play)] - [ProducesResponseType(StatusCodes.Status302Found)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetDirectStream(Identifier identifier) - { - // TODO: Remove the /api and use a proxy rewrite instead. - return Redirect($"/api/video/{await _GetPath64(identifier)}/direct"); - } - - /// - /// Get master playlist - /// - /// - /// Get a master playlist containing all possible video qualities and audios available for this resource. - /// Note that the direct stream is missing (since the direct is not an hls stream) and - /// subtitles/fonts are not included to support more codecs than just webvtt. - /// - /// The ID or slug of the . - /// The master playlist of this episode. - /// No episode with the given ID or slug could be found. - [HttpGet("{identifier:id}/master.m3u8")] - [PartialPermission(Kind.Play)] - [ProducesResponseType(StatusCodes.Status302Found)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetMaster(Identifier identifier) - { - // TODO: Remove the /api and use a proxy rewrite instead. - return Redirect($"/api/video/{await _GetPath64(identifier)}/master.m3u8"); - } - - /// - /// Get file info - /// - /// - /// Identify metadata about a file. - /// - /// The ID or slug of the . - /// The media infos of the file. - /// No episode with the given ID or slug could be found. - [HttpGet("{identifier:id}/info")] - [PartialPermission(Kind.Read)] - public async Task GetInfo(Identifier identifier) - { - await _Proxy($"{await _GetPath64(identifier)}/info"); - } - - /// - /// Get thumbnail sprite - /// - /// - /// Get a sprite file containing all the thumbnails of the show. - /// - /// The ID or slug of the . - /// A sprite with an image for every X seconds of the video file. - /// No episode with the given ID or slug could be found. - [HttpGet("{identifier:id}/thumbnails.png")] - [PartialPermission(Kind.Read)] - public async Task GetThumbnails(Identifier identifier) - { - await _Proxy($"{await _GetPath64(identifier)}/thumbnails.png"); - } - - /// - /// Get thumbnail vtt - /// - /// - /// Get a vtt file containing timing/position of thumbnails inside the sprite file. - /// https://developer.bitmovin.com/playback/docs/webvtt-based-thumbnails for more info. - /// - /// The ID or slug of the . - /// A vtt file containing metadata about timing and x/y/width/height of the sprites of /thumbnails.png. - /// No episode with the given ID or slug could be found. - [HttpGet("{identifier:id}/thumbnails.vtt")] - [PartialPermission(Kind.Read)] - public async Task GetThumbnailsVtt(Identifier identifier) - { - await _Proxy($"{await _GetPath64(identifier)}/thumbnails.vtt"); - } -} diff --git a/back/src/Kyoo.Core/Views/InfoApi.cs b/back/src/Kyoo.Core/Views/InfoApi.cs deleted file mode 100644 index ac0e2f22..00000000 --- a/back/src/Kyoo.Core/Views/InfoApi.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Authentication.Models; -using Kyoo.Core.Controllers; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Authentication.Views; - -/// -/// Info about the current instance -/// -[ApiController] -[Route("info")] -[ApiDefinition("Info", Group = UsersGroup)] -public class InfoApi(PermissionOption options, MiscRepository info, IConfiguration configuration) - : ControllerBase -{ - public async Task> GetInfo() - { - return Ok( - new ServerInfo() - { - AllowGuests = options.Default.Any(), - RequireVerification = options.RequireVerification, - GuestPermissions = options.Default.ToList(), - PublicUrl = options.PublicUrl, - Oidc = options - .OIDC.Select(x => new KeyValuePair( - x.Key, - new() { DisplayName = x.Value.DisplayName, LogoUrl = x.Value.LogoUrl, } - )) - .ToDictionary(x => x.Key, x => x.Value), - SetupStatus = await info.GetSetupStep(), - PasswordLoginEnabled = !configuration.GetValue( - "AUTHENTICATION_DISABLE_PASSWORD_LOGIN", - false - ), - RegistrationEnabled = !configuration.GetValue( - "AUTHENTICATION_DISABLE_USER_REGISTRATION", - false - ), - } - ); - } -} diff --git a/back/src/Kyoo.Core/Views/Metadata/IssueApi.cs b/back/src/Kyoo.Core/Views/Metadata/IssueApi.cs deleted file mode 100644 index f1d0a83a..00000000 --- a/back/src/Kyoo.Core/Views/Metadata/IssueApi.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// Create or list issues on the instance -/// -[Route("issues")] -[Route("issue", Order = AlternativeRoute)] -[ApiController] -[PartialPermission(nameof(Issue), Group = Group.Admin)] -[ApiDefinition("Issue", Group = AdminGroup)] -public class IssueApi(IIssueRepository issues) : Controller -{ - /// - /// Get count - /// - /// - /// Get the number of issues that match the filters. - /// - /// A list of filters to respect. - /// How many issues matched that filter. - /// Invalid filters. - [HttpGet("count")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - public async Task> GetCount([FromQuery] Filter filter) - { - return await issues.GetCount(filter); - } - - /// - /// Get all issues - /// - /// - /// Get all issues that match the given filter. - /// - /// Filter the returned items. - /// A list of issues that match every filters. - /// Invalid filters or sort information. - [HttpGet] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - public async Task>> GetAll([FromQuery] Filter? filter) - { - return Ok(await issues.GetAll(filter)); - } - - /// - /// Upsert issue - /// - /// - /// Create or update an issue. - /// - /// The issue to create. - /// The created issue. - /// The issue in the request body is invalid. - [HttpPost] - [PartialPermission(Kind.Create)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - public async Task> Create([FromBody] Issue issue) - { - return await issues.Upsert(issue); - } - - /// - /// Delete issues - /// - /// - /// Delete all issues matching the given filters. - /// - /// The list of filters. - /// The item(s) has successfully been deleted. - /// One or multiple filters are invalid. - [HttpDelete] - [PartialPermission(Kind.Delete)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - public async Task Delete([FromQuery] Filter filter) - { - if (filter == null) - return BadRequest( - new RequestError("Incule a filter to delete items, all items won't be deleted.") - ); - - await issues.DeleteAll(filter); - return NoContent(); - } -} diff --git a/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs b/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs deleted file mode 100644 index a3148359..00000000 --- a/back/src/Kyoo.Core/Views/Metadata/StudioApi.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// Information about one or multiple . -/// -[Route("studios")] -[Route("studio", Order = AlternativeRoute)] -[ApiController] -[PartialPermission(nameof(Show))] -[ApiDefinition("Studios", Group = MetadataGroup)] -public class StudioApi : CrudApi -{ - /// - /// The library manager used to modify or retrieve information in the data store. - /// - private readonly ILibraryManager _libraryManager; - - /// - /// Create a new . - /// - /// - /// The library manager used to modify or retrieve information in the data store. - /// - public StudioApi(ILibraryManager libraryManager) - : base(libraryManager.Studios) - { - _libraryManager = libraryManager; - } - - /// - /// Get shows - /// - /// - /// List shows that were made by this specific studio. - /// - /// The ID or slug of the . - /// A key to sort shows by. - /// An optional list of filters. - /// The number of shows to return. - /// The aditional fields to include in the result. - /// A page of shows. - /// The filters or the sort parameters are invalid. - /// No studio with the given ID or slug could be found. - [HttpGet("{identifier:id}/shows")] - [HttpGet("{identifier:id}/show", Order = AlternativeRoute)] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetShows( - Identifier identifier, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] Pagination pagination, - [FromQuery] Include fields - ) - { - ICollection resources = await _libraryManager.Shows.GetAll( - Filter.And(filter, identifier.Matcher(x => x.StudioId, x => x.Studio!.Slug)), - sortBy, - fields, - pagination - ); - - if ( - !resources.Any() - && await _libraryManager.Studios.GetOrDefault(identifier.IsSame()) == null - ) - return NotFound(); - return Page(resources, pagination.Limit); - } -} diff --git a/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs b/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs deleted file mode 100644 index a8a7a73c..00000000 --- a/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs +++ /dev/null @@ -1,270 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Core.Controllers; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// Information about one or multiple . -/// -[Route("collections")] -[Route("collection", Order = AlternativeRoute)] -[ApiController] -[PartialPermission(nameof(Collection))] -[ApiDefinition("Collections", Group = ResourcesGroup)] -public class CollectionApi( - IRepository movies, - IRepository shows, - CollectionRepository collections, - LibraryItemRepository items -) : CrudThumbsApi(collections) -{ - /// - /// Refresh - /// - /// - /// Ask a metadata refresh. - /// - /// The ID or slug of the . - /// Nothing - /// No episode with the given ID or slug could be found. - [HttpPost("{identifier:id}/refresh")] - [PartialPermission(Kind.Write)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task Refresh(Identifier identifier, [FromServices] IScanner scanner) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await collections.Get(slug)).Id - ); - await scanner.SendRefreshRequest(nameof(Collection), id); - return NoContent(); - } - - /// - /// Add a movie - /// - /// - /// Add a movie in the collection. - /// - /// The ID or slug of the . - /// The ID or slug of the to add. - /// Nothing if successful. - /// No collection or movie with the given ID could be found. - /// The specified movie is already in this collection. - [HttpPut("{identifier:id}/movies/{movie:id}")] - [HttpPut("{identifier:id}/movie/{movie:id}", Order = AlternativeRoute)] - [PartialPermission(Kind.Write)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task AddMovie(Identifier identifier, Identifier movie) - { - Guid collectionId = await identifier.Match( - async id => (await collections.Get(id)).Id, - async slug => (await collections.Get(slug)).Id - ); - Guid movieId = await movie.Match( - async id => (await movies.Get(id)).Id, - async slug => (await movies.Get(slug)).Id - ); - await collections.AddMovie(collectionId, movieId); - return NoContent(); - } - - /// - /// Add a show - /// - /// - /// Add a show in the collection. - /// - /// The ID or slug of the . - /// The ID or slug of the to add. - /// Nothing if successful. - /// No collection or show with the given ID could be found. - /// The specified show is already in this collection. - [HttpPut("{identifier:id}/shows/{show:id}")] - [HttpPut("{identifier:id}/show/{show:id}", Order = AlternativeRoute)] - [PartialPermission(Kind.Write)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status409Conflict)] - public async Task AddShow(Identifier identifier, Identifier show) - { - Guid collectionId = await identifier.Match( - async id => (await collections.Get(id)).Id, - async slug => (await collections.Get(slug)).Id - ); - Guid showId = await show.Match( - async id => (await shows.Get(id)).Id, - async slug => (await shows.Get(slug)).Id - ); - await collections.AddShow(collectionId, showId); - return NoContent(); - } - - /// - /// Get items in collection - /// - /// - /// Lists the items that are contained in the collection with the given id or slug. - /// - /// The ID or slug of the . - /// A key to sort items by. - /// An optional list of filters. - /// The number of items to return. - /// The aditional fields to include in the result. - /// A page of items. - /// The filters or the sort parameters are invalid. - /// No collection with the given ID could be found. - [HttpGet("{identifier:id}/items")] - [HttpGet("{identifier:id}/item", Order = AlternativeRoute)] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetItems( - Identifier identifier, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] Pagination pagination, - [FromQuery] Include? fields - ) - { - Guid collectionId = await identifier.Match( - id => Task.FromResult(id), - async slug => (await collections.Get(slug)).Id - ); - ICollection resources = await items.GetAllOfCollection( - collectionId, - filter, - sortBy == new Sort.Default() - ? new Sort.By(nameof(Movie.AirDate)) - : sortBy, - fields, - pagination - ); - - if ( - !resources.Any() - && await collections.GetOrDefault(identifier.IsSame()) == null - ) - return NotFound(); - return Page(resources, pagination.Limit); - } - - /// - /// Get shows in collection - /// - /// - /// Lists the shows that are contained in the collection with the given id or slug. - /// - /// The ID or slug of the . - /// A key to sort shows by. - /// An optional list of filters. - /// The number of shows to return. - /// The additional fields to include in the result. - /// A page of shows. - /// The filters or the sort parameters are invalid. - /// No collection with the given ID could be found. - [HttpGet("{identifier:id}/shows")] - [HttpGet("{identifier:id}/show", Order = AlternativeRoute)] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetShows( - Identifier identifier, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] Pagination pagination, - [FromQuery] Include? fields - ) - { - ICollection resources = await shows.GetAll( - Filter.And(filter, identifier.IsContainedIn(x => x.Collections)), - sortBy == new Sort.Default() ? new Sort.By(x => x.AirDate) : sortBy, - fields, - pagination - ); - - if ( - !resources.Any() - && await collections.GetOrDefault(identifier.IsSame()) == null - ) - return NotFound(); - return Page(resources, pagination.Limit); - } - - /// - /// Get movies in collection - /// - /// - /// Lists the movies that are contained in the collection with the given id or slug. - /// - /// The ID or slug of the . - /// A key to sort movies by. - /// An optional list of filters. - /// The number of movies to return. - /// The aditional fields to include in the result. - /// A page of movies. - /// The filters or the sort parameters are invalid. - /// No collection with the given ID could be found. - [HttpGet("{identifier:id}/movies")] - [HttpGet("{identifier:id}/movie", Order = AlternativeRoute)] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetMovies( - Identifier identifier, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] Pagination pagination, - [FromQuery] Include? fields - ) - { - ICollection resources = await movies.GetAll( - Filter.And(filter, identifier.IsContainedIn(x => x.Collections)), - sortBy == new Sort.Default() ? new Sort.By(x => x.AirDate) : sortBy, - fields, - pagination - ); - - if ( - !resources.Any() - && await collections.GetOrDefault(identifier.IsSame()) == null - ) - return NotFound(); - return Page(resources, pagination.Limit); - } -} diff --git a/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs b/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs deleted file mode 100644 index b26c196c..00000000 --- a/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs +++ /dev/null @@ -1,221 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// Information about one or multiple . -/// -[Route("episodes")] -[Route("episode", Order = AlternativeRoute)] -[ApiController] -[PartialPermission(nameof(Episode))] -[ApiDefinition("Episodes", Group = ResourcesGroup)] -public class EpisodeApi(ILibraryManager libraryManager) - : TranscoderApi(libraryManager.Episodes) -{ - /// - /// Refresh - /// - /// - /// Ask a metadata refresh. - /// - /// The ID or slug of the . - /// Nothing - /// No episode with the given ID or slug could be found. - [HttpPost("{identifier:id}/refresh")] - [PartialPermission(Kind.Write)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task Refresh(Identifier identifier, [FromServices] IScanner scanner) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Episodes.Get(slug)).Id - ); - await scanner.SendRefreshRequest(nameof(Episode), id); - return NoContent(); - } - - /// - /// Get episode's show - /// - /// - /// Get the show that this episode is part of. - /// - /// The ID or slug of the . - /// The aditional fields to include in the result. - /// The show that contains this episode. - /// No episode with the given ID or slug could be found. - [HttpGet("{identifier:id}/show")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> GetShow( - Identifier identifier, - [FromQuery] Include fields - ) - { - return await libraryManager.Shows.Get( - identifier.IsContainedIn(x => x.Episodes!), - fields - ); - } - - /// - /// Get episode's season - /// - /// - /// Get the season that this episode is part of. - /// - /// The ID or slug of the . - /// The aditional fields to include in the result. - /// The season that contains this episode. - /// The episode is not part of a season. - /// No episode with the given ID or slug could be found. - [HttpGet("{identifier:id}/season")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> GetSeason( - Identifier identifier, - [FromQuery] Include fields - ) - { - Season? ret = await libraryManager.Seasons.GetOrDefault( - identifier.IsContainedIn(x => x.Episodes!), - fields - ); - if (ret != null) - return ret; - Episode? episode = await identifier.Match( - id => libraryManager.Episodes.GetOrDefault(id), - slug => libraryManager.Episodes.GetOrDefault(slug) - ); - return episode == null ? NotFound() : NoContent(); - } - - /// - /// Get watch status - /// - /// - /// Get when an item has been wathed and if it was watched. - /// - /// The ID or slug of the . - /// The status. - /// This episode does not have a specific status. - /// No episode with the given ID or slug could be found. - [HttpGet("{identifier:id}/watchStatus")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetWatchStatus(Identifier identifier) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Episodes.Get(slug)).Id - ); - return await libraryManager.WatchStatus.GetEpisodeStatus(id, User.GetIdOrThrow()); - } - - /// - /// Set watch status - /// - /// - /// Set when an item has been wathed and if it was watched. - /// - /// The ID or slug of the . - /// The new watch status. - /// Where the user stopped watching (in seconds). - /// Where the user stopped watching (in percent). - /// The newly set status. - /// The status has been set - /// The status was not considered impactfull enough to be saved (less then 5% of watched for example). - /// No episode with the given ID or slug could be found. - [HttpPost("{identifier:id}/watchStatus")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task SetWatchStatus( - Identifier identifier, - WatchStatus status, - int? watchedTime, - int? percent - ) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Episodes.Get(slug)).Id - ); - return await libraryManager.WatchStatus.SetEpisodeStatus( - id, - User.GetIdOrThrow(), - status, - watchedTime, - percent - ); - } - - /// - /// Delete watch status - /// - /// - /// Delete watch status (to rewatch for example). - /// - /// The ID or slug of the . - /// The newly set status. - /// The status has been deleted. - /// No episode with the given ID or slug could be found. - [HttpDelete("{identifier:id}/watchStatus")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task DeleteWatchStatus(Identifier identifier) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Episodes.Get(slug)).Id - ); - await libraryManager.WatchStatus.DeleteEpisodeStatus(id, User.GetIdOrThrow()); - } - - protected override async Task GetPath(Identifier identifier) - { - string path = await identifier.Match( - async id => (await Repository.Get(id)).Path, - async slug => (await Repository.Get(slug)).Path - ); - return path; - } -} diff --git a/back/src/Kyoo.Core/Views/Resources/LibraryItemApi.cs b/back/src/Kyoo.Core/Views/Resources/LibraryItemApi.cs deleted file mode 100644 index 37f28c26..00000000 --- a/back/src/Kyoo.Core/Views/Resources/LibraryItemApi.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// Endpoint for items that are not part of a specific library. -/// An item can ether represent a collection or a show. -/// -[Route("items")] -[Route("item", Order = AlternativeRoute)] -[ApiController] -[PartialPermission("LibraryItem")] -[ApiDefinition("Items", Group = ResourcesGroup)] -public class LibraryItemApi(IRepository libraryItems) - : CrudThumbsApi(libraryItems) { } diff --git a/back/src/Kyoo.Core/Views/Resources/MovieApi.cs b/back/src/Kyoo.Core/Views/Resources/MovieApi.cs deleted file mode 100644 index 274b4e31..00000000 --- a/back/src/Kyoo.Core/Views/Resources/MovieApi.cs +++ /dev/null @@ -1,232 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// Information about one or multiple . -/// -[Route("movies")] -[Route("movie", Order = AlternativeRoute)] -[ApiController] -[PartialPermission(nameof(Movie))] -[ApiDefinition("Movie", Group = ResourcesGroup)] -public class MovieApi(ILibraryManager libraryManager) : TranscoderApi(libraryManager.Movies) -{ - /// - /// Refresh - /// - /// - /// Ask a metadata refresh. - /// - /// The ID or slug of the . - /// Nothing - /// No episode with the given ID or slug could be found. - [HttpPost("{identifier:id}/refresh")] - [PartialPermission(Kind.Write)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task Refresh(Identifier identifier, [FromServices] IScanner scanner) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Movies.Get(slug)).Id - ); - await scanner.SendRefreshRequest(nameof(Movie), id); - return NoContent(); - } - - /// - /// Get studio that made the show - /// - /// - /// Get the studio that made the show. - /// - /// The ID or slug of the . - /// The aditional fields to include in the result. - /// The studio that made the show. - /// No show with the given ID or slug could be found. - [HttpGet("{identifier:id}/studio")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> GetStudio( - Identifier identifier, - [FromQuery] Include fields - ) - { - return await libraryManager.Studios.Get( - identifier.IsContainedIn(x => x.Movies!), - fields - ); - } - - /// - /// Get collections containing this show - /// - /// - /// List the collections that contain this show. - /// - /// The ID or slug of the . - /// A key to sort collections by. - /// An optional list of filters. - /// The number of collections to return. - /// The aditional fields to include in the result. - /// A page of collections. - /// The filters or the sort parameters are invalid. - /// No show with the given ID or slug could be found. - [HttpGet("{identifier:id}/collections")] - [HttpGet("{identifier:id}/collection", Order = AlternativeRoute)] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetCollections( - Identifier identifier, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] Pagination pagination, - [FromQuery] Include fields - ) - { - ICollection resources = await libraryManager.Collections.GetAll( - Filter.And(filter, identifier.IsContainedIn(x => x.Movies)), - sortBy, - fields, - pagination - ); - - if ( - !resources.Any() - && await libraryManager.Movies.GetOrDefault(identifier.IsSame()) == null - ) - return NotFound(); - return Page(resources, pagination.Limit); - } - - /// - /// Get watch status - /// - /// - /// Get when an item has been wathed and if it was watched. - /// - /// The ID or slug of the . - /// The status. - /// This movie does not have a specific status. - /// No movie with the given ID or slug could be found. - [HttpGet("{identifier:id}/watchStatus")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetWatchStatus(Identifier identifier) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Movies.Get(slug)).Id - ); - return await libraryManager.WatchStatus.GetMovieStatus(id, User.GetIdOrThrow()); - } - - /// - /// Set watch status - /// - /// - /// Set when an item has been wathed and if it was watched. - /// - /// The ID or slug of the . - /// The new watch status. - /// Where the user stopped watching. - /// Where the user stopped watching (in percent). - /// The newly set status. - /// The status has been set - /// The status was not considered impactfull enough to be saved (less then 5% of watched for example). - /// WatchedTime can't be specified if status is not watching. - /// No movie with the given ID or slug could be found. - [HttpPost("{identifier:id}/watchStatus")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task SetWatchStatus( - Identifier identifier, - WatchStatus status, - int? watchedTime, - int? percent - ) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Movies.Get(slug)).Id - ); - return await libraryManager.WatchStatus.SetMovieStatus( - id, - User.GetIdOrThrow(), - status, - watchedTime, - percent - ); - } - - /// - /// Delete watch status - /// - /// - /// Delete watch status (to rewatch for example). - /// - /// The ID or slug of the . - /// The newly set status. - /// The status has been deleted. - /// No movie with the given ID or slug could be found. - [HttpDelete("{identifier:id}/watchStatus")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task DeleteWatchStatus(Identifier identifier) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Movies.Get(slug)).Id - ); - await libraryManager.WatchStatus.DeleteMovieStatus(id, User.GetIdOrThrow()); - } - - protected override async Task GetPath(Identifier identifier) - { - string path = await identifier.Match( - async id => (await Repository.Get(id)).Path, - async slug => (await Repository.Get(slug)).Path - ); - return path; - } -} diff --git a/back/src/Kyoo.Core/Views/Resources/NewsApi.cs b/back/src/Kyoo.Core/Views/Resources/NewsApi.cs deleted file mode 100644 index 84aa8f3b..00000000 --- a/back/src/Kyoo.Core/Views/Resources/NewsApi.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// List new items added to kyoo. -/// -[Route("news")] -[Route("new", Order = AlternativeRoute)] -[ApiController] -[PartialPermission("LibraryItem")] -[ApiDefinition("News", Group = ResourcesGroup)] -public class NewsApi(IRepository news) : CrudThumbsApi(news) { } diff --git a/back/src/Kyoo.Core/Views/Resources/SearchApi.cs b/back/src/Kyoo.Core/Views/Resources/SearchApi.cs deleted file mode 100644 index 14050382..00000000 --- a/back/src/Kyoo.Core/Views/Resources/SearchApi.cs +++ /dev/null @@ -1,216 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// An endpoint to search for every resources of kyoo. Searching for only a specific type of resource -/// is available on the said endpoint. -/// -[Route("search")] -[ApiController] -[ApiDefinition("Search", Group = OtherGroup)] -public class SearchApi : BaseApi -{ - private readonly ISearchManager _searchManager; - - public SearchApi(ISearchManager searchManager) - { - _searchManager = searchManager; - } - - // TODO: add facets - - /// - /// Search collections - /// - /// - /// Search for collections - /// - /// The query to search for. - /// Sort information about the query (sort by, sort order). - /// How many items per page should be returned, where should the page start... - /// The aditional fields to include in the result. - /// A list of collections found for the specified query. - [HttpGet("collections")] - [HttpGet("collection", Order = AlternativeRoute)] - [Permission(nameof(Collection), Kind.Read)] - [ApiDefinition("Collections", Group = ResourcesGroup)] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> SearchCollections( - [FromQuery] string? q, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] SearchPagination pagination, - [FromQuery] Include fields - ) - { - return SearchPage( - await _searchManager.SearchCollections(q, sortBy, filter, pagination, fields) - ); - } - - /// - /// Search shows - /// - /// - /// Search for shows - /// - /// The query to search for. - /// Sort information about the query (sort by, sort order). - /// How many items per page should be returned, where should the page start... - /// The aditional fields to include in the result. - /// A list of shows found for the specified query. - [HttpGet("shows")] - [HttpGet("show", Order = AlternativeRoute)] - [Permission(nameof(Show), Kind.Read)] - [ApiDefinition("Shows", Group = ResourcesGroup)] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> SearchShows( - [FromQuery] string? q, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] SearchPagination pagination, - [FromQuery] Include fields - ) - { - return SearchPage(await _searchManager.SearchShows(q, sortBy, filter, pagination, fields)); - } - - /// - /// Search movie - /// - /// - /// Search for movie - /// - /// The query to search for. - /// Sort information about the query (sort by, sort order). - /// How many items per page should be returned, where should the page start... - /// The aditional fields to include in the result. - /// A list of movies found for the specified query. - [HttpGet("movies")] - [HttpGet("movie", Order = AlternativeRoute)] - [Permission(nameof(Movie), Kind.Read)] - [ApiDefinition("Movies", Group = ResourcesGroup)] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> SearchMovies( - [FromQuery] string? q, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] SearchPagination pagination, - [FromQuery] Include fields - ) - { - return SearchPage(await _searchManager.SearchMovies(q, sortBy, filter, pagination, fields)); - } - - /// - /// Search items - /// - /// - /// Search for items - /// - /// The query to search for. - /// Sort information about the query (sort by, sort order). - /// How many items per page should be returned, where should the page start... - /// The aditional fields to include in the result. - /// A list of items found for the specified query. - [HttpGet("items")] - [HttpGet("item", Order = AlternativeRoute)] - [Permission(nameof(ILibraryItem), Kind.Read)] - [ApiDefinition("Items", Group = ResourcesGroup)] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> SearchItems( - [FromQuery] string? q, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] SearchPagination pagination, - [FromQuery] Include fields - ) - { - return SearchPage(await _searchManager.SearchItems(q, sortBy, filter, pagination, fields)); - } - - /// - /// Search episodes - /// - /// - /// Search for episodes - /// - /// The query to search for. - /// Sort information about the query (sort by, sort order). - /// How many items per page should be returned, where should the page start... - /// The aditional fields to include in the result. - /// A list of episodes found for the specified query. - [HttpGet("episodes")] - [HttpGet("episode", Order = AlternativeRoute)] - [Permission(nameof(Episode), Kind.Read)] - [ApiDefinition("Episodes", Group = ResourcesGroup)] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> SearchEpisodes( - [FromQuery] string? q, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] SearchPagination pagination, - [FromQuery] Include fields - ) - { - return SearchPage( - await _searchManager.SearchEpisodes(q, sortBy, filter, pagination, fields) - ); - } - - /// - /// Search studios - /// - /// - /// Search for studios - /// - /// The query to search for. - /// Sort information about the query (sort by, sort order). - /// How many items per page should be returned, where should the page start... - /// The aditional fields to include in the result. - /// A list of studios found for the specified query. - [HttpGet("studios")] - [HttpGet("studio", Order = AlternativeRoute)] - [Permission(nameof(Studio), Kind.Read)] - [ApiDefinition("Studios", Group = MetadataGroup)] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> SearchStudios( - [FromQuery] string? q, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] SearchPagination pagination, - [FromQuery] Include fields - ) - { - return SearchPage( - await _searchManager.SearchStudios(q, sortBy, filter, pagination, fields) - ); - } -} diff --git a/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs b/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs deleted file mode 100644 index dcbdae8c..00000000 --- a/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs +++ /dev/null @@ -1,138 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// Information about one or multiple . -/// -[Route("seasons")] -[Route("season", Order = AlternativeRoute)] -[ApiController] -[PartialPermission(nameof(Season))] -[ApiDefinition("Seasons", Group = ResourcesGroup)] -public class SeasonApi(ILibraryManager libraryManager) - : CrudThumbsApi(libraryManager.Seasons) -{ - /// - /// Refresh - /// - /// - /// Ask a metadata refresh. - /// - /// The ID or slug of the . - /// Nothing - /// No episode with the given ID or slug could be found. - [HttpPost("{identifier:id}/refresh")] - [PartialPermission(Kind.Write)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task Refresh(Identifier identifier, [FromServices] IScanner scanner) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Seasons.Get(slug)).Id - ); - await scanner.SendRefreshRequest(nameof(Season), id); - return NoContent(); - } - - /// - /// Get episodes in the season - /// - /// - /// List the episodes that are part of the specified season. - /// - /// The ID or slug of the . - /// A key to sort episodes by. - /// An optional list of filters. - /// The number of episodes to return. - /// The aditional fields to include in the result. - /// A page of episodes. - /// The filters or the sort parameters are invalid. - /// No season with the given ID or slug could be found. - [HttpGet("{identifier:id}/episodes")] - [HttpGet("{identifier:id}/episode", Order = AlternativeRoute)] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetEpisode( - Identifier identifier, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] Pagination pagination, - [FromQuery] Include fields - ) - { - ICollection resources = await libraryManager.Episodes.GetAll( - Filter.And(filter, identifier.Matcher(x => x.SeasonId, x => x.Season!.Slug)), - sortBy, - fields, - pagination - ); - - if ( - !resources.Any() - && await libraryManager.Seasons.GetOrDefault(identifier.IsSame()) == null - ) - return NotFound(); - return Page(resources, pagination.Limit); - } - - /// - /// Get season's show - /// - /// - /// Get the show that this season is part of. - /// - /// The ID or slug of the . - /// The aditional fields to include in the result. - /// The show that contains this season. - /// No season with the given ID or slug could be found. - [HttpGet("{identifier:id}/show")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> GetShow( - Identifier identifier, - [FromQuery] Include fields - ) - { - Show? ret = await libraryManager.Shows.GetOrDefault( - identifier.IsContainedIn(x => x.Seasons!), - fields - ); - if (ret == null) - return NotFound(); - return ret; - } -} diff --git a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs b/back/src/Kyoo.Core/Views/Resources/ShowApi.cs deleted file mode 100644 index 0517095a..00000000 --- a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs +++ /dev/null @@ -1,295 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// Information about one or multiple . -/// -[Route("shows")] -[Route("show", Order = AlternativeRoute)] -[ApiController] -[PartialPermission(nameof(Show))] -[ApiDefinition("Shows", Group = ResourcesGroup)] -public class ShowApi(ILibraryManager libraryManager) : CrudThumbsApi(libraryManager.Shows) -{ - /// - /// Refresh - /// - /// - /// Ask a metadata refresh. - /// - /// The ID or slug of the . - /// Nothing - /// No show with the given ID or slug could be found. - [HttpPost("{identifier:id}/refresh")] - [PartialPermission(Kind.Write)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task Refresh(Identifier identifier, [FromServices] IScanner scanner) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Shows.Get(slug)).Id - ); - await scanner.SendRefreshRequest(nameof(Show), id); - return NoContent(); - } - - /// - /// Get seasons of this show - /// - /// - /// List the seasons that are part of the specified show. - /// - /// The ID or slug of the . - /// A key to sort seasons by. - /// An optional list of filters. - /// The number of seasons to return. - /// The aditional fields to include in the result. - /// A page of seasons. - /// The filters or the sort parameters are invalid. - /// No show with the given ID or slug could be found. - [HttpGet("{identifier:id}/seasons")] - [HttpGet("{identifier:id}/season", Order = AlternativeRoute)] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetSeasons( - Identifier identifier, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] Pagination pagination, - [FromQuery] Include fields - ) - { - ICollection resources = await libraryManager.Seasons.GetAll( - Filter.And(filter, identifier.Matcher(x => x.ShowId, x => x.Show!.Slug)), - sortBy, - fields, - pagination - ); - - if ( - !resources.Any() - && await libraryManager.Shows.GetOrDefault(identifier.IsSame()) == null - ) - return NotFound(); - return Page(resources, pagination.Limit); - } - - /// - /// Get episodes of this show - /// - /// - /// List the episodes that are part of the specified show. - /// - /// The ID or slug of the . - /// A key to sort episodes by. - /// An optional list of filters. - /// The number of episodes to return. - /// The aditional fields to include in the result. - /// A page of episodes. - /// The filters or the sort parameters are invalid. - /// No show with the given ID or slug could be found. - [HttpGet("{identifier:id}/episodes")] - [HttpGet("{identifier:id}/episode", Order = AlternativeRoute)] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetEpisodes( - Identifier identifier, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] Pagination pagination, - [FromQuery] Include fields - ) - { - ICollection resources = await libraryManager.Episodes.GetAll( - Filter.And(filter, identifier.Matcher(x => x.ShowId, x => x.Show!.Slug)), - sortBy, - fields, - pagination - ); - - if ( - !resources.Any() - && await libraryManager.Shows.GetOrDefault(identifier.IsSame()) == null - ) - return NotFound(); - return Page(resources, pagination.Limit); - } - - /// - /// Get studio that made the show - /// - /// - /// Get the studio that made the show. - /// - /// The ID or slug of the . - /// The aditional fields to include in the result. - /// The studio that made the show. - /// No show with the given ID or slug could be found. - [HttpGet("{identifier:id}/studio")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> GetStudio( - Identifier identifier, - [FromQuery] Include fields - ) - { - return await libraryManager.Studios.Get( - identifier.IsContainedIn(x => x.Shows!), - fields - ); - } - - /// - /// Get collections containing this show - /// - /// - /// List the collections that contain this show. - /// - /// The ID or slug of the . - /// A key to sort collections by. - /// An optional list of filters. - /// The number of collections to return. - /// The aditional fields to include in the result. - /// A page of collections. - /// The filters or the sort parameters are invalid. - /// No show with the given ID or slug could be found. - [HttpGet("{identifier:id}/collections")] - [HttpGet("{identifier:id}/collection", Order = AlternativeRoute)] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetCollections( - Identifier identifier, - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, - [FromQuery] Pagination pagination, - [FromQuery] Include fields - ) - { - ICollection resources = await libraryManager.Collections.GetAll( - Filter.And(filter, identifier.IsContainedIn(x => x.Shows!)), - sortBy, - fields, - pagination - ); - - if ( - !resources.Any() - && await libraryManager.Shows.GetOrDefault(identifier.IsSame()) == null - ) - return NotFound(); - return Page(resources, pagination.Limit); - } - - /// - /// Get watch status - /// - /// - /// Get when an item has been wathed and if it was watched. - /// - /// The ID or slug of the . - /// The status. - /// This show does not have a specific status. - /// No show with the given ID or slug could be found. - [HttpGet("{identifier:id}/watchStatus")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetWatchStatus(Identifier identifier) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Shows.Get(slug)).Id - ); - return await libraryManager.WatchStatus.GetShowStatus(id, User.GetIdOrThrow()); - } - - /// - /// Set watch status - /// - /// - /// Set when an item has been wathed and if it was watched. - /// - /// The ID or slug of the . - /// The new watch status. - /// The newly set status. - /// The status has been set - /// The status was not considered impactfull enough to be saved (less then 5% of watched for example). - /// No movie with the given ID or slug could be found. - [HttpPost("{identifier:id}/watchStatus")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task SetWatchStatus(Identifier identifier, WatchStatus status) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Shows.Get(slug)).Id - ); - return await libraryManager.WatchStatus.SetShowStatus(id, User.GetIdOrThrow(), status); - } - - /// - /// Delete watch status - /// - /// - /// Delete watch status (to rewatch for example). - /// - /// The ID or slug of the . - /// The newly set status. - /// The status has been deleted. - /// No show with the given ID or slug could be found. - [HttpDelete("{identifier:id}/watchStatus")] - [UserOnly] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task DeleteWatchStatus(Identifier identifier) - { - Guid id = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Shows.Get(slug)).Id - ); - await libraryManager.WatchStatus.DeleteShowStatus(id, User.GetIdOrThrow()); - } -} diff --git a/back/src/Kyoo.Core/Views/Resources/UserApi.cs b/back/src/Kyoo.Core/Views/Resources/UserApi.cs deleted file mode 100644 index 63d00d7d..00000000 --- a/back/src/Kyoo.Core/Views/Resources/UserApi.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.IO; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// Information about one or multiple . -/// -[Route("users")] -[Route("user", Order = AlternativeRoute)] -[ApiController] -[PartialPermission(nameof(User), Group = Group.Admin)] -[ApiDefinition("Users", Group = ResourcesGroup)] -public class UserApi(ILibraryManager libraryManager, IThumbnailsManager thumbs) - : CrudApi(libraryManager!.Users) -{ - /// - /// Get profile picture - /// - /// - /// Get the profile picture of someone - /// - [HttpGet("{identifier:id}/logo")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] - public async Task GetProfilePicture(Identifier identifier) - { - Guid gid = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Users.Get(slug)).Id - ); - Stream img = await thumbs.GetUserImage(gid); - if (identifier.Is("random")) - Response.Headers.CacheControl = $"public, no-store"; - else - { - // Allow clients to cache the image for 6 month. - Response.Headers.CacheControl = $"public, max-age={60 * 60 * 24 * 31 * 6}"; - } - return File(img, "image/webp", true); - } - - /// - /// Set profile picture - /// - /// - /// Set user profile picture - /// - [HttpPost("{identifier:id}/logo")] - [PartialPermission(Kind.Write)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] - public async Task SetProfilePicture(Identifier identifier, IFormFile picture) - { - if (picture == null || picture.Length == 0) - return BadRequest(); - Guid gid = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Users.Get(slug)).Id - ); - await thumbs.SetUserImage(gid, picture.OpenReadStream()); - return NoContent(); - } - - /// - /// Delete profile picture - /// - /// - /// Delete your profile picture - /// - /// The user is not authenticated. - /// The given access token is invalid. - [HttpDelete("{identifier:id}/logo")] - [PartialPermission(Kind.Delete)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] - public async Task DeleteProfilePicture(Identifier identifier) - { - Guid gid = await identifier.Match( - id => Task.FromResult(id), - async slug => (await libraryManager.Users.Get(slug)).Id - ); - await thumbs.SetUserImage(gid, null); - return NoContent(); - } -} diff --git a/back/src/Kyoo.Core/Views/Resources/WatchlistApi.cs b/back/src/Kyoo.Core/Views/Resources/WatchlistApi.cs deleted file mode 100644 index 0e733d6d..00000000 --- a/back/src/Kyoo.Core/Views/Resources/WatchlistApi.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Exceptions; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Kyoo.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api; - -/// -/// List times on the user's watchlist -/// -[Route("watchlist")] -[ApiController] -[PartialPermission("Watchlist")] -[ApiDefinition("Watchlist", Group = ResourcesGroup)] -[UserOnly] -public class WatchlistApi(IWatchStatusRepository repository) : BaseApi -{ - /// - /// Get all - /// - /// - /// Get all resources in the user's watchlist - /// - /// Filter the returned items. - /// How many items per page should be returned, where should the page start... - /// The aditional fields to include in the result. - /// A list of resources that match every filters. - /// Invalid filters or sort information. - [HttpGet] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - public async Task>> GetAll( - [FromQuery] Filter? filter, - [FromQuery] Pagination pagination, - [FromQuery] Include? fields - ) - { - if (User.GetId() == null) - throw new UnauthorizedException(); - ICollection resources = await repository.GetAll(filter, fields, pagination); - - return Page(resources, pagination.Limit); - } -} diff --git a/back/src/Kyoo.Meilisearch/FilterExtensionMethods.cs b/back/src/Kyoo.Meilisearch/FilterExtensionMethods.cs deleted file mode 100644 index d32d2a63..00000000 --- a/back/src/Kyoo.Meilisearch/FilterExtensionMethods.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Kyoo.Abstractions.Models.Utils; -using static System.Text.Json.JsonNamingPolicy; - -namespace Kyoo.Meiliseach; - -internal static class FilterExtensionMethods -{ - public static string? CreateMeilisearchFilter(this Filter? filter) - { - return filter switch - { - Filter.And and - => $"({and.First.CreateMeilisearchFilter()}) AND ({and.Second.CreateMeilisearchFilter()})", - Filter.Or or - => $"({or.First.CreateMeilisearchFilter()}) OR ({or.Second.CreateMeilisearchFilter()})", - Filter.Gt gt => CreateBasicFilterString(gt.Property, ">", gt.Value), - Filter.Lt lt => CreateBasicFilterString(lt.Property, "<", lt.Value), - Filter.Ge ge => CreateBasicFilterString(ge.Property, ">=", ge.Value), - Filter.Le le => CreateBasicFilterString(le.Property, "<=", le.Value), - Filter.Eq eq => CreateBasicFilterString(eq.Property, "=", eq.Value), - Filter.Has has => CreateBasicFilterString(has.Property, "=", has.Value), - Filter.Ne ne => CreateBasicFilterString(ne.Property, "!=", ne.Value), - Filter.Not not => $"NOT ({not.Filter.CreateMeilisearchFilter()})", - Filter.CmpRandom - => throw new ValidationException("Random comparison is not supported."), - _ => null - }; - } - - private static string CreateBasicFilterString(string property, string @operator, object? value) - { - return $"{CamelCase.ConvertName(property)} {@operator} {value.InMeilsearchFilterFormat()}"; - } - - private static object? InMeilsearchFilterFormat(this object? value) - { - return value switch - { - null => null, - string s => s.Any(char.IsWhiteSpace) ? $"\"{s.Replace("\"", "\\\"")}\"" : s, - DateTimeOffset dateTime => dateTime.ToUnixTimeSeconds(), - DateOnly date => date.ToUnixTimeSeconds(), - _ => value - }; - } - - public static long ToUnixTimeSeconds(this DateOnly date) - { - return new DateTimeOffset(date.ToDateTime(new TimeOnly())).ToUnixTimeSeconds(); - } -} diff --git a/back/src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj b/back/src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj deleted file mode 100644 index e0814793..00000000 --- a/back/src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - enable - Kyoo.Meilisearch - - - - - - - - - - - diff --git a/back/src/Kyoo.Meilisearch/MeiliSync.cs b/back/src/Kyoo.Meilisearch/MeiliSync.cs deleted file mode 100644 index e995ce6a..00000000 --- a/back/src/Kyoo.Meilisearch/MeiliSync.cs +++ /dev/null @@ -1,112 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections; -using System.Dynamic; -using System.Reflection; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Meilisearch; -using static System.Text.Json.JsonNamingPolicy; - -namespace Kyoo.Meiliseach; - -public class MeiliSync -{ - private readonly MeilisearchClient _client; - - public MeiliSync(MeilisearchClient client) - { - _client = client; - - IRepository.OnCreated += (x) => CreateOrUpdate("items", x, nameof(Movie)); - IRepository.OnEdited += (x) => CreateOrUpdate("items", x, nameof(Movie)); - IRepository.OnDeleted += (x) => _Delete("items", x.Id, nameof(Movie)); - IRepository.OnCreated += (x) => CreateOrUpdate("items", x, nameof(Show)); - IRepository.OnEdited += (x) => CreateOrUpdate("items", x, nameof(Show)); - IRepository.OnDeleted += (x) => _Delete("items", x.Id, nameof(Show)); - IRepository.OnCreated += (x) => CreateOrUpdate("items", x, nameof(Collection)); - IRepository.OnEdited += (x) => CreateOrUpdate("items", x, nameof(Collection)); - IRepository.OnDeleted += (x) => _Delete("items", x.Id, nameof(Collection)); - - IRepository.OnCreated += (x) => CreateOrUpdate(nameof(Episode), x); - IRepository.OnEdited += (x) => CreateOrUpdate(nameof(Episode), x); - IRepository.OnDeleted += (x) => _Delete(nameof(Episode), x.Id); - - IRepository.OnCreated += (x) => CreateOrUpdate(nameof(Studio), x); - IRepository.OnEdited += (x) => CreateOrUpdate(nameof(Studio), x); - IRepository.OnDeleted += (x) => _Delete(nameof(Studio), x.Id); - } - - public Task CreateOrUpdate(string index, IResource item, string? kind = null) - { - if (kind != null) - { - dynamic expando = new ExpandoObject(); - var dictionary = (IDictionary)expando; - - foreach (PropertyInfo property in item.GetType().GetProperties()) - dictionary.Add( - CamelCase.ConvertName(property.Name), - ConvertToMeilisearchFormat(property.GetValue(item)) - ); - dictionary.Add("ref", $"{kind}-{item.Id}"); - expando.kind = kind; - return _client.Index(index).AddDocumentsAsync(new[] { expando }); - } - return _client.Index(index).AddDocumentsAsync(new[] { item }); - } - - private Task _Delete(string index, Guid id, string? kind = null) - { - if (kind != null) - { - return _client.Index(index).DeleteOneDocumentAsync($"{kind}/{id}"); - } - return _client.Index(index).DeleteOneDocumentAsync(id.ToString()); - } - - private object? ConvertToMeilisearchFormat(object? value) - { - return value switch - { - null => null, - string => value, - Enum => value.ToString(), - IEnumerable enumerable - => enumerable.Cast().Select(ConvertToMeilisearchFormat).ToArray(), - DateTimeOffset dateTime => dateTime.ToUnixTimeSeconds(), - DateOnly date => date.ToUnixTimeSeconds(), - _ => value - }; - } - - public async Task SyncEverything(ILibraryManager database) - { - foreach (Movie movie in await database.Movies.GetAll(limit: 0)) - await CreateOrUpdate("items", movie, nameof(Movie)); - foreach (Show show in await database.Shows.GetAll(limit: 0)) - await CreateOrUpdate("items", show, nameof(Show)); - foreach (Collection collection in await database.Collections.GetAll(limit: 0)) - await CreateOrUpdate("items", collection, nameof(Collection)); - foreach (Episode episode in await database.Episodes.GetAll(limit: 0)) - await CreateOrUpdate(nameof(Episode), episode); - foreach (Studio studio in await database.Studios.GetAll(limit: 0)) - await CreateOrUpdate(nameof(Studio), studio); - } -} diff --git a/back/src/Kyoo.Meilisearch/MeilisearchModule.cs b/back/src/Kyoo.Meilisearch/MeilisearchModule.cs deleted file mode 100644 index d3e6f47b..00000000 --- a/back/src/Kyoo.Meilisearch/MeilisearchModule.cs +++ /dev/null @@ -1,160 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Meilisearch; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using static System.Text.Json.JsonNamingPolicy; - -namespace Kyoo.Meiliseach; - -public static class MeilisearchModule -{ - public static Dictionary IndexSettings => - new() - { - { - "items", - new Settings() - { - SearchableAttributes = new[] - { - CamelCase.ConvertName(nameof(Movie.Name)), - CamelCase.ConvertName(nameof(Movie.Slug)), - CamelCase.ConvertName(nameof(Movie.Aliases)), - CamelCase.ConvertName(nameof(Movie.Path)), - CamelCase.ConvertName(nameof(Movie.Tags)), - CamelCase.ConvertName(nameof(Movie.Overview)), - }, - FilterableAttributes = new[] - { - CamelCase.ConvertName(nameof(Movie.Genres)), - CamelCase.ConvertName(nameof(Movie.Status)), - CamelCase.ConvertName(nameof(Movie.AirDate)), - CamelCase.ConvertName(nameof(Show.StartAir)), - CamelCase.ConvertName(nameof(Show.EndAir)), - CamelCase.ConvertName(nameof(Movie.StudioId)), - "kind" - }, - SortableAttributes = new[] - { - CamelCase.ConvertName(nameof(Movie.AirDate)), - CamelCase.ConvertName(nameof(Movie.AddedDate)), - CamelCase.ConvertName(nameof(Movie.Rating)), - CamelCase.ConvertName(nameof(Movie.Runtime)), - }, - DisplayedAttributes = new[] { CamelCase.ConvertName(nameof(Movie.Id)), "kind" }, - RankingRules = new[] - { - "words", - "typo", - "proximity", - "attribute", - "sort", - "exactness", - $"{CamelCase.ConvertName(nameof(Movie.Rating))}:desc", - } - // TODO: Add stopwords - } - }, - { - nameof(Episode), - new Settings() - { - SearchableAttributes = new[] - { - CamelCase.ConvertName(nameof(Episode.Name)), - CamelCase.ConvertName(nameof(Episode.Overview)), - CamelCase.ConvertName(nameof(Episode.Slug)), - CamelCase.ConvertName(nameof(Episode.Path)), - }, - FilterableAttributes = new[] - { - CamelCase.ConvertName(nameof(Episode.SeasonNumber)), - }, - SortableAttributes = new[] - { - CamelCase.ConvertName(nameof(Episode.ReleaseDate)), - CamelCase.ConvertName(nameof(Episode.AddedDate)), - CamelCase.ConvertName(nameof(Episode.SeasonNumber)), - CamelCase.ConvertName(nameof(Episode.EpisodeNumber)), - CamelCase.ConvertName(nameof(Episode.AbsoluteNumber)), - }, - DisplayedAttributes = new[] { CamelCase.ConvertName(nameof(Episode.Id)), }, - // TODO: Add stopwords - } - }, - { - nameof(Studio), - new Settings() - { - SearchableAttributes = new[] - { - CamelCase.ConvertName(nameof(Studio.Name)), - CamelCase.ConvertName(nameof(Studio.Slug)), - }, - FilterableAttributes = Array.Empty(), - SortableAttributes = Array.Empty(), - DisplayedAttributes = new[] { CamelCase.ConvertName(nameof(Studio.Id)), }, - // TODO: Add stopwords - } - }, - }; - - public static async Task Initialize(IServiceProvider provider) - { - MeilisearchClient client = provider.GetRequiredService(); - - await _CreateIndex(client, "items", true); - await _CreateIndex(client, nameof(Episode), false); - await _CreateIndex(client, nameof(Studio), false); - } - - public static async Task SyncDatabase(IServiceProvider provider) - { - await using AsyncServiceScope scope = provider.CreateAsyncScope(); - ILibraryManager database = scope.ServiceProvider.GetRequiredService(); - await scope.ServiceProvider.GetRequiredService().SyncEverything(database); - } - - private static async Task _CreateIndex(MeilisearchClient client, string index, bool hasKind) - { - TaskInfo task = await client.CreateIndexAsync( - index, - hasKind ? "ref" : CamelCase.ConvertName(nameof(IResource.Id)) - ); - await client.WaitForTaskAsync(task.TaskUid); - await client.Index(index).UpdateSettingsAsync(IndexSettings[index]); - } - - /// - public static void ConfigureMeilisearch(this WebApplicationBuilder builder) - { - builder.Services.AddSingleton( - new MeilisearchClient( - builder.Configuration.GetValue("MEILI_HOST", "http://meilisearch:7700"), - builder.Configuration.GetValue("MEILI_MASTER_KEY") - ) - ); - builder.Services.AddScoped(); - builder.Services.AddSingleton(); - } -} diff --git a/back/src/Kyoo.Meilisearch/SearchManager.cs b/back/src/Kyoo.Meilisearch/SearchManager.cs deleted file mode 100644 index 7f7ccaf4..00000000 --- a/back/src/Kyoo.Meilisearch/SearchManager.cs +++ /dev/null @@ -1,229 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.ComponentModel.DataAnnotations; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Utils; -using Meilisearch; -using static System.Text.Json.JsonNamingPolicy; - -namespace Kyoo.Meiliseach; - -public class SearchManager : ISearchManager -{ - private readonly MeilisearchClient _client; - private readonly ILibraryManager _libraryManager; - - private static IEnumerable _GetSortsBy(string index, Sort? sort) - where T : IQuery - { - return sort switch - { - Sort.Default => Array.Empty(), - Sort.By @sortBy - => MeilisearchModule - .IndexSettings[index] - .SortableAttributes.Contains( - sortBy.Key, - StringComparer.InvariantCultureIgnoreCase - ) - ? new[] - { - $"{CamelCase.ConvertName(sortBy.Key)}:{(sortBy.Desendant ? "desc" : "asc")}" - } - : throw new ValidationException($"Invalid sorting mode: {sortBy.Key}"), - Sort.Conglomerate(var list) => list.SelectMany(x => _GetSortsBy(index, x)), - Sort.Random - => throw new ValidationException( - "Random sorting is not supported while searching." - ), - _ => Array.Empty(), - }; - } - - public SearchManager(MeilisearchClient client, ILibraryManager libraryManager) - { - _client = client; - _libraryManager = libraryManager; - } - - private async Task.SearchResult> _Search( - string index, - string? query, - string? where = null, - Sort? sortBy = default, - SearchPagination? pagination = default, - Include? include = default - ) - where T : class, IResource, IQuery - { - // TODO: add filters and facets - ISearchable res = await _client - .Index(index) - .SearchAsync( - query, - new SearchQuery() - { - Filter = where, - Sort = _GetSortsBy(index, sortBy), - Limit = pagination?.Limit ?? 50, - Offset = pagination?.Skip ?? 0, - } - ); - return new SearchPage.SearchResult - { - Query = query, - Items = await _libraryManager - .Repository() - .FromIds(res.Hits.Select(x => x.Id).ToList(), include), - }; - } - - /// - public Task.SearchResult> SearchItems( - string? query, - Sort sortBy, - Filter? filter, - SearchPagination pagination, - Include? include = default - ) - { - return _Search( - "items", - query, - filter.CreateMeilisearchFilter(), - sortBy, - pagination, - include - ); - } - - /// - public Task.SearchResult> SearchMovies( - string? query, - Sort sortBy, - Filter? filter, - SearchPagination pagination, - Include? include = default - ) - { - return _Search( - "items", - query, - _CreateMediaTypeFilter(filter), - sortBy, - pagination, - include - ); - } - - /// - public Task.SearchResult> SearchShows( - string? query, - Sort sortBy, - Filter? filter, - SearchPagination pagination, - Include? include = default - ) - { - return _Search( - "items", - query, - _CreateMediaTypeFilter(filter), - sortBy, - pagination, - include - ); - } - - /// - public Task.SearchResult> SearchCollections( - string? query, - Sort sortBy, - Filter? filter, - SearchPagination pagination, - Include? include = default - ) - { - return _Search( - "items", - query, - _CreateMediaTypeFilter(filter), - sortBy, - pagination, - include - ); - } - - /// - public Task.SearchResult> SearchEpisodes( - string? query, - Sort sortBy, - Filter? filter, - SearchPagination pagination, - Include? include = default - ) - { - return _Search( - nameof(Episode), - query, - filter.CreateMeilisearchFilter(), - sortBy, - pagination, - include - ); - } - - /// - public Task.SearchResult> SearchStudios( - string? query, - Sort sortBy, - Filter? filter, - SearchPagination pagination, - Include? include = default - ) - { - return _Search( - nameof(Studio), - query, - filter.CreateMeilisearchFilter(), - sortBy, - pagination, - include - ); - } - - private string _CreateMediaTypeFilter(Filter? filter) - where T : ILibraryItem - { - string filterString = $"kind = {typeof(T).Name}"; - if (filter is not null) - { - filterString += $" AND ({filter.CreateMeilisearchFilter()})"; - } - return filterString; - } - - private class IdResource - { - public Guid Id { get; set; } - - public string? Kind { get; set; } - } -} diff --git a/back/src/Kyoo.Postgresql/DatabaseContext.cs b/back/src/Kyoo.Postgresql/DatabaseContext.cs deleted file mode 100644 index 7a717cc2..00000000 --- a/back/src/Kyoo.Postgresql/DatabaseContext.cs +++ /dev/null @@ -1,476 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Exceptions; -using Kyoo.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; - -namespace Kyoo.Postgresql; - -public abstract class DatabaseContext : DbContext -{ - private readonly IHttpContextAccessor _accessor; - - /// - /// Calculate the MD5 of a string, can only be used in database context. - /// - /// The string to hash - /// The hash - public static string MD5(string str) => throw new NotSupportedException(); - - public Guid? CurrentUserId => _accessor.HttpContext?.User.GetId(); - - public DbSet Collections { get; set; } - - public DbSet Movies { get; set; } - - public DbSet Shows { get; set; } - - public DbSet Seasons { get; set; } - - public DbSet Episodes { get; set; } - - public DbSet Studios { get; set; } - - public DbSet Users { get; set; } - - public DbSet MovieWatchStatus { get; set; } - - public DbSet ShowWatchStatus { get; set; } - - public DbSet EpisodeWatchStatus { get; set; } - - public DbSet Issues { get; set; } - - public DbSet Options { get; set; } - - /// - /// Add a many to many link between two resources. - /// - /// Types are order dependant. You can't inverse the order. Please always put the owner first. - /// The ID of the first resource. - /// The ID of the second resource. - /// The first resource type of the relation. It is the owner of the second - /// The second resource type of the relation. It is the contained resource. - public void AddLinks(Guid first, Guid second) - where T1 : class, IResource - where T2 : class, IResource - { - Set>(LinkName()) - .Add( - new Dictionary - { - [LinkNameFk()] = first, - [LinkNameFk()] = second - } - ); - } - - protected DatabaseContext(IHttpContextAccessor accessor) - { - _accessor = accessor; - } - - protected DatabaseContext(DbContextOptions options, IHttpContextAccessor accessor) - : base(options) - { - _accessor = accessor; - } - - protected abstract string LinkName() - where T : IResource - where T2 : IResource; - - protected abstract string LinkNameFk() - where T : IResource; - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - base.OnConfiguring(optionsBuilder); - optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); - } - - private static void _HasJson( - ModelBuilder builder, - Expression>> property - ) - where T : class - { - // TODO: Waiting for https://github.com/dotnet/efcore/issues/29825 - // modelBuilder.Entity() - // .OwnsOne(x => x.ExternalId, x => - // { - // x.ToJson(); - // }); - builder - .Entity() - .Property(property) - .HasConversion( - v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null), - v => - JsonSerializer.Deserialize>( - v, - (JsonSerializerOptions?)null - )! - ) - .HasColumnType("json"); - builder - .Entity() - .Property(property) - .Metadata.SetValueComparer( - new ValueComparer>( - (c1, c2) => c1!.SequenceEqual(c2!), - c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())) - ) - ); - } - - private static void _HasMetadata(ModelBuilder modelBuilder) - where T : class, IMetadata - { - _HasJson(modelBuilder, x => x.ExternalId); - } - - private static void _HasImages(ModelBuilder modelBuilder) - where T : class, IThumbnails - { - modelBuilder.Entity().OwnsOne(x => x.Poster, x => x.ToJson()); - modelBuilder.Entity().OwnsOne(x => x.Thumbnail, x => x.ToJson()); - modelBuilder.Entity().OwnsOne(x => x.Logo, x => x.ToJson()); - } - - private static void _HasAddedDate(ModelBuilder modelBuilder) - where T : class, IAddedDate - { - modelBuilder - .Entity() - .Property(x => x.AddedDate) - .HasDefaultValueSql("now() at time zone 'utc'") - .ValueGeneratedOnAdd(); - } - - private static void _HasRefreshDate(ModelBuilder builder) - where T : class, IRefreshable - { - // schedule a refresh soon since metadata can change frequently for recently added items ond online databases - builder - .Entity() - .Property(x => x.NextMetadataRefresh) - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'") - .ValueGeneratedOnAdd(); - } - - private void _HasManyToMany( - ModelBuilder modelBuilder, - Expression?>> firstNavigation, - Expression?>> secondNavigation - ) - where T : class, IResource - where T2 : class, IResource - { - modelBuilder - .Entity() - .HasMany(secondNavigation) - .WithMany(firstNavigation) - .UsingEntity>( - LinkName(), - x => - x.HasOne() - .WithMany() - .HasForeignKey(LinkNameFk()) - .OnDelete(DeleteBehavior.Cascade), - x => - x.HasOne() - .WithMany() - .HasForeignKey(LinkNameFk()) - .OnDelete(DeleteBehavior.Cascade) - ); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - modelBuilder.Entity().Ignore(x => x.FirstEpisode).Ignore(x => x.AirDate); - modelBuilder.Entity().Ignore(x => x.PreviousEpisode).Ignore(x => x.NextEpisode); - - modelBuilder - .Entity() - .HasMany(x => x.Seasons) - .WithOne(x => x.Show) - .OnDelete(DeleteBehavior.Cascade); - modelBuilder - .Entity() - .HasMany(x => x.Episodes) - .WithOne(x => x.Show) - .OnDelete(DeleteBehavior.Cascade); - modelBuilder - .Entity() - .HasMany(x => x.Episodes) - .WithOne(x => x.Season) - .OnDelete(DeleteBehavior.Cascade); - - modelBuilder - .Entity() - .HasOne(x => x.Studio) - .WithMany(x => x.Movies) - .OnDelete(DeleteBehavior.SetNull); - modelBuilder - .Entity() - .HasOne(x => x.Studio) - .WithMany(x => x.Shows) - .OnDelete(DeleteBehavior.SetNull); - - _HasManyToMany(modelBuilder, x => x.Movies, x => x.Collections); - _HasManyToMany(modelBuilder, x => x.Shows, x => x.Collections); - - _HasMetadata(modelBuilder); - _HasMetadata(modelBuilder); - _HasMetadata(modelBuilder); - _HasMetadata(modelBuilder); - _HasMetadata(modelBuilder); - _HasJson(modelBuilder, x => x.ExternalId); - - _HasImages(modelBuilder); - _HasImages(modelBuilder); - _HasImages(modelBuilder); - _HasImages(modelBuilder); - _HasImages(modelBuilder); - - _HasAddedDate(modelBuilder); - _HasAddedDate(modelBuilder); - _HasAddedDate(modelBuilder); - _HasAddedDate(modelBuilder); - _HasAddedDate(modelBuilder); - _HasAddedDate(modelBuilder); - _HasAddedDate(modelBuilder); - - _HasRefreshDate(modelBuilder); - _HasRefreshDate(modelBuilder); - _HasRefreshDate(modelBuilder); - _HasRefreshDate(modelBuilder); - _HasRefreshDate(modelBuilder); - - modelBuilder - .Entity() - .HasKey(x => new { User = x.UserId, Movie = x.MovieId }); - modelBuilder - .Entity() - .HasKey(x => new { User = x.UserId, Show = x.ShowId }); - modelBuilder - .Entity() - .HasKey(x => new { User = x.UserId, Episode = x.EpisodeId }); - - modelBuilder - .Entity() - .HasOne(x => x.Movie) - .WithMany(x => x.Watched) - .HasForeignKey(x => x.MovieId) - .OnDelete(DeleteBehavior.Cascade); - modelBuilder - .Entity() - .HasOne(x => x.Show) - .WithMany(x => x.Watched) - .HasForeignKey(x => x.ShowId) - .OnDelete(DeleteBehavior.Cascade); - modelBuilder - .Entity() - .HasOne(x => x.NextEpisode) - .WithMany() - .HasForeignKey(x => x.NextEpisodeId) - .OnDelete(DeleteBehavior.SetNull); - modelBuilder - .Entity() - .HasOne(x => x.Episode) - .WithMany(x => x.Watched) - .HasForeignKey(x => x.EpisodeId) - .OnDelete(DeleteBehavior.Cascade); - - modelBuilder.Entity().HasQueryFilter(x => x.UserId == CurrentUserId); - modelBuilder.Entity().HasQueryFilter(x => x.UserId == CurrentUserId); - modelBuilder.Entity().HasQueryFilter(x => x.UserId == CurrentUserId); - - modelBuilder.Entity().Navigation(x => x.NextEpisode).AutoInclude(); - - _HasAddedDate(modelBuilder); - _HasAddedDate(modelBuilder); - _HasAddedDate(modelBuilder); - - modelBuilder.Entity().Ignore(x => x.WatchStatus); - modelBuilder.Entity().Ignore(x => x.WatchStatus); - modelBuilder.Entity().Ignore(x => x.WatchStatus); - - modelBuilder.Entity().HasIndex(x => x.Slug).IsUnique(); - modelBuilder.Entity().HasIndex(x => x.Slug).IsUnique(); - modelBuilder.Entity().HasIndex(x => x.Slug).IsUnique(); - modelBuilder.Entity().HasIndex(x => x.Slug).IsUnique(); - modelBuilder - .Entity() - .HasIndex(x => new { ShowID = x.ShowId, x.SeasonNumber }) - .IsUnique(); - modelBuilder.Entity().HasIndex(x => x.Slug).IsUnique(); - modelBuilder - .Entity() - .HasIndex(x => new - { - ShowID = x.ShowId, - x.SeasonNumber, - x.EpisodeNumber, - x.AbsoluteNumber - }) - .IsUnique(); - modelBuilder.Entity().HasIndex(x => x.Slug).IsUnique(); - modelBuilder.Entity().HasIndex(x => x.Slug).IsUnique(); - modelBuilder.Entity().HasIndex(x => x.Username).IsUnique(); - - modelBuilder.Entity().Ignore(x => x.Links); - - modelBuilder.Entity().HasKey(x => new { x.Domain, x.Cause }); - - _HasJson(modelBuilder, x => x.Settings); - _HasJson(modelBuilder, x => x.ExternalId); - _HasJson(modelBuilder, x => x.Extra); - - modelBuilder.Entity().HasKey(x => x.Key); - } - - public override int SaveChanges() - { - try - { - return base.SaveChanges(); - } - catch (DbUpdateException ex) - { - DiscardChanges(); - if (IsDuplicateException(ex)) - throw new DuplicatedItemException(); - throw; - } - } - - public override int SaveChanges(bool acceptAllChangesOnSuccess) - { - try - { - return base.SaveChanges(acceptAllChangesOnSuccess); - } - catch (DbUpdateException ex) - { - DiscardChanges(); - if (IsDuplicateException(ex)) - throw new DuplicatedItemException(); - throw; - } - } - - public override async Task SaveChangesAsync( - bool acceptAllChangesOnSuccess, - CancellationToken cancellationToken = default - ) - { - try - { - return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); - } - catch (DbUpdateException ex) - { - DiscardChanges(); - if (IsDuplicateException(ex)) - throw new DuplicatedItemException(); - throw; - } - } - - public override async Task SaveChangesAsync(CancellationToken cancellationToken = default) - { - try - { - return await base.SaveChangesAsync(cancellationToken); - } - catch (DbUpdateException ex) - { - DiscardChanges(); - if (IsDuplicateException(ex)) - throw new DuplicatedItemException(); - throw; - } - } - - public async Task SaveChangesAsync( - Func> getExisting, - CancellationToken cancellationToken = default - ) - { - try - { - return await SaveChangesAsync(cancellationToken); - } - catch (DbUpdateException ex) - { - DiscardChanges(); - if (IsDuplicateException(ex)) - throw new DuplicatedItemException(await getExisting()); - throw; - } - catch (DuplicatedItemException) - { - throw new DuplicatedItemException(await getExisting()); - } - } - - public async Task SaveIfNoDuplicates(CancellationToken cancellationToken = default) - { - try - { - return await SaveChangesAsync(cancellationToken); - } - catch (DuplicatedItemException) - { - return -1; - } - } - - public T? LocalEntity(string slug) - where T : class, IResource - { - return ChangeTracker.Entries().FirstOrDefault(x => x.Entity.Slug == slug)?.Entity; - } - - protected abstract bool IsDuplicateException(Exception ex); - - public void DiscardChanges() - { - foreach ( - EntityEntry entry in ChangeTracker.Entries().Where(x => x.State != EntityState.Detached) - ) - { - entry.State = EntityState.Detached; - } - } -} diff --git a/back/src/Kyoo.Postgresql/DbConfigurationProvider.cs b/back/src/Kyoo.Postgresql/DbConfigurationProvider.cs deleted file mode 100644 index 9fa1b1ee..00000000 --- a/back/src/Kyoo.Postgresql/DbConfigurationProvider.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Linq; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; - -public class DbConfigurationProvider(Action action) : ConfigurationProvider -{ - public override void Load() - { - DbContextOptionsBuilder builder = new(); - action(builder); - using var context = new PostgresContext(builder.Options, null!); - Data = context.Options.ToDictionary(c => c.Key, c => c.Value)!; - } -} - -public class DbConfigurationSource(Action action) : IConfigurationSource -{ - public IConfigurationProvider Build(IConfigurationBuilder builder) => - new DbConfigurationProvider(action); -} - -public class ServerOption -{ - public string Key { get; set; } - public string Value { get; set; } -} diff --git a/back/src/Kyoo.Postgresql/Kyoo.Postgresql.csproj b/back/src/Kyoo.Postgresql/Kyoo.Postgresql.csproj deleted file mode 100644 index d68944fe..00000000 --- a/back/src/Kyoo.Postgresql/Kyoo.Postgresql.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - Kyoo.Postgresql - Kyoo.Postgresql - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - diff --git a/back/src/Kyoo.Postgresql/Migrations/20231128171554_Initial.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20231128171554_Initial.Designer.cs deleted file mode 100644 index aa0ea928..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20231128171554_Initial.Designer.cs +++ /dev/null @@ -1,1082 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20231128171554_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("UserId"); - - b1.ToTable("users"); - - b1.WithOwner() - .HasForeignKey("UserId") - .HasConstraintName("fk_users_users_id"); - }); - - b.Navigation("Logo"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20231128171554_Initial.cs b/back/src/Kyoo.Postgresql/Migrations/20231128171554_Initial.cs deleted file mode 100644 index f1c7b924..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20231128171554_Initial.cs +++ /dev/null @@ -1,560 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations; - -/// -public partial class Initial : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder - .AlterDatabase() - .Annotation( - "Npgsql:Enum:genre", - "action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western" - ) - .Annotation("Npgsql:Enum:status", "unknown,finished,airing,planned"); - - migrationBuilder.CreateTable( - name: "collections", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - name = table.Column(type: "text", nullable: false), - overview = table.Column(type: "text", nullable: true), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - poster_source = table.Column(type: "text", nullable: true), - poster_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - thumbnail_source = table.Column(type: "text", nullable: true), - thumbnail_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - logo_source = table.Column(type: "text", nullable: true), - logo_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - external_id = table.Column(type: "json", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("pk_collections", x => x.id); - } - ); - - migrationBuilder.CreateTable( - name: "studios", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - name = table.Column(type: "text", nullable: false), - external_id = table.Column(type: "json", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("pk_studios", x => x.id); - } - ); - - migrationBuilder.CreateTable( - name: "users", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - username = table.Column(type: "text", nullable: false), - email = table.Column(type: "text", nullable: false), - password = table.Column(type: "text", nullable: false), - permissions = table.Column(type: "text[]", nullable: false), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - logo_source = table.Column(type: "text", nullable: true), - logo_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ) - }, - constraints: table => - { - table.PrimaryKey("pk_users", x => x.id); - } - ); - - migrationBuilder.CreateTable( - name: "movies", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - name = table.Column(type: "text", nullable: false), - tagline = table.Column(type: "text", nullable: true), - aliases = table.Column(type: "text[]", nullable: false), - path = table.Column(type: "text", nullable: false), - overview = table.Column(type: "text", nullable: true), - tags = table.Column(type: "text[]", nullable: false), - genres = table.Column(type: "genre[]", nullable: false), - status = table.Column(type: "status", nullable: false), - rating = table.Column(type: "integer", nullable: false), - runtime = table.Column(type: "integer", nullable: false), - air_date = table.Column(type: "timestamp with time zone", nullable: true), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - poster_source = table.Column(type: "text", nullable: true), - poster_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - thumbnail_source = table.Column(type: "text", nullable: true), - thumbnail_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - logo_source = table.Column(type: "text", nullable: true), - logo_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - trailer = table.Column(type: "text", nullable: true), - external_id = table.Column(type: "json", nullable: false), - studio_id = table.Column(type: "uuid", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("pk_movies", x => x.id); - table.ForeignKey( - name: "fk_movies_studios_studio_id", - column: x => x.studio_id, - principalTable: "studios", - principalColumn: "id", - onDelete: ReferentialAction.SetNull - ); - } - ); - - migrationBuilder.CreateTable( - name: "shows", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - name = table.Column(type: "text", nullable: false), - tagline = table.Column(type: "text", nullable: true), - aliases = table.Column>(type: "text[]", nullable: false), - overview = table.Column(type: "text", nullable: true), - tags = table.Column>(type: "text[]", nullable: false), - genres = table.Column>(type: "genre[]", nullable: false), - status = table.Column(type: "status", nullable: false), - rating = table.Column(type: "integer", nullable: false), - start_air = table.Column( - type: "timestamp with time zone", - nullable: true - ), - end_air = table.Column(type: "timestamp with time zone", nullable: true), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - poster_source = table.Column(type: "text", nullable: true), - poster_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - thumbnail_source = table.Column(type: "text", nullable: true), - thumbnail_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - logo_source = table.Column(type: "text", nullable: true), - logo_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - trailer = table.Column(type: "text", nullable: true), - external_id = table.Column(type: "json", nullable: false), - studio_id = table.Column(type: "uuid", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("pk_shows", x => x.id); - table.ForeignKey( - name: "fk_shows_studios_studio_id", - column: x => x.studio_id, - principalTable: "studios", - principalColumn: "id", - onDelete: ReferentialAction.SetNull - ); - } - ); - - migrationBuilder.CreateTable( - name: "link_collection_movie", - columns: table => new - { - collection_id = table.Column(type: "uuid", nullable: false), - movie_id = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey( - "pk_link_collection_movie", - x => new { x.collection_id, x.movie_id } - ); - table.ForeignKey( - name: "fk_link_collection_movie_collections_collection_id", - column: x => x.collection_id, - principalTable: "collections", - principalColumn: "id", - onDelete: ReferentialAction.Cascade - ); - table.ForeignKey( - name: "fk_link_collection_movie_movies_movie_id", - column: x => x.movie_id, - principalTable: "movies", - principalColumn: "id", - onDelete: ReferentialAction.Cascade - ); - } - ); - - migrationBuilder.CreateTable( - name: "link_collection_show", - columns: table => new - { - collection_id = table.Column(type: "uuid", nullable: false), - show_id = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey( - "pk_link_collection_show", - x => new { x.collection_id, x.show_id } - ); - table.ForeignKey( - name: "fk_link_collection_show_collections_collection_id", - column: x => x.collection_id, - principalTable: "collections", - principalColumn: "id", - onDelete: ReferentialAction.Cascade - ); - table.ForeignKey( - name: "fk_link_collection_show_shows_show_id", - column: x => x.show_id, - principalTable: "shows", - principalColumn: "id", - onDelete: ReferentialAction.Cascade - ); - } - ); - - migrationBuilder.CreateTable( - name: "seasons", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - show_id = table.Column(type: "uuid", nullable: false), - season_number = table.Column(type: "integer", nullable: false), - name = table.Column(type: "text", nullable: true), - overview = table.Column(type: "text", nullable: true), - start_date = table.Column( - type: "timestamp with time zone", - nullable: true - ), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - end_date = table.Column(type: "timestamp with time zone", nullable: true), - poster_source = table.Column(type: "text", nullable: true), - poster_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - thumbnail_source = table.Column(type: "text", nullable: true), - thumbnail_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - logo_source = table.Column(type: "text", nullable: true), - logo_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - external_id = table.Column(type: "json", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("pk_seasons", x => x.id); - table.ForeignKey( - name: "fk_seasons_shows_show_id", - column: x => x.show_id, - principalTable: "shows", - principalColumn: "id", - onDelete: ReferentialAction.Cascade - ); - } - ); - - migrationBuilder.CreateTable( - name: "episodes", - columns: table => new - { - id = table.Column(type: "uuid", nullable: false), - slug = table.Column( - type: "character varying(256)", - maxLength: 256, - nullable: false - ), - show_id = table.Column(type: "uuid", nullable: false), - season_id = table.Column(type: "uuid", nullable: true), - season_number = table.Column(type: "integer", nullable: true), - episode_number = table.Column(type: "integer", nullable: true), - absolute_number = table.Column(type: "integer", nullable: true), - path = table.Column(type: "text", nullable: false), - name = table.Column(type: "text", nullable: true), - overview = table.Column(type: "text", nullable: true), - runtime = table.Column(type: "integer", nullable: false), - release_date = table.Column( - type: "timestamp with time zone", - nullable: true - ), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - poster_source = table.Column(type: "text", nullable: true), - poster_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - thumbnail_source = table.Column(type: "text", nullable: true), - thumbnail_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - logo_source = table.Column(type: "text", nullable: true), - logo_blurhash = table.Column( - type: "character varying(32)", - maxLength: 32, - nullable: true - ), - external_id = table.Column(type: "json", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("pk_episodes", x => x.id); - table.ForeignKey( - name: "fk_episodes_seasons_season_id", - column: x => x.season_id, - principalTable: "seasons", - principalColumn: "id", - onDelete: ReferentialAction.Cascade - ); - table.ForeignKey( - name: "fk_episodes_shows_show_id", - column: x => x.show_id, - principalTable: "shows", - principalColumn: "id", - onDelete: ReferentialAction.Cascade - ); - } - ); - - migrationBuilder.CreateIndex( - name: "ix_collections_slug", - table: "collections", - column: "slug", - unique: true - ); - - migrationBuilder.CreateIndex( - name: "ix_episodes_season_id", - table: "episodes", - column: "season_id" - ); - - migrationBuilder.CreateIndex( - name: "ix_episodes_show_id_season_number_episode_number_absolute_numb", - table: "episodes", - columns: new[] { "show_id", "season_number", "episode_number", "absolute_number" }, - unique: true - ); - - migrationBuilder.CreateIndex( - name: "ix_episodes_slug", - table: "episodes", - column: "slug", - unique: true - ); - - migrationBuilder.CreateIndex( - name: "ix_link_collection_movie_movie_id", - table: "link_collection_movie", - column: "movie_id" - ); - - migrationBuilder.CreateIndex( - name: "ix_link_collection_show_show_id", - table: "link_collection_show", - column: "show_id" - ); - - migrationBuilder.CreateIndex( - name: "ix_movies_slug", - table: "movies", - column: "slug", - unique: true - ); - - migrationBuilder.CreateIndex( - name: "ix_movies_studio_id", - table: "movies", - column: "studio_id" - ); - - migrationBuilder.CreateIndex( - name: "ix_seasons_show_id_season_number", - table: "seasons", - columns: new[] { "show_id", "season_number" }, - unique: true - ); - - migrationBuilder.CreateIndex( - name: "ix_seasons_slug", - table: "seasons", - column: "slug", - unique: true - ); - - migrationBuilder.CreateIndex( - name: "ix_shows_slug", - table: "shows", - column: "slug", - unique: true - ); - - migrationBuilder.CreateIndex( - name: "ix_shows_studio_id", - table: "shows", - column: "studio_id" - ); - - migrationBuilder.CreateIndex( - name: "ix_studios_slug", - table: "studios", - column: "slug", - unique: true - ); - - migrationBuilder.CreateIndex( - name: "ix_users_slug", - table: "users", - column: "slug", - unique: true - ); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable(name: "episodes"); - - migrationBuilder.DropTable(name: "link_collection_movie"); - - migrationBuilder.DropTable(name: "link_collection_show"); - - migrationBuilder.DropTable(name: "users"); - - migrationBuilder.DropTable(name: "seasons"); - - migrationBuilder.DropTable(name: "movies"); - - migrationBuilder.DropTable(name: "collections"); - - migrationBuilder.DropTable(name: "shows"); - - migrationBuilder.DropTable(name: "studios"); - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20231204000849_Watchlist.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20231204000849_Watchlist.Designer.cs deleted file mode 100644 index 2d6f5523..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20231204000849_Watchlist.Designer.cs +++ /dev/null @@ -1,1299 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20231204000849_Watchlist")] - partial class Watchlist - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("UserId"); - - b1.ToTable("users"); - - b1.WithOwner() - .HasForeignKey("UserId") - .HasConstraintName("fk_users_users_id"); - }); - - b.Navigation("Logo"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20231204000849_Watchlist.cs b/back/src/Kyoo.Postgresql/Migrations/20231204000849_Watchlist.cs deleted file mode 100644 index 6b2ca664..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20231204000849_Watchlist.cs +++ /dev/null @@ -1,220 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using Kyoo.Abstractions.Models; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations; - -/// -public partial class Watchlist : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder - .AlterDatabase() - .Annotation( - "Npgsql:Enum:genre", - "action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western" - ) - .Annotation("Npgsql:Enum:status", "unknown,finished,airing,planned") - .Annotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned") - .OldAnnotation( - "Npgsql:Enum:genre", - "action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western" - ) - .OldAnnotation("Npgsql:Enum:status", "unknown,finished,airing,planned"); - - migrationBuilder.CreateTable( - name: "episode_watch_status", - columns: table => new - { - user_id = table.Column(type: "uuid", nullable: false), - episode_id = table.Column(type: "uuid", nullable: false), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - played_date = table.Column( - type: "timestamp with time zone", - nullable: true - ), - status = table.Column(type: "watch_status", nullable: false), - watched_time = table.Column(type: "integer", nullable: true), - watched_percent = table.Column(type: "integer", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("pk_episode_watch_status", x => new { x.user_id, x.episode_id }); - table.ForeignKey( - name: "fk_episode_watch_status_episodes_episode_id", - column: x => x.episode_id, - principalTable: "episodes", - principalColumn: "id", - onDelete: ReferentialAction.Cascade - ); - table.ForeignKey( - name: "fk_episode_watch_status_users_user_id", - column: x => x.user_id, - principalTable: "users", - principalColumn: "id", - onDelete: ReferentialAction.Cascade - ); - } - ); - - migrationBuilder.CreateTable( - name: "movie_watch_status", - columns: table => new - { - user_id = table.Column(type: "uuid", nullable: false), - movie_id = table.Column(type: "uuid", nullable: false), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - played_date = table.Column( - type: "timestamp with time zone", - nullable: true - ), - status = table.Column(type: "watch_status", nullable: false), - watched_time = table.Column(type: "integer", nullable: true), - watched_percent = table.Column(type: "integer", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("pk_movie_watch_status", x => new { x.user_id, x.movie_id }); - table.ForeignKey( - name: "fk_movie_watch_status_movies_movie_id", - column: x => x.movie_id, - principalTable: "movies", - principalColumn: "id", - onDelete: ReferentialAction.Cascade - ); - table.ForeignKey( - name: "fk_movie_watch_status_users_user_id", - column: x => x.user_id, - principalTable: "users", - principalColumn: "id", - onDelete: ReferentialAction.Cascade - ); - } - ); - - migrationBuilder.CreateTable( - name: "show_watch_status", - columns: table => new - { - user_id = table.Column(type: "uuid", nullable: false), - show_id = table.Column(type: "uuid", nullable: false), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ), - played_date = table.Column( - type: "timestamp with time zone", - nullable: true - ), - status = table.Column(type: "watch_status", nullable: false), - unseen_episodes_count = table.Column(type: "integer", nullable: false), - next_episode_id = table.Column(type: "uuid", nullable: true), - watched_time = table.Column(type: "integer", nullable: true), - watched_percent = table.Column(type: "integer", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("pk_show_watch_status", x => new { x.user_id, x.show_id }); - table.ForeignKey( - name: "fk_show_watch_status_episodes_next_episode_id", - column: x => x.next_episode_id, - principalTable: "episodes", - principalColumn: "id" - ); - table.ForeignKey( - name: "fk_show_watch_status_shows_show_id", - column: x => x.show_id, - principalTable: "shows", - principalColumn: "id", - onDelete: ReferentialAction.Cascade - ); - table.ForeignKey( - name: "fk_show_watch_status_users_user_id", - column: x => x.user_id, - principalTable: "users", - principalColumn: "id", - onDelete: ReferentialAction.Cascade - ); - } - ); - - migrationBuilder.CreateIndex( - name: "ix_episode_watch_status_episode_id", - table: "episode_watch_status", - column: "episode_id" - ); - - migrationBuilder.CreateIndex( - name: "ix_movie_watch_status_movie_id", - table: "movie_watch_status", - column: "movie_id" - ); - - migrationBuilder.CreateIndex( - name: "ix_show_watch_status_next_episode_id", - table: "show_watch_status", - column: "next_episode_id" - ); - - migrationBuilder.CreateIndex( - name: "ix_show_watch_status_show_id", - table: "show_watch_status", - column: "show_id" - ); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable(name: "episode_watch_status"); - - migrationBuilder.DropTable(name: "movie_watch_status"); - - migrationBuilder.DropTable(name: "show_watch_status"); - - migrationBuilder - .AlterDatabase() - .Annotation( - "Npgsql:Enum:genre", - "action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western" - ) - .Annotation("Npgsql:Enum:status", "unknown,finished,airing,planned") - .OldAnnotation( - "Npgsql:Enum:genre", - "action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western" - ) - .OldAnnotation("Npgsql:Enum:status", "unknown,finished,airing,planned") - .OldAnnotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned"); - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20231220093441_Settings.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20231220093441_Settings.Designer.cs deleted file mode 100644 index 3b23bdc0..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20231220093441_Settings.Designer.cs +++ /dev/null @@ -1,1304 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20231220093441_Settings")] - partial class Settings - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("UserId"); - - b1.ToTable("users"); - - b1.WithOwner() - .HasForeignKey("UserId") - .HasConstraintName("fk_users_users_id"); - }); - - b.Navigation("Logo"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20231220093441_Settings.cs b/back/src/Kyoo.Postgresql/Migrations/20231220093441_Settings.cs deleted file mode 100644 index 8eb837cd..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20231220093441_Settings.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations; - -/// -public partial class Settings : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "settings", - table: "users", - type: "json", - nullable: false, - defaultValue: "{}" - ); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn(name: "settings", table: "users"); - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240120154137_RuntimeNullable.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20240120154137_RuntimeNullable.Designer.cs deleted file mode 100644 index 174321cb..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240120154137_RuntimeNullable.Designer.cs +++ /dev/null @@ -1,1307 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20240120154137_RuntimeNullable")] - partial class RuntimeNullable - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("UserId"); - - b1.ToTable("users"); - - b1.WithOwner() - .HasForeignKey("UserId") - .HasConstraintName("fk_users_users_id"); - }); - - b.Navigation("Logo"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240120154137_RuntimeNullable.cs b/back/src/Kyoo.Postgresql/Migrations/20240120154137_RuntimeNullable.cs deleted file mode 100644 index 63ec85c9..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240120154137_RuntimeNullable.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations; - -/// -public partial class RuntimeNullable : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "runtime", - table: "movies", - type: "integer", - nullable: true, - oldClrType: typeof(int), - oldType: "integer" - ); - - migrationBuilder.AlterColumn( - name: "runtime", - table: "episodes", - type: "integer", - nullable: true, - oldClrType: typeof(int), - oldType: "integer" - ); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "runtime", - table: "movies", - type: "integer", - nullable: false, - defaultValue: 0, - oldClrType: typeof(int), - oldType: "integer", - oldNullable: true - ); - - migrationBuilder.AlterColumn( - name: "runtime", - table: "episodes", - type: "integer", - nullable: false, - defaultValue: 0, - oldClrType: typeof(int), - oldType: "integer", - oldNullable: true - ); - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240204193443_RemoveUserLogo.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20240204193443_RemoveUserLogo.Designer.cs deleted file mode 100644 index 62149ac6..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240204193443_RemoveUserLogo.Designer.cs +++ /dev/null @@ -1,1276 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20240204193443_RemoveUserLogo")] - partial class RemoveUserLogo - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240204193443_RemoveUserLogo.cs b/back/src/Kyoo.Postgresql/Migrations/20240204193443_RemoveUserLogo.cs deleted file mode 100644 index 6907cb10..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240204193443_RemoveUserLogo.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace Kyoo.Postgresql.Migrations; - -/// -public partial class RemoveUserLogo : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn(name: "logo_blurhash", table: "users"); - - migrationBuilder.DropColumn(name: "logo_source", table: "users"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "logo_blurhash", - table: "users", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo_source", - table: "users", - type: "text", - nullable: true - ); - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240217143306_AddIssues.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20240217143306_AddIssues.Designer.cs deleted file mode 100644 index 19d8b20e..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240217143306_AddIssues.Designer.cs +++ /dev/null @@ -1,1309 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20240217143306_AddIssues")] - partial class AddIssues - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Issue", b => - { - b.Property("Domain") - .HasColumnType("text") - .HasColumnName("domain"); - - b.Property("Cause") - .HasColumnType("text") - .HasColumnName("cause"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Extra") - .IsRequired() - .HasColumnType("json") - .HasColumnName("extra"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text") - .HasColumnName("reason"); - - b.HasKey("Domain", "Cause") - .HasName("pk_issues"); - - b.ToTable("issues", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240217143306_AddIssues.cs b/back/src/Kyoo.Postgresql/Migrations/20240217143306_AddIssues.cs deleted file mode 100644 index 8aec0204..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240217143306_AddIssues.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations; - -/// -public partial class AddIssues : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "fk_show_watch_status_episodes_next_episode_id", - table: "show_watch_status" - ); - - migrationBuilder.CreateTable( - name: "issues", - columns: table => new - { - domain = table.Column(type: "text", nullable: false), - cause = table.Column(type: "text", nullable: false), - reason = table.Column(type: "text", nullable: false), - extra = table.Column(type: "json", nullable: false), - added_date = table.Column( - type: "timestamp with time zone", - nullable: false, - defaultValueSql: "now() at time zone 'utc'" - ) - }, - constraints: table => - { - table.PrimaryKey("pk_issues", x => new { x.domain, x.cause }); - } - ); - - migrationBuilder.AddForeignKey( - name: "fk_show_watch_status_episodes_next_episode_id", - table: "show_watch_status", - column: "next_episode_id", - principalTable: "episodes", - principalColumn: "id", - onDelete: ReferentialAction.SetNull - ); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "fk_show_watch_status_episodes_next_episode_id", - table: "show_watch_status" - ); - - migrationBuilder.DropTable(name: "issues"); - - migrationBuilder.AddForeignKey( - name: "fk_show_watch_status_episodes_next_episode_id", - table: "show_watch_status", - column: "next_episode_id", - principalTable: "episodes", - principalColumn: "id" - ); - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240219170615_AddPlayPermission.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20240219170615_AddPlayPermission.Designer.cs deleted file mode 100644 index 03822c11..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240219170615_AddPlayPermission.Designer.cs +++ /dev/null @@ -1,1309 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20240219170615_AddPlayPermission")] - partial class AddPlayPermission - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Issue", b => - { - b.Property("Domain") - .HasColumnType("text") - .HasColumnName("domain"); - - b.Property("Cause") - .HasColumnType("text") - .HasColumnName("cause"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Extra") - .IsRequired() - .HasColumnType("json") - .HasColumnName("extra"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text") - .HasColumnName("reason"); - - b.HasKey("Domain", "Cause") - .HasName("pk_issues"); - - b.ToTable("issues", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240219170615_AddPlayPermission.cs b/back/src/Kyoo.Postgresql/Migrations/20240219170615_AddPlayPermission.cs deleted file mode 100644 index 0144dbdb..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240219170615_AddPlayPermission.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations; - -/// -public partial class AddPlayPermission : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - // language=PostgreSQL - migrationBuilder.Sql( - "update users set permissions = ARRAY_APPEND(permissions, 'overall.play');" - ); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) { } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240229202049_AddUserExternalId.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20240229202049_AddUserExternalId.Designer.cs deleted file mode 100644 index e4a49eee..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240229202049_AddUserExternalId.Designer.cs +++ /dev/null @@ -1,1314 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20240229202049_AddUserExternalId")] - partial class AddUserExternalId - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Issue", b => - { - b.Property("Domain") - .HasColumnType("text") - .HasColumnName("domain"); - - b.Property("Cause") - .HasColumnType("text") - .HasColumnName("cause"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Extra") - .IsRequired() - .HasColumnType("json") - .HasColumnName("extra"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text") - .HasColumnName("reason"); - - b.HasKey("Domain", "Cause") - .HasName("pk_issues"); - - b.ToTable("issues", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240229202049_AddUserExternalId.cs b/back/src/Kyoo.Postgresql/Migrations/20240229202049_AddUserExternalId.cs deleted file mode 100644 index 4eda4dfc..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240229202049_AddUserExternalId.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations; - -/// -public partial class AddUserExternalId : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "external_id", - table: "users", - type: "json", - nullable: false, - defaultValue: "{}" - ); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn(name: "external_id", table: "users"); - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240302151906_MakePasswordOptional.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20240302151906_MakePasswordOptional.Designer.cs deleted file mode 100644 index 387ae7cf..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240302151906_MakePasswordOptional.Designer.cs +++ /dev/null @@ -1,1313 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20240302151906_MakePasswordOptional")] - partial class MakePasswordOptional - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Issue", b => - { - b.Property("Domain") - .HasColumnType("text") - .HasColumnName("domain"); - - b.Property("Cause") - .HasColumnType("text") - .HasColumnName("cause"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Extra") - .IsRequired() - .HasColumnType("json") - .HasColumnName("extra"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text") - .HasColumnName("reason"); - - b.HasKey("Domain", "Cause") - .HasName("pk_issues"); - - b.ToTable("issues", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Password") - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240302151906_MakePasswordOptional.cs b/back/src/Kyoo.Postgresql/Migrations/20240302151906_MakePasswordOptional.cs deleted file mode 100644 index c6ff637e..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240302151906_MakePasswordOptional.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations; - -/// -public partial class MakePasswordOptional : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "password", - table: "users", - type: "text", - nullable: true, - oldClrType: typeof(string), - oldType: "text" - ); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "password", - table: "users", - type: "text", - nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "text", - oldNullable: true - ); - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240324174638_UseDateOnly.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20240324174638_UseDateOnly.Designer.cs deleted file mode 100644 index b8e4163f..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240324174638_UseDateOnly.Designer.cs +++ /dev/null @@ -1,1317 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20240324174638_UseDateOnly")] - partial class UseDateOnly - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned", "deleted" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("date") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Issue", b => - { - b.Property("Domain") - .HasColumnType("text") - .HasColumnName("domain"); - - b.Property("Cause") - .HasColumnType("text") - .HasColumnName("cause"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Extra") - .IsRequired() - .HasColumnType("json") - .HasColumnName("extra"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text") - .HasColumnName("reason"); - - b.HasKey("Domain", "Cause") - .HasName("pk_issues"); - - b.ToTable("issues", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("date") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("date") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("date") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("date") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("date") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Password") - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.HasIndex("Username") - .IsUnique() - .HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240324174638_UseDateOnly.cs b/back/src/Kyoo.Postgresql/Migrations/20240324174638_UseDateOnly.cs deleted file mode 100644 index eb82dddf..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240324174638_UseDateOnly.cs +++ /dev/null @@ -1,177 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations; - -/// -public partial class UseDateOnly : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder - .AlterDatabase() - .Annotation( - "Npgsql:Enum:genre", - "action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western" - ) - .Annotation("Npgsql:Enum:status", "unknown,finished,airing,planned") - .Annotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned,deleted") - .OldAnnotation( - "Npgsql:Enum:genre", - "action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western" - ) - .OldAnnotation("Npgsql:Enum:status", "unknown,finished,airing,planned") - .OldAnnotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned"); - - migrationBuilder.AlterColumn( - name: "start_air", - table: "shows", - type: "date", - nullable: true, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone", - oldNullable: true - ); - - migrationBuilder.AlterColumn( - name: "end_air", - table: "shows", - type: "date", - nullable: true, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone", - oldNullable: true - ); - - migrationBuilder.AlterColumn( - name: "start_date", - table: "seasons", - type: "date", - nullable: true, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone", - oldNullable: true - ); - - migrationBuilder.AlterColumn( - name: "end_date", - table: "seasons", - type: "date", - nullable: true, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone", - oldNullable: true - ); - - migrationBuilder.AlterColumn( - name: "air_date", - table: "movies", - type: "date", - nullable: true, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone", - oldNullable: true - ); - - migrationBuilder.AlterColumn( - name: "release_date", - table: "episodes", - type: "date", - nullable: true, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone", - oldNullable: true - ); - - migrationBuilder.CreateIndex( - name: "ix_users_username", - table: "users", - column: "username", - unique: true - ); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex(name: "ix_users_username", table: "users"); - - migrationBuilder - .AlterDatabase() - .Annotation( - "Npgsql:Enum:genre", - "action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western" - ) - .Annotation("Npgsql:Enum:status", "unknown,finished,airing,planned") - .Annotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned") - .OldAnnotation( - "Npgsql:Enum:genre", - "action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western" - ) - .OldAnnotation("Npgsql:Enum:status", "unknown,finished,airing,planned") - .OldAnnotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned,deleted"); - - migrationBuilder.AlterColumn( - name: "start_air", - table: "shows", - type: "timestamp with time zone", - nullable: true, - oldClrType: typeof(DateOnly), - oldType: "date", - oldNullable: true - ); - - migrationBuilder.AlterColumn( - name: "end_air", - table: "shows", - type: "timestamp with time zone", - nullable: true, - oldClrType: typeof(DateOnly), - oldType: "date", - oldNullable: true - ); - - migrationBuilder.AlterColumn( - name: "start_date", - table: "seasons", - type: "timestamp with time zone", - nullable: true, - oldClrType: typeof(DateOnly), - oldType: "date", - oldNullable: true - ); - - migrationBuilder.AlterColumn( - name: "end_date", - table: "seasons", - type: "timestamp with time zone", - nullable: true, - oldClrType: typeof(DateOnly), - oldType: "date", - oldNullable: true - ); - - migrationBuilder.AlterColumn( - name: "air_date", - table: "movies", - type: "timestamp with time zone", - nullable: true, - oldClrType: typeof(DateOnly), - oldType: "date", - oldNullable: true - ); - - migrationBuilder.AlterColumn( - name: "release_date", - table: "episodes", - type: "timestamp with time zone", - nullable: true, - oldClrType: typeof(DateOnly), - oldType: "date", - oldNullable: true - ); - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240401213942_AddGenres.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20240401213942_AddGenres.Designer.cs deleted file mode 100644 index 758e37da..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240401213942_AddGenres.Designer.cs +++ /dev/null @@ -1,1380 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20240401213942_AddGenres")] - partial class AddGenres - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.3") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum( - modelBuilder, - "genre", - new[] - { - "action", - "adventure", - "animation", - "comedy", - "crime", - "documentary", - "drama", - "family", - "fantasy", - "history", - "horror", - "music", - "mystery", - "romance", - "science_fiction", - "thriller", - "war", - "western", - "kids", - "news", - "reality", - "soap", - "talk", - "politics" - } - ); - NpgsqlModelBuilderExtensions.HasPostgresEnum( - modelBuilder, - "status", - new[] { "unknown", "finished", "airing", "planned" } - ); - NpgsqlModelBuilderExtensions.HasPostgresEnum( - modelBuilder, - "watch_status", - new[] { "completed", "watching", "droped", "planned", "deleted" } - ); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Collection", - b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview").HasColumnType("text").HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id").HasName("pk_collections"); - - b.HasIndex("Slug").IsUnique().HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Episode", - b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name").HasColumnType("text").HasColumnName("name"); - - b.Property("Overview").HasColumnType("text").HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("date") - .HasColumnName("release_date"); - - b.Property("Runtime").HasColumnType("integer").HasColumnName("runtime"); - - b.Property("SeasonId").HasColumnType("uuid").HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId").HasColumnType("uuid").HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id").HasName("pk_episodes"); - - b.HasIndex("SeasonId").HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug").IsUnique().HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName( - "ix_episodes_show_id_season_number_episode_number_absolute_numb" - ); - - b.ToTable("episodes", (string)null); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.EpisodeWatchStatus", - b => - { - b.Property("UserId").HasColumnType("uuid").HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId").HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId").HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Issue", - b => - { - b.Property("Domain").HasColumnType("text").HasColumnName("domain"); - - b.Property("Cause").HasColumnType("text").HasColumnName("cause"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Extra") - .IsRequired() - .HasColumnType("json") - .HasColumnName("extra"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text") - .HasColumnName("reason"); - - b.HasKey("Domain", "Cause").HasName("pk_issues"); - - b.ToTable("issues", (string)null); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Movie", - b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("date") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview").HasColumnType("text").HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating").HasColumnType("integer").HasColumnName("rating"); - - b.Property("Runtime").HasColumnType("integer").HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status").HasColumnType("status").HasColumnName("status"); - - b.Property("StudioId").HasColumnType("uuid").HasColumnName("studio_id"); - - b.Property("Tagline").HasColumnType("text").HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer").HasColumnType("text").HasColumnName("trailer"); - - b.HasKey("Id").HasName("pk_movies"); - - b.HasIndex("Slug").IsUnique().HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId").HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.MovieWatchStatus", - b => - { - b.Property("UserId").HasColumnType("uuid").HasColumnName("user_id"); - - b.Property("MovieId").HasColumnType("uuid").HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId").HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId").HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Season", - b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("date") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name").HasColumnType("text").HasColumnName("name"); - - b.Property("Overview").HasColumnType("text").HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId").HasColumnType("uuid").HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("date") - .HasColumnName("start_date"); - - b.HasKey("Id").HasName("pk_seasons"); - - b.HasIndex("Slug").IsUnique().HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Show", - b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir").HasColumnType("date").HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview").HasColumnType("text").HasColumnName("overview"); - - b.Property("Rating").HasColumnType("integer").HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("date") - .HasColumnName("start_air"); - - b.Property("Status").HasColumnType("status").HasColumnName("status"); - - b.Property("StudioId").HasColumnType("uuid").HasColumnName("studio_id"); - - b.Property("Tagline").HasColumnType("text").HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer").HasColumnType("text").HasColumnName("trailer"); - - b.HasKey("Id").HasName("pk_shows"); - - b.HasIndex("Slug").IsUnique().HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId").HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.ShowWatchStatus", - b => - { - b.Property("UserId").HasColumnType("uuid").HasColumnName("user_id"); - - b.Property("ShowId").HasColumnType("uuid").HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId").HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId").HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Studio", - b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id").HasName("pk_studios"); - - b.HasIndex("Slug").IsUnique().HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.User", - b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Password").HasColumnType("text").HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id").HasName("pk_users"); - - b.HasIndex("Slug").IsUnique().HasDatabaseName("ix_users_slug"); - - b.HasIndex("Username").IsUnique().HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - } - ); - - modelBuilder.Entity( - "link_collection_movie", - b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id").HasColumnType("uuid").HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id").HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id").HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - } - ); - - modelBuilder.Entity( - "link_collection_show", - b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id").HasColumnType("uuid").HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id").HasName("pk_link_collection_show"); - - b.HasIndex("show_id").HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Collection", - b => - { - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Logo", - b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - } - ); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Poster", - b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - } - ); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Thumbnail", - b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - } - ); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Episode", - b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Logo", - b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - } - ); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Poster", - b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - } - ); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Thumbnail", - b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - } - ); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.EpisodeWatchStatus", - b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Movie", - b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Logo", - b1 => - { - b1.Property("MovieId").HasColumnType("uuid").HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - } - ); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Poster", - b1 => - { - b1.Property("MovieId").HasColumnType("uuid").HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - } - ); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Thumbnail", - b1 => - { - b1.Property("MovieId").HasColumnType("uuid").HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - } - ); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.MovieWatchStatus", - b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Season", - b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Logo", - b1 => - { - b1.Property("SeasonId").HasColumnType("uuid").HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - } - ); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Poster", - b1 => - { - b1.Property("SeasonId").HasColumnType("uuid").HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - } - ); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Thumbnail", - b1 => - { - b1.Property("SeasonId").HasColumnType("uuid").HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - } - ); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Show", - b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Logo", - b1 => - { - b1.Property("ShowId").HasColumnType("uuid").HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - } - ); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Poster", - b1 => - { - b1.Property("ShowId").HasColumnType("uuid").HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - } - ); - - b.OwnsOne( - "Kyoo.Abstractions.Models.Image", - "Thumbnail", - b1 => - { - b1.Property("ShowId").HasColumnType("uuid").HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - } - ); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.ShowWatchStatus", - b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - } - ); - - modelBuilder.Entity( - "link_collection_movie", - b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - } - ); - - modelBuilder.Entity( - "link_collection_show", - b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Episode", - b => - { - b.Navigation("Watched"); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Movie", - b => - { - b.Navigation("Watched"); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Season", - b => - { - b.Navigation("Episodes"); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Show", - b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - } - ); - - modelBuilder.Entity( - "Kyoo.Abstractions.Models.Studio", - b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - } - ); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240401213942_AddGenres.cs b/back/src/Kyoo.Postgresql/Migrations/20240401213942_AddGenres.cs deleted file mode 100644 index 6cb5aa67..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240401213942_AddGenres.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations; - -/// -public partial class AddGenres : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder - .AlterDatabase() - .Annotation( - "Npgsql:Enum:genre", - "action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western,kids,news,reality,soap,talk,politics" - ) - .Annotation("Npgsql:Enum:status", "unknown,finished,airing,planned") - .Annotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned,deleted") - .OldAnnotation( - "Npgsql:Enum:genre", - "action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western" - ) - .OldAnnotation("Npgsql:Enum:status", "unknown,finished,airing,planned") - .OldAnnotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned,deleted"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder - .AlterDatabase() - .Annotation( - "Npgsql:Enum:genre", - "action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western" - ) - .Annotation("Npgsql:Enum:status", "unknown,finished,airing,planned") - .Annotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned,deleted") - .OldAnnotation( - "Npgsql:Enum:genre", - "action,adventure,animation,comedy,crime,documentary,drama,family,fantasy,history,horror,music,mystery,romance,science_fiction,thriller,war,western,kids,news,reality,soap,talk,politics" - ) - .OldAnnotation("Npgsql:Enum:status", "unknown,finished,airing,planned") - .OldAnnotation("Npgsql:Enum:watch_status", "completed,watching,droped,planned,deleted"); - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240414212454_AddNextRefresh.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20240414212454_AddNextRefresh.Designer.cs deleted file mode 100644 index b02a5760..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240414212454_AddNextRefresh.Designer.cs +++ /dev/null @@ -1,1347 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20240414212454_AddNextRefresh")] - partial class AddNextRefresh - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.4") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western", "kids", "news", "reality", "soap", "talk", "politics" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned", "deleted" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("date") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Issue", b => - { - b.Property("Domain") - .HasColumnType("text") - .HasColumnName("domain"); - - b.Property("Cause") - .HasColumnType("text") - .HasColumnName("cause"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Extra") - .IsRequired() - .HasColumnType("json") - .HasColumnName("extra"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text") - .HasColumnName("reason"); - - b.HasKey("Domain", "Cause") - .HasName("pk_issues"); - - b.ToTable("issues", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("date") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("date") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("date") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("date") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("date") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Password") - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.HasIndex("Username") - .IsUnique() - .HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("logo_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("logo_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("poster_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("poster_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("id"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("thumbnail_blurhash"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text") - .HasColumnName("thumbnail_source"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240414212454_AddNextRefresh.cs b/back/src/Kyoo.Postgresql/Migrations/20240414212454_AddNextRefresh.cs deleted file mode 100644 index c1875fd6..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240414212454_AddNextRefresh.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations; - -/// -public partial class AddNextRefresh : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "next_metadata_refresh", - table: "shows", - type: "timestamp with time zone", - nullable: true, - defaultValueSql: "now() at time zone 'utc' + interval '2 hours'" - ); - - migrationBuilder.AddColumn( - name: "next_metadata_refresh", - table: "seasons", - type: "timestamp with time zone", - nullable: true, - defaultValueSql: "now() at time zone 'utc' + interval '2 hours'" - ); - - migrationBuilder.AddColumn( - name: "next_metadata_refresh", - table: "movies", - type: "timestamp with time zone", - nullable: true, - defaultValueSql: "now() at time zone 'utc' + interval '2 hours'" - ); - - migrationBuilder.AddColumn( - name: "next_metadata_refresh", - table: "episodes", - type: "timestamp with time zone", - nullable: true, - defaultValueSql: "now() at time zone 'utc' + interval '2 hours'" - ); - - migrationBuilder.AddColumn( - name: "next_metadata_refresh", - table: "collections", - type: "timestamp with time zone", - nullable: true, - defaultValueSql: "now() at time zone 'utc' + interval '2 hours'" - ); - - // language=PostgreSQL - migrationBuilder.Sql( - """ - update episodes as e set external_id = ( - SELECT jsonb_build_object( - 'themoviedatabase', jsonb_build_object( - 'ShowId', s.external_id->'themoviedatabase'->'DataId', - 'Season', e.season_number, - 'Episode', e.episode_number, - 'Link', null - ) - ) - FROM shows AS s - WHERE s.id = e.show_id - ); - """ - ); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn(name: "next_metadata_refresh", table: "shows"); - - migrationBuilder.DropColumn(name: "next_metadata_refresh", table: "seasons"); - - migrationBuilder.DropColumn(name: "next_metadata_refresh", table: "movies"); - - migrationBuilder.DropColumn(name: "next_metadata_refresh", table: "episodes"); - - migrationBuilder.DropColumn(name: "next_metadata_refresh", table: "collections"); - - // language=PostgreSQL - migrationBuilder.Sql("update episodes as e set external_id = '{}';"); - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240420124608_ReworkImages.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20240420124608_ReworkImages.Designer.cs deleted file mode 100644 index 0157c3cd..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240420124608_ReworkImages.Designer.cs +++ /dev/null @@ -1,1375 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20240420124608_ReworkImages")] - partial class ReworkImages - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.4") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western", "kids", "news", "reality", "soap", "talk", "politics" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned", "deleted" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("date") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Issue", b => - { - b.Property("Domain") - .HasColumnType("text") - .HasColumnName("domain"); - - b.Property("Cause") - .HasColumnType("text") - .HasColumnName("cause"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Extra") - .IsRequired() - .HasColumnType("json") - .HasColumnName("extra"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text") - .HasColumnName("reason"); - - b.HasKey("Domain", "Cause") - .HasName("pk_issues"); - - b.ToTable("issues", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("date") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("date") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("date") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("date") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("date") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Password") - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.HasIndex("Username") - .IsUnique() - .HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("CollectionId") - .HasName("pk_collections"); - - b1.ToTable("collections"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_collection_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240420124608_ReworkImages.cs b/back/src/Kyoo.Postgresql/Migrations/20240420124608_ReworkImages.cs deleted file mode 100644 index 699c74a9..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240420124608_ReworkImages.cs +++ /dev/null @@ -1,464 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - /// - public partial class ReworkImages : Migration - { - private void MigrateImage(MigrationBuilder migrationBuilder, string table, string type) - { - migrationBuilder.Sql( - $""" - update {table} as r set {type} = json_build_object( - 'Id', gen_random_uuid(), - 'Source', r.{type}_source, - 'Blurhash', r.{type}_blurhash - ) - where r.{type}_source is not null - """ - ); - } - - private void UnMigrateImage(MigrationBuilder migrationBuilder, string table, string type) - { - migrationBuilder.Sql( - $""" - update {table} as r - set {type}_source = r.{type}->>'Source', - {type}_blurhash = r.{type}->>'Blurhash' - """ - ); - } - - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "logo", - table: "shows", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster", - table: "shows", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail", - table: "shows", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo", - table: "seasons", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster", - table: "seasons", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail", - table: "seasons", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo", - table: "movies", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster", - table: "movies", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail", - table: "movies", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo", - table: "episodes", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster", - table: "episodes", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail", - table: "episodes", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo", - table: "collections", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster", - table: "collections", - type: "jsonb", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail", - table: "collections", - type: "jsonb", - nullable: true - ); - - MigrateImage(migrationBuilder, "shows", "logo"); - MigrateImage(migrationBuilder, "shows", "poster"); - MigrateImage(migrationBuilder, "shows", "thumbnail"); - - MigrateImage(migrationBuilder, "seasons", "logo"); - MigrateImage(migrationBuilder, "seasons", "poster"); - MigrateImage(migrationBuilder, "seasons", "thumbnail"); - - MigrateImage(migrationBuilder, "movies", "logo"); - MigrateImage(migrationBuilder, "movies", "poster"); - MigrateImage(migrationBuilder, "movies", "thumbnail"); - - MigrateImage(migrationBuilder, "episodes", "logo"); - MigrateImage(migrationBuilder, "episodes", "poster"); - MigrateImage(migrationBuilder, "episodes", "thumbnail"); - - MigrateImage(migrationBuilder, "collections", "logo"); - MigrateImage(migrationBuilder, "collections", "poster"); - MigrateImage(migrationBuilder, "collections", "thumbnail"); - - migrationBuilder.DropColumn(name: "logo_blurhash", table: "shows"); - migrationBuilder.DropColumn(name: "logo_source", table: "shows"); - migrationBuilder.DropColumn(name: "poster_blurhash", table: "shows"); - migrationBuilder.DropColumn(name: "poster_source", table: "shows"); - migrationBuilder.DropColumn(name: "thumbnail_blurhash", table: "shows"); - migrationBuilder.DropColumn(name: "thumbnail_source", table: "shows"); - - migrationBuilder.DropColumn(name: "logo_blurhash", table: "seasons"); - migrationBuilder.DropColumn(name: "logo_source", table: "seasons"); - migrationBuilder.DropColumn(name: "poster_blurhash", table: "seasons"); - migrationBuilder.DropColumn(name: "poster_source", table: "seasons"); - migrationBuilder.DropColumn(name: "thumbnail_blurhash", table: "seasons"); - migrationBuilder.DropColumn(name: "thumbnail_source", table: "seasons"); - - migrationBuilder.DropColumn(name: "logo_blurhash", table: "movies"); - migrationBuilder.DropColumn(name: "logo_source", table: "movies"); - migrationBuilder.DropColumn(name: "poster_blurhash", table: "movies"); - migrationBuilder.DropColumn(name: "poster_source", table: "movies"); - migrationBuilder.DropColumn(name: "thumbnail_blurhash", table: "movies"); - migrationBuilder.DropColumn(name: "thumbnail_source", table: "movies"); - - migrationBuilder.DropColumn(name: "logo_blurhash", table: "episodes"); - migrationBuilder.DropColumn(name: "logo_source", table: "episodes"); - migrationBuilder.DropColumn(name: "poster_blurhash", table: "episodes"); - migrationBuilder.DropColumn(name: "poster_source", table: "episodes"); - migrationBuilder.DropColumn(name: "thumbnail_blurhash", table: "episodes"); - migrationBuilder.DropColumn(name: "thumbnail_source", table: "episodes"); - - migrationBuilder.DropColumn(name: "logo_blurhash", table: "collections"); - migrationBuilder.DropColumn(name: "logo_source", table: "collections"); - migrationBuilder.DropColumn(name: "poster_blurhash", table: "collections"); - migrationBuilder.DropColumn(name: "poster_source", table: "collections"); - migrationBuilder.DropColumn(name: "thumbnail_blurhash", table: "collections"); - migrationBuilder.DropColumn(name: "thumbnail_source", table: "collections"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "logo_blurhash", - table: "shows", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo_source", - table: "shows", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster_blurhash", - table: "shows", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster_source", - table: "shows", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail_blurhash", - table: "shows", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail_source", - table: "shows", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo_blurhash", - table: "seasons", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo_source", - table: "seasons", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster_blurhash", - table: "seasons", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster_source", - table: "seasons", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail_blurhash", - table: "seasons", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail_source", - table: "seasons", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo_blurhash", - table: "movies", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo_source", - table: "movies", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster_blurhash", - table: "movies", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster_source", - table: "movies", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail_blurhash", - table: "movies", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail_source", - table: "movies", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo_blurhash", - table: "episodes", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo_source", - table: "episodes", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster_blurhash", - table: "episodes", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster_source", - table: "episodes", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail_blurhash", - table: "episodes", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail_source", - table: "episodes", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo_blurhash", - table: "collections", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "logo_source", - table: "collections", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster_blurhash", - table: "collections", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "poster_source", - table: "collections", - type: "text", - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail_blurhash", - table: "collections", - type: "character varying(32)", - maxLength: 32, - nullable: true - ); - - migrationBuilder.AddColumn( - name: "thumbnail_source", - table: "collections", - type: "text", - nullable: true - ); - - UnMigrateImage(migrationBuilder, "shows", "logo"); - UnMigrateImage(migrationBuilder, "shows", "poster"); - UnMigrateImage(migrationBuilder, "shows", "thumbnail"); - - UnMigrateImage(migrationBuilder, "seasons", "logo"); - UnMigrateImage(migrationBuilder, "seasons", "poster"); - UnMigrateImage(migrationBuilder, "seasons", "thumbnail"); - - UnMigrateImage(migrationBuilder, "movies", "logo"); - UnMigrateImage(migrationBuilder, "movies", "poster"); - UnMigrateImage(migrationBuilder, "movies", "thumbnail"); - - UnMigrateImage(migrationBuilder, "episodes", "logo"); - UnMigrateImage(migrationBuilder, "episodes", "poster"); - UnMigrateImage(migrationBuilder, "episodes", "thumbnail"); - - UnMigrateImage(migrationBuilder, "collections", "logo"); - UnMigrateImage(migrationBuilder, "collections", "poster"); - UnMigrateImage(migrationBuilder, "collections", "thumbnail"); - - migrationBuilder.DropColumn(name: "logo", table: "shows"); - migrationBuilder.DropColumn(name: "poster", table: "shows"); - migrationBuilder.DropColumn(name: "thumbnail", table: "shows"); - migrationBuilder.DropColumn(name: "logo", table: "seasons"); - migrationBuilder.DropColumn(name: "poster", table: "seasons"); - migrationBuilder.DropColumn(name: "thumbnail", table: "seasons"); - migrationBuilder.DropColumn(name: "logo", table: "movies"); - migrationBuilder.DropColumn(name: "poster", table: "movies"); - migrationBuilder.DropColumn(name: "thumbnail", table: "movies"); - migrationBuilder.DropColumn(name: "logo", table: "episodes"); - migrationBuilder.DropColumn(name: "poster", table: "episodes"); - migrationBuilder.DropColumn(name: "thumbnail", table: "episodes"); - migrationBuilder.DropColumn(name: "logo", table: "collections"); - migrationBuilder.DropColumn(name: "poster", table: "collections"); - migrationBuilder.DropColumn(name: "thumbnail", table: "collections"); - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240423151632_AddServerOptions.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20240423151632_AddServerOptions.Designer.cs deleted file mode 100644 index eedca224..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240423151632_AddServerOptions.Designer.cs +++ /dev/null @@ -1,1395 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20240423151632_AddServerOptions")] - partial class AddServerOptions - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.4") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western", "kids", "news", "reality", "soap", "talk", "politics" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned", "deleted" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("date") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Issue", b => - { - b.Property("Domain") - .HasColumnType("text") - .HasColumnName("domain"); - - b.Property("Cause") - .HasColumnType("text") - .HasColumnName("cause"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Extra") - .IsRequired() - .HasColumnType("json") - .HasColumnName("extra"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text") - .HasColumnName("reason"); - - b.HasKey("Domain", "Cause") - .HasName("pk_issues"); - - b.ToTable("issues", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("date") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("date") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("date") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("date") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("date") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Password") - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.HasIndex("Username") - .IsUnique() - .HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("ServerOption", b => - { - b.Property("Key") - .HasColumnType("text") - .HasColumnName("key"); - - b.Property("Value") - .IsRequired() - .HasColumnType("text") - .HasColumnName("value"); - - b.HasKey("Key") - .HasName("pk_options"); - - b.ToTable("options", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("CollectionId") - .HasName("pk_collections"); - - b1.ToTable("collections"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_collection_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240423151632_AddServerOptions.cs b/back/src/Kyoo.Postgresql/Migrations/20240423151632_AddServerOptions.cs deleted file mode 100644 index 03429d64..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240423151632_AddServerOptions.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Security.Cryptography; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - /// - public partial class AddServerOptions : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "options", - columns: table => new - { - key = table.Column(type: "text", nullable: false), - value = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("pk_options", x => x.key); - } - ); - byte[] secret = new byte[128]; - using var rng = RandomNumberGenerator.Create(); - rng.GetBytes(secret); - migrationBuilder.InsertData( - "options", - new[] { "key", "value" }, - new[] { "AUTHENTICATION_SECRET", Convert.ToBase64String(secret) } - ); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable(name: "options"); - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240506175054_FixSeasonMetadataId.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20240506175054_FixSeasonMetadataId.Designer.cs deleted file mode 100644 index 37506ee3..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240506175054_FixSeasonMetadataId.Designer.cs +++ /dev/null @@ -1,1395 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20240506175054_FixSeasonMetadataId")] - partial class FixSeasonMetadataId - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.4") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western", "kids", "news", "reality", "soap", "talk", "politics" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned", "deleted" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("date") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Issue", b => - { - b.Property("Domain") - .HasColumnType("text") - .HasColumnName("domain"); - - b.Property("Cause") - .HasColumnType("text") - .HasColumnName("cause"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Extra") - .IsRequired() - .HasColumnType("json") - .HasColumnName("extra"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text") - .HasColumnName("reason"); - - b.HasKey("Domain", "Cause") - .HasName("pk_issues"); - - b.ToTable("issues", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("date") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("date") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("date") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("date") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("date") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Password") - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.HasIndex("Username") - .IsUnique() - .HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("ServerOption", b => - { - b.Property("Key") - .HasColumnType("text") - .HasColumnName("key"); - - b.Property("Value") - .IsRequired() - .HasColumnType("text") - .HasColumnName("value"); - - b.HasKey("Key") - .HasName("pk_options"); - - b.ToTable("options", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("CollectionId") - .HasName("pk_collections"); - - b1.ToTable("collections"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_collection_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20240506175054_FixSeasonMetadataId.cs b/back/src/Kyoo.Postgresql/Migrations/20240506175054_FixSeasonMetadataId.cs deleted file mode 100644 index c4bb048b..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20240506175054_FixSeasonMetadataId.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - /// - public partial class FixSeasonMetadataId : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - // language=PostgreSQL - migrationBuilder.Sql( - """ - update seasons as s set external_id = ( - SELECT jsonb_build_object( - 'themoviedatabase', jsonb_build_object( - 'DataId', sh.external_id->'themoviedatabase'->'DataId', - 'Link', s.external_id->'themoviedatabase'->'Link' - ) - ) - FROM shows AS sh - WHERE sh.id = s.show_id - ); - """ - ); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) { } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs b/back/src/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs deleted file mode 100644 index 7a5a8fb8..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs +++ /dev/null @@ -1,1392 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - partial class PostgresContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.4") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "genre", new[] { "action", "adventure", "animation", "comedy", "crime", "documentary", "drama", "family", "fantasy", "history", "horror", "music", "mystery", "romance", "science_fiction", "thriller", "war", "western", "kids", "news", "reality", "soap", "talk", "politics" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "status", new[] { "unknown", "finished", "airing", "planned" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "watch_status", new[] { "completed", "watching", "droped", "planned", "deleted" }); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("date") - .HasColumnName("release_date"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("SeasonId") - .HasColumnType("uuid") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_episodes"); - - b.HasIndex("SeasonId") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowId", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("EpisodeId") - .HasColumnType("uuid") - .HasColumnName("episode_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "EpisodeId") - .HasName("pk_episode_watch_status"); - - b.HasIndex("EpisodeId") - .HasDatabaseName("ix_episode_watch_status_episode_id"); - - b.ToTable("episode_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Issue", b => - { - b.Property("Domain") - .HasColumnType("text") - .HasColumnName("domain"); - - b.Property("Cause") - .HasColumnType("text") - .HasColumnName("cause"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Extra") - .IsRequired() - .HasColumnType("json") - .HasColumnName("extra"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text") - .HasColumnName("reason"); - - b.HasKey("Domain", "Cause") - .HasName("pk_issues"); - - b.ToTable("issues", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("AirDate") - .HasColumnType("date") - .HasColumnName("air_date"); - - b.Property("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Runtime") - .HasColumnType("integer") - .HasColumnName("runtime"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_movies"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_movies_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_movies_studio_id"); - - b.ToTable("movies", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("MovieId") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "MovieId") - .HasName("pk_movie_watch_status"); - - b.HasIndex("MovieId") - .HasDatabaseName("ix_movie_watch_status_movie_id"); - - b.ToTable("movie_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("EndDate") - .HasColumnType("date") - .HasColumnName("end_date"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("date") - .HasColumnName("start_date"); - - b.HasKey("Id") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowId", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property>("Aliases") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("date") - .HasColumnName("end_air"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property>("Genres") - .IsRequired() - .HasColumnType("genre[]") - .HasColumnName("genres"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("NextMetadataRefresh") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("next_metadata_refresh") - .HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Rating") - .HasColumnType("integer") - .HasColumnName("rating"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("date") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioId") - .HasColumnType("uuid") - .HasColumnName("studio_id"); - - b.Property("Tagline") - .HasColumnType("text") - .HasColumnName("tagline"); - - b.Property>("Tags") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("tags"); - - b.Property("Trailer") - .HasColumnType("text") - .HasColumnName("trailer"); - - b.HasKey("Id") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioId") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.Property("UserId") - .HasColumnType("uuid") - .HasColumnName("user_id"); - - b.Property("ShowId") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("NextEpisodeId") - .HasColumnType("uuid") - .HasColumnName("next_episode_id"); - - b.Property("PlayedDate") - .HasColumnType("timestamp with time zone") - .HasColumnName("played_date"); - - b.Property("Status") - .HasColumnType("watch_status") - .HasColumnName("status"); - - b.Property("UnseenEpisodesCount") - .HasColumnType("integer") - .HasColumnName("unseen_episodes_count"); - - b.Property("WatchedPercent") - .HasColumnType("integer") - .HasColumnName("watched_percent"); - - b.Property("WatchedTime") - .HasColumnType("integer") - .HasColumnName("watched_time"); - - b.HasKey("UserId", "ShowId") - .HasName("pk_show_watch_status"); - - b.HasIndex("NextEpisodeId") - .HasDatabaseName("ix_show_watch_status_next_episode_id"); - - b.HasIndex("ShowId") - .HasDatabaseName("ix_show_watch_status_show_id"); - - b.ToTable("show_watch_status", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.HasKey("Id") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("AddedDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasColumnName("added_date") - .HasDefaultValueSql("now() at time zone 'utc'"); - - b.Property("Email") - .IsRequired() - .HasColumnType("text") - .HasColumnName("email"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("json") - .HasColumnName("external_id"); - - b.Property("Password") - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Settings") - .IsRequired() - .HasColumnType("json") - .HasColumnName("settings"); - - b.Property("Slug") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)") - .HasColumnName("slug"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("Id") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.HasIndex("Username") - .IsUnique() - .HasDatabaseName("ix_users_username"); - - b.ToTable("users", (string)null); - }); - - modelBuilder.Entity("ServerOption", b => - { - b.Property("Key") - .HasColumnType("text") - .HasColumnName("key"); - - b.Property("Value") - .IsRequired() - .HasColumnType("text") - .HasColumnName("value"); - - b.HasKey("Key") - .HasName("pk_options"); - - b.ToTable("options", (string)null); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("movie_id") - .HasColumnType("uuid") - .HasColumnName("movie_id"); - - b.HasKey("collection_id", "movie_id") - .HasName("pk_link_collection_movie"); - - b.HasIndex("movie_id") - .HasDatabaseName("ix_link_collection_movie_movie_id"); - - b.ToTable("link_collection_movie", (string)null); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("uuid") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("uuid") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show", (string)null); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("CollectionId") - .HasName("pk_collections"); - - b1.ToTable("collections"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_collection_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("CollectionId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("CollectionId"); - - b1.ToTable("collections"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("CollectionId") - .HasConstraintName("fk_collections_collections_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonId") - .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("fk_episodes_seasons_season_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episodes_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("EpisodeId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("EpisodeId"); - - b1.ToTable("episodes"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("EpisodeId") - .HasConstraintName("fk_episodes_episodes_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Season"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.EpisodeWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Watched") - .HasForeignKey("EpisodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_episodes_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_episode_watch_status_users_user_id"); - - b.Navigation("Episode"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Movies") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_movies_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("MovieId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("MovieId"); - - b1.ToTable("movies"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("MovieId") - .HasConstraintName("fk_movies_movies_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.MovieWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Movie", "Movie") - .WithMany("Watched") - .HasForeignKey("MovieId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_movies_movie_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_movie_watch_status_users_user_id"); - - b.Navigation("Movie"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_seasons_shows_show_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("SeasonId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("SeasonId"); - - b1.ToTable("seasons"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("SeasonId") - .HasConstraintName("fk_seasons_seasons_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Show"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_shows_studios_studio_id"); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Logo", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.ToJson("logo"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Poster", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.ToJson("poster"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.OwnsOne("Kyoo.Abstractions.Models.Image", "Thumbnail", b1 => - { - b1.Property("ShowId") - .HasColumnType("uuid"); - - b1.Property("Blurhash") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b1.Property("Id") - .HasColumnType("uuid"); - - b1.Property("Source") - .IsRequired() - .HasColumnType("text"); - - b1.HasKey("ShowId"); - - b1.ToTable("shows"); - - b1.ToJson("thumbnail"); - - b1.WithOwner() - .HasForeignKey("ShowId") - .HasConstraintName("fk_shows_shows_id"); - }); - - b.Navigation("Logo"); - - b.Navigation("Poster"); - - b.Navigation("Studio"); - - b.Navigation("Thumbnail"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.ShowWatchStatus", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "NextEpisode") - .WithMany() - .HasForeignKey("NextEpisodeId") - .OnDelete(DeleteBehavior.SetNull) - .HasConstraintName("fk_show_watch_status_episodes_next_episode_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Watched") - .HasForeignKey("ShowId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_shows_show_id"); - - b.HasOne("Kyoo.Abstractions.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_show_watch_status_users_user_id"); - - b.Navigation("NextEpisode"); - - b.Navigation("Show"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("link_collection_movie", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Movie", null) - .WithMany() - .HasForeignKey("movie_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_movie_movies_movie_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_collections_collection_id"); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_link_collection_show_shows_show_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Movie", b => - { - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("Seasons"); - - b.Navigation("Watched"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("Movies"); - - b.Navigation("Shows"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/PostgresContext.cs b/back/src/Kyoo.Postgresql/PostgresContext.cs deleted file mode 100644 index 8052fdcc..00000000 --- a/back/src/Kyoo.Postgresql/PostgresContext.cs +++ /dev/null @@ -1,147 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text.RegularExpressions; -using Dapper; -using EFCore.NamingConventions.Internal; -using InterpolatedSql.SqlBuilders; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql.Utils; -using Kyoo.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Design; -using Microsoft.EntityFrameworkCore.Query.SqlExpressions; -using Microsoft.Extensions.Configuration; -using Npgsql; - -namespace Kyoo.Postgresql; - -public class PostgresContext(DbContextOptions options, IHttpContextAccessor accessor) - : DatabaseContext(options, accessor) -{ - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder.UseProjectables(); - optionsBuilder.UseSnakeCaseNamingConvention(); - base.OnConfiguring(optionsBuilder); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.HasPostgresEnum(); - modelBuilder.HasPostgresEnum(); - modelBuilder.HasPostgresEnum(); - - modelBuilder - .HasDbFunction(typeof(DatabaseContext).GetMethod(nameof(MD5))!) - .HasTranslation(args => new SqlFunctionExpression( - "md5", - args, - nullable: true, - argumentsPropagateNullability: [false], - type: args[0].Type, - typeMapping: args[0].TypeMapping - )); - - SqlMapper.TypeMapProvider = (type) => - { - return new CustomPropertyTypeMap( - type, - (type, name) => - { - string newName = Regex.Replace( - name, - "(^|_)([a-z])", - (match) => match.Groups[2].Value.ToUpperInvariant() - ); - // TODO: Add images handling here (name: poster_source, newName: PosterSource) should set Poster.Source - return type.GetProperty(newName)!; - } - ); - }; - SqlMapper.AddTypeHandler( - typeof(Dictionary), - new JsonTypeHandler>() - ); - SqlMapper.AddTypeHandler( - typeof(Dictionary), - new JsonTypeHandler>() - ); - SqlMapper.AddTypeHandler( - typeof(Dictionary), - new JsonTypeHandler>() - ); - SqlMapper.AddTypeHandler( - typeof(Dictionary), - new JsonTypeHandler>() - ); - SqlMapper.AddTypeHandler(typeof(Image), new JsonTypeHandler()); - SqlMapper.AddTypeHandler(typeof(List), new ListTypeHandler()); - SqlMapper.AddTypeHandler(typeof(List), new ListTypeHandler()); - SqlMapper.AddTypeHandler(typeof(Wrapper), new Wrapper.Handler()); - InterpolatedSqlBuilderOptions.DefaultOptions.ReuseIdenticalParameters = true; - InterpolatedSqlBuilderOptions.DefaultOptions.AutoFixSingleQuotes = false; - - base.OnModelCreating(modelBuilder); - } - - /// - protected override string LinkName() - { - SnakeCaseNameRewriter rewriter = new(CultureInfo.InvariantCulture); - return rewriter.RewriteName("Link" + typeof(T).Name + typeof(T2).Name); - } - - /// - protected override string LinkNameFk() - { - SnakeCaseNameRewriter rewriter = new(CultureInfo.InvariantCulture); - return rewriter.RewriteName(typeof(T).Name + "ID"); - } - - /// - protected override bool IsDuplicateException(Exception ex) - { - return ex.InnerException - is PostgresException - { - SqlState: PostgresErrorCodes.UniqueViolation - or PostgresErrorCodes.ForeignKeyViolation - }; - } -} - -public class PostgresContextBuilder : IDesignTimeDbContextFactory -{ - public PostgresContext CreateDbContext(string[] args) - { - IConfigurationRoot config = new ConfigurationBuilder() - .AddEnvironmentVariables() - .AddCommandLine(args) - .Build(); - NpgsqlDataSource dataSource = PostgresModule.CreateDataSource(config); - DbContextOptionsBuilder builder = new(); - builder.UseNpgsql(dataSource); - - return new PostgresContext(builder.Options, null!); - } -} diff --git a/back/src/Kyoo.Postgresql/PostgresModule.cs b/back/src/Kyoo.Postgresql/PostgresModule.cs deleted file mode 100644 index f7ad8ff2..00000000 --- a/back/src/Kyoo.Postgresql/PostgresModule.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Data.Common; -using Kyoo.Abstractions.Models; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Npgsql; - -namespace Kyoo.Postgresql; - -public static class PostgresModule -{ - public static NpgsqlDataSource CreateDataSource(IConfiguration configuration) - { - var connectionString = configuration.GetValue("POSTGRES_URL"); - - // Load the connection string from the environment variable, as well as standard libpq environment variables - // (PGUSER, PGPASSWORD, PGHOST, PGPORT, PGDATABASE, etc.) - NpgsqlConnectionStringBuilder conBuilder = new(connectionString ?? ""); - // Set defaults when no explicit connection string is provided. This cannot be set if the connection string - // is provided, or it will override connection string values. - if (string.IsNullOrEmpty(connectionString)) - { - conBuilder.Pooling = true; - conBuilder.MaxPoolSize = 95; - conBuilder.Timeout = 30; - } - - string? oldVarUsername = configuration.GetValue("POSTGRES_USER"); - if (!string.IsNullOrEmpty(oldVarUsername)) - conBuilder.Username = oldVarUsername; - if (string.IsNullOrEmpty(conBuilder.Username)) - conBuilder.Username = "KyooUser"; - - string? oldVarPassword = configuration.GetValue("POSTGRES_PASSWORD"); - if (!string.IsNullOrEmpty(oldVarPassword)) - conBuilder.Password = oldVarPassword; - if (string.IsNullOrEmpty(conBuilder.Password)) - conBuilder.Password = "KyooPassword"; - - string? oldVarHost = configuration.GetValue("POSTGRES_SERVER"); - if (!string.IsNullOrEmpty(oldVarHost)) - conBuilder.Host = oldVarHost; - if (string.IsNullOrEmpty(conBuilder.Host)) - conBuilder.Host = "postgres"; - - int? oldVarPort = configuration.GetValue("POSTGRES_PORT"); - if (oldVarPort != null && oldVarPort != 0) - conBuilder.Port = oldVarPort.Value; - if (conBuilder.Port == 0) - conBuilder.Port = 5432; - - string? oldVarDatabase = configuration.GetValue("POSTGRES_DB"); - if (!string.IsNullOrEmpty(oldVarDatabase)) - conBuilder.Database = oldVarDatabase; - if (string.IsNullOrEmpty(conBuilder.Database)) - conBuilder.Database = "kyooDB"; - - NpgsqlDataSourceBuilder dsBuilder = new(conBuilder.ConnectionString); - dsBuilder.MapEnum(); - dsBuilder.MapEnum(); - dsBuilder.MapEnum(); - return dsBuilder.Build(); - } - - public static void ConfigurePostgres(this WebApplicationBuilder builder) - { - NpgsqlDataSource dataSource = CreateDataSource(builder.Configuration); - builder.Services.AddDbContext( - x => - { - x.UseNpgsql(dataSource); - if (builder.Environment.IsDevelopment()) - x.EnableDetailedErrors().EnableSensitiveDataLogging(); - }, - ServiceLifetime.Transient - ); - builder.Services.AddTransient( - (services) => services.GetRequiredService().Database.GetDbConnection() - ); - - builder.Services.AddHealthChecks().AddDbContextCheck(); - builder.Configuration.AddDbConfigurationProvider(x => x.UseNpgsql(dataSource)); - } - - private static void AddDbConfigurationProvider( - this IConfigurationBuilder builder, - Action action - ) - { - builder.Add(new DbConfigurationSource(action)); - } -} diff --git a/back/src/Kyoo.Postgresql/Utils/JsonTypeHandler.cs b/back/src/Kyoo.Postgresql/Utils/JsonTypeHandler.cs deleted file mode 100644 index e9655389..00000000 --- a/back/src/Kyoo.Postgresql/Utils/JsonTypeHandler.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Data; -using System.Text.Json; -using Npgsql; -using NpgsqlTypes; -using static Dapper.SqlMapper; - -namespace Kyoo.Postgresql.Utils; - -public class JsonTypeHandler : TypeHandler - where T : class -{ - public override T? Parse(object value) - { - if (value is string str) - return JsonSerializer.Deserialize(str); - return default; - } - - public override void SetValue(IDbDataParameter parameter, T? value) - { - parameter.Value = JsonSerializer.Serialize(value); - ((NpgsqlParameter)parameter).NpgsqlDbType = NpgsqlDbType.Jsonb; - } -} diff --git a/back/src/Kyoo.Postgresql/Utils/ListTypeHandler.cs b/back/src/Kyoo.Postgresql/Utils/ListTypeHandler.cs deleted file mode 100644 index 837528b7..00000000 --- a/back/src/Kyoo.Postgresql/Utils/ListTypeHandler.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Data; -using System.Linq; -using Dapper; - -namespace Kyoo.Postgresql.Utils; - -// See https://github.com/DapperLib/Dapper/issues/1424 -public class ListTypeHandler : SqlMapper.TypeHandler> -{ - public override List Parse(object value) - { - T[] typedValue = (T[])value; // looks like Dapper did not indicate the property type to Npgsql, so it defaults to string[] (default CLR type for text[] PostgreSQL type) - return typedValue?.ToList() ?? []; - } - - public override void SetValue(IDbDataParameter parameter, List? value) - { - parameter.Value = value; // no need to convert to string[] in this direction - } -} diff --git a/back/src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj b/back/src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj deleted file mode 100644 index b159b96e..00000000 --- a/back/src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - enable - Kyoo.RabbitMq - - - - - - - - - - - diff --git a/back/src/Kyoo.RabbitMq/Message.cs b/back/src/Kyoo.RabbitMq/Message.cs deleted file mode 100644 index 72fa2f37..00000000 --- a/back/src/Kyoo.RabbitMq/Message.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Text; -using System.Text.Json; -using Kyoo.Utils; - -namespace Kyoo.RabbitMq; - -public class Message -{ - public string Action { get; set; } - public string Type { get; set; } - public T Value { get; set; } - - public string AsRoutingKey() - { - return $"{Type}.{Action}"; - } - - public byte[] AsBytes() - { - return Encoding.UTF8.GetBytes(JsonSerializer.Serialize(this, Utility.JsonOptions)); - } -} diff --git a/back/src/Kyoo.RabbitMq/RabbitMqModule.cs b/back/src/Kyoo.RabbitMq/RabbitMqModule.cs deleted file mode 100644 index 9b2da76b..00000000 --- a/back/src/Kyoo.RabbitMq/RabbitMqModule.cs +++ /dev/null @@ -1,223 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Net.Security; -using System.Security.Cryptography.X509Certificates; -using Kyoo.Abstractions.Controllers; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.WebUtilities; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Primitives; -using RabbitMQ.Client; - -namespace Kyoo.RabbitMq; - -public static class RabbitMqModule -{ - public static void ConfigureRabbitMq(this WebApplicationBuilder builder) - { - builder.Services.AddSingleton(_ => - { - ConnectionFactory factory = new(); - - // See https://www.rabbitmq.com/docs/uri-spec - string? connectionString = builder.Configuration.GetValue("RABBITMQ_URL"); - if (!string.IsNullOrEmpty(connectionString)) - factory._ConfigureFactoryWithConnectionString(connectionString); - else - factory._ConfigureFactoryWithEnvironmentVars(builder.Configuration); - - return factory.CreateConnection(); - }); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - } - - private static void _ConfigureFactoryWithConnectionString( - this ConnectionFactory factory, - string? connectionString - ) - { - if (string.IsNullOrEmpty(connectionString)) - return; - - // Important: setting this property will not use any query parameters, so they must be parsed here instead.. - factory.Uri = new Uri(connectionString); - - // Support query parameters defined here: - // https://www.rabbitmq.com/docs/uri-query-parameters - Dictionary queryParameters = QueryHelpers.ParseQuery( - factory.Uri.Query - ); - - queryParameters.TryGetValue("heartbeat", out StringValues heartbeats); - if (int.TryParse(heartbeats.LastOrDefault(), out int heartbeatValue)) - factory.RequestedHeartbeat = TimeSpan.FromSeconds(heartbeatValue); - - queryParameters.TryGetValue("connection_timeout", out StringValues connectionTimeouts); - if (int.TryParse(connectionTimeouts.LastOrDefault(), out int connectionTimeoutValue)) - factory.RequestedConnectionTimeout = TimeSpan.FromSeconds(connectionTimeoutValue); - - queryParameters.TryGetValue("channel_max", out StringValues channelMaxValues); - if (ushort.TryParse(channelMaxValues.LastOrDefault(), out ushort channelMaxValue)) - factory.RequestedChannelMax = channelMaxValue; - - if (!factory.Ssl.Enabled) - return; - - queryParameters.TryGetValue("cacertfile", out StringValues caCertFiles); - var caCertFile = caCertFiles.LastOrDefault(); - if (!string.IsNullOrEmpty(caCertFile)) - { - // Load the cert once at startup instead of on every connection. - X509Certificate2Collection rootCACollection = []; - rootCACollection.ImportFromPemFile(caCertFile); - - // This is a custom validator that obeys the set SslPolicyErrors, while also using the CA cert specified in the query string. - factory.Ssl.CertificateValidationCallback = ( - sender, - certificate, - chain, - sslPolicyErrors - ) => - { - // If no cert was provided - if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNotAvailable)) - { - // Accept the cert anyway if the client was explicitly configured to ignore this. - if ( - factory.Ssl.AcceptablePolicyErrors.HasFlag( - SslPolicyErrors.RemoteCertificateNotAvailable - ) - ) - return true; - // Otherwise, reject it. - return false; - } - - // If the cert hostname does not match - if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch)) - { - // Accept the cert anyway if the client was explicitly configured to ignore this. - if ( - factory.Ssl.AcceptablePolicyErrors.HasFlag( - SslPolicyErrors.RemoteCertificateNameMismatch - ) - ) - return true; - // Otherwise, reject it. - return false; - } - - // This shouldn't ever happen, and is mostly just here to satisfy the linter - if (chain == null || certificate == null) - return false; - - // Verify that the certificate came from the specified CA. - chain.ChainPolicy.ExtraStore.AddRange( - chain.ChainElements.Select(x => x.Certificate).ToArray() - ); - chain.ChainPolicy.CustomTrustStore.Clear(); - chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - chain.ChainPolicy.CustomTrustStore.AddRange(rootCACollection); - - return chain.Build(new X509Certificate2(certificate)); - }; - } - - queryParameters.TryGetValue("certfile", out var certfiles); - var certfile = certfiles.LastOrDefault(); - queryParameters.TryGetValue("keyfile", out var keyfiles); - var keyfile = keyfiles.LastOrDefault(); - if (!string.IsNullOrEmpty(certfile) && !string.IsNullOrEmpty(keyfile)) - factory.Ssl.Certs = [X509Certificate2.CreateFromPemFile(certfile, keyfile)]; - - queryParameters.TryGetValue("verify", out var verifyValues); - switch (verifyValues.LastOrDefault()) - { - case "verify_none": - factory.Ssl.AcceptablePolicyErrors = ~SslPolicyErrors.None; - break; - case "verify_peer": - factory.Ssl.AcceptablePolicyErrors = SslPolicyErrors.None; - break; - } - - queryParameters.TryGetValue("server_name_indication", out StringValues sniValues); - var sni = sniValues.LastOrDefault(); - if (!string.IsNullOrEmpty(sni)) - { - if (sni == "disabled") // Special value, see https://www.rabbitmq.com/docs/ssl#erlang-ssl - { - factory.Ssl.ServerName = null; - factory.Ssl.AcceptablePolicyErrors |= SslPolicyErrors.RemoteCertificateNameMismatch; - } - else - factory.Ssl.ServerName = sni; - } - - queryParameters.TryGetValue("auth_mechanism", out StringValues authMechanisms); - if (authMechanisms.Count > 0) - { - factory.AuthMechanisms.Clear(); - foreach (var authMechanism in authMechanisms) - { - switch (authMechanism) - { - case "external": - factory.AuthMechanisms.Add(new ExternalMechanismFactory()); - break; - case "plain": - factory.AuthMechanisms.Add(new PlainMechanismFactory()); - break; - default: - throw new NotSupportedException( - $"Unsupported authentication mechanism: {authMechanism}" - ); - } - } - } - } - - private static void _ConfigureFactoryWithEnvironmentVars( - this ConnectionFactory factory, - IConfigurationManager configuration - ) - { - factory.UserName = _GetNonEmptyString( - configuration.GetValue("RABBITMQ_DEFAULT_USER"), - factory.UserName, - "guest" - ); - factory.Password = _GetNonEmptyString( - configuration.GetValue("RABBITMQ_DEFAULT_PASS"), - factory.Password, - "guest" - ); - factory.HostName = _GetNonEmptyString( - configuration.GetValue("RABBITMQ_HOST"), - factory.HostName, - "rabbitmq" - ); - factory.Port = configuration.GetValue("RABBITMQ_PORT", 5672); - } - - private static string _GetNonEmptyString(params string?[] values) => - values.FirstOrDefault(string.IsNullOrEmpty) ?? string.Empty; -} diff --git a/back/src/Kyoo.RabbitMq/RabbitProducer.cs b/back/src/Kyoo.RabbitMq/RabbitProducer.cs deleted file mode 100644 index f7433dd7..00000000 --- a/back/src/Kyoo.RabbitMq/RabbitProducer.cs +++ /dev/null @@ -1,132 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using RabbitMQ.Client; - -namespace Kyoo.RabbitMq; - -public class RabbitProducer -{ - private readonly IModel _channel; - - public RabbitProducer(IConnection rabbitConnection) - { - _channel = rabbitConnection.CreateModel(); - - if (!doesExchangeExist(rabbitConnection, "events.resource")) - _channel.ExchangeDeclare("events.resource", ExchangeType.Topic); - _ListenResourceEvents("events.resource"); - _ListenResourceEvents("events.resource"); - _ListenResourceEvents("events.resource"); - _ListenResourceEvents("events.resource"); - _ListenResourceEvents("events.resource"); - _ListenResourceEvents("events.resource"); - _ListenResourceEvents("events.resource"); - - if (!doesExchangeExist(rabbitConnection, "events.watched")) - _channel.ExchangeDeclare("events.watched", ExchangeType.Topic); - IWatchStatusRepository.OnMovieStatusChangedHandler += _PublishWatchStatus("movie"); - IWatchStatusRepository.OnShowStatusChangedHandler += _PublishWatchStatus("show"); - IWatchStatusRepository.OnEpisodeStatusChangedHandler += _PublishWatchStatus( - "episode" - ); - } - - /// - /// Checks if the exchange exists. Needed to avoid crashing when re-declaring an existing - /// queue with different parameters. - /// - /// The RabbitMQ connection. - /// The name of the channel. - /// True if the queue exists, false otherwise. - private bool doesExchangeExist(IConnection rabbitConnection, string exchangeName) - { - // If the queue does not exist when QueueDeclarePassive is called, - // an exception will be thrown. According to the docs, when this - // happens, the entire channel should be thrown away. - using var channel = rabbitConnection.CreateModel(); - try - { - channel.ExchangeDeclarePassive(exchangeName); - return true; - } - catch (Exception) - { - return false; - } - } - - private void _ListenResourceEvents(string exchange) - where T : IResource, IQuery - { - string type = typeof(T).Name.ToLowerInvariant(); - - IRepository.OnCreated += _Publish(exchange, type, "created"); - IRepository.OnEdited += _Publish(exchange, type, "edited"); - IRepository.OnDeleted += _Publish(exchange, type, "deleted"); - } - - private IRepository.ResourceEventHandler _Publish( - string exchange, - string type, - string action - ) - where T : IResource, IQuery - { - return (T resource) => - { - Message message = - new() - { - Action = action, - Type = type, - Value = resource, - }; - _channel.BasicPublish( - exchange, - routingKey: message.AsRoutingKey(), - body: message.AsBytes() - ); - return Task.CompletedTask; - }; - } - - private IWatchStatusRepository.ResourceEventHandler> _PublishWatchStatus( - string resource - ) - { - return (status) => - { - Message> message = - new() - { - Type = resource, - Action = status.Status.ToString().ToLowerInvariant(), - Value = status, - }; - _channel.BasicPublish( - exchange: "events.watched", - routingKey: message.AsRoutingKey(), - body: message.AsBytes() - ); - return Task.CompletedTask; - }; - } -} diff --git a/back/src/Kyoo.RabbitMq/ScannerProducer.cs b/back/src/Kyoo.RabbitMq/ScannerProducer.cs deleted file mode 100644 index 0411e250..00000000 --- a/back/src/Kyoo.RabbitMq/ScannerProducer.cs +++ /dev/null @@ -1,87 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Text; -using System.Text.Json; -using Kyoo.Abstractions.Controllers; -using Kyoo.Utils; -using RabbitMQ.Client; - -namespace Kyoo.RabbitMq; - -public class ScannerProducer : IScanner -{ - private readonly IModel _channel; - - public ScannerProducer(IConnection rabbitConnection) - { - _channel = rabbitConnection.CreateModel(); - if (!doesQueueExist(rabbitConnection, "scanner")) - _channel.QueueDeclare("scanner", exclusive: false, autoDelete: false); - if (!doesQueueExist(rabbitConnection, "scanner.rescan")) - _channel.QueueDeclare("scanner.rescan", exclusive: false, autoDelete: false); - } - - /// - /// Checks if the queue exists. Needed to avoid crashing when re-declaring an existing - /// queue with different parameters. - /// - /// The RabbitMQ connection. - /// The name of the channel. - /// True if the queue exists, false otherwise. - private bool doesQueueExist(IConnection rabbitConnection, string queueName) - { - // If the queue does not exist when QueueDeclarePassive is called, - // an exception will be thrown. According to the docs, when this - // happens, the entire channel should be thrown away. - using var channel = rabbitConnection.CreateModel(); - try - { - channel.QueueDeclarePassive(queueName); - return true; - } - catch (Exception) - { - return false; - } - } - - private Task _Publish(T message, string queue = "scanner") - { - var body = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(message, Utility.JsonOptions)); - _channel.BasicPublish("", routingKey: queue, body: body); - return Task.CompletedTask; - } - - public Task SendRescanRequest() - { - var message = new { Action = "rescan", }; - return _Publish(message, queue: "scanner.rescan"); - } - - public Task SendRefreshRequest(string kind, Guid id) - { - var message = new - { - Action = "refresh", - Kind = kind.ToLowerInvariant(), - Id = id - }; - return _Publish(message); - } -} diff --git a/back/src/Kyoo.Swagger/ApiSorter.cs b/back/src/Kyoo.Swagger/ApiSorter.cs deleted file mode 100644 index 0334805c..00000000 --- a/back/src/Kyoo.Swagger/ApiSorter.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Linq; -using Kyoo.Swagger.Models; -using NSwag; -using NSwag.Generation.AspNetCore; - -namespace Kyoo.Swagger; - -/// -/// A class to sort apis. -/// -public static class ApiSorter -{ - /// - /// Sort apis by alphabetical orders. - /// - /// The swagger settings to update. - public static void SortApis(this AspNetCoreOpenApiDocumentGeneratorSettings options) - { - options.PostProcess += postProcess => - { - // We can't reorder items by assigning the sorted value to the Paths variable since it has no setter. - List> sorted = postProcess - .Paths.OrderBy(x => x.Key) - .ToList(); - postProcess.Paths.Clear(); - foreach ((string key, OpenApiPathItem value) in sorted) - postProcess.Paths.Add(key, value); - }; - - options.PostProcess += postProcess => - { - if (!postProcess.ExtensionData.TryGetValue("x-tagGroups", out object list)) - return; - List tagGroups = (List)list; - postProcess.ExtensionData["x-tagGroups"] = tagGroups - .OrderBy(x => x.Name) - .Select(x => - { - x.Name = x.Name[(x.Name.IndexOf(':') + 1)..]; - x.Tags = x.Tags.OrderBy(y => y).ToList(); - return x; - }) - .ToList(); - }; - } -} diff --git a/back/src/Kyoo.Swagger/ApiTagsFilter.cs b/back/src/Kyoo.Swagger/ApiTagsFilter.cs deleted file mode 100644 index 7c001c1b..00000000 --- a/back/src/Kyoo.Swagger/ApiTagsFilter.cs +++ /dev/null @@ -1,123 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Swagger.Models; -using Namotion.Reflection; -using NSwag; -using NSwag.Generation.AspNetCore; -using NSwag.Generation.Processors.Contexts; - -namespace Kyoo.Swagger; - -/// -/// A class to handle Api Groups (OpenApi tags and x-tagGroups). -/// Tags should be specified via and this filter will map this to the -/// . -/// -public static class ApiTagsFilter -{ - /// - /// The main operation filter that will map every . - /// - /// The processor context, this is given by the AddOperationFilter method. - /// This always return true since it should not remove operations. - public static bool OperationFilter(OperationProcessorContext context) - { - ApiDefinitionAttribute def = - context.ControllerType.GetCustomAttribute(); - string name = def?.Name ?? context.ControllerType.Name; - - ApiDefinitionAttribute methodOverride = - context.MethodInfo.GetCustomAttribute(); - if (methodOverride != null) - name = methodOverride.Name; - - context.OperationDescription.Operation.Tags.Add(name); - if (context.Document.Tags.All(x => x.Name != name)) - { - context.Document.Tags.Add( - new OpenApiTag - { - Name = name, - Description = context.ControllerType.GetXmlDocsSummary() - } - ); - } - - if (def?.Group == null) - return true; - - context.Document.ExtensionData ??= new Dictionary(); - context.Document.ExtensionData.TryAdd("x-tagGroups", new List()); - List obj = (List)context.Document.ExtensionData["x-tagGroups"]; - TagGroups existing = obj.FirstOrDefault(x => x.Name == def.Group); - if (existing != null) - { - if (!existing.Tags.Contains(def.Name)) - existing.Tags.Add(def.Name); - } - else - { - obj.Add( - new TagGroups - { - Name = def.Group, - Tags = new List { def.Name } - } - ); - } - - return true; - } - - /// - /// This add every tags that are not in a x-tagGroups to a new tagGroups named "Other". - /// Since tags that are not in a tagGroups are not shown, this is necessary if you want them displayed. - /// - /// - /// The document to do this for. This should be done in the PostProcess part of the document or after - /// the main operation filter (see ) has finished. - /// - public static void AddLeftoversToOthersGroup(this OpenApiDocument postProcess) - { - List tagGroups = (List)postProcess.ExtensionData["x-tagGroups"]; - List tagsWithoutGroup = postProcess - .Tags.Select(x => x.Name) - .Where(x => tagGroups.SelectMany(y => y.Tags).All(y => y != x)) - .ToList(); - if (tagsWithoutGroup.Any()) - { - tagGroups.Add(new TagGroups { Name = "Others", Tags = tagsWithoutGroup }); - } - } - - /// - /// Use to create tags and groups of tags on the resulting swagger - /// document. - /// - /// The settings of the swagger document. - public static void UseApiTags(this AspNetCoreOpenApiDocumentGeneratorSettings options) - { - options.AddOperationFilter(OperationFilter); - options.PostProcess += x => x.AddLeftoversToOthersGroup(); - } -} diff --git a/back/src/Kyoo.Swagger/GenericResponseProvider.cs b/back/src/Kyoo.Swagger/GenericResponseProvider.cs deleted file mode 100644 index a3aeaf78..00000000 --- a/back/src/Kyoo.Swagger/GenericResponseProvider.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Utils; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.ApplicationModels; - -namespace Kyoo.Swagger; - -/// -/// A filter that change 's -/// that where set to to the -/// return type of the method. -/// -/// -/// This is only useful when the return type of the method is a generics type and that can't be specified in the -/// attribute directly (since attributes don't support generics). This should not be used otherwise. -/// -public class GenericResponseProvider : IApplicationModelProvider -{ - /// - public int Order => -1; - - /// - public void OnProvidersExecuted(ApplicationModelProviderContext context) { } - - /// - public void OnProvidersExecuting(ApplicationModelProviderContext context) - { - foreach (ActionModel action in context.Result.Controllers.SelectMany(x => x.Actions)) - { - IEnumerable responses = action - .Filters.OfType() - .Where(x => x.Type == typeof(ActionResult<>)); - foreach (ProducesResponseTypeAttribute response in responses) - { - Type type = action.ActionMethod.ReturnType; - type = - Utility.GetGenericDefinition(type, typeof(Task<>))?.GetGenericArguments()[0] - ?? type; - type = - Utility - .GetGenericDefinition(type, typeof(ActionResult<>)) - ?.GetGenericArguments()[0] ?? type; - response.Type = type; - } - } - } -} diff --git a/back/src/Kyoo.Swagger/Kyoo.Swagger.csproj b/back/src/Kyoo.Swagger/Kyoo.Swagger.csproj deleted file mode 100644 index c1d504fe..00000000 --- a/back/src/Kyoo.Swagger/Kyoo.Swagger.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - Kyoo.Swagger - Kyoo.Swagger - disable - - - - - - - diff --git a/back/src/Kyoo.Swagger/Models/TagGroups.cs b/back/src/Kyoo.Swagger/Models/TagGroups.cs deleted file mode 100644 index e16d4580..00000000 --- a/back/src/Kyoo.Swagger/Models/TagGroups.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using Newtonsoft.Json; -using NSwag; - -namespace Kyoo.Swagger.Models; - -/// -/// A class representing a group of tags in the -/// -public class TagGroups -{ - /// - /// The name of the tag group. - /// - [JsonProperty(PropertyName = "name")] - public string Name { get; set; } - - /// - /// The list of tags in this group. - /// - [JsonProperty(PropertyName = "tags")] - public List Tags { get; set; } -} diff --git a/back/src/Kyoo.Swagger/OperationPermissionProcessor.cs b/back/src/Kyoo.Swagger/OperationPermissionProcessor.cs deleted file mode 100644 index d56727a3..00000000 --- a/back/src/Kyoo.Swagger/OperationPermissionProcessor.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Kyoo.Abstractions.Models.Permissions; -using NSwag; -using NSwag.Generation.Processors; -using NSwag.Generation.Processors.Contexts; - -namespace Kyoo.Swagger; - -/// -/// An operation processor that adds permissions information from the and the -/// . -/// -public class OperationPermissionProcessor : IOperationProcessor -{ - /// - public bool Process(OperationProcessorContext context) - { - context.OperationDescription.Operation.Security ??= new List(); - OpenApiSecurityRequirement perms = context - .MethodInfo.GetCustomAttributes() - .Aggregate( - new OpenApiSecurityRequirement(), - (agg, _) => - { - agg[nameof(Kyoo)] = Array.Empty(); - return agg; - } - ); - - perms = context - .MethodInfo.GetCustomAttributes() - .Aggregate( - perms, - (agg, cur) => - { - ICollection permissions = _GetPermissionsList(agg, cur.Group); - permissions.Add($"{cur.Type}.{cur.Kind.ToString().ToLower()}"); - agg[nameof(Kyoo)] = permissions; - return agg; - } - ); - - PartialPermissionAttribute controller = - context.ControllerType.GetCustomAttribute(); - if (controller != null) - { - perms = context - .MethodInfo.GetCustomAttributes() - .Aggregate( - perms, - (agg, cur) => - { - Group? group = - controller.Group != Group.Overall ? controller.Group : cur.Group; - string type = controller.Type ?? cur.Type; - Kind? kind = controller.Type == null ? controller.Kind : cur.Kind; - ICollection permissions = _GetPermissionsList( - agg, - group ?? Group.Overall - ); - permissions.Add($"{type}.{kind!.Value.ToString().ToLower()}"); - agg[nameof(Kyoo)] = permissions; - return agg; - } - ); - } - - context.OperationDescription.Operation.Security.Add(perms); - return true; - } - - private static ICollection _GetPermissionsList( - OpenApiSecurityRequirement security, - Group group - ) - { - return security.TryGetValue(group.ToString(), out IEnumerable perms) - ? perms.ToList() - : new List(); - } -} diff --git a/back/src/Kyoo.Swagger/SwaggerModule.cs b/back/src/Kyoo.Swagger/SwaggerModule.cs deleted file mode 100644 index f1e40776..00000000 --- a/back/src/Kyoo.Swagger/SwaggerModule.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Reflection; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Mvc.ApplicationModels; -using Microsoft.Extensions.DependencyInjection; -using NJsonSchema; -using NJsonSchema.Generation.TypeMappers; -using NSwag; -using NSwag.Generation.AspNetCore; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Swagger; - -public static class SwaggerModule -{ - public static void ConfigureOpenApi(this IServiceCollection services) - { - services.AddTransient(); - services.AddOpenApiDocument(document => - { - document.Title = "Kyoo API"; - // TODO use a real multi-line description in markdown. - document.Description = "The Kyoo's public API"; - document.Version = Assembly.GetExecutingAssembly().GetName().Version!.ToString(3); - document.DocumentName = "v1"; - document.UseControllerSummaryAsTagDescription = true; - document.PostProcess = options => - { - options.Info.Contact = new OpenApiContact - { - Name = "Kyoo's github", - Url = "https://github.com/zoriya/Kyoo" - }; - options.Info.License = new OpenApiLicense - { - Name = "GPL-3.0-or-later", - Url = "https://github.com/zoriya/Kyoo/blob/master/LICENSE" - }; - - options.Info.ExtensionData ??= new Dictionary(); - options.Info.ExtensionData["x-logo"] = new - { - url = "/banner.png", - backgroundColor = "#FFFFFF", - altText = "Kyoo's logo" - }; - }; - document.UseApiTags(); - document.SortApis(); - document.AddOperationFilter(x => - { - if (x is AspNetCoreOperationProcessorContext ctx) - return ctx.ApiDescription.ActionDescriptor.AttributeRouteInfo?.Order - != AlternativeRoute; - return true; - }); - document.SchemaSettings.TypeMappers.Add( - new PrimitiveTypeMapper( - typeof(Identifier), - x => - { - x.IsNullableRaw = false; - x.Type = JsonObjectType.String | JsonObjectType.Integer; - } - ) - ); - - document.AddSecurity( - nameof(Kyoo), - new OpenApiSecurityScheme - { - Type = OpenApiSecuritySchemeType.Http, - Scheme = "Bearer", - BearerFormat = "JWT", - Description = "The user's bearer" - } - ); - document.OperationProcessors.Add(new OperationPermissionProcessor()); - }); - } - - public static void UseKyooOpenApi(this IApplicationBuilder app) - { - app.UseOpenApi(); - app.UseReDoc(x => - { - x.Path = "/doc"; - x.TransformToExternalPath = (internalUiRoute, _) => "/api" + internalUiRoute; - x.AdditionalSettings["theme"] = new - { - colors = new { primary = new { main = "#e13e13" } } - }; - }); - } -} diff --git a/back/stylecop.json b/back/stylecop.json deleted file mode 100644 index 4a27621c..00000000 --- a/back/stylecop.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", - "settings": { - "documentationRules": { - "copyrightText": "Kyoo - A portable and vast media library solution.\nCopyright (c) Kyoo.\n\nSee AUTHORS.md and LICENSE file in the project root for full license information.\n\nKyoo is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nKyoo is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Kyoo. If not, see .", - "xmlHeader": false - } - } -} diff --git a/back/tests/.gitignore b/back/tests/.gitignore deleted file mode 100644 index f7275bbb..00000000 --- a/back/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -venv/ diff --git a/back/tests/auth/auth.robot b/back/tests/auth/auth.robot deleted file mode 100644 index 02b7617b..00000000 --- a/back/tests/auth/auth.robot +++ /dev/null @@ -1,82 +0,0 @@ -*** Settings *** -Documentation Tests of the /auth route. -... Ensures that the user can authenticate on kyoo. - -Resource ../rest.resource - - -*** Keywords *** -Login - [Documentation] Shortcut to login with the given username for future requests - [Arguments] ${username} - &{res}= POST /auth/login {"username": "${username}", "password": "password-${username}"} - Output - Integer response status 200 - String response body access_token - Set Headers {"Authorization": "Bearer ${res.body.access_token}"} - -Register - [Documentation] Shortcut to register with the given username for future requests - [Arguments] ${username} - &{res}= POST - ... /auth/register - ... {"username": "${username}", "password": "password-${username}", "email": "${username}@kyoo.moe"} - Output - Integer response status 200 - String response body access_token - Set Headers {"Authorization": "Bearer ${res.body.access_token}"} - -Logout - [Documentation] Logout the current user, only the local client is affected. - Set Headers {"Authorization": ""} - - -*** Test Cases *** -Me cant be accessed without an account - Get /auth/me - Output - Integer response status 401 - -Bad Account - [Documentation] Login fails if user does not exist - POST /auth/login {"username": "i-don-t-exist", "password": "pass"} - Output - Integer response status 403 - -Register - [Documentation] Create a new user and login in it - Register user-1 - [Teardown] DELETE /auth/me - -Register Duplicates - [Documentation] If two users tries to register with the same username, it fails - Register user-duplicate - # We can't use the `Register` keyword because it assert for success - POST /auth/register {"username": "user-duplicate", "password": "pass", "email": "mail@kyoo.moe"} - Output - Integer response status 409 - [Teardown] DELETE /auth/me - -Delete Account - [Documentation] Check if a user can delete it's account - Register I-should-be-deleted - DELETE /auth/me - Output - Integer response status 204 - -Login - [Documentation] Create a new user and login in it - Register login-user - ${res}= GET /auth/me - Output - Integer response status 200 - String response body username login-user - - Logout - Login login-user - ${me}= Get /auth/me - Output - Output ${me} - Should Be Equal As Strings ${res["body"]} ${me["body"]} - - [Teardown] DELETE /auth/me diff --git a/back/tests/pyproject.toml b/back/tests/pyproject.toml deleted file mode 100644 index e5e1011e..00000000 --- a/back/tests/pyproject.toml +++ /dev/null @@ -1,4 +0,0 @@ -[tool.robotidy] -configure = [ - "MergeAndOrderSections:order=comments,settings,keywords,variables,testcases" -] diff --git a/back/tests/rest.resource b/back/tests/rest.resource deleted file mode 100644 index 348bbc08..00000000 --- a/back/tests/rest.resource +++ /dev/null @@ -1,4 +0,0 @@ -*** Settings *** -Documentation Common things to handle rest requests - -Library REST http://localhost:8901/api diff --git a/shell.nix b/shell.nix index adc98603..11835154 100644 --- a/shell.nix +++ b/shell.nix @@ -4,7 +4,6 @@ pkgs.mkShell { inputsFrom = [ (import ./api/shell.nix {inherit pkgs;}) (import ./auth/shell.nix {inherit pkgs;}) - (import ./back/shell.nix {inherit pkgs;}) (import ./chart/shell.nix {inherit pkgs;}) (import ./front/shell.nix {inherit pkgs;}) (import ./scanner/shell.nix {inherit pkgs;})