Compare commits

..

No commits in common. "develop" and "v4.43.10" have entirely different histories.

231 changed files with 14936 additions and 20451 deletions

View file

@ -24,7 +24,7 @@ jobs:
with: with:
node-version: '18' node-version: '18'
- uses: actions/cache@v4 - uses: actions/cache@v2
with: with:
path: | path: |
'**/node_modules' '**/node_modules'

View file

@ -15,7 +15,7 @@ jobs:
node-version: '18' node-version: '18'
- name: NodeModules Cache - name: NodeModules Cache
uses: actions/cache@v4 uses: actions/cache@v2
with: with:
path: '**/node_modules' path: '**/node_modules'
key: node_modules-${{ hashFiles('**/yarn.lock') }} key: node_modules-${{ hashFiles('**/yarn.lock') }}
@ -27,7 +27,7 @@ jobs:
run: yarn --cwd ./src/Ombi/ClientApp run build run: yarn --cwd ./src/Ombi/ClientApp run build
- name: Publish UI Artifacts - name: Publish UI Artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: angular_dist name: angular_dist
path: | path: |
@ -39,10 +39,10 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1 - uses: actions/setup-dotnet@v1
with: with:
dotnet-version: '8.0.x' dotnet-version: '6.0.x'
- name: Nuget Cache - name: Nuget Cache
uses: actions/cache@v4 uses: actions/cache@v2
with: with:
path: ~/.nuget/packages path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
@ -84,10 +84,10 @@ jobs:
strategy: strategy:
matrix: matrix:
include: include:
- os: win-x64 - os: win10-x64
format: zip format: zip
compression: zip compression: zip
- os: win-x86 - os: win10-x86
format: zip format: zip
compression: zip compression: zip
- os: linux-x64 - os: linux-x64
@ -106,13 +106,13 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1 - uses: actions/setup-dotnet@v1
with: with:
dotnet-version: '8.0.x' dotnet-version: '6.0.x'
- uses: actions/setup-dotnet@v1 - uses: actions/setup-dotnet@v1
with: with:
dotnet-version: '5.0.x' dotnet-version: '5.0.x'
- name: Nuget Cache - name: Nuget Cache
uses: actions/cache@v4 uses: actions/cache@v2
with: with:
path: ~/.nuget/packages path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
@ -130,7 +130,7 @@ jobs:
working-directory: src/Ombi working-directory: src/Ombi
- name: Download Angular - name: Download Angular
uses: actions/download-artifact@v4 uses: actions/download-artifact@v2
with: with:
name: angular_dist name: angular_dist
path: ~/src/Ombi/dist path: ~/src/Ombi/dist
@ -156,7 +156,7 @@ jobs:
directory: 'src/Ombi/${{ matrix.os }}' directory: 'src/Ombi/${{ matrix.os }}'
- name: Publish Release - name: Publish Release
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v2
with: with:
name: ${{ matrix.os }} name: ${{ matrix.os }}
path: | path: |
@ -170,7 +170,7 @@ jobs:
- name: Download Artifacts - name: Download Artifacts
id: download id: download
uses: actions/download-artifact@v4 uses: actions/download-artifact@v2
with: with:
path: artifacts path: artifacts
@ -190,7 +190,7 @@ jobs:
if: contains(github.ref, 'develop') if: contains(github.ref, 'develop')
with: with:
prerelease: true prerelease: true
generate_release_notes: false generate_release_notes: true
body: ${{ needs.versioning.outputs.changelog }} body: ${{ needs.versioning.outputs.changelog }}
name: ${{ needs.versioning.outputs.tag }} name: ${{ needs.versioning.outputs.tag }}
tag_name: ${{ needs.versioning.outputs.tag }} tag_name: ${{ needs.versioning.outputs.tag }}

View file

@ -17,7 +17,7 @@
# fetch-depth: 0 # fetch-depth: 0
# - name: NodeModules Cache # - name: NodeModules Cache
# uses: actions/cache@v4 # uses: actions/cache@v2
# with: # with:
# path: '**/node_modules' # path: '**/node_modules'
# key: node_modules-${{ hashFiles('**/yarn.lock') }} # key: node_modules-${{ hashFiles('**/yarn.lock') }}

View file

@ -37,7 +37,7 @@ jobs:
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v1
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
@ -48,7 +48,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v3 uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell. # Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl # 📚 https://git.io/JvXDl
@ -62,4 +62,4 @@ jobs:
# make release # make release
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3 uses: github/codeql-action/analyze@v1

View file

@ -20,7 +20,7 @@ jobs:
node-version: '18' node-version: '18'
- name: NodeModules Cache - name: NodeModules Cache
uses: actions/cache@v4 uses: actions/cache@v2
with: with:
path: '**/node_modules' path: '**/node_modules'
key: node_modules-${{ hashFiles('**/yarn.lock') }} key: node_modules-${{ hashFiles('**/yarn.lock') }}
@ -38,10 +38,10 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1 - uses: actions/setup-dotnet@v1
with: with:
dotnet-version: '8.0.x' dotnet-version: '6.0.x'
- name: Nuget Cache - name: Nuget Cache
uses: actions/cache@v4 uses: actions/cache@v2
with: with:
path: ~/.nuget/packages path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
@ -76,10 +76,10 @@ jobs:
strategy: strategy:
matrix: matrix:
include: include:
- os: win-x64 - os: win10-x64
format: zip format: zip
compression: zip compression: zip
- os: win-x86 - os: win10-x86
format: zip format: zip
compression: zip compression: zip
- os: linux-x64 - os: linux-x64
@ -98,10 +98,10 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1 - uses: actions/setup-dotnet@v1
with: with:
dotnet-version: '8.0.x' dotnet-version: '6.0.x'
- name: Nuget Cache - name: Nuget Cache
uses: actions/cache@v4 uses: actions/cache@v2
with: with:
path: ~/.nuget/packages path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}

File diff suppressed because it is too large Load diff

961
README.md

File diff suppressed because it is too large Load diff

2309
src/.idea/.idea.Ombi/.idea/contentModel.xml generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,440 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DBNavigator.Project.DDLFileAttachmentManager">
<mappings />
<preferences />
</component>
<component name="DBNavigator.Project.DatabaseAssistantManager">
<assistants />
</component>
<component name="DBNavigator.Project.DatabaseBrowserManager">
<autoscroll-to-editor value="false" />
<autoscroll-from-editor value="true" />
<show-object-properties value="true" />
<loaded-nodes />
</component>
<component name="DBNavigator.Project.DatabaseFileManager">
<open-files />
</component>
<component name="DBNavigator.Project.ExecutionManager">
<retain-sticky-names value="false" />
</component>
<component name="DBNavigator.Project.ParserDiagnosticsManager">
<diagnostics-history />
</component>
<component name="DBNavigator.Project.Settings">
<connections />
<browser-settings>
<general>
<display-mode value="TABBED" />
<navigation-history-size value="100" />
<show-object-details value="false" />
<enable-sticky-paths value="true" />
</general>
<filters>
<object-type-filter>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="true" />
<object-type name="ROLE" enabled="true" />
<object-type name="PRIVILEGE" enabled="true" />
<object-type name="CHARSET" enabled="true" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED_VIEW" enabled="true" />
<object-type name="NESTED_TABLE" enabled="true" />
<object-type name="COLUMN" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET_TRIGGER" enabled="true" />
<object-type name="DATABASE_TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="true" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="TYPE_ATTRIBUTE" enabled="true" />
<object-type name="ARGUMENT" enabled="true" />
<object-type name="JAVA_CLASS" enabled="true" />
<object-type name="JAVA_INNER_CLASS" enabled="true" />
<object-type name="JAVA_FIELD" enabled="true" />
<object-type name="JAVA_METHOD" enabled="true" />
<object-type name="DIMENSION" enabled="true" />
<object-type name="CLUSTER" enabled="true" />
<object-type name="DBLINK" enabled="true" />
<object-type name="CREDENTIAL" enabled="true" />
<object-type name="AI_PROFILE" enabled="true" />
</object-type-filter>
</filters>
<sorting>
<object-type name="COLUMN" sorting-type="NAME" />
<object-type name="FUNCTION" sorting-type="NAME" />
<object-type name="PROCEDURE" sorting-type="NAME" />
<object-type name="ARGUMENT" sorting-type="POSITION" />
<object-type name="TYPE ATTRIBUTE" sorting-type="POSITION" />
</sorting>
<default-editors>
<object-type name="VIEW" editor-type="SELECTION" />
<object-type name="PACKAGE" editor-type="SELECTION" />
<object-type name="TYPE" editor-type="SELECTION" />
</default-editors>
</browser-settings>
<navigation-settings>
<lookup-filters>
<lookup-objects>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="false" />
<object-type name="ROLE" enabled="false" />
<object-type name="PRIVILEGE" enabled="false" />
<object-type name="CHARSET" enabled="false" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED VIEW" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET TRIGGER" enabled="true" />
<object-type name="DATABASE TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="false" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="JAVA CLASS" enabled="true" />
<object-type name="INNER CLASS" enabled="true" />
<object-type name="JAVA FIELD" enabled="true" />
<object-type name="JAVA METHOD" enabled="true" />
<object-type name="JAVA PARAMETER" enabled="true" />
<object-type name="DIMENSION" enabled="false" />
<object-type name="CLUSTER" enabled="false" />
<object-type name="DBLINK" enabled="false" />
<object-type name="CREDENTIAL" enabled="false" />
</lookup-objects>
<force-database-load value="false" />
<prompt-connection-selection value="true" />
<prompt-schema-selection value="true" />
</lookup-filters>
</navigation-settings>
<dataset-grid-settings>
<general>
<enable-zooming value="true" />
<enable-column-tooltip value="true" />
</general>
<sorting>
<nulls-first value="true" />
<max-sorting-columns value="4" />
</sorting>
<audit-columns>
<column-names value="" />
<visible value="true" />
<editable value="false" />
</audit-columns>
</dataset-grid-settings>
<dataset-editor-settings>
<text-editor-popup>
<active value="false" />
<active-if-empty value="false" />
<data-length-threshold value="100" />
<popup-delay value="1000" />
</text-editor-popup>
<values-actions-popup>
<show-popup-button value="true" />
<element-count-threshold value="1000" />
<data-length-threshold value="250" />
</values-actions-popup>
<general>
<fetch-block-size value="100" />
<fetch-timeout value="30" />
<trim-whitespaces value="true" />
<convert-empty-strings-to-null value="true" />
<select-content-on-cell-edit value="true" />
<large-value-preview-active value="true" />
</general>
<filters>
<prompt-filter-dialog value="true" />
<default-filter-type value="BASIC" />
</filters>
<qualified-text-editor text-length-threshold="300">
<content-types>
<content-type name="Text" enabled="true" />
<content-type name="Properties" enabled="true" />
<content-type name="XML" enabled="true" />
<content-type name="DTD" enabled="true" />
<content-type name="HTML" enabled="true" />
<content-type name="XHTML" enabled="true" />
<content-type name="CSS" enabled="true" />
<content-type name="SQL" enabled="true" />
<content-type name="PL/SQL" enabled="true" />
<content-type name="JavaScript" enabled="true" />
<content-type name="JSON" enabled="true" />
<content-type name="JSON5" enabled="true" />
<content-type name="YAML" enabled="true" />
<content-type name="C#" enabled="true" />
</content-types>
</qualified-text-editor>
<record-navigation>
<navigation-target value="VIEWER" />
</record-navigation>
</dataset-editor-settings>
<code-editor-settings>
<general>
<show-object-navigation-gutter value="false" />
<show-spec-declaration-navigation-gutter value="true" />
<enable-spellchecking value="true" />
<enable-reference-spellchecking value="false" />
</general>
<confirmations>
<save-changes value="false" />
<revert-changes value="true" />
<exit-on-changes value="ASK" />
</confirmations>
</code-editor-settings>
<code-completion-settings>
<filters>
<basic-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="false" />
<filter-element type="OBJECT" id="view" selected="false" />
<filter-element type="OBJECT" id="materialized view" selected="false" />
<filter-element type="OBJECT" id="index" selected="false" />
<filter-element type="OBJECT" id="constraint" selected="false" />
<filter-element type="OBJECT" id="trigger" selected="false" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="false" />
<filter-element type="OBJECT" id="procedure" selected="false" />
<filter-element type="OBJECT" id="function" selected="false" />
<filter-element type="OBJECT" id="package" selected="false" />
<filter-element type="OBJECT" id="type" selected="false" />
<filter-element type="OBJECT" id="dimension" selected="false" />
<filter-element type="OBJECT" id="cluster" selected="false" />
<filter-element type="OBJECT" id="dblink" selected="false" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</basic-filter>
<extended-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</extended-filter>
</filters>
<sorting enabled="true">
<sorting-element type="RESERVED_WORD" id="keyword" />
<sorting-element type="RESERVED_WORD" id="datatype" />
<sorting-element type="OBJECT" id="column" />
<sorting-element type="OBJECT" id="table" />
<sorting-element type="OBJECT" id="view" />
<sorting-element type="OBJECT" id="materialized view" />
<sorting-element type="OBJECT" id="index" />
<sorting-element type="OBJECT" id="constraint" />
<sorting-element type="OBJECT" id="trigger" />
<sorting-element type="OBJECT" id="synonym" />
<sorting-element type="OBJECT" id="sequence" />
<sorting-element type="OBJECT" id="procedure" />
<sorting-element type="OBJECT" id="function" />
<sorting-element type="OBJECT" id="package" />
<sorting-element type="OBJECT" id="type" />
<sorting-element type="OBJECT" id="dimension" />
<sorting-element type="OBJECT" id="cluster" />
<sorting-element type="OBJECT" id="dblink" />
<sorting-element type="OBJECT" id="schema" />
<sorting-element type="OBJECT" id="role" />
<sorting-element type="OBJECT" id="user" />
<sorting-element type="RESERVED_WORD" id="function" />
<sorting-element type="RESERVED_WORD" id="parameter" />
</sorting>
<format>
<enforce-code-style-case value="true" />
</format>
</code-completion-settings>
<execution-engine-settings>
<statement-execution>
<fetch-block-size value="100" />
<execution-timeout value="20" />
<debug-execution-timeout value="600" />
<focus-result value="false" />
<prompt-execution value="false" />
</statement-execution>
<script-execution>
<command-line-interfaces />
<execution-timeout value="300" />
</script-execution>
<method-execution>
<execution-timeout value="30" />
<debug-execution-timeout value="600" />
<parameter-history-size value="10" />
</method-execution>
</execution-engine-settings>
<operation-settings>
<transactions>
<uncommitted-changes>
<on-project-close value="ASK" />
<on-disconnect value="ASK" />
<on-autocommit-toggle value="ASK" />
</uncommitted-changes>
<multiple-uncommitted-changes>
<on-commit value="ASK" />
<on-rollback value="ASK" />
</multiple-uncommitted-changes>
</transactions>
<session-browser>
<disconnect-session value="ASK" />
<kill-session value="ASK" />
<reload-on-filter-change value="false" />
</session-browser>
<compiler>
<compile-type value="KEEP" />
<compile-dependencies value="ASK" />
<always-show-controls value="false" />
</compiler>
</operation-settings>
<ddl-file-settings>
<extensions>
<mapping file-type-id="VIEW" extensions="vw" />
<mapping file-type-id="TRIGGER" extensions="trg" />
<mapping file-type-id="PROCEDURE" extensions="prc" />
<mapping file-type-id="FUNCTION" extensions="fnc" />
<mapping file-type-id="PACKAGE" extensions="pkg" />
<mapping file-type-id="PACKAGE_SPEC" extensions="pks" />
<mapping file-type-id="PACKAGE_BODY" extensions="pkb" />
<mapping file-type-id="TYPE" extensions="tpe" />
<mapping file-type-id="TYPE_SPEC" extensions="tps" />
<mapping file-type-id="TYPE_BODY" extensions="tpb" />
<mapping file-type-id="JAVA_SOURCE" extensions="sql" />
</extensions>
<general>
<lookup-ddl-files value="true" />
<create-ddl-files value="false" />
<synchronize-ddl-files value="true" />
<use-qualified-names value="false" />
<make-scripts-rerunnable value="true" />
</general>
</ddl-file-settings>
<assistant-settings>
<credential-settings>
<credentials />
</credential-settings>
</assistant-settings>
<general-settings>
<regional-settings>
<date-format value="MEDIUM" />
<number-format value="UNGROUPED" />
<locale value="SYSTEM_DEFAULT" />
<use-custom-formats value="false" />
</regional-settings>
<environment>
<environment-types>
<environment-type id="development" name="Development" description="Development environment" color="-2430209/-12296320" readonly-code="false" readonly-data="false" />
<environment-type id="integration" name="Integration" description="Integration environment" color="-2621494/-12163514" readonly-code="true" readonly-data="false" />
<environment-type id="production" name="Production" description="Productive environment" color="-11574/-10271420" readonly-code="true" readonly-data="true" />
<environment-type id="other" name="Other" description="" color="-1576/-10724543" readonly-code="false" readonly-data="false" />
</environment-types>
<visibility-settings>
<connection-tabs value="true" />
<dialog-headers value="true" />
<object-editor-tabs value="true" />
<script-editor-tabs value="false" />
<execution-result-tabs value="true" />
</visibility-settings>
</environment>
</general-settings>
</component>
</project>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="UserContentModel"> <component name="ContentModelUserStore">
<attachedFolders /> <attachedFolders />
<explicitIncludes /> <explicitIncludes />
<explicitExcludes /> <explicitExcludes />

8
src/.idea/.idea.Ombi/.idea/modules.xml generated Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.Ombi/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.Ombi/riderModule.iml" />
</modules>
</component>
</project>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RiderProjectSettingsUpdater">
<option name="singleClickDiffPreview" value="1" />
<option name="vcsConfiguration" value="3" />
</component>
</project>

View file

@ -1,20 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="57001998-efde-494a-80b3-d7acfc91f770" name="Default Changelist" comment=""> <list default="true" id="57001998-efde-494a-80b3-d7acfc91f770" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/Ombi/ClientApp/src/app/settings/plex/plex.component.html" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/ClientApp/src/app/settings/plex/plex.component.html" afterDir="false" /> <change afterPath="$PROJECT_DIR$/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Ombi.Core/Models/Search/V2/Music/ArtistInformation.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/contentModel.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/contentModel.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/config/applicationhost.config" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/config/applicationhost.config" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Ombi.Api.MusicBrainz/Models/Artist/ArtistInformation.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.Api.MusicBrainz/Models/Artist/ArtistInformation.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Ombi.DependencyInjection/IocExtensions.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.DependencyInjection/IocExtensions.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Ombi/ClientApp/src/app/media-details/components/artist/artist-details.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/ClientApp/src/app/media-details/components/artist/artist-details.component.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Ombi/ClientApp/src/app/services/searchV2.service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/ClientApp/src/app/services/searchV2.service.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Ombi/Controllers/V2/SearchController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/Controllers/V2/SearchController.cs" afterDir="false" />
</list> </list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" /> <option name="LAST_RESOLUTION" value="IGNORE" />
</component> </component>
<component name="DpaMonitoringSettings">
<option name="firstShow" value="false" />
</component>
<component name="FileEditorManager"> <component name="FileEditorManager">
<leaf> <leaf>
<file pinned="false" current-in-tab="false"> <file pinned="false" current-in-tab="false">
@ -232,75 +237,27 @@
<component name="Git.Settings"> <component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
</component> </component>
<component name="GitHubPullRequestSearchHistory">{
&quot;lastFilter&quot;: {
&quot;state&quot;: &quot;OPEN&quot;,
&quot;assignee&quot;: &quot;tidusjar&quot;
}
}</component>
<component name="GitToolBoxStore">
<option name="recentBranches">
<RecentBranches>
<option name="branchesForRepo">
<list>
<RecentBranchesForRepo>
<option name="branches">
<list>
<RecentBranch>
<option name="branchName" value="wizard-database" />
<option name="lastUsedInstant" value="1735917525" />
</RecentBranch>
<RecentBranch>
<option name="branchName" value="develop" />
<option name="lastUsedInstant" value="1735917524" />
</RecentBranch>
</list>
</option>
<option name="repositoryRootUrl" value="file://$PROJECT_DIR$/.." />
</RecentBranchesForRepo>
</list>
</option>
</RecentBranches>
</option>
</component>
<component name="GithubProjectSettings">
<option name="branchProtectionPatterns">
<list>
<option value="master" />
<option value="develop" />
</list>
</option>
</component>
<component name="GithubPullRequestsUISettings">{
&quot;selectedUrlAndAccountId&quot;: {
&quot;url&quot;: &quot;https://github.com/ombi-app/ombi&quot;,
&quot;accountId&quot;: &quot;22dd09fe-fb9e-48a4-bfcc-3c152edf3f25&quot;
}
}</component>
<component name="HighlightingSettingsPerFile"> <component name="HighlightingSettingsPerFile">
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/990126b794024fe2bd16aebdd37eba1d7b600/93/25662f04/ServerVersion.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/3bd4df5aff92cabbc4d630be64227073db1b8539b3a1e47786b4b189d7cdb7/DbContext.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/449b441523c469ed34ff5a5e14f0bafcd8f097aa463655303dc19048fa44ac3/EntityFrameworkServiceCollectionExtensions.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/7d81b2d4f22bee75e5438c707251ae43cb0974c207db91ffc159118c84b4eb9/ServiceProvider.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/a424e6912048b4cd25715f158e789aae24db5c2911d9e622d39bc6ac3246c6/MySqlConnectionStringBuilder.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/bd1d5c50194fea68ff3559c160230b0ab50f5acf4ce3061bffd6d62958e2182/ExceptionDispatchInfo.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/e9881a453a581134c1a18331ac1f8f1201a5382a685bf2a40777fa22619/DbContextOptions`.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/IMusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/MusicSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/RecentlyAddedEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/UserStatsEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/IMultiSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Models/Search/V2/Music/ArtistInformation.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.DependencyInjection/IocExtensions.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Helpers.Tests/EmbyHelperTests.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$PROJECT_DIR$/Ombi.Helpers.Tests/EmbyHelperTests.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Schedule.Tests/OmbiQuartzTests.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$PROJECT_DIR$/Ombi.Schedule.Tests/OmbiQuartzTests.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/Models/Artist/ArtistInformation.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi/Controllers/V2/SearchController.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$PROJECT_DIR$/Ombi/Controllers/V2/SearchController.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.DependencyInjection/IocExtensions.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/IMultiSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/IMusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Models/Search/V2/Music/ArtistInformation.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="mock:///Dummy.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/RecentlyAddedEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="mock:///Dummy.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/MusicSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi/Program.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$PROJECT_DIR$/Ombi/Program.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/UserStatsEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="mock:///Dummy.cs" root0="FORCE_HIGHLIGHTING" />
</component> </component>
<component name="IdeDocumentHistory"> <component name="IdeDocumentHistory">
<option name="CHANGED_PATHS"> <option name="CHANGED_PATHS">
@ -318,17 +275,12 @@
<component name="PackageJsonUpdateNotifier"> <component name="PackageJsonUpdateNotifier">
<dismissed value="$PROJECT_DIR$/Ombi/ClientApp/package.json" /> <dismissed value="$PROJECT_DIR$/Ombi/ClientApp/package.json" />
</component> </component>
<component name="ProjectColorInfo">{
&quot;customColor&quot;: &quot;&quot;,
&quot;associatedIndex&quot;: 0
}</component>
<component name="ProjectFrameBounds" extendedState="6"> <component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="1087" /> <option name="x" value="1087" />
<option name="y" value="-1113" /> <option name="y" value="-1113" />
<option name="width" value="1400" /> <option name="width" value="1400" />
<option name="height" value="1000" /> <option name="height" value="1000" />
</component> </component>
<component name="ProjectId" id="2wGwbN5gDqLwyiO1WJdlwJzZ5M9" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true"> <component name="ProjectLevelVcsManager" settingsEditedManually="true">
<ConfirmationsSetting value="2" id="Add" /> <ConfirmationsSetting value="2" id="Add" />
</component> </component>
@ -391,26 +343,27 @@
<pane id="FileSystemExplorer" /> <pane id="FileSystemExplorer" />
</panes> </panes>
</component> </component>
<component name="ProjectViewState"> <component name="PropertiesComponent">
<option name="hideEmptyMiddlePackages" value="true" /> <property name="ASKED_SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
<option name="showLibraryContents" value="true" /> <property name="Rider.DefaultBreakpoints.AreToggled" value="true" />
<property name="Rider.ProjectViewActivator.IsNotFirstRun" value="true" />
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="nodejs_package_manager_path" value="npm" />
</component> </component>
<component name="PropertiesComponent"><![CDATA[{ <component name="RunDashboard">
"keyToString": { <option name="ruleStates">
".NET Launch Settings Profile.Ombi.Schedule.Tests.executor": "Run", <list>
".NET Launch Settings Profile.Ombi.executor": "Debug", <RuleState>
"RunOnceActivity.ShowReadmeOnStart": "true", <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
"RunOnceActivity.git.unshallow": "true", </RuleState>
"fb34c741-04ca-4b4f-8ea1-651a011b42c8.executor": "Debug", <RuleState>
"git-widget-placeholder": "watchlist-expired-notification", <option name="name" value="StatusDashboardGroupingRule" />
"node.js.detected.package.eslint": "true", </RuleState>
"node.js.selected.package.eslint": "(autodetect)", </list>
"node.js.selected.package.tslint": "(autodetect)", </option>
"nodejs_package_manager_path": "yarn", </component>
"vue.rearranger.settings.migration": "true" <component name="RunManager" selected=".NET Launch Settings Profile.Ombi: IIS Express">
}
}]]></component>
<component name="RunManager" selected=".NET Launch Settings Profile.Ombi">
<configuration name="Ombi" type="LaunchSettings" factoryName=".NET Launch Settings Profile"> <configuration name="Ombi" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
<option name="LAUNCH_PROFILE_PROJECT_FILE_PATH" value="$PROJECT_DIR$/Ombi/Ombi.csproj" /> <option name="LAUNCH_PROFILE_PROJECT_FILE_PATH" value="$PROJECT_DIR$/Ombi/Ombi.csproj" />
<option name="LAUNCH_PROFILE_TFM" value=".NETCoreApp,Version=v2.2" /> <option name="LAUNCH_PROFILE_TFM" value=".NETCoreApp,Version=v2.2" />
@ -423,7 +376,7 @@
<option name="SEND_DEBUG_REQUEST" value="1" /> <option name="SEND_DEBUG_REQUEST" value="1" />
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" /> <option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" enabled="true" />
</method> </method>
</configuration> </configuration>
<configuration name="Ombi: IIS Express" type="LaunchSettings" factoryName=".NET Launch Settings Profile"> <configuration name="Ombi: IIS Express" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
@ -438,7 +391,7 @@
<option name="SEND_DEBUG_REQUEST" value="1" /> <option name="SEND_DEBUG_REQUEST" value="1" />
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" /> <option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" enabled="true" />
</method> </method>
</configuration> </configuration>
<configuration name="Ombi.Schedule.Tests" type="LaunchSettings" factoryName=".NET Launch Settings Profile"> <configuration name="Ombi.Schedule.Tests" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
@ -453,7 +406,7 @@
<option name="SEND_DEBUG_REQUEST" value="1" /> <option name="SEND_DEBUG_REQUEST" value="1" />
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" /> <option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" enabled="true" />
</method> </method>
</configuration> </configuration>
<configuration name="Ombi.Schedule.Tests: IIS Express" type="LaunchSettings" factoryName=".NET Launch Settings Profile"> <configuration name="Ombi.Schedule.Tests: IIS Express" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
@ -468,7 +421,7 @@
<option name="SEND_DEBUG_REQUEST" value="1" /> <option name="SEND_DEBUG_REQUEST" value="1" />
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" /> <option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" enabled="true" />
</method> </method>
</configuration> </configuration>
<configuration name="Ombi.Updater" type="LaunchSettings" factoryName=".NET Launch Settings Profile"> <configuration name="Ombi.Updater" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
@ -483,11 +436,10 @@
<option name="SEND_DEBUG_REQUEST" value="1" /> <option name="SEND_DEBUG_REQUEST" value="1" />
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" /> <option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
<method v="2"> <method v="2">
<option name="Build" /> <option name="Build" enabled="true" />
</method> </method>
</configuration> </configuration>
</component> </component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager"> <component name="TaskManager">
<task active="true" id="Default" summary="Default task"> <task active="true" id="Default" summary="Default task">
<changelist id="57001998-efde-494a-80b3-d7acfc91f770" name="Default Changelist" comment="" /> <changelist id="57001998-efde-494a-80b3-d7acfc91f770" name="Default Changelist" comment="" />
@ -496,9 +448,6 @@
<option name="presentableId" value="Default" /> <option name="presentableId" value="Default" />
<updated>1563957157468</updated> <updated>1563957157468</updated>
<workItem from="1563957162999" duration="5401000" /> <workItem from="1563957162999" duration="5401000" />
<workItem from="1745681294313" duration="1814000" />
<workItem from="1747080279165" duration="838000" />
<workItem from="1747082180432" duration="1994000" />
</task> </task>
<servers /> <servers />
</component> </component>
@ -544,11 +493,7 @@
</layout> </layout>
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" /> <option name="version" value="1" />
</component>
<component name="UnityProjectConfiguration" hasMinimizedUI="false" />
<component name="VcsManagerConfiguration">
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
</component> </component>
<component name="XDebuggerManager"> <component name="XDebuggerManager">
<breakpoint-manager> <breakpoint-manager>
@ -560,7 +505,7 @@
<line-breakpoint enabled="true" type="DotNet Breakpoints"> <line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs</url> <url>file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs</url>
<line>48</line> <line>48</line>
<properties documentPath="$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs"> <properties documentPath="$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs" initialLine="48">
<startOffsets> <startOffsets>
<option value="1518" /> <option value="1518" />
</startOffsets> </startOffsets>
@ -573,12 +518,12 @@
<line-breakpoint enabled="true" type="DotNet Breakpoints"> <line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url> <url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url>
<line>59</line> <line>59</line>
<properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" containingFunctionPresentation="Method 'MultiSearch'"> <properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" initialLine="59">
<startOffsets> <startOffsets>
<option value="2369" /> <option value="2276" />
</startOffsets> </startOffsets>
<endOffsets> <endOffsets>
<option value="2576" /> <option value="2316" />
</endOffsets> </endOffsets>
</properties> </properties>
<option name="timeStamp" value="4" /> <option name="timeStamp" value="4" />
@ -586,12 +531,12 @@
<line-breakpoint enabled="true" type="DotNet Breakpoints"> <line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url> <url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url>
<line>49</line> <line>49</line>
<properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" containingFunctionPresentation="Method 'MultiSearch'"> <properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" initialLine="49">
<startOffsets> <startOffsets>
<option value="1903" /> <option value="2001" />
</startOffsets> </startOffsets>
<endOffsets> <endOffsets>
<option value="1945" /> <option value="2002" />
</endOffsets> </endOffsets>
</properties> </properties>
<option name="timeStamp" value="5" /> <option name="timeStamp" value="5" />
@ -599,55 +544,16 @@
<line-breakpoint enabled="true" type="DotNet Breakpoints"> <line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs</url> <url>file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs</url>
<line>30</line> <line>30</line>
<properties documentPath="$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs" containingFunctionPresentation="Method 'SearchArtist'"> <properties documentPath="$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs" initialLine="30">
<startOffsets> <startOffsets>
<option value="833" /> <option value="917" />
</startOffsets> </startOffsets>
<endOffsets> <endOffsets>
<option value="834" /> <option value="1016" />
</endOffsets> </endOffsets>
</properties> </properties>
<option name="timeStamp" value="7" /> <option name="timeStamp" value="7" />
</line-breakpoint> </line-breakpoint>
<line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs</url>
<line>110</line>
<properties documentPath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" containingFunctionPresentation="Method 'Execute'">
<startOffsets>
<option value="5123" />
</startOffsets>
<endOffsets>
<option value="5206" />
</endOffsets>
</properties>
<option name="timeStamp" value="10" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs</url>
<line>77</line>
<properties documentPath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" containingFunctionPresentation="Method 'Execute'">
<startOffsets>
<option value="3324" />
</startOffsets>
<endOffsets>
<option value="3365" />
</endOffsets>
</properties>
<option name="timeStamp" value="11" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs</url>
<line>100</line>
<properties documentPath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" containingFunctionPresentation="Method 'Execute'">
<startOffsets>
<option value="4602" />
</startOffsets>
<endOffsets>
<option value="4636" />
</endOffsets>
</properties>
<option name="timeStamp" value="12" />
</line-breakpoint>
</breakpoints> </breakpoints>
</breakpoint-manager> </breakpoint-manager>
<watches-manager> <watches-manager>

14
src/.idea/.idea.Ombi/riderModule.iml generated Normal file
View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="RIDER_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$USER_HOME$/.nuget/packages/microsoft.net.test.sdk/16.0.1/build/netcoreapp1.0" />
<content url="file://$USER_HOME$/.nuget/packages/nunit3testadapter/3.13.0/build/netcoreapp1.0/NUnit3.TestAdapter.dll" />
<content url="file://$USER_HOME$/.nuget/packages/nunit3testadapter/3.13.0/build/netcoreapp1.0/NUnit3.TestAdapter.pdb" />
<content url="file://$USER_HOME$/.nuget/packages/nunit3testadapter/3.13.0/build/netcoreapp1.0/nunit.engine.netstandard.dll" />
<content url="file://$MODULE_DIR$/../../../CHANGELOG.md" />
<content url="file://$MODULE_DIR$/../../../appveyor.yml" />
<content url="file://$MODULE_DIR$/../../../build.cake" />
<content url="file://$MODULE_DIR$/../.." />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -1,31 +1,22 @@
{ {
"version": "2.0.0", "version": "0.1.0",
"command": "dotnet", "command": "dotnet",
"isShellCommand": true,
"args": [], "args": [],
"tasks": [ "tasks": [
{ {
"label": "build", "taskName": "build",
"type": "shell",
"command": "dotnet",
"args": [ "args": [
"build",
"${workspaceRoot}/Ombi/Ombi.csproj" "${workspaceRoot}/Ombi/Ombi.csproj"
], ],
"problemMatcher": "$msCompile", "isBuildCommand": true,
"group": { "problemMatcher": "$msCompile"
"_id": "build",
"isDefault": false
}
}, },
{ {
"label": "lint", "taskName": "lint",
"type": "shell",
"command": "npm", "command": "npm",
"args": [ "isShellCommand": true,
"run", "args": ["run", "lint"]
"lint"
],
"problemMatcher": []
} }
] ]
} }

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -29,6 +29,5 @@ namespace Ombi.Api.Plex
Task<PlexAddWrapper> AddUser(string emailAddress, string serverId, string authToken, int[] libs); Task<PlexAddWrapper> AddUser(string emailAddress, string serverId, string authToken, int[] libs);
Task<PlexWatchlistContainer> GetWatchlist(string plexToken, CancellationToken cancellationToken); Task<PlexWatchlistContainer> GetWatchlist(string plexToken, CancellationToken cancellationToken);
Task<PlexWatchlistMetadataContainer> GetWatchlistMetadata(string ratingKey, string plexToken, CancellationToken cancellationToken); Task<PlexWatchlistMetadataContainer> GetWatchlistMetadata(string ratingKey, string plexToken, CancellationToken cancellationToken);
Task<bool> Ping(string authToken, CancellationToken cancellationToken = default);
} }
} }

View file

@ -22,18 +22,6 @@ namespace Ombi.Api.Plex.Models.Friends
/// </summary> /// </summary>
[XmlAttribute(AttributeName = "home")] [XmlAttribute(AttributeName = "home")]
public bool HomeUser { get; set; } public bool HomeUser { get; set; }
[XmlElement(ElementName = "Server")]
public PlexUserServer[] Server { get; set; }
}
public class PlexUserServer
{
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "serverId")]
public string ServerId { get; set; }
} }
[XmlRoot(ElementName = "MediaContainer")] [XmlRoot(ElementName = "MediaContainer")]

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -68,7 +68,7 @@ namespace Ombi.Api.Plex
private const string FriendsUri = "https://plex.tv/api/users"; private const string FriendsUri = "https://plex.tv/api/users";
private const string GetAccountUri = "https://plex.tv/users/account.json"; private const string GetAccountUri = "https://plex.tv/users/account.json";
private const string ServerUri = "https://plex.tv/pms/servers.xml"; private const string ServerUri = "https://plex.tv/pms/servers.xml";
private const string WatchlistUri = "https://discover.provider.plex.tv/"; private const string WatchlistUri = "https://metadata.provider.plex.tv/";
/// <summary> /// <summary>
/// Sign into the Plex API /// Sign into the Plex API
@ -320,30 +320,6 @@ namespace Ombi.Api.Plex
return result; return result;
} }
/// <summary>
/// Pings the Plex API to validate if a token is still valid
/// </summary>
/// <param name="authToken">The authentication token to validate</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>True if the token is valid, false otherwise</returns>
public async Task<bool> Ping(string authToken, CancellationToken cancellationToken = default)
{
try
{
var request = new Request("api/v2/ping", "https://plex.tv/", HttpMethod.Get);
await AddHeaders(request, authToken);
// We don't need to parse the response, just check if the request succeeds
await Api.Request(request, cancellationToken);
return true;
}
catch
{
// If the request fails (401, 403, etc.), the token is invalid
return false;
}
}
/// <summary> /// <summary>
/// Adds the required headers and also the authorization header /// Adds the required headers and also the authorization header

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -11,7 +11,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -13,7 +13,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" /> <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -11,8 +11,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Polly" Version="7.2.3" /> <PackageReference Include="Polly" Version="7.2.3" />
</ItemGroup> </ItemGroup>

View file

@ -300,13 +300,13 @@ namespace Ombi.Core.Tests.Engine
{ {
UserId = "id1", UserId = "id1",
RequestType = RequestType.Movie, RequestType = RequestType.Movie,
RequestDate = today.AddMinutes(-1), RequestDate = today.AddHours(-1),
}, },
new RequestLog new RequestLog
{ {
UserId = "id1", UserId = "id1",
RequestType = RequestType.Movie, RequestType = RequestType.Movie,
RequestDate = today.AddMinutes(-2), RequestDate = today.AddHours(-2),
}, },
}; };
var repoMock = _mocker.GetMock<IRepository<RequestLog>>(); var repoMock = _mocker.GetMock<IRepository<RequestLog>>();

View file

@ -298,13 +298,13 @@ namespace Ombi.Core.Tests.Engine
{ {
UserId = "id1", UserId = "id1",
RequestType = RequestType.Album, RequestType = RequestType.Album,
RequestDate = today.AddMinutes(-1), RequestDate = today.AddHours(-1),
}, },
new RequestLog new RequestLog
{ {
UserId = "id1", UserId = "id1",
RequestType = RequestType.Album, RequestType = RequestType.Album,
RequestDate = today.AddMinutes(-2), RequestDate = today.AddHours(-2),
}, },
}; };
var repoMock = _mocker.GetMock<IRepository<RequestLog>>(); var repoMock = _mocker.GetMock<IRepository<RequestLog>>();

View file

@ -304,7 +304,7 @@ namespace Ombi.Core.Tests.Engine
{ {
UserId = "id1", UserId = "id1",
RequestType = RequestType.TvShow, RequestType = RequestType.TvShow,
RequestDate = today.AddMinutes(-1), RequestDate = today.AddHours(-1),
EpisodeCount = 1, EpisodeCount = 1,
}, },
new RequestLog new RequestLog
@ -312,7 +312,7 @@ namespace Ombi.Core.Tests.Engine
UserId = "id1", UserId = "id1",
RequestType = RequestType.TvShow, RequestType = RequestType.TvShow,
EpisodeCount = 1, EpisodeCount = 1,
RequestDate = today.AddMinutes(-2), RequestDate = today.AddHours(-2),
}, },
}; };
var repoMock = _mocker.GetMock<IRepository<RequestLog>>(); var repoMock = _mocker.GetMock<IRepository<RequestLog>>();
@ -345,7 +345,7 @@ namespace Ombi.Core.Tests.Engine
{ {
UserId = "id1", UserId = "id1",
RequestType = RequestType.TvShow, RequestType = RequestType.TvShow,
RequestDate = today.AddMinutes(-1), RequestDate = today.AddHours(-1),
EpisodeCount = 5, EpisodeCount = 5,
}, },
new RequestLog new RequestLog
@ -353,7 +353,7 @@ namespace Ombi.Core.Tests.Engine
UserId = "id1", UserId = "id1",
RequestType = RequestType.TvShow, RequestType = RequestType.TvShow,
EpisodeCount = 4, EpisodeCount = 4,
RequestDate = today.AddMinutes(-2), RequestDate = today.AddHours(-2),
}, },
}; };
var repoMock = _mocker.GetMock<IRepository<RequestLog>>(); var repoMock = _mocker.GetMock<IRepository<RequestLog>>();

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
@ -9,7 +9,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoFixture" Version="4.18.0" /> <PackageReference Include="AutoFixture" Version="4.18.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="Moq" Version="4.18.2" /> <PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="Moq.AutoMock" Version="3.4.0" /> <PackageReference Include="Moq.AutoMock" Version="3.4.0" />
<PackageReference Include="Nunit" Version="3.13.3" /> <PackageReference Include="Nunit" Version="3.13.3" />

View file

@ -1,52 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Ombi.Api.Plex;
namespace Ombi.Core.Authentication
{
public interface IPlexTokenKeepAliveService
{
Task<bool> KeepTokenAliveAsync(string token, CancellationToken cancellationToken);
}
public class PlexTokenKeepAliveService : IPlexTokenKeepAliveService
{
private readonly IPlexApi _plexApi;
private readonly ILogger<PlexTokenKeepAliveService> _logger;
public PlexTokenKeepAliveService(IPlexApi plexApi, ILogger<PlexTokenKeepAliveService> logger)
{
_plexApi = plexApi;
_logger = logger;
}
public async Task<bool> KeepTokenAliveAsync(string token, CancellationToken cancellationToken)
{
try
{
if (string.IsNullOrEmpty(token))
{
_logger.LogWarning("Token is null or empty");
return false;
}
// Use the Ping method to validate the token
var isValid = await _plexApi.Ping(token, cancellationToken);
if (!isValid)
{
_logger.LogWarning("Token validation failed - token may be expired or invalid");
}
return isValid;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while keeping token alive");
return false;
}
}
}
}

View file

@ -598,13 +598,13 @@ namespace Ombi.Core.Engine
public async Task<RequestEngineResult> ApproveMovieById(int requestId, bool is4K) public async Task<RequestEngineResult> ApproveMovieById(int requestId, bool is4K)
{ {
var request = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == requestId); var request = await MovieRepository.Find(requestId);
return await ApproveMovie(request, is4K); return await ApproveMovie(request, is4K);
} }
public async Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason, bool is4K) public async Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason, bool is4K)
{ {
var request = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == modelId); var request = await MovieRepository.Find(modelId);
if (request == null) if (request == null)
{ {
return new RequestEngineResult return new RequestEngineResult
@ -790,7 +790,7 @@ namespace Ombi.Core.Engine
public async Task<RequestEngineResult> ReProcessRequest(int requestId, bool is4K, CancellationToken cancellationToken) public async Task<RequestEngineResult> ReProcessRequest(int requestId, bool is4K, CancellationToken cancellationToken)
{ {
var request = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == requestId); var request = await MovieRepository.Find(requestId);
if (request == null) if (request == null)
{ {
return new RequestEngineResult return new RequestEngineResult
@ -805,7 +805,7 @@ namespace Ombi.Core.Engine
public async Task<RequestEngineResult> MarkUnavailable(int modelId, bool is4K) public async Task<RequestEngineResult> MarkUnavailable(int modelId, bool is4K)
{ {
var request = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == modelId); var request = await MovieRepository.Find(modelId);
if (request == null) if (request == null)
{ {
return new RequestEngineResult return new RequestEngineResult
@ -834,7 +834,7 @@ namespace Ombi.Core.Engine
public async Task<RequestEngineResult> MarkAvailable(int modelId, bool is4K) public async Task<RequestEngineResult> MarkAvailable(int modelId, bool is4K)
{ {
var request = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == modelId); var request = await MovieRepository.Find(modelId);
if (request == null) if (request == null)
{ {
return new RequestEngineResult return new RequestEngineResult

View file

@ -1,67 +0,0 @@
using System;
using System.Text;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
using Ombi.Core.Models;
using Polly;
using Pomelo.EntityFrameworkCore.MySql.Storage.Internal;
namespace Ombi.Core.Helpers;
public static class DatabaseConfigurationSetup
{
public static void ConfigurePostgres(DbContextOptionsBuilder options, PerDatabaseConfiguration config)
{
options.UseNpgsql(config.ConnectionString, b =>
{
b.EnableRetryOnFailure();
}).ReplaceService<ISqlGenerationHelper, NpgsqlCaseInsensitiveSqlGenerationHelper>();
}
public static void ConfigureMySql(DbContextOptionsBuilder options, PerDatabaseConfiguration config)
{
if (string.IsNullOrEmpty(config.ConnectionString))
{
throw new ArgumentNullException("ConnectionString for the MySql/Mariadb database is empty");
}
options.UseMySql(config.ConnectionString, GetServerVersion(config.ConnectionString), b =>
{
//b.CharSetBehavior(Pomelo.EntityFrameworkCore.MySql.Infrastructure.CharSetBehavior.NeverAppend); // ##ISSUE, link to migrations?
b.EnableRetryOnFailure();
});
}
private static ServerVersion GetServerVersion(string connectionString)
{
// Workaround Windows bug, that can lead to the following exception:
//
// MySqlConnector.MySqlException (0x80004005): SSL Authentication Error
// ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
// ---> System.ComponentModel.Win32Exception (0x8009030F): The message or signature supplied for verification has been altered
//
// See https://github.com/dotnet/runtime/issues/17005#issuecomment-305848835
//
// Also workaround for the fact, that ServerVersion.AutoDetect() does not use any retrying strategy.
ServerVersion serverVersion = null;
#pragma warning disable EF1001
var retryPolicy = Policy.Handle<Exception>(exception => MySqlTransientExceptionDetector.ShouldRetryOn(exception))
#pragma warning restore EF1001
.WaitAndRetry(3, (count, context) => TimeSpan.FromMilliseconds(count * 250));
serverVersion = retryPolicy.Execute(() => serverVersion = ServerVersion.AutoDetect(connectionString));
return serverVersion;
}
public class NpgsqlCaseInsensitiveSqlGenerationHelper : NpgsqlSqlGenerationHelper
{
const string EFMigrationsHisory = "__EFMigrationsHistory";
public NpgsqlCaseInsensitiveSqlGenerationHelper(RelationalSqlGenerationHelperDependencies dependencies)
: base(dependencies) { }
public override string DelimitIdentifier(string identifier) =>
base.DelimitIdentifier(identifier == EFMigrationsHisory ? identifier : identifier.ToLower());
public override void DelimitIdentifier(StringBuilder builder, string identifier)
=> base.DelimitIdentifier(builder, identifier == EFMigrationsHisory ? identifier : identifier.ToLower());
}
}

View file

@ -1,10 +0,0 @@
namespace Ombi.Core.Helpers;
public class FileSystem : IFileSystem
{
public bool FileExists(string path)
{
return System.IO.File.Exists(path);
}
// Implement other file system operations as needed
}

View file

@ -1,7 +0,0 @@
namespace Ombi.Core.Helpers;
public interface IFileSystem
{
bool FileExists(string path);
// Add other file system operations as needed
}

View file

@ -1,40 +0,0 @@
using System.IO;
namespace Ombi.Core.Models;
public class DatabaseConfiguration
{
public const string SqliteDatabase = "Sqlite";
public DatabaseConfiguration()
{
}
public DatabaseConfiguration(string defaultSqlitePath)
{
OmbiDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "Ombi.db")}");
SettingsDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "OmbiSettings.db")}");
ExternalDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "OmbiExternal.db")}");
}
public PerDatabaseConfiguration OmbiDatabase { get; set; }
public PerDatabaseConfiguration SettingsDatabase { get; set; }
public PerDatabaseConfiguration ExternalDatabase { get; set; }
}
public class PerDatabaseConfiguration
{
public PerDatabaseConfiguration(string type, string connectionString)
{
Type = type;
ConnectionString = connectionString;
}
// Used in Deserialization
public PerDatabaseConfiguration()
{
}
public string Type { get; set; }
public string ConnectionString { get; set; }
}

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -13,8 +13,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoMapper" Version="12.0.0" /> <PackageReference Include="AutoMapper" Version="12.0.0" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.5" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.5" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.9" />
<PackageReference Include="MusicBrainzAPI" Version="2.5.0" /> <PackageReference Include="MusicBrainzAPI" Version="2.5.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" /> <PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />

View file

@ -182,10 +182,7 @@ namespace Ombi.Core.Senders
if (settings.SendUserTags) if (settings.SendUserTags)
{ {
var userTag = await GetOrCreateTag(model, settings); var userTag = await GetOrCreateTag(model, settings);
if (userTag != null) tags.Add(userTag.id);
{
tags.Add(userTag.id);
}
} }
// Overrides on the request take priority // Overrides on the request take priority
@ -201,9 +198,7 @@ namespace Ombi.Core.Senders
List<MovieResponse> movies; List<MovieResponse> movies;
// Check if the movie already exists? Since it could be unmonitored // Check if the movie already exists? Since it could be unmonitored
// Get the appropriate Radarr instance settings for existence check movies = await _radarrV3Api.GetMovies(settings.ApiKey, settings.FullUri);
var existenceCheckSettings = is4k ? await _radarr4KSettings.GetSettingsAsync() : settings;
movies = await _radarrV3Api.GetMovies(existenceCheckSettings.ApiKey, existenceCheckSettings.FullUri);
var existingMovie = movies.FirstOrDefault(x => x.tmdbId == model.TheMovieDbId); var existingMovie = movies.FirstOrDefault(x => x.tmdbId == model.TheMovieDbId);
if (existingMovie == null) if (existingMovie == null)
@ -251,12 +246,6 @@ namespace Ombi.Core.Senders
private async Task<Tag> GetOrCreateTag(MovieRequests model, RadarrSettings s) private async Task<Tag> GetOrCreateTag(MovieRequests model, RadarrSettings s)
{ {
if (model.RequestedUser == null)
{
_log.LogWarning("Cannot create tag - RequestedUser is null for movie request {MovieTitle}", model.Title);
return null;
}
var tagName = model.RequestedUser.UserName; var tagName = model.RequestedUser.UserName;
// Does tag exist? // Does tag exist?

View file

@ -133,14 +133,7 @@ namespace Ombi.Core.Senders
string seriesType; string seriesType;
int? tagToUse = null; int? tagToUse = null;
Logger.LogInformation("Starting SendToSonarr for series {Title} (TvDbId: {TvDbId})", model.ParentRequest.Title, model.ParentRequest.TvDbId);
Logger.LogInformation("Series type: {SeriesType}", model.SeriesType);
var profiles = await UserQualityProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId); var profiles = await UserQualityProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId);
if (profiles != null)
{
Logger.LogInformation("Found user quality profile for user {UserId}", model.RequestedUserId);
}
if (model.SeriesType == SeriesType.Anime) if (model.SeriesType == SeriesType.Anime)
{ {
@ -148,10 +141,8 @@ namespace Ombi.Core.Senders
// For some reason, if we haven't got one use the first root folder in Sonarr // For some reason, if we haven't got one use the first root folder in Sonarr
if (!int.TryParse(s.RootPathAnime, out int animePath)) if (!int.TryParse(s.RootPathAnime, out int animePath))
{ {
Logger.LogWarning("Failed to parse RootPathAnime: {RootPathAnime}, falling back to main root path", s.RootPathAnime);
animePath = int.Parse(s.RootPath); // Set it to the main root folder if we have no anime folder. animePath = int.Parse(s.RootPath); // Set it to the main root folder if we have no anime folder.
} }
Logger.LogInformation("Using anime path ID: {AnimePath}", animePath);
rootFolderPath = await GetSonarrRootPath(animePath, s); rootFolderPath = await GetSonarrRootPath(animePath, s);
languageProfileId = s.LanguageProfileAnime > 0 ? s.LanguageProfileAnime : s.LanguageProfile; languageProfileId = s.LanguageProfileAnime > 0 ? s.LanguageProfileAnime : s.LanguageProfile;
@ -163,7 +154,6 @@ namespace Ombi.Core.Senders
{ {
if (profiles.SonarrRootPathAnime > 0) if (profiles.SonarrRootPathAnime > 0)
{ {
Logger.LogInformation("Using user's anime root path override: {RootPath}", profiles.SonarrRootPathAnime);
rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPathAnime, s); rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPathAnime, s);
} }
if (profiles.SonarrQualityProfileAnime > 0) if (profiles.SonarrQualityProfileAnime > 0)
@ -179,13 +169,11 @@ namespace Ombi.Core.Senders
int.TryParse(s.QualityProfile, out qualityToUse); int.TryParse(s.QualityProfile, out qualityToUse);
// Get the root path from the rootfolder selected. // Get the root path from the rootfolder selected.
// For some reason, if we haven't got one use the first root folder in Sonarr // For some reason, if we haven't got one use the first root folder in Sonarr
Logger.LogInformation("Using standard path ID: {RootPath}", s.RootPath);
rootFolderPath = await GetSonarrRootPath(int.Parse(s.RootPath), s); rootFolderPath = await GetSonarrRootPath(int.Parse(s.RootPath), s);
if (profiles != null) if (profiles != null)
{ {
if (profiles.SonarrRootPath > 0) if (profiles.SonarrRootPath > 0)
{ {
Logger.LogInformation("Using user's standard root path override: {RootPath}", profiles.SonarrRootPath);
rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPath, s); rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPath, s);
} }
if (profiles.SonarrQualityProfile > 0) if (profiles.SonarrQualityProfile > 0)
@ -205,7 +193,6 @@ namespace Ombi.Core.Senders
if (model.ParentRequest.RootFolder.HasValue && model.ParentRequest.RootFolder.Value > 0) if (model.ParentRequest.RootFolder.HasValue && model.ParentRequest.RootFolder.Value > 0)
{ {
Logger.LogInformation("Using request root folder override: {RootFolder}", model.ParentRequest.RootFolder.Value);
rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder.Value, s); rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder.Value, s);
} }
@ -214,8 +201,6 @@ namespace Ombi.Core.Senders
languageProfileId = model.ParentRequest.LanguageProfile.Value; languageProfileId = model.ParentRequest.LanguageProfile.Value;
} }
Logger.LogInformation("Final root folder path: {RootFolderPath}", rootFolderPath);
try try
{ {
if (tagToUse.HasValue) if (tagToUse.HasValue)
@ -535,36 +520,17 @@ namespace Ombi.Core.Senders
private async Task<string> GetSonarrRootPath(int pathId, SonarrSettings sonarrSettings) private async Task<string> GetSonarrRootPath(int pathId, SonarrSettings sonarrSettings)
{ {
Logger.LogInformation("Getting Sonarr root path for ID: {PathId}", pathId);
var rootFoldersResult = await SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri); var rootFoldersResult = await SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri);
if (rootFoldersResult == null || !rootFoldersResult.Any())
{
Logger.LogError("No root folders returned from Sonarr API");
return string.Empty;
}
Logger.LogInformation("Found {Count} root folders in Sonarr", rootFoldersResult.Count());
foreach (var folder in rootFoldersResult)
{
Logger.LogDebug("Root folder - ID: {Id}, Path: {Path}", folder.id, folder.path);
}
if (pathId == 0) if (pathId == 0)
{ {
var defaultPath = rootFoldersResult.FirstOrDefault()?.path; return rootFoldersResult.FirstOrDefault().path;
Logger.LogInformation("Using first root folder as default: {Path}", defaultPath);
return defaultPath;
} }
var matchingFolder = rootFoldersResult.FirstOrDefault(r => r.id == pathId); foreach (var r in rootFoldersResult?.Where(r => r.id == pathId))
if (matchingFolder != null)
{ {
Logger.LogInformation("Found matching root folder for ID {PathId}: {Path}", pathId, matchingFolder.path); return r.path;
return matchingFolder.path;
} }
Logger.LogError("No matching root folder found for ID: {PathId}", pathId);
return string.Empty; return string.Empty;
} }
} }

View file

@ -1,69 +0,0 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Helpers;
namespace Ombi.Core.Services;
public class DatabaseConfigurationService : IDatabaseConfigurationService
{
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
public DatabaseConfigurationService(
ILogger<DatabaseConfigurationService> logger,
IFileSystem fileSystem)
{
_logger = logger;
_fileSystem = fileSystem;
}
public async Task<bool> ConfigureDatabase(string databaseType, string connectionString, CancellationToken token)
{
var i = StartupSingleton.Instance;
if (string.IsNullOrEmpty(i.StoragePath))
{
i.StoragePath = string.Empty;
}
var databaseFileLocation = Path.Combine(i.StoragePath, "database.json");
if (_fileSystem.FileExists(databaseFileLocation))
{
var error = $"The database file at '{databaseFileLocation}' already exists";
_logger.LogError(error);
return false;
}
var configuration = new DatabaseConfiguration
{
ExternalDatabase = new PerDatabaseConfiguration(databaseType, connectionString),
OmbiDatabase = new PerDatabaseConfiguration(databaseType, connectionString),
SettingsDatabase = new PerDatabaseConfiguration(databaseType, connectionString)
};
var json = JsonConvert.SerializeObject(configuration, Formatting.Indented);
_logger.LogInformation("Writing database configuration to file");
try
{
await File.WriteAllTextAsync(databaseFileLocation, json, token);
}
catch (Exception e)
{
_logger.LogError(e, "Failed to write database configuration to file");
return false;
}
_logger.LogInformation("Database configuration written to file");
return true;
}
}

View file

@ -1,11 +0,0 @@
using System.Threading;
using System.Threading.Tasks;
namespace Ombi.Core.Services;
public interface IDatabaseConfigurationService
{
const string MySqlDatabase = "MySQL";
const string PostgresDatabase = "Postgres";
Task<bool> ConfigureDatabase(string databaseType, string connectionString, CancellationToken token);
}

View file

@ -107,7 +107,6 @@ namespace Ombi.DependencyInjection
services.AddTransient<IMusicSender, MusicSender>(); services.AddTransient<IMusicSender, MusicSender>();
services.AddTransient<IMassEmailSender, MassEmailSender>(); services.AddTransient<IMassEmailSender, MassEmailSender>();
services.AddTransient<IPlexOAuthManager, PlexOAuthManager>(); services.AddTransient<IPlexOAuthManager, PlexOAuthManager>();
services.AddTransient<IPlexTokenKeepAliveService, PlexTokenKeepAliveService>();
services.AddTransient<IVoteEngine, VoteEngine>(); services.AddTransient<IVoteEngine, VoteEngine>();
services.AddTransient<IDemoMovieSearchEngine, DemoMovieSearchEngine>(); services.AddTransient<IDemoMovieSearchEngine, DemoMovieSearchEngine>();
services.AddTransient<IDemoTvSearchEngine, DemoTvSearchEngine>(); services.AddTransient<IDemoTvSearchEngine, DemoTvSearchEngine>();
@ -237,8 +236,6 @@ namespace Ombi.DependencyInjection
services.AddScoped<IFeatureService, FeatureService>(); services.AddScoped<IFeatureService, FeatureService>();
services.AddTransient<IRecentlyRequestedService, RecentlyRequestedService>(); services.AddTransient<IRecentlyRequestedService, RecentlyRequestedService>();
services.AddTransient<IPlexService, PlexService>(); services.AddTransient<IPlexService, PlexService>();
services.AddSingleton<IFileSystem, FileSystem>();
services.AddSingleton<IDatabaseConfigurationService, DatabaseConfigurationService>();
} }
public static void RegisterJobs(this IServiceCollection services) public static void RegisterJobs(this IServiceCollection services)

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -11,9 +11,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.5" /> <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.9" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
@ -10,7 +10,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="nunit" Version="3.13.3" /> <PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" /> <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" />
</ItemGroup> </ItemGroup>

View file

@ -14,7 +14,6 @@
IssueResolved = 9, IssueResolved = 9,
IssueComment = 10, IssueComment = 10,
Newsletter = 11, Newsletter = 11,
PartiallyAvailable = 12, PartiallyAvailable = 12
PlexWatchlistTokenExpired = 13
} }
} }

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -13,8 +13,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="EasyCrypto" Version="4.6.0" /> <PackageReference Include="EasyCrypto" Version="4.6.0" />
<PackageReference Include="LazyCache.AspNetCore" Version="2.4.0" /> <PackageReference Include="LazyCache.AspNetCore" Version="2.4.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" /> <PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
<PackageReference Include="Quartz" Version="3.6.2" /> <PackageReference Include="Quartz" Version="3.6.2" />

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -118,39 +118,39 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="NewAlbums" xml:space="preserve"> <data name="NewAlbums" xml:space="preserve">
<value>Novos álbuns</value> <value>New Albums</value>
</data> </data>
<data name="NewMovies" xml:space="preserve"> <data name="NewMovies" xml:space="preserve">
<value>Novos filmes</value> <value>New Movies</value>
</data> </data>
<data name="NewTV" xml:space="preserve"> <data name="NewTV" xml:space="preserve">
<value>Novas séries</value> <value>New TV</value>
</data> </data>
<data name="GenresLabel" xml:space="preserve"> <data name="GenresLabel" xml:space="preserve">
<value>Gêneros:</value> <value>Gêneros:</value>
</data> </data>
<data name="AlbumTypeLabel" xml:space="preserve"> <data name="AlbumTypeLabel" xml:space="preserve">
<value>Tipo:</value> <value>Type:</value>
</data> </data>
<data name="SeasonLabel" xml:space="preserve"> <data name="SeasonLabel" xml:space="preserve">
<value>Temporadas:</value> <value>Temporada:</value>
</data> </data>
<data name="EpisodesLabel" xml:space="preserve"> <data name="EpisodesLabel" xml:space="preserve">
<value>Episódios:</value> <value>Episodes:</value>
</data> </data>
<data name="PoweredBy" xml:space="preserve"> <data name="PoweredBy" xml:space="preserve">
<value>Distribuído por</value> <value>Powered by</value>
</data> </data>
<data name="Unsubscribe" xml:space="preserve"> <data name="Unsubscribe" xml:space="preserve">
<value>Cancelar subscrição</value> <value>Unsubscribe</value>
</data> </data>
<data name="Album" xml:space="preserve"> <data name="Album" xml:space="preserve">
<value>Álbum</value> <value>Album</value>
</data> </data>
<data name="Movie" xml:space="preserve"> <data name="Movie" xml:space="preserve">
<value>Filmes</value> <value>Movie</value>
</data> </data>
<data name="TvShow" xml:space="preserve"> <data name="TvShow" xml:space="preserve">
<value>Séries</value> <value>TV Show</value>
</data> </data>
</root> </root>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -12,16 +12,19 @@ namespace Ombi.Notifications.Templates
{ {
if (string.IsNullOrEmpty(_templateLocation)) if (string.IsNullOrEmpty(_templateLocation))
{ {
//_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net6.0", "Templates", #if DEBUG
// "BasicTemplate.html"); _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net6.0", "Templates",
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "BasicTemplate.html"); "BasicTemplate.html");
#else
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates","BasicTemplate.html");
#endif
} }
return _templateLocation; return _templateLocation;
} }
} }
private string _templateLocation; private string _templateLocation;
private const string SubjectKey = "{@SUBJECT}"; private const string SubjectKey = "{@SUBJECT}";
private const string BodyKey = "{@BODY}"; private const string BodyKey = "{@BODY}";
private const string Poster = "{@POSTER}"; private const string Poster = "{@POSTER}";
@ -40,10 +43,9 @@ namespace Ombi.Notifications.Templates
return sb.ToString(); return sb.ToString();
} }
private string GetPosterContent(string imgsrc, string url) private string GetPosterContent(string imgsrc, string url) {
{
string posterContent; string posterContent;
if (string.IsNullOrEmpty(imgsrc)) if (string.IsNullOrEmpty(imgsrc))
{ {
posterContent = string.Empty; posterContent = string.Empty;

View file

@ -13,9 +13,11 @@ namespace Ombi.Notifications.Templates
{ {
if (string.IsNullOrEmpty(_templateLocation)) if (string.IsNullOrEmpty(_templateLocation))
{ {
//_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net6.0", "Templates", "NewsletterTemplate.html"); #if DEBUG
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net6.0", "Templates", "NewsletterTemplate.html");
#else
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "NewsletterTemplate.html"); _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "NewsletterTemplate.html");
#endif
} }
return _templateLocation; return _templateLocation;
} }

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>
@ -10,7 +10,7 @@
<PackageReference Include="Nunit" Version="3.13.3" /> <PackageReference Include="Nunit" Version="3.13.3" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.15.2" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.15.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" /> <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="17.6.2"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="17.6.2"></packagereference>
<PackageReference Include="Moq" Version="4.18.2" /> <PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="Moq.AutoMock" Version="3.4.0" /> <PackageReference Include="Moq.AutoMock" Version="3.4.0" />

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -12,7 +12,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Ensure.That" Version="10.1.0" /> <PackageReference Include="Ensure.That" Version="10.1.0" />
<PackageReference Include="MailKit" Version="4.6.0" /> <PackageReference Include="MailKit" Version="4.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -91,12 +91,12 @@ namespace Ombi.Schedule.Tests
new Issues new Issues
{ {
Status = IssueStatus.Resolved, Status = IssueStatus.Resolved,
ResovledDate = DateTime.UtcNow.AddDays(-2) ResovledDate = DateTime.Now.AddDays(-2)
}, },
new Issues new Issues
{ {
Status = IssueStatus.Resolved, Status = IssueStatus.Resolved,
ResovledDate = DateTime.UtcNow.AddDays(-4) ResovledDate = DateTime.Now.AddDays(-4)
} }
}; };

View file

@ -1,10 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>
@ -13,10 +10,10 @@
<PackageReference Include="Moq" Version="4.18.2" /> <PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="Moq.AutoMock" Version="3.4.0" /> <PackageReference Include="Moq.AutoMock" Version="3.4.0" />
<PackageReference Include="Nunit" Version="3.13.3" /> <PackageReference Include="Nunit" Version="3.13.3" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.15.2" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.15.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" /> <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" /> <packagereference Include="Microsoft.NET.Test.Sdk" Version="17.6.2"></packagereference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -182,6 +182,8 @@ namespace Ombi.Schedule.Tests
_mocker.Verify<OmbiUserManager>(x => x.UpdateAsync(It.IsAny<OmbiUser>()), Times.Never); _mocker.Verify<OmbiUserManager>(x => x.UpdateAsync(It.IsAny<OmbiUser>()), Times.Never);
} }
[Test] [Test]
public async Task Import_Doesnt_Import_Banned_Users() public async Task Import_Doesnt_Import_Banned_Users()
{ {
@ -245,15 +247,7 @@ namespace Ombi.Schedule.Tests
Id = "id", Id = "id",
Title = "title", Title = "title",
Username = "username", Username = "username",
HomeUser = true, HomeUser = true
Server = new PlexUserServer[]
{
new PlexUserServer
{
Id = "1",
ServerId = "123"
}
}
} }
} }
}); });
@ -263,6 +257,7 @@ namespace Ombi.Schedule.Tests
_mocker.Setup<OmbiUserManager, Task<IdentityResult>>(x => x.AddToRoleAsync(It.Is<OmbiUser>(x => x.UserName == "plex"), OmbiRoles.RequestMovie)) _mocker.Setup<OmbiUserManager, Task<IdentityResult>>(x => x.AddToRoleAsync(It.Is<OmbiUser>(x => x.UserName == "plex"), OmbiRoles.RequestMovie))
.ReturnsAsync(IdentityResult.Success); .ReturnsAsync(IdentityResult.Success);
await _subject.Execute(null); await _subject.Execute(null);
_mocker.Verify<OmbiUserManager>(x => x.CreateAsync(It.IsAny<OmbiUser>()), Times.Once); _mocker.Verify<OmbiUserManager>(x => x.CreateAsync(It.IsAny<OmbiUser>()), Times.Once);
@ -311,15 +306,7 @@ namespace Ombi.Schedule.Tests
{ {
Email = "email", Email = "email",
Id = "id", Id = "id",
Username = "plex", Username = "plex"
Server = new PlexUserServer[]
{
new PlexUserServer
{
Id = "1",
ServerId = "123"
}
}
} }
} }
}); });
@ -344,9 +331,9 @@ namespace Ombi.Schedule.Tests
ImportPlexAdmin = false, ImportPlexAdmin = false,
ImportPlexUsers = true, ImportPlexUsers = true,
DefaultRoles = new List<string> DefaultRoles = new List<string>
{ {
OmbiRoles.RequestMovie OmbiRoles.RequestMovie
} }
}); });
_mocker.Setup<IPlexApi, Task<PlexUsers>>(x => x.GetUsers(It.IsAny<string>())).ReturnsAsync(new PlexUsers _mocker.Setup<IPlexApi, Task<PlexUsers>>(x => x.GetUsers(It.IsAny<string>())).ReturnsAsync(new PlexUsers
{ {
@ -356,15 +343,7 @@ namespace Ombi.Schedule.Tests
{ {
Email = "email", Email = "email",
Id = "PLEX_ID", Id = "PLEX_ID",
Username = "user", Username = "user"
Server = new PlexUserServer[]
{
new PlexUserServer
{
Id = "1",
ServerId = "123"
}
}
} }
} }
}); });
@ -461,98 +440,5 @@ namespace Ombi.Schedule.Tests
_mocker.Verify<IUserDeletionEngine>(x => x.DeleteUser(It.Is<OmbiUser>(x => x.ProviderUserId == "ADMIN_ID" && x.Email == "ADMIN@ADMIN.CO" && x.UserName == "Admin")), Times.Never); _mocker.Verify<IUserDeletionEngine>(x => x.DeleteUser(It.Is<OmbiUser>(x => x.ProviderUserId == "ADMIN_ID" && x.Email == "ADMIN@ADMIN.CO" && x.UserName == "Admin")), Times.Never);
} }
[Test]
public async Task Import_Skips_Users_Without_Server_Access()
{
_mocker.Setup<ISettingsService<UserManagementSettings>, Task<UserManagementSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new UserManagementSettings { ImportPlexAdmin = false, ImportPlexUsers = true });
_mocker.Setup<IPlexApi, Task<PlexUsers>>(x => x.GetUsers(It.IsAny<string>())).ReturnsAsync(new PlexUsers
{
User = new UserFriends[]
{
new UserFriends
{
Email = "email",
Id = "NoServer",
Title = "title",
Username = "username",
Server = null
}
}
});
await _subject.Execute(null);
_mocker.Verify<OmbiUserManager>(x => x.CreateAsync(It.IsAny<OmbiUser>()), Times.Never);
}
[Test]
public async Task Import_Skips_Users_With_Empty_Server_Array()
{
_mocker.Setup<ISettingsService<UserManagementSettings>, Task<UserManagementSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new UserManagementSettings { ImportPlexAdmin = false, ImportPlexUsers = true });
_mocker.Setup<IPlexApi, Task<PlexUsers>>(x => x.GetUsers(It.IsAny<string>())).ReturnsAsync(new PlexUsers
{
User = new UserFriends[]
{
new UserFriends
{
Email = "email",
Id = "EmptyServer",
Title = "title",
Username = "username",
Server = new PlexUserServer[0]
}
}
});
await _subject.Execute(null);
_mocker.Verify<OmbiUserManager>(x => x.CreateAsync(It.IsAny<OmbiUser>()), Times.Never);
}
[Test]
public async Task Import_Creates_User_With_Server_Access()
{
_mocker.Setup<ISettingsService<UserManagementSettings>, Task<UserManagementSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new UserManagementSettings { ImportPlexAdmin = false, ImportPlexUsers = true });
_mocker.Setup<IPlexApi, Task<PlexUsers>>(x => x.GetUsers(It.IsAny<string>())).ReturnsAsync(new PlexUsers
{
User = new UserFriends[]
{
new UserFriends
{
Email = "email",
Id = "HasServer",
Title = "title",
Username = "username",
Server = new PlexUserServer[]
{
new PlexUserServer
{
Id = "1",
ServerId = "123"
}
}
}
}
});
_mocker.Setup<OmbiUserManager, Task<IdentityResult>>(x => x.CreateAsync(It.Is<OmbiUser>(x =>
x.UserName == "username" &&
x.Email == "email" &&
x.ProviderUserId == "HasServer" &&
x.UserType == UserType.PlexUser)))
.ReturnsAsync(IdentityResult.Success);
await _subject.Execute(null);
_mocker.Verify<OmbiUserManager>(x => x.CreateAsync(It.Is<OmbiUser>(x =>
x.UserName == "username" &&
x.Email == "email" &&
x.ProviderUserId == "HasServer" &&
x.UserType == UserType.PlexUser)), Times.Once);
}
} }
} }

View file

@ -20,11 +20,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Notifications.Models;
using Ombi.Core.Notifications;
using Ombi.Helpers;
using Ombi.Core;
using Ombi.Core.Authentication;
namespace Ombi.Schedule.Tests namespace Ombi.Schedule.Tests
{ {
@ -40,15 +35,12 @@ namespace Ombi.Schedule.Tests
public void Setup() public void Setup()
{ {
_mocker = new AutoMocker(); _mocker = new AutoMocker();
var um = MockHelper.MockUserManager(new List<OmbiUser> { new OmbiUser { Id = "abc", Email = "email@email.com", UserType = UserType.PlexUser, MediaServerToken = "token1", UserName = "abc", NormalizedUserName = "ABC" } }); var um = MockHelper.MockUserManager(new List<OmbiUser> { new OmbiUser { Id = "abc", UserType = UserType.PlexUser, MediaServerToken = "token1", UserName = "abc", NormalizedUserName = "ABC" } });
_mocker.Use(um); _mocker.Use(um);
_context = _mocker.GetMock<IJobExecutionContext>(); _context = _mocker.GetMock<IJobExecutionContext>();
_context.Setup(x => x.CancellationToken).Returns(CancellationToken.None); _context.Setup(x => x.CancellationToken).Returns(CancellationToken.None);
// Mock the keep-alive service to return true by default
_mocker.Use<IPlexTokenKeepAliveService>(Mock.Of<IPlexTokenKeepAliveService>(s => s.KeepTokenAliveAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()) == Task.FromResult(true)));
_subject = _mocker.CreateInstance<PlexWatchlistImport>(); _subject = _mocker.CreateInstance<PlexWatchlistImport>();
_mocker.Setup<IRepository<PlexWatchlistUserError>, IQueryable<PlexWatchlistUserError>>(x => x.GetAll()).Returns(new List<PlexWatchlistUserError>().AsQueryable().BuildMock()); _mocker.Setup<IRepository<PlexWatchlistUserError>, IQueryable<PlexWatchlistUserError>>(x => x.GetAll()).Returns(new List<PlexWatchlistUserError>().AsQueryable().BuildMock());
_mocker.Setup<INotificationHelper>(x => x.Notify(It.IsAny<NotificationOptions>()));
} }
[Test] [Test]
@ -690,6 +682,7 @@ namespace Ombi.Schedule.Tests
[Test] [Test]
public async Task MovieRequestFromWatchList_AlreadyImported() public async Task MovieRequestFromWatchList_AlreadyImported()
{ {
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true }); _mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer _mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
{ {
@ -726,7 +719,7 @@ namespace Ombi.Schedule.Tests
} }
}); });
_mocker.Setup<IExternalRepository<PlexWatchlistHistory>, IQueryable<PlexWatchlistHistory>>(x => x.GetAll()).Returns(new List<PlexWatchlistHistory> { new PlexWatchlistHistory { Id = 1, TmdbId = "123", UserId = "abc" } }.AsQueryable()); _mocker.Setup<IExternalRepository<PlexWatchlistHistory>, IQueryable<PlexWatchlistHistory>>(x => x.GetAll()).Returns(new List<PlexWatchlistHistory> { new PlexWatchlistHistory { Id = 1, TmdbId = "123" } }.AsQueryable());
await _subject.Execute(_context.Object); await _subject.Execute(_context.Object);
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never); _mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
_mocker.Verify<IPlexApi>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once); _mocker.Verify<IPlexApi>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
@ -785,99 +778,5 @@ namespace Ombi.Schedule.Tests
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Once); _mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Once);
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Once); _mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Once);
} }
[Test]
public async Task AuthenticationError_NotificationsEnabled_WithEmail_SendsNotification()
{
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true, NotifyOnWatchlistTokenExpiration = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new PlexWatchlistContainer { AuthError = true });
// Act
await _subject.Execute(_context.Object);
// Assert
_mocker.Verify<INotificationHelper>(x => x.Notify(It.Is<NotificationOptions>(n =>
n.NotificationType == NotificationType.PlexWatchlistTokenExpired &&
n.Recipient == "email@email.com" &&
n.Substitutes["UserName"] == "abc"
)), Times.Once);
}
[Test]
public async Task AuthenticationError_NotificationsDisabled_WithEmail_DoesNotSendNotification()
{
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true, NotifyOnWatchlistTokenExpiration = false });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new PlexWatchlistContainer { AuthError = true });
// Act
await _subject.Execute(_context.Object);
// Assert
_mocker.Verify<INotificationHelper>(x => x.Notify(It.IsAny<NotificationOptions>()), Times.Never);
}
[Test]
public async Task AuthenticationError_NotificationsEnabled_NoEmail_DoesNotSendNotification()
{
// Arrange
var user = new OmbiUser { Id = "abc", UserType = UserType.PlexUser, MediaServerToken = "token1", UserName = "abc", NormalizedUserName = "ABC" };
var um = MockHelper.MockUserManager(new List<OmbiUser> { user });
_mocker.Use(um);
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true, NotifyOnWatchlistTokenExpiration = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new PlexWatchlistContainer { AuthError = true });
_subject = _mocker.CreateInstance<PlexWatchlistImport>();
// Act
await _subject.Execute(_context.Object);
// Assert
_mocker.Verify<INotificationHelper>(x => x.Notify(It.IsAny<NotificationOptions>()), Times.Never);
}
[Test]
public async Task SkipsUserIfTokenKeepAliveFails()
{
// Arrange: Set up the keep-alive service to return false (token invalid/expired)
var keepAliveMock = new Mock<IPlexTokenKeepAliveService>();
keepAliveMock.Setup(x => x.KeepTokenAliveAsync(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(false);
_mocker.Use(keepAliveMock.Object);
_subject = _mocker.CreateInstance<PlexWatchlistImport>();
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
// Act
await _subject.Execute(_context.Object);
// Assert: Should not attempt to import watchlist if keep-alive fails
keepAliveMock.Verify(x => x.KeepTokenAliveAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
_mocker.Verify<IPlexApi>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
_mocker.Verify<INotificationHelper>(x => x.Notify(It.IsAny<NotificationOptions>()), Times.Never); // or Times.Once if notification is expected
}
[Test]
public async Task CallsKeepAliveForEachPlexUser()
{
// Arrange: Multiple Plex users
var users = new List<OmbiUser>
{
new OmbiUser { Id = "abc1", UserType = UserType.PlexUser, MediaServerToken = "abc1", UserName = "abc1", NormalizedUserName = "ABC1" },
new OmbiUser { Id = "abc2", UserType = UserType.PlexUser, MediaServerToken = "abc2", UserName = "abc2", NormalizedUserName = "ABC2" },
};
var um = MockHelper.MockUserManager(users);
_mocker.Use(um);
var keepAliveMock = new Mock<IPlexTokenKeepAliveService>();
keepAliveMock.Setup(x => x.KeepTokenAliveAsync(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(true);
_mocker.Use(keepAliveMock.Object);
_subject = _mocker.CreateInstance<PlexWatchlistImport>();
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
// Act
await _subject.Execute(_context.Object);
// Assert: KeepAlive should be called for each user
keepAliveMock.Verify(x => x.KeepTokenAliveAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(users.Count));
}
} }
} }

View file

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:62604/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Ombi.Schedule.Tests": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:62605/"
}
}
}

View file

@ -174,18 +174,13 @@ namespace Ombi.Schedule.Jobs.Emby
} }
if (parent.TheMovieDbId.IsNullOrEmpty()) if (parent.TheMovieDbId.IsNullOrEmpty())
{ {
_logger.LogWarning($"Episode {episode.Name} for Tv Show {parent.Title} Doesn't have a valid TheMovieDbId. Skipping."); _logger.LogWarning($"Episode {episode.Name} is not linked to a TMDB series. Skipping.");
return;
}
if (!int.TryParse(parent.TheMovieDbId, out var parentMovieDb))
{
_logger.LogWarning($"Episode {episode.Name} for Tv Show {parent.Title} Doesn't have a valid TheMovieDbId. Skipping.");
return; return;
} }
await AddToContent(content, new UserPlayedEpisode() await AddToContent(content, new UserPlayedEpisode()
{ {
TheMovieDbId = parentMovieDb, TheMovieDbId = int.Parse(parent.TheMovieDbId),
SeasonNumber = episode.ParentIndexNumber, SeasonNumber = episode.ParentIndexNumber,
EpisodeNumber = episode.IndexNumber, EpisodeNumber = episode.IndexNumber,
UserId = user.Id UserId = user.Id
@ -201,7 +196,7 @@ namespace Ombi.Schedule.Jobs.Emby
await AddToContent(content, new UserPlayedEpisode() await AddToContent(content, new UserPlayedEpisode()
{ {
TheMovieDbId = parentMovieDb, TheMovieDbId = int.Parse(parent.TheMovieDbId),
SeasonNumber = episode.ParentIndexNumber, SeasonNumber = episode.ParentIndexNumber,
EpisodeNumber = episodeNumber, EpisodeNumber = episodeNumber,
UserId = user.Id UserId = user.Id

View file

@ -7,7 +7,6 @@ using Microsoft.Extensions.Logging;
using Ombi.Api.Emby; using Ombi.Api.Emby;
using Ombi.Api.Jellyfin; using Ombi.Api.Jellyfin;
using Ombi.Api.Plex; using Ombi.Api.Plex;
using Ombi.Api.Plex.Models;
using Ombi.Api.TheMovieDb; using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models; using Ombi.Api.TheMovieDb.Models;
using Ombi.Api.TvMaze; using Ombi.Api.TvMaze;
@ -287,18 +286,7 @@ namespace Ombi.Schedule.Jobs.Ombi
continue; continue;
} }
var servers = settings.Servers[0]; var servers = settings.Servers[0];
PlexMetadata metaData = null; var metaData = await _plexApi.GetMetadata(servers.PlexAuthToken, settings.Servers[0].FullUri, movie.Key);
try
{
metaData = await _plexApi.GetMetadata(servers.PlexAuthToken, settings.Servers[0].FullUri, movie.Key);
}
catch (Exception e)
{
_log.LogError($"Could not find the metadata for title: '{movie.Title}', skipping");
_log.LogDebug(e, $"Could not find the metadata for title: '{movie.Title}', skipping");
continue;
}
var guids = new List<string>(); var guids = new List<string>();
var meta = metaData.MediaContainer.Metadata.FirstOrDefault(); var meta = metaData.MediaContainer.Metadata.FirstOrDefault();

View file

@ -304,13 +304,6 @@ namespace Ombi.Schedule.Jobs.Plex
var existing = await Repo.GetFirstContentByCustom(x => x.Title == movie.title var existing = await Repo.GetFirstContentByCustom(x => x.Title == movie.title
&& x.ReleaseYear == movie.year.ToString() && x.ReleaseYear == movie.year.ToString()
&& x.Type == MediaType.Movie); && x.Type == MediaType.Movie);
if (existing == null)
{
// Let's just check the key
existing = await Repo.GetByKey(movie.ratingKey);
}
if (existing != null) if (existing != null)
{ {
// We need to see if this is a different quality, // We need to see if this is a different quality,
@ -347,7 +340,13 @@ namespace Ombi.Schedule.Jobs.Plex
Logger.LogDebug($"We already have movie {movie.title}"); Logger.LogDebug($"We already have movie {movie.title}");
continue; continue;
} }
//var hasSameKey = await Repo.GetByKey(movie.ratingKey);
//if (hasSameKey != null)
//{
// await Repo.Delete(hasSameKey);
//}
Logger.LogDebug("Adding movie {0}", movie.title); Logger.LogDebug("Adding movie {0}", movie.title);
var guids = new List<string>(); var guids = new List<string>();

View file

@ -120,13 +120,6 @@ namespace Ombi.Schedule.Jobs.Plex
foreach (var plexUser in users.User) foreach (var plexUser in users.User)
{ {
// Skip users without server access
if (plexUser.Server == null || !plexUser.Server.Any())
{
_log.LogInformation($"Skipping user {plexUser.Username ?? plexUser.Id} as they have no server access");
continue;
}
// Check if we should import this user // Check if we should import this user
if (userManagementSettings.BannedPlexUserIds.Contains(plexUser.Id)) if (userManagementSettings.BannedPlexUserIds.Contains(plexUser.Id))
{ {

View file

@ -22,11 +22,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Notifications.Models;
using Ombi.Core.Notifications;
using Microsoft.AspNetCore.Identity;
using Ombi.Store.Repository.Requests;
using Ombi.Core;
namespace Ombi.Schedule.Jobs.Plex namespace Ombi.Schedule.Jobs.Plex
{ {
@ -42,13 +37,11 @@ namespace Ombi.Schedule.Jobs.Plex
private readonly IExternalRepository<PlexWatchlistHistory> _watchlistRepo; private readonly IExternalRepository<PlexWatchlistHistory> _watchlistRepo;
private readonly IRepository<PlexWatchlistUserError> _userError; private readonly IRepository<PlexWatchlistUserError> _userError;
private readonly IMovieDbApi _movieDbApi; private readonly IMovieDbApi _movieDbApi;
private readonly INotificationHelper _notificationHelper;
private readonly IPlexTokenKeepAliveService _tokenKeepAliveService;
public PlexWatchlistImport(IPlexApi plexApi, ISettingsService<PlexSettings> settings, OmbiUserManager ombiUserManager, public PlexWatchlistImport(IPlexApi plexApi, ISettingsService<PlexSettings> settings, OmbiUserManager ombiUserManager,
IMovieRequestEngine movieRequestEngine, ITvRequestEngine tvRequestEngine, INotificationHubService notificationHubService, IMovieRequestEngine movieRequestEngine, ITvRequestEngine tvRequestEngine, INotificationHubService notificationHubService,
ILogger<PlexWatchlistImport> logger, IExternalRepository<PlexWatchlistHistory> watchlistRepo, IRepository<PlexWatchlistUserError> userError, ILogger<PlexWatchlistImport> logger, IExternalRepository<PlexWatchlistHistory> watchlistRepo, IRepository<PlexWatchlistUserError> userError,
IMovieDbApi movieDbApi, INotificationHelper notificationHelper, IPlexTokenKeepAliveService tokenKeepAliveService) IMovieDbApi movieDbApi)
{ {
_plexApi = plexApi; _plexApi = plexApi;
_settings = settings; _settings = settings;
@ -60,8 +53,6 @@ namespace Ombi.Schedule.Jobs.Plex
_watchlistRepo = watchlistRepo; _watchlistRepo = watchlistRepo;
_userError = userError; _userError = userError;
_movieDbApi = movieDbApi; _movieDbApi = movieDbApi;
_notificationHelper = notificationHelper;
_tokenKeepAliveService = tokenKeepAliveService;
} }
public async Task Execute(IJobExecutionContext context) public async Task Execute(IJobExecutionContext context)
@ -99,36 +90,6 @@ namespace Ombi.Schedule.Jobs.Plex
} }
_logger.LogDebug($"Starting Watchlist Import for {user.UserName} with token {user.MediaServerToken}"); _logger.LogDebug($"Starting Watchlist Import for {user.UserName} with token {user.MediaServerToken}");
// Keep the token alive before attempting watchlist import
var keepAliveSuccess = await _tokenKeepAliveService.KeepTokenAliveAsync(user.MediaServerToken, context?.CancellationToken ?? CancellationToken.None);
if (!keepAliveSuccess)
{
_logger.LogWarning($"Token for user '{user.UserName}' is invalid or expired (keep-alive failed). Recording error and skipping.");
await _userError.Add(new PlexWatchlistUserError
{
UserId = user.Id,
MediaServerToken = user.MediaServerToken,
});
// Send notification to user about token expiration
if (settings.NotifyOnWatchlistTokenExpiration && !string.IsNullOrEmpty(user.Email))
{
var notificationModel = new NotificationOptions
{
NotificationType = NotificationType.PlexWatchlistTokenExpired,
Recipient = user.Email,
DateTime = DateTime.Now,
Substitutes = new Dictionary<string, string>
{
{ "UserName", user.UserName }
}
};
await _notificationHelper.Notify(notificationModel);
}
continue;
}
var watchlist = await _plexApi.GetWatchlist(user.MediaServerToken, context?.CancellationToken ?? CancellationToken.None); var watchlist = await _plexApi.GetWatchlist(user.MediaServerToken, context?.CancellationToken ?? CancellationToken.None);
if (watchlist?.AuthError ?? false) if (watchlist?.AuthError ?? false)
{ {
@ -138,22 +99,6 @@ namespace Ombi.Schedule.Jobs.Plex
UserId = user.Id, UserId = user.Id,
MediaServerToken = user.MediaServerToken, MediaServerToken = user.MediaServerToken,
}); });
// Send notification to user about token expiration
if (settings.NotifyOnWatchlistTokenExpiration && !string.IsNullOrEmpty(user.Email))
{
var notificationModel = new NotificationOptions
{
NotificationType = NotificationType.PlexWatchlistTokenExpired,
Recipient = user.Email,
DateTime = DateTime.Now,
Substitutes = new Dictionary<string, string>
{
{ "UserName", user.UserName }
}
};
await _notificationHelper.Notify(notificationModel);
}
continue; continue;
} }
if (watchlist == null || !(watchlist.MediaContainer?.Metadata?.Any() ?? false)) if (watchlist == null || !(watchlist.MediaContainer?.Metadata?.Any() ?? false))
@ -183,7 +128,7 @@ namespace Ombi.Schedule.Jobs.Plex
} }
// Check to see if we have already imported this item // Check to see if we have already imported this item
var alreadyImported = _watchlistRepo.GetAll().Any(x => x.TmdbId == providerIds.TheMovieDb && x.UserId == user.Id); var alreadyImported = _watchlistRepo.GetAll().Any(x => x.TmdbId == providerIds.TheMovieDb);
if (alreadyImported) if (alreadyImported)
{ {
_logger.LogDebug($"{item.title} already imported via Plex WatchList, skipping"); _logger.LogDebug($"{item.title} already imported via Plex WatchList, skipping");
@ -257,14 +202,14 @@ namespace Ombi.Schedule.Jobs.Plex
if (response.ErrorCode == ErrorCode.AlreadyRequested) if (response.ErrorCode == ErrorCode.AlreadyRequested)
{ {
_logger.LogDebug($"Movie already requested for user '{user.UserName}'"); _logger.LogDebug($"Movie already requested for user '{user.UserName}'");
await AddToHistory(theMovieDbId, user.Id); await AddToHistory(theMovieDbId);
return; return;
} }
_logger.LogInformation($"Error adding title from PlexWatchlist for user '{user.UserName}'. Message: '{response.ErrorMessage}'"); _logger.LogInformation($"Error adding title from PlexWatchlist for user '{user.UserName}'. Message: '{response.ErrorMessage}'");
} }
else else
{ {
await AddToHistory(theMovieDbId, user.Id); await AddToHistory(theMovieDbId);
_logger.LogInformation($"Added title from PlexWatchlist for user '{user.UserName}'. {response.Message}"); _logger.LogInformation($"Added title from PlexWatchlist for user '{user.UserName}'. {response.Message}");
} }
@ -285,26 +230,24 @@ namespace Ombi.Schedule.Jobs.Plex
if (response.ErrorCode == ErrorCode.AlreadyRequested) if (response.ErrorCode == ErrorCode.AlreadyRequested)
{ {
_logger.LogDebug($"Show already requested for user '{user.UserName}'"); _logger.LogDebug($"Show already requested for user '{user.UserName}'");
await AddToHistory(theMovieDbId, user.Id); await AddToHistory(theMovieDbId);
return; return;
} }
_logger.LogInformation($"Error adding title from PlexWatchlist for user '{user.UserName}'. Message: '{response.ErrorMessage}'"); _logger.LogInformation($"Error adding title from PlexWatchlist for user '{user.UserName}'. Message: '{response.ErrorMessage}'");
} }
else else
{ {
await AddToHistory(theMovieDbId, user.Id); await AddToHistory(theMovieDbId);
_logger.LogInformation($"Added title from PlexWatchlist for user '{user.UserName}'. {response.Message}"); _logger.LogInformation($"Added title from PlexWatchlist for user '{user.UserName}'. {response.Message}");
} }
} }
private async Task AddToHistory(int theMovieDbId, string userId) private async Task AddToHistory(int theMovieDbId)
{ {
// Add to the watchlist history // Add to the watchlist history
var history = new PlexWatchlistHistory var history = new PlexWatchlistHistory
{ {
TmdbId = theMovieDbId.ToString(), TmdbId = theMovieDbId.ToString()
AddedAt = DateTime.UtcNow,
UserId = userId
}; };
await _watchlistRepo.Add(history); await _watchlistRepo.Add(history);
} }

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup><TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
@ -11,7 +10,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="nunit" Version="3.13.3" /> <PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" /> <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" />
</ItemGroup> </ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -11,7 +11,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Quartz" Version="3.6.2" /> <PackageReference Include="Quartz" Version="3.6.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup> </ItemGroup>

View file

@ -9,7 +9,6 @@ namespace Ombi.Core.Settings.Models.External
public bool Enable { get; set; } public bool Enable { get; set; }
public bool EnableWatchlistImport { get; set; } public bool EnableWatchlistImport { get; set; }
public bool MonitorAll { get; set; } public bool MonitorAll { get; set; }
public bool NotifyOnWatchlistTokenExpiration { get; set; }
/// <summary> /// <summary>
/// This is the ClientId for OAuth /// This is the ClientId for OAuth
/// </summary> /// </summary>

View file

@ -217,16 +217,6 @@ namespace Ombi.Store.Context
Enabled = true, Enabled = true,
}; };
break; break;
case NotificationType.PlexWatchlistTokenExpired:
notificationToAdd = new NotificationTemplates
{
NotificationType = notificationType,
Message = "Hello {UserName}! Your Plex watchlist token has expired. Please re-authenticate with Ombi to continue using the watchlist feature.",
Subject = "Plex Watchlist Token Expired",
Agent = agent,
Enabled = true,
};
break;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }

View file

@ -1,16 +0,0 @@
using Microsoft.EntityFrameworkCore;
namespace Ombi.Store.Context.Postgres
{
public sealed class ExternalPostgresContext : ExternalContext
{
private static bool _created;
public ExternalPostgresContext(DbContextOptions<ExternalPostgresContext> options) : base(options)
{
if (_created) return;
_created = true;
Database.Migrate();
}
}
}

View file

@ -1,22 +0,0 @@
using Microsoft.EntityFrameworkCore;
namespace Ombi.Store.Context.Postgres
{
public sealed class OmbiPostgresContext : OmbiContext
{
private static bool _created;
public OmbiPostgresContext(DbContextOptions<OmbiPostgresContext> options) : base(options)
{
if (_created) return;
_created = true;
Database.Migrate();
}
public override void Dispose()
{
base.Dispose();
}
}
}

View file

@ -1,17 +0,0 @@
using System;
using System.Runtime.CompilerServices;
namespace Ombi.Store.Context.Postgres;
public static class PostgresModuleInitializer
{
#pragma warning disable CA2255
// This is required to ensure that Npgsql uses a timestamp behavior that does not require a timezone
// Reference: https://stackoverflow.com/a/73586129
[ModuleInitializer]
#pragma warning restore CA2255
public static void Initialize()
{
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
}
}

View file

@ -1,16 +0,0 @@
using Microsoft.EntityFrameworkCore;
namespace Ombi.Store.Context.Postgres
{
public sealed class SettingsPostgresContext : SettingsContext
{
private static bool _created;
public SettingsPostgresContext(DbContextOptions<SettingsPostgresContext> options) : base(options)
{
if (_created) return;
_created = true;
Database.Migrate();
}
}
}

View file

@ -1,5 +1,4 @@
using System; using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations.Schema;
namespace Ombi.Store.Entities namespace Ombi.Store.Entities
{ {
@ -7,7 +6,5 @@ namespace Ombi.Store.Entities
public class PlexWatchlistHistory : Entity public class PlexWatchlistHistory : Entity
{ {
public string TmdbId { get; set; } public string TmdbId { get; set; }
public string UserId { get; set; }
public DateTime AddedAt { get; set; }
} }
} }

View file

@ -44,21 +44,6 @@ namespace Ombi.Store.Entities.Requests
public DateTime MarkedAsDenied4K { get; set; } public DateTime MarkedAsDenied4K { get; set; }
public string DeniedReason4K { get; set; } public string DeniedReason4K { get; set; }
[NotMapped]
public RequestCombination RequestCombination
{
get
{
if (Has4KRequest && RequestedDate != default)
{
return RequestCombination.Both;
}
if (Has4KRequest) { return RequestCombination.FourK; }
return RequestCombination.Normal;
}
}
/// <summary> /// <summary>
/// Only Use for setting the Language Code, Use the LanguageCode property for reading /// Only Use for setting the Language Code, Use the LanguageCode property for reading

View file

@ -1,9 +0,0 @@
namespace Ombi.Store.Entities.Requests
{
public enum RequestCombination
{
Normal,
FourK,
Both
}
}

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