Compare commits

..

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

420 changed files with 26611 additions and 39966 deletions

35
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,35 @@
---
name: "\U0001F41B Bug report"
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Logs (Logs directory where Ombi is located)**
If applicable, a snippet of the logs that seems relevant to the bug if present.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
**Ombi Version (please complete the following information):**
- Version [e.g. 4.0.958]
- Media Server [e.g. Plex]
- Database Type: SQLite (Please change if using MySQL)
**Additional context**
Add any other context about the problem here.

View file

@ -1,50 +0,0 @@
name: "\U0001F41B Bug report"
description: 'Report a reproducible bug in Ombi'
body:
- type: markdown
attributes:
value: Thanks for taking the time to file a bug report! Please fill out this form as completely as possible.
- type: markdown
attributes:
value: |
If you leave out sections there is a high likelihood your issue will be closed.
If you have a question or you think your issue might be caused by your application code, you can get help from the community on [Discord](https://discord.gg/Sa7wNWb).
- type: textarea
attributes:
label: Summary
description: |
Clearly describe what the expected behavior is vs. what is actually happening. Please include any reproduction steps that is required to reproduce this issue.
If your summary is simply, for example: "I cannot setup Plex", then you will need to [continue debugging on your own](https://docs.ombi.app/) to more precisely define your issue before proceeding.
validations:
required: true
- type: input
id: version
attributes:
label: Ombi Version
description: What version of ombi are you running?
validations:
required: true
- type: dropdown
attributes:
label: What platform(s) does this occur on?
multiple: true
options:
- Docker
- Windows
- Linux
validations:
required: true
- type: dropdown
attributes:
label: What database are you using?
options:
- SQLite (Default)
- MySQL
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell

View file

@ -3,8 +3,8 @@ contact_links:
- name: Docs - name: Docs
url: https://docs.ombi.app/ url: https://docs.ombi.app/
about: The Ombi documentation should help guide you through installation and setup as well as help resolve common problems and answer frequently asked questions about: The Ombi documentation should help guide you through installation and setup as well as help resolve common problems and answer frequently asked questions
- name: Discord support - name: Reddit support
url: https://discord.gg/Sa7wNWb url: https://www.reddit.com/r/Ombi
about: Ask questions about Ombi about: Ask questions about Ombi
- name: Feature suggestions - name: Feature suggestions
url: https://ombifeatures.featureupvote.com url: https://ombifeatures.featureupvote.com

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

@ -1,47 +1,47 @@
# name: 'Chromatic' name: 'Chromatic'
# # Event for the workflow # Event for the workflow
# on: on:
# push: push:
# workflow_dispatch: workflow_dispatch:
# # List of jobs # List of jobs
# jobs: jobs:
# storybook-build: storybook-build:
# # Operating System # Operating System
# runs-on: ubuntu-latest runs-on: ubuntu-latest
# steps: steps:
# - name: Checkout repository - name: Checkout repository
# uses: actions/checkout@v2 uses: actions/checkout@v2
# with: with:
# 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') }}
# - name: Install dependencies - name: Install dependencies
# working-directory: ./src/Ombi/ClientApp working-directory: ./src/Ombi/ClientApp
# run: yarn run: yarn
# - name: Publish to Chromatic - name: Publish to Chromatic
# if: github.ref != 'refs/heads/master' if: github.ref != 'refs/heads/master'
# uses: chromaui/action@v1 uses: chromaui/action@v1
# with: with:
# projectToken: 7c47e1a1a4bd projectToken: 7c47e1a1a4bd
# exitZeroOnChanges: true exitZeroOnChanges: true
# workingDir: ./src/Ombi/ClientApp workingDir: ./src/Ombi/ClientApp
# buildScriptName: storybookbuild buildScriptName: storybookbuild
# exitOnceUploaded: true exitOnceUploaded: true
# - name: Publish to Chromatic and auto accept changes - name: Publish to Chromatic and auto accept changes
# if: github.ref == 'refs/heads/develop' if: github.ref == 'refs/heads/develop'
# uses: chromaui/action@v1 uses: chromaui/action@v1
# with: with:
# projectToken: 7c47e1a1a4bd projectToken: 7c47e1a1a4bd
# autoAcceptChanges: true # 👈 Option to accept all changes autoAcceptChanges: true # 👈 Option to accept all changes
# workingDir: ./src/Ombi/ClientApp workingDir: ./src/Ombi/ClientApp
# buildScriptName: storybookbuild buildScriptName: storybookbuild
# exitOnceUploaded: true exitOnceUploaded: true

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

@ -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'
@ -43,8 +43,8 @@ jobs:
- name: Run Docker Image - name: Run Docker Image
run: nohup docker run --rm -p 5000:5000 ombi & run: nohup docker run --rm -p 5000:5000 ombi &
- name: Run Wiremock # - name: Run Wiremock Plex
run: nohup docker run --rm -p 32400:8080 --name wiremock wiremock/wiremock:2.35.0 & # run: nohup docker run -it --rm -p 32400:8080 --name wiremock wiremock/wiremock:2.35.0
- name: Sleep for server to start - name: Sleep for server to start
run: sleep 20 run: sleep 20
@ -61,7 +61,7 @@ jobs:
# nohup dotnet run --project ./src/Ombi -- --host http://*:3577 & # nohup dotnet run --project ./src/Ombi -- --host http://*:3577 &
- name: Cypress Tests - name: Cypress Tests
uses: cypress-io/github-action@v4 uses: cypress-io/github-action@v2.8.2
with: with:
record: true record: true
browser: chrome browser: chrome
@ -73,9 +73,3 @@ jobs:
env: env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Stop Docker
if: always()
run: |
docker ps -q | xargs -I {} docker logs {}
docker container kill $(docker ps -q)

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') }}

2
.gitignore vendored
View file

@ -7,7 +7,7 @@
*.userosscache *.userosscache
*.sln.docstates *.sln.docstates
# User-specific files # User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs *.userprefs
# Build results # Build results

File diff suppressed because it is too large Load diff

853
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

@ -7,7 +7,6 @@ namespace Ombi.Api.Discord.Models
{ {
public string content { get; set; } public string content { get; set; }
public string username { get; set; } public string username { get; set; }
public string avatar_url { get; set; }
public List<DiscordEmbeds> embeds { get; set; } public List<DiscordEmbeds> embeds { get; set; }
} }
@ -33,6 +32,7 @@ namespace Ombi.Api.Discord.Models
{ {
public string name { get; set; } public string name { get; set; }
public string url { get; set; } public string url { get; set; }
public string iconurl { get; set; }
} }
public class DiscordField public class DiscordField

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

@ -56,7 +56,7 @@ namespace Ombi.Api.Emby
return obj; return obj;
} }
public async Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri, string clientIpAddress) public async Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri)
{ {
var request = new Request("emby/users/authenticatebyname", baseUri, HttpMethod.Post); var request = new Request("emby/users/authenticatebyname", baseUri, HttpMethod.Post);
var body = new var body = new
@ -71,11 +71,6 @@ namespace Ombi.Api.Emby
$"MediaBrowser Client=\"Ombi\", Device=\"Ombi\", DeviceId=\"v3\", Version=\"v3\""); $"MediaBrowser Client=\"Ombi\", Device=\"Ombi\", DeviceId=\"v3\", Version=\"v3\"");
AddHeaders(request, apiKey); AddHeaders(request, apiKey);
if (!string.IsNullOrEmpty(clientIpAddress))
{
request.AddHeader("X-Forwarded-For", clientIpAddress);
}
var obj = await Api.Request<EmbyUser>(request); var obj = await Api.Request<EmbyUser>(request);
return obj; return obj;
} }
@ -253,49 +248,5 @@ namespace Ombi.Api.Emby
req.AddContentHeader("Content-Type", "application/json"); req.AddContentHeader("Content-Type", "application/json");
req.AddHeader("Device", "Ombi"); req.AddHeader("Device", "Ombi");
} }
public async Task<EmbyItemContainer<EmbyMovie>> GetMoviesPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri) =>
await GetPlayed<EmbyMovie>("Movie", apiKey, userId, baseUri, startIndex, count, parentIdFilder, "ProviderIds");
public async Task<EmbyItemContainer<EmbyEpisodes>> GetTvPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri) =>
await GetPlayed<EmbyEpisodes>("Episode", apiKey, userId, baseUri, startIndex, count, parentIdFilder);
private async Task<EmbyItemContainer<T>> GetPlayed<T>(
string type,
string apiKey,
string userId,
string baseUri,
int startIndex,
int count,
string parentIdFilder = default,
string fields = default)
{
var request = new Request($"emby/items", baseUri, HttpMethod.Get);
request.AddQueryString("Recursive", true.ToString());
request.AddQueryString("IncludeItemTypes", type);
if (!string.IsNullOrEmpty(fields))
{
request.AddQueryString("Fields", fields);
}
request.AddQueryString("UserId", userId);
request.AddQueryString("isPlayed", true.ToString());
// paginate and display recently played items first
request.AddQueryString("sortBy", "DatePlayed");
request.AddQueryString("SortOrder", "Descending");
request.AddQueryString("startIndex", startIndex.ToString());
request.AddQueryString("limit", count.ToString());
if (!string.IsNullOrEmpty(parentIdFilder))
{
request.AddQueryString("ParentId", parentIdFilder);
}
AddHeaders(request, apiKey);
var obj = await Api.Request<EmbyItemContainer<T>>(request);
return obj;
}
} }
} }

View file

@ -11,7 +11,7 @@ namespace Ombi.Api.Emby
{ {
Task<EmbySystemInfo> GetSystemInformation(string apiKey, string baseUrl); Task<EmbySystemInfo> GetSystemInformation(string apiKey, string baseUrl);
Task<List<EmbyUser>> GetUsers(string baseUri, string apiKey); Task<List<EmbyUser>> GetUsers(string baseUri, string apiKey);
Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri, string clientIpAddress); Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri);
Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, string parentIdFilder, int startIndex, int count, string userId, Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, string parentIdFilder, int startIndex, int count, string userId,
string baseUri); string baseUri);
@ -32,8 +32,5 @@ namespace Ombi.Api.Emby
Task<EmbyItemContainer<EmbyMovie>> RecentlyAddedMovies(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri); Task<EmbyItemContainer<EmbyMovie>> RecentlyAddedMovies(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
Task<EmbyItemContainer<EmbyEpisodes>> RecentlyAddedEpisodes(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri); Task<EmbyItemContainer<EmbyEpisodes>> RecentlyAddedEpisodes(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
Task<EmbyItemContainer<EmbySeries>> RecentlyAddedShows(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri); Task<EmbyItemContainer<EmbySeries>> RecentlyAddedShows(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
Task<EmbyItemContainer<EmbyMovie>> GetMoviesPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
Task<EmbyItemContainer<EmbyEpisodes>> GetTvPlayed(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
} }
} }

View file

@ -5,8 +5,30 @@ namespace Ombi.Api.Emby.Models.Movie
public class EmbyMovie public class EmbyMovie
{ {
public string Name { get; set; } public string Name { get; set; }
public string ServerId { get; set; }
public string Id { get; set; } public string Id { get; set; }
public string Container { get; set; }
public DateTime PremiereDate { get; set; }
public object[] ProductionLocations { get; set; }
public string OfficialRating { get; set; }
public float CommunityRating { get; set; }
public long RunTimeTicks { get; set; }
public string PlayAccess { get; set; }
public int ProductionYear { get; set; }
public bool IsPlaceHolder { get; set; }
public bool IsHD { get; set; }
public bool IsFolder { get; set; }
public string Type { get; set; } public string Type { get; set; }
public int LocalTrailerCount { get; set; }
public EmbyUserdata UserData { get; set; }
public string VideoType { get; set; }
public EmbyImagetags ImageTags { get; set; }
public string[] BackdropImageTags { get; set; }
public string LocationType { get; set; }
public string MediaType { get; set; }
public bool HasSubtitles { get; set; }
public int CriticRating { get; set; }
public string Overview { get; set; }
public EmbyProviderids ProviderIds { get; set; } public EmbyProviderids ProviderIds { get; set; }
public EmbyMediastream[] MediaStreams { get; set; } public EmbyMediastream[] MediaStreams { 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>

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

@ -5,8 +5,30 @@ namespace Ombi.Api.Jellyfin.Models.Movie
public class JellyfinMovie public class JellyfinMovie
{ {
public string Name { get; set; } public string Name { get; set; }
public string ServerId { get; set; }
public string Id { get; set; } public string Id { get; set; }
public string Container { get; set; }
public DateTime PremiereDate { get; set; }
public object[] ProductionLocations { get; set; }
public string OfficialRating { get; set; }
public float CommunityRating { get; set; }
public long RunTimeTicks { get; set; }
public string PlayAccess { get; set; }
public int ProductionYear { get; set; }
public bool IsPlaceHolder { get; set; }
public bool IsHD { get; set; }
public bool IsFolder { get; set; }
public string Type { get; set; } public string Type { get; set; }
public int LocalTrailerCount { get; set; }
public JellyfinUserdata UserData { get; set; }
public string VideoType { get; set; }
public JellyfinImagetags ImageTags { get; set; }
public string[] BackdropImageTags { get; set; }
public string LocationType { get; set; }
public string MediaType { get; set; }
public bool HasSubtitles { get; set; }
public int CriticRating { get; set; }
public string Overview { get; set; }
public JellyfinProviderids ProviderIds { get; set; } public JellyfinProviderids ProviderIds { get; set; }
public JellyfinMediastream[] MediaStreams { get; set; } public JellyfinMediastream[] MediaStreams { 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>

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

@ -21,7 +21,7 @@ namespace Ombi.Api.Plex
Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, string itemId); Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, string itemId);
Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, string ratingKey); Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, string ratingKey);
Task<PlexContainer> GetAllEpisodes(string authToken, string host, string section, int start, int retCount); Task<PlexContainer> GetAllEpisodes(string authToken, string host, string section, int start, int retCount);
Task<PlexUsers> GetUsers(string authToken); Task<PlexFriends> GetUsers(string authToken);
Task<PlexAccount> GetAccount(string authToken); Task<PlexAccount> GetAccount(string authToken);
Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId); Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId);
Task<OAuthContainer> GetPin(int pinId); Task<OAuthContainer> GetPin(int pinId);
@ -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

@ -2,42 +2,46 @@
namespace Ombi.Api.Plex.Models.Friends namespace Ombi.Api.Plex.Models.Friends
{ {
[XmlRoot(ElementName = "User")] [XmlRoot(ElementName = "Server")]
public class UserFriends public class Server
{ {
[XmlAttribute(AttributeName = "id")] [XmlAttribute(AttributeName = "id")]
public string Id { get; set; } public string Id { get; set; }
/// <summary> [XmlAttribute(AttributeName = "serverId")]
/// Title is for Home Users only public string ServerId { get; set; }
/// </summary> [XmlAttribute(AttributeName = "machineIdentifier")]
public string MachineIdentifier { get; set; }
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "lastSeenAt")]
public string LastSeenAt { get; set; }
[XmlAttribute(AttributeName = "numLibraries")]
public string NumLibraries { get; set; }
[XmlAttribute(AttributeName = "owned")]
public string Owned { get; set; }
}
[XmlRoot(ElementName = "User")]
public class UserFriends
{
[XmlElement(ElementName = "Server")]
public Server Server { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "title")] [XmlAttribute(AttributeName = "title")]
public string Title { get; set; } public string Title { get; set; }
[XmlAttribute(AttributeName = "username")] [XmlAttribute(AttributeName = "username")]
public string Username { get; set; } public string Username { get; set; }
[XmlAttribute(AttributeName = "email")] [XmlAttribute(AttributeName = "email")]
public string Email { get; set; } public string Email { get; set; }
/// <summary> [XmlAttribute(AttributeName = "recommendationsPlaylistId")]
/// DO NOT USE THIS public string RecommendationsPlaylistId { get; set; }
/// Home Users can actually be an unmanaged account with an email/username to log in. [XmlAttribute(AttributeName = "thumb")]
/// </summary> public string Thumb { get; set; }
[XmlAttribute(AttributeName = "home")]
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")]
public class PlexUsers public class PlexFriends
{ {
[XmlElement(ElementName = "User")] [XmlElement(ElementName = "User")]
public UserFriends[] User { get; set; } public UserFriends[] User { 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>

View file

@ -65,10 +65,10 @@ namespace Ombi.Api.Plex
} }
private const string SignInUri = "https://plex.tv/users/sign_in.json"; private const string SignInUri = "https://plex.tv/users/sign_in.json";
private const string FriendsUri = "https://plex.tv/api/users"; private const string FriendsUri = "https://plex.tv/pms/friends/all";
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
@ -195,12 +195,12 @@ namespace Ombi.Api.Plex
/// </summary> /// </summary>
/// <param name="authToken"></param> /// <param name="authToken"></param>
/// <returns></returns> /// <returns></returns>
public async Task<PlexUsers> GetUsers(string authToken) public async Task<PlexFriends> GetUsers(string authToken)
{ {
var request = new Request(string.Empty, FriendsUri, HttpMethod.Get, ContentType.Xml); var request = new Request(string.Empty, FriendsUri, HttpMethod.Get, ContentType.Xml);
await AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexUsers>(request); return await Api.Request<PlexFriends>(request);
} }
public async Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId) public async Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId)
@ -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

@ -3,6 +3,23 @@ namespace Ombi.Api.Sonarr
public class SystemStatus public class SystemStatus
{ {
public string version { get; set; } public string version { get; set; }
public string buildTime { get; set; }
public bool isDebug { get; set; }
public bool isProduction { get; set; }
public bool isAdmin { get; set; }
public bool isUserInteractive { get; set; }
public string startupPath { get; set; }
public string appData { get; set; }
public string osVersion { get; set; }
public bool isMonoRuntime { get; set; }
public bool isMono { get; set; }
public bool isLinux { get; set; }
public bool isOsx { get; set; }
public bool isWindows { get; set; }
public string branch { get; set; }
public string authentication { get; set; }
public string sqliteVersion { get; set; }
public string urlBase { get; set; } public string urlBase { get; set; }
public string runtimeVersion { 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>

View file

@ -49,7 +49,7 @@ namespace Ombi.Api.Sonarr
public async Task<List<MonitoredEpisodeResult>> MonitorEpisode(int[] episodeIds, bool monitor, string apiKey, string baseUrl) public async Task<List<MonitoredEpisodeResult>> MonitorEpisode(int[] episodeIds, bool monitor, string apiKey, string baseUrl)
{ {
var request = new Request($"{ApiBaseUrl}episode/monitor", baseUrl, HttpMethod.Put); var request = new Request($"{ApiBaseUrl}Episode/monitor", baseUrl, HttpMethod.Put);
request.AddHeader("X-Api-Key", apiKey); request.AddHeader("X-Api-Key", apiKey);
request.AddJsonBody(new { episodeIds = episodeIds, monitored = monitor }); request.AddJsonBody(new { episodeIds = episodeIds, monitored = monitor });
return await Api.Request<List<MonitoredEpisodeResult>>(request); return await Api.Request<List<MonitoredEpisodeResult>>(request);

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

@ -52,7 +52,6 @@ namespace Ombi.Core.Tests.Engine
_subject = _mocker.CreateInstance<MovieRequestEngine>(); _subject = _mocker.CreateInstance<MovieRequestEngine>();
var list = DbHelper.GetQueryableMockDbSet(new RequestSubscription()); var list = DbHelper.GetQueryableMockDbSet(new RequestSubscription());
_mocker.Setup<IRepository<RequestSubscription>, IQueryable<RequestSubscription>>(x => x.GetAll()).Returns(new List<RequestSubscription>().AsQueryable().BuildMock()); _mocker.Setup<IRepository<RequestSubscription>, IQueryable<RequestSubscription>>(x => x.GetAll()).Returns(new List<RequestSubscription>().AsQueryable().BuildMock());
_mocker.Setup<IUserPlayedMovieRepository, IQueryable<UserPlayedMovie>>(x => x.GetAll()).Returns(new List<UserPlayedMovie>().AsQueryable().BuildMock());
} }
[Test] [Test]

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

@ -46,9 +46,8 @@ namespace Ombi.Core.Tests.Engine.V2
var requestSubs = new Mock<IRepository<RequestSubscription>>(); var requestSubs = new Mock<IRepository<RequestSubscription>>();
var mediaCache = new Mock<IMediaCacheService>(); var mediaCache = new Mock<IMediaCacheService>();
var featureService = new Mock<IFeatureService>(); var featureService = new Mock<IFeatureService>();
var userPlayedMovieRepository = new Mock<IUserPlayedMovieRepository>();
_engine = new MovieRequestEngine(movieApi.Object, requestService.Object, user.Object, notificationHelper.Object, rules.Object, movieSender.Object, _engine = new MovieRequestEngine(movieApi.Object, requestService.Object, user.Object, notificationHelper.Object, rules.Object, movieSender.Object,
logger.Object, userManager.Object, requestLogRepo.Object, cache.Object, ombiSettings.Object, requestSubs.Object, mediaCache.Object, featureService.Object, userPlayedMovieRepository.Object); logger.Object, userManager.Object, requestLogRepo.Object, cache.Object, ombiSettings.Object, requestSubs.Object, mediaCache.Object, featureService.Object);
} }
[Test] [Test]

View file

@ -1,15 +1,15 @@
<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>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoFixture" Version="4.18.0" /> <PackageReference Include="AutoFixture" Version="4.17.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" />
@ -18,7 +18,8 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<packagereference Include="Microsoft.NET.Test.Sdk" Version="17.6.2"></packagereference> <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="17.3.2"></packagereference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -54,17 +54,6 @@ namespace Ombi.Core.Tests.Rule.Request
Assert.True(request.Approved); Assert.True(request.Approved);
} }
[Test]
public async Task Should_ReturnSuccess_WhenAdminAndRequest4KMovie()
{
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.Admin)).ReturnsAsync(true);
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie, Is4kRequest = true };
var result = await Rule.Execute(request);
Assert.True(result.Success);
Assert.True(request.Approved);
}
[Test] [Test]
public async Task Should_ReturnSuccess_WhenAdminAndRequestTV() public async Task Should_ReturnSuccess_WhenAdminAndRequestTV()
{ {
@ -87,17 +76,6 @@ namespace Ombi.Core.Tests.Rule.Request
Assert.True(request.Approved); Assert.True(request.Approved);
} }
[Test]
public async Task Should_ReturnSuccess_WhenAutoApprove4KMovieAndRequest4KMovie()
{
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApprove4KMovie)).ReturnsAsync(true);
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie, Is4kRequest = true };
var result = await Rule.Execute(request);
Assert.True(result.Success);
Assert.True(request.Approved);
}
[Test] [Test]
public async Task Should_ReturnFail_WhenAutoApproveMovie_And_RequestTV() public async Task Should_ReturnFail_WhenAutoApproveMovie_And_RequestTV()
{ {
@ -137,7 +115,7 @@ namespace Ombi.Core.Tests.Rule.Request
public async Task Should_ReturnFail_WhenAutoApproveTV_And_RequestMovie() public async Task Should_ReturnFail_WhenAutoApproveTV_And_RequestMovie()
{ {
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApproveTv)).ReturnsAsync(true); UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApproveTv)).ReturnsAsync(true);
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie }; var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request); var result = await Rule.Execute(request);
Assert.True(result.Success); Assert.True(result.Success);
@ -148,7 +126,7 @@ namespace Ombi.Core.Tests.Rule.Request
public async Task Should_ReturnFail_WhenNoClaimsAndRequestMovie() public async Task Should_ReturnFail_WhenNoClaimsAndRequestMovie()
{ {
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), It.IsAny<string>())).ReturnsAsync(false); UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), It.IsAny<string>())).ReturnsAsync(false);
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie }; var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request); var result = await Rule.Execute(request);
Assert.True(result.Success); Assert.True(result.Success);

View file

@ -51,20 +51,11 @@ namespace Ombi.Core.Tests.Rule.Request
} }
[Test] [Test]
public async Task Should_ReturnSuccess_WhenRequestingMovieWithAutoApproveRole() public async Task Should_ReturnSuccess_WhenRequestingMovie4KWithMovieRole()
{
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApproveMovie)).ReturnsAsync(true);
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);
Assert.True(result.Success);
}
[Test]
public async Task Should_ReturnSuccess_WhenRequestingMovie4KWithMovie4KRole()
{ {
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.RequestMovie)).ReturnsAsync(true);
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.Request4KMovie)).ReturnsAsync(true); UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.Request4KMovie)).ReturnsAsync(true);
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie, Is4kRequest = true }; var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie, Has4KRequest = true };
var result = await Rule.Execute(request); var result = await Rule.Execute(request);
Assert.True(result.Success); Assert.True(result.Success);
@ -83,29 +74,15 @@ namespace Ombi.Core.Tests.Rule.Request
} }
[Test] [Test]
public async Task Should_ReturnSuccess_WhenRequestingMovie4KWithAutoApprove4K() public async Task Should_ReturnSuccess_WhenRequestingMovie4KWithAutoApprove()
{
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.RequestMovie)).ReturnsAsync(false);
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApproveMovie)).ReturnsAsync(false);
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.Request4KMovie)).ReturnsAsync(false);
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApprove4KMovie)).ReturnsAsync(true);
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie, Is4kRequest = true };
var result = await Rule.Execute(request);
Assert.True(result.Success);
}
[Test]
public async Task Should_ReturnFailure_WhenRequestingMovie4KWithout4KRoles()
{ {
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.RequestMovie)).ReturnsAsync(true); UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.RequestMovie)).ReturnsAsync(true);
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApproveMovie)).ReturnsAsync(true); UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApproveMovie)).ReturnsAsync(true);
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.Request4KMovie)).ReturnsAsync(false); UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.Request4KMovie)).ReturnsAsync(false);
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApprove4KMovie)).ReturnsAsync(false); var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie, Has4KRequest = true };
var request = new MovieRequests() { RequestType = Store.Entities.RequestType.Movie, Is4kRequest = true };
var result = await Rule.Execute(request); var result = await Rule.Execute(request);
Assert.False(result.Success); Assert.True(result.Success);
} }
[Test] [Test]

View file

@ -173,7 +173,6 @@ namespace Ombi.Core.Tests.Services
[Test] [Test]
[Ignore("Flaky")]
public async Task GetRecentlyRequested_HideUsernames() public async Task GetRecentlyRequested_HideUsernames()
{ {
_mocker.Setup<ISettingsService<CustomizationSettings>, Task<CustomizationSettings>>(x => x.GetSettingsAsync()) _mocker.Setup<ISettingsService<CustomizationSettings>, Task<CustomizationSettings>>(x => x.GetSettingsAsync())
@ -183,25 +182,20 @@ namespace Ombi.Core.Tests.Services
var releaseDate = new DateTime(2019, 01, 01); var releaseDate = new DateTime(2019, 01, 01);
var requestDate = DateTime.Now; var requestDate = DateTime.Now;
var movies = _fixture.CreateMany<MovieRequests>(10).ToList(); var movies = _fixture.CreateMany<MovieRequests>(10);
var albums = _fixture.CreateMany<AlbumRequest>(10); var albums = _fixture.CreateMany<AlbumRequest>(10);
var chilRequests = _fixture.CreateMany<ChildRequests>(10); var chilRequests = _fixture.CreateMany<ChildRequests>(10);
movies.Add(_fixture.Build<MovieRequests>().With(x => x.RequestedUserId, "a").With(x => x.Title, "unit").Create());
_mocker.Setup<IMovieRequestRepository, IQueryable<MovieRequests>>(x => x.GetAll()).Returns(movies.AsQueryable().BuildMock()); _mocker.Setup<IMovieRequestRepository, IQueryable<MovieRequests>>(x => x.GetAll()).Returns(movies.AsQueryable().BuildMock());
_mocker.Setup<IMusicRequestRepository, IQueryable<AlbumRequest>>(x => x.GetAll()).Returns(albums.AsQueryable().BuildMock()); _mocker.Setup<IMusicRequestRepository, IQueryable<AlbumRequest>>(x => x.GetAll()).Returns(albums.AsQueryable().BuildMock());
_mocker.Setup<ITvRequestRepository, IQueryable<ChildRequests>>(x => x.GetChild()).Returns(chilRequests.AsQueryable().BuildMock()); _mocker.Setup<ITvRequestRepository, IQueryable<ChildRequests>>(x => x.GetChild()).Returns(chilRequests.AsQueryable().BuildMock());
_mocker.Setup<ICurrentUser, Task<OmbiUser>>(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "test", Id = "a", Alias = "alias", UserType = UserType.LocalUser }); _mocker.Setup<ICurrentUser, Task<OmbiUser>>(x => x.GetUser()).ReturnsAsync(new OmbiUser { UserName = "test", Alias = "alias", UserType = UserType.LocalUser });
_mocker.Setup<ICurrentUser, string>(x => x.Username).Returns("test"); _mocker.Setup<ICurrentUser, string>(x => x.Username).Returns("test");
_mocker.Setup<OmbiUserManager, Task<bool>>(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), It.IsAny<string>())).ReturnsAsync(false); _mocker.Setup<OmbiUserManager, Task<bool>>(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), It.IsAny<string>())).ReturnsAsync(false);
var result = await _subject.GetRecentlyRequested(CancellationToken.None); var result = await _subject.GetRecentlyRequested(CancellationToken.None);
Assert.Multiple(() => CollectionAssert.IsEmpty(result.Where(x => !string.IsNullOrEmpty(x.Username) && !string.IsNullOrEmpty(x.UserId)));
{
Assert.That(result.Count, Is.EqualTo(1));
Assert.That(result.First().Title, Is.EqualTo("unit"));
});
} }
} }
} }

View file

@ -69,8 +69,6 @@ namespace Ombi.Core.Authentication
private readonly ISettingsService<EmbySettings> _embySettings; private readonly ISettingsService<EmbySettings> _embySettings;
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings; private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
private readonly ISettingsService<AuthenticationSettings> _authSettings; private readonly ISettingsService<AuthenticationSettings> _authSettings;
private string _clientIpAddress;
public string ClientIpAddress { get => _clientIpAddress; set => _clientIpAddress = value; }
public override async Task<bool> CheckPasswordAsync(OmbiUser user, string password) public override async Task<bool> CheckPasswordAsync(OmbiUser user, string password)
{ {
@ -90,7 +88,7 @@ namespace Ombi.Core.Authentication
} }
if (user.UserType == UserType.EmbyUser || user.UserType == UserType.EmbyConnectUser) if (user.UserType == UserType.EmbyUser || user.UserType == UserType.EmbyConnectUser)
{ {
return await CheckEmbyPasswordAsync(user, password, ClientIpAddress); return await CheckEmbyPasswordAsync(user, password);
} }
if (user.UserType == UserType.JellyfinUser) if (user.UserType == UserType.JellyfinUser)
{ {
@ -170,7 +168,7 @@ namespace Ombi.Core.Authentication
/// <param name="user"></param> /// <param name="user"></param>
/// <param name="password"></param> /// <param name="password"></param>
/// <returns></returns> /// <returns></returns>
private async Task<bool> CheckEmbyPasswordAsync(OmbiUser user, string password, string clientIpAddress="") private async Task<bool> CheckEmbyPasswordAsync(OmbiUser user, string password)
{ {
var embySettings = await _embySettings.GetSettingsAsync(); var embySettings = await _embySettings.GetSettingsAsync();
var client = _embyApi.CreateClient(embySettings); var client = _embyApi.CreateClient(embySettings);
@ -198,7 +196,7 @@ namespace Ombi.Core.Authentication
{ {
try try
{ {
var result = await client.LogIn(user.UserName, password, server.ApiKey, server.FullUri, clientIpAddress); var result = await client.LogIn(user.UserName, password, server.ApiKey, server.FullUri);
if (result != null) if (result != null)
{ {
return true; return true;

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

@ -33,8 +33,7 @@ namespace Ombi.Core.Engine
INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger<MovieRequestEngine> log, INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger<MovieRequestEngine> log,
OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache, OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache,
ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService, ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService,
IFeatureService featureService, IFeatureService featureService)
IUserPlayedMovieRepository userPlayedMovieRepository)
: base(user, requestService, r, manager, cache, ombiSettings, sub) : base(user, requestService, r, manager, cache, ombiSettings, sub)
{ {
MovieApi = movieApi; MovieApi = movieApi;
@ -44,7 +43,6 @@ namespace Ombi.Core.Engine
_requestLog = rl; _requestLog = rl;
_mediaCacheService = mediaCacheService; _mediaCacheService = mediaCacheService;
_featureService = featureService; _featureService = featureService;
_userPlayedMovieRepository = userPlayedMovieRepository;
} }
private IMovieDbApi MovieApi { get; } private IMovieDbApi MovieApi { get; }
@ -54,7 +52,6 @@ namespace Ombi.Core.Engine
private readonly IRepository<RequestLog> _requestLog; private readonly IRepository<RequestLog> _requestLog;
private readonly IMediaCacheService _mediaCacheService; private readonly IMediaCacheService _mediaCacheService;
private readonly IFeatureService _featureService; private readonly IFeatureService _featureService;
protected readonly IUserPlayedMovieRepository _userPlayedMovieRepository;
/// <summary> /// <summary>
/// Requests the movie. /// Requests the movie.
@ -80,8 +77,7 @@ namespace Ombi.Core.Engine
var userDetails = await GetUser(); var userDetails = await GetUser();
var canRequestOnBehalf = model.RequestOnBehalf.HasValue(); var canRequestOnBehalf = model.RequestOnBehalf.HasValue();
var isAdmin = Username.Equals("API", StringComparison.CurrentCultureIgnoreCase) var isAdmin = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser)
|| await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser)
|| await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin); || await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin);
if (canRequestOnBehalf && !isAdmin) if (canRequestOnBehalf && !isAdmin)
{ {
@ -256,7 +252,7 @@ namespace Ombi.Core.Engine
var requests = await (OrderMovies(allRequests, orderFilter.OrderType)).Skip(position).Take(count) var requests = await (OrderMovies(allRequests, orderFilter.OrderType)).Skip(position).Take(count)
.ToListAsync(); .ToListAsync();
await FillAdditionalFields(shouldHide, requests); await CheckForSubscription(shouldHide.UserId, requests);
return new RequestsViewModel<MovieRequests> return new RequestsViewModel<MovieRequests>
{ {
Collection = requests, Collection = requests,
@ -300,7 +296,7 @@ namespace Ombi.Core.Engine
var total = requests.Count(); var total = requests.Count();
requests = requests.Skip(position).Take(count).ToList(); requests = requests.Skip(position).Take(count).ToList();
await FillAdditionalFields(shouldHide, requests); await CheckForSubscription(shouldHide.UserId, requests);
return new RequestsViewModel<MovieRequests> return new RequestsViewModel<MovieRequests>
{ {
Collection = requests, Collection = requests,
@ -385,7 +381,7 @@ namespace Ombi.Core.Engine
// TODO fix this so we execute this on the server // TODO fix this so we execute this on the server
requests = requests.Skip(position).Take(count).ToList(); requests = requests.Skip(position).Take(count).ToList();
await FillAdditionalFields(shouldHide, requests); await CheckForSubscription(shouldHide.UserId, requests);
return new RequestsViewModel<MovieRequests> return new RequestsViewModel<MovieRequests>
{ {
Collection = requests, Collection = requests,
@ -428,7 +424,7 @@ namespace Ombi.Core.Engine
var total = requests.Count(); var total = requests.Count();
requests = requests.Skip(position).Take(count).ToList(); requests = requests.Skip(position).Take(count).ToList();
await FillAdditionalFields(shouldHide, requests); await CheckForSubscription(shouldHide.UserId, requests);
return new RequestsViewModel<MovieRequests> return new RequestsViewModel<MovieRequests>
{ {
Collection = requests, Collection = requests,
@ -510,25 +506,18 @@ namespace Ombi.Core.Engine
allRequests = await MovieRepository.GetWithUser().ToListAsync(); allRequests = await MovieRepository.GetWithUser().ToListAsync();
} }
await FillAdditionalFields(shouldHide, allRequests); await CheckForSubscription(shouldHide.UserId, allRequests);
return allRequests; return allRequests;
} }
public async Task<MovieRequests> GetRequest(int requestId) public async Task<MovieRequests> GetRequest(int requestId)
{ {
var shouldHide = await HideFromOtherUsers();
// TODO: this query should return the request only if the user is allowed to see it (see shouldHide implementations)
var request = await MovieRepository.GetWithUser().Where(x => x.Id == requestId).FirstOrDefaultAsync(); var request = await MovieRepository.GetWithUser().Where(x => x.Id == requestId).FirstOrDefaultAsync();
await FillAdditionalFields(shouldHide, new List<MovieRequests> { request }); await CheckForSubscription((await GetUser()).Id, new List<MovieRequests> { request });
return request; return request;
} }
private async Task FillAdditionalFields(HideResult shouldHide, List<MovieRequests> requests)
{
await CheckForSubscription(shouldHide.UserId, requests);
await CheckForPlayed(shouldHide, requests);
}
private async Task CheckForSubscription(string UserId, List<MovieRequests> movieRequests) private async Task CheckForSubscription(string UserId, List<MovieRequests> movieRequests)
{ {
@ -555,23 +544,6 @@ namespace Ombi.Core.Engine
} }
} }
private async Task CheckForPlayed(HideResult shouldHide, List<MovieRequests> movieRequests)
{
var theMovieDbIds = movieRequests.Select(x => x.TheMovieDbId);
var plays = await _userPlayedMovieRepository.GetAll().Where(x =>
theMovieDbIds.Contains(x.TheMovieDbId))
.ToListAsync();
foreach (var request in movieRequests)
{
request.WatchedByRequestedUser = plays.Exists(x => x.TheMovieDbId == request.TheMovieDbId && x.UserId == request.RequestedUserId);
if (!shouldHide.Hide)
{
request.PlayedByUsersCount = plays.Count(x => x.TheMovieDbId == request.TheMovieDbId);
}
}
}
/// <summary> /// <summary>
/// Searches the movie request. /// Searches the movie request.
/// </summary> /// </summary>
@ -591,20 +563,20 @@ namespace Ombi.Core.Engine
} }
var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList(); var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList();
await FillAdditionalFields(shouldHide, results); await CheckForSubscription(shouldHide.UserId, results);
return results; return results;
} }
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 +762,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 +777,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 +806,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

@ -35,8 +35,7 @@ namespace Ombi.Core.Engine
public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, ICurrentUser user, public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, ICurrentUser user,
INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, ILogger<TvRequestEngine> logger, INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, ILogger<TvRequestEngine> logger,
ITvSender sender, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache, ITvSender sender, IRepository<RequestLog> rl, ISettingsService<OmbiSettings> settings, ICacheService cache,
IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService, IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService) : base(user, requestService, rule, manager, cache, settings, sub)
IUserPlayedEpisodeRepository userPlayedEpisodeRepository) : base(user, requestService, rule, manager, cache, settings, sub)
{ {
TvApi = tvApi; TvApi = tvApi;
MovieDbApi = movApi; MovieDbApi = movApi;
@ -45,7 +44,6 @@ namespace Ombi.Core.Engine
TvSender = sender; TvSender = sender;
_requestLog = rl; _requestLog = rl;
_mediaCacheService = mediaCacheService; _mediaCacheService = mediaCacheService;
_userPlayedEpisodeRepository = userPlayedEpisodeRepository;
} }
private INotificationHelper NotificationHelper { get; } private INotificationHelper NotificationHelper { get; }
@ -56,7 +54,6 @@ namespace Ombi.Core.Engine
private readonly ILogger<TvRequestEngine> _logger; private readonly ILogger<TvRequestEngine> _logger;
private readonly IRepository<RequestLog> _requestLog; private readonly IRepository<RequestLog> _requestLog;
private readonly IMediaCacheService _mediaCacheService; private readonly IMediaCacheService _mediaCacheService;
private readonly IUserPlayedEpisodeRepository _userPlayedEpisodeRepository;
public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv) public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv)
{ {
@ -164,7 +161,7 @@ namespace Ombi.Core.Engine
var user = await GetUser(); var user = await GetUser();
var canRequestOnBehalf = tv.RequestOnBehalf.HasValue(); var canRequestOnBehalf = tv.RequestOnBehalf.HasValue();
var isAdmin = Username.Equals("API", StringComparison.CurrentCultureIgnoreCase) || await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin); var isAdmin = await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin);
if (tv.RequestOnBehalf.HasValue() && !isAdmin) if (tv.RequestOnBehalf.HasValue() && !isAdmin)
{ {
return new RequestEngineResult return new RequestEngineResult
@ -295,7 +292,7 @@ namespace Ombi.Core.Engine
.Skip(position).Take(count).ToListAsync(); .Skip(position).Take(count).ToListAsync();
} }
await FillAdditionalFields(shouldHide, allRequests); await CheckForSubscription(shouldHide, allRequests);
return new RequestsViewModel<TvRequests> return new RequestsViewModel<TvRequests>
{ {
@ -331,7 +328,7 @@ namespace Ombi.Core.Engine
return new RequestsViewModel<TvRequests>(); return new RequestsViewModel<TvRequests>();
} }
await FillAdditionalFields(shouldHide, allRequests); await CheckForSubscription(shouldHide, allRequests);
return new RequestsViewModel<TvRequests> return new RequestsViewModel<TvRequests>
{ {
@ -354,7 +351,7 @@ namespace Ombi.Core.Engine
allRequests = await TvRepository.Get().ToListAsync(); allRequests = await TvRepository.Get().ToListAsync();
} }
await FillAdditionalFields(shouldHide, allRequests); await CheckForSubscription(shouldHide, allRequests);
return allRequests; return allRequests;
} }
@ -399,7 +396,7 @@ namespace Ombi.Core.Engine
? allRequests.OrderBy(x => prop.GetValue(x)).ToList() ? allRequests.OrderBy(x => prop.GetValue(x)).ToList()
: allRequests.OrderByDescending(x => prop.GetValue(x)).ToList(); : allRequests.OrderByDescending(x => prop.GetValue(x)).ToList();
await FillAdditionalFields(shouldHide, allRequests); await CheckForSubscription(shouldHide, allRequests);
// Make sure we do not show duplicate child requests // Make sure we do not show duplicate child requests
allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList(); allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList();
@ -472,7 +469,7 @@ namespace Ombi.Core.Engine
? allRequests.OrderBy(x => prop.GetValue(x)).ToList() ? allRequests.OrderBy(x => prop.GetValue(x)).ToList()
: allRequests.OrderByDescending(x => prop.GetValue(x)).ToList(); : allRequests.OrderByDescending(x => prop.GetValue(x)).ToList();
await FillAdditionalFields(shouldHide, allRequests); await CheckForSubscription(shouldHide, allRequests);
// Make sure we do not show duplicate child requests // Make sure we do not show duplicate child requests
allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList(); allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList();
@ -526,7 +523,7 @@ namespace Ombi.Core.Engine
allRequests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase) allRequests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase)
? allRequests.OrderBy(x => prop.GetValue(x)).ToList() ? allRequests.OrderBy(x => prop.GetValue(x)).ToList()
: allRequests.OrderByDescending(x => prop.GetValue(x)).ToList(); : allRequests.OrderByDescending(x => prop.GetValue(x)).ToList();
await FillAdditionalFields(shouldHide, allRequests); await CheckForSubscription(shouldHide, allRequests);
// Make sure we do not show duplicate child requests // Make sure we do not show duplicate child requests
allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList(); allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList();
@ -554,7 +551,7 @@ namespace Ombi.Core.Engine
allRequests = await TvRepository.GetLite().ToListAsync(); allRequests = await TvRepository.GetLite().ToListAsync();
} }
await FillAdditionalFields(shouldHide, allRequests); await CheckForSubscription(shouldHide, allRequests);
return allRequests; return allRequests;
} }
@ -573,7 +570,7 @@ namespace Ombi.Core.Engine
request = await TvRepository.Get().Where(x => x.Id == requestId).FirstOrDefaultAsync(); request = await TvRepository.Get().Where(x => x.Id == requestId).FirstOrDefaultAsync();
} }
await FillAdditionalFields(shouldHide, new List<TvRequests>{request}); await CheckForSubscription(shouldHide, new List<TvRequests>{request});
return request; return request;
} }
@ -627,7 +624,7 @@ namespace Ombi.Core.Engine
allRequests = await TvRepository.GetChild().Include(x => x.SeasonRequests).Where(x => x.ParentRequestId == tvId).ToListAsync(); allRequests = await TvRepository.GetChild().Include(x => x.SeasonRequests).Where(x => x.ParentRequestId == tvId).ToListAsync();
} }
await FillAdditionalFields(shouldHide, allRequests); await CheckForSubscription(shouldHide, allRequests);
return allRequests; return allRequests;
} }
@ -646,7 +643,7 @@ namespace Ombi.Core.Engine
} }
var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync(); var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync();
await FillAdditionalFields(shouldHide, results); await CheckForSubscription(shouldHide, results);
return results; return results;
} }
@ -867,20 +864,14 @@ namespace Ombi.Core.Engine
} }
} }
private async Task FillAdditionalFields(HideResult shouldHide, List<TvRequests> x) private async Task CheckForSubscription(HideResult shouldHide, List<TvRequests> x)
{ {
foreach (var tvRequest in x) foreach (var tvRequest in x)
{ {
await FillAdditionalFields(shouldHide, tvRequest.ChildRequests); await CheckForSubscription(shouldHide, tvRequest.ChildRequests);
} }
} }
private async Task FillAdditionalFields(HideResult shouldHide, List<ChildRequests> childRequests)
{
await CheckForSubscription(shouldHide, childRequests);
CheckForPlayed(shouldHide, childRequests);
}
private async Task CheckForSubscription(HideResult shouldHide, List<ChildRequests> childRequests) private async Task CheckForSubscription(HideResult shouldHide, List<ChildRequests> childRequests)
{ {
var sub = _subscriptionRepository.GetAll(); var sub = _subscriptionRepository.GetAll();
@ -905,59 +896,6 @@ namespace Ombi.Core.Engine
} }
} }
private class EpisodeKey
{
public int SeasonNumber;
public int EpisodeNumber;
}
private void CheckForPlayed(HideResult shouldHide, List<ChildRequests> childRequests)
{
var theMovieDbIds = childRequests.Select(x => x.Id);
foreach (var request in childRequests)
{
var requestedEpisodes = GetEpisodesKeys(request);
var playedEpisodes = _userPlayedEpisodeRepository
.GetAll()
.Where(x => x.TheMovieDbId == request.Id && x.UserId == request.RequestedUserId)
.AsEnumerable()
.Join(requestedEpisodes,
played => new { played.SeasonNumber, played.EpisodeNumber },
requested => new { requested.SeasonNumber, requested.EpisodeNumber },
(played, requested) => new { played });
var playedCount = playedEpisodes.Count();
var toWatchCount = requestedEpisodes.Count();
if (playedCount == 0 || toWatchCount == 0)
{
request.RequestedUserPlayedProgress = 0;
}
else
{
request.RequestedUserPlayedProgress = 100 * playedCount / toWatchCount;
}
}
}
private List<EpisodeKey> GetEpisodesKeys(ChildRequests request)
{
List<EpisodeKey> result = new List<EpisodeKey>();
foreach(var season in request.SeasonRequests)
{
foreach(var episode in season.Episodes)
{
result.Add(new EpisodeKey
{
SeasonNumber = season.SeasonNumber,
EpisodeNumber = episode.EpisodeNumber
});
}
}
return result;
}
private async Task<RequestEngineResult> AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest, string requestOnBehalf, int rootFolder, int qualityProfile) private async Task<RequestEngineResult> AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest, string requestOnBehalf, int rootFolder, int qualityProfile)
{ {
// Add the child // Add the child

View file

@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.VisualBasic;
using Ombi.Core.Authentication; using Ombi.Core.Authentication;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests; using Ombi.Store.Entities.Requests;

View file

@ -200,14 +200,19 @@ namespace Ombi.Core.Engine.V2
public async Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies(int currentPosition, int amountToLoad) public async Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies(int currentPosition, int amountToLoad)
{ {
var langCode = await DefaultLanguageCode(null); var langCode = await DefaultLanguageCode(null);
var isOldTrendingSourceEnabled = await _feature.FeatureEnabled(FeatureNames.OldTrendingSource);
var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems); var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems);
var results = new List<MovieDbSearchResult>(); var results = new List<MovieDbSearchResult>();
foreach (var pagesToLoad in pages) foreach (var pagesToLoad in pages)
{ {
var search = () => (isOldTrendingSourceEnabled) ?
MovieApi.NowPlaying(langCode, pagesToLoad.Page)
: MovieApi.TrendingMovies(langCode, pagesToLoad.Page);
var apiResult = await Cache.GetOrAddAsync(nameof(NowPlayingMovies) + pagesToLoad.Page + langCode, var apiResult = await Cache.GetOrAddAsync(nameof(NowPlayingMovies) + pagesToLoad.Page + langCode,
() => MovieApi.TrendingMovies(langCode, pagesToLoad.Page), DateTimeOffset.Now.AddHours(12)); search, DateTimeOffset.Now.AddHours(12));
results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
} }
return await TransformMovieResultsToResponse(results); return await TransformMovieResultsToResponse(results);

View file

@ -138,13 +138,17 @@ namespace Ombi.Core.Engine.V2
public async Task<IEnumerable<SearchTvShowViewModel>> Trending(int currentlyLoaded, int amountToLoad) public async Task<IEnumerable<SearchTvShowViewModel>> Trending(int currentlyLoaded, int amountToLoad)
{ {
var langCode = await DefaultLanguageCode(null); var langCode = await DefaultLanguageCode(null);
var isOldTrendingSourceEnabled = await _feature.FeatureEnabled(FeatureNames.OldTrendingSource);
var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit); var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit);
var results = new List<MovieDbSearchResult>(); var results = new List<MovieDbSearchResult>();
foreach (var pagesToLoad in pages) foreach (var pagesToLoad in pages)
{ {
var search = ( async () => (isOldTrendingSourceEnabled) ?
await _movieApi.TopRatedTv(langCode, pagesToLoad.Page)
: await _movieApi.TrendingTv(langCode, pagesToLoad.Page));
var apiResult = await Cache.GetOrAddAsync(nameof(Trending) + langCode + pagesToLoad.Page, var apiResult = await Cache.GetOrAddAsync(nameof(Trending) + langCode + pagesToLoad.Page,
() => _movieApi.TrendingTv(langCode, pagesToLoad.Page), DateTimeOffset.Now.AddHours(12)); search, DateTimeOffset.Now.AddHours(12));
results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take));
} }

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

@ -16,7 +16,6 @@ namespace Ombi.Core.Models.Requests
public string Overview { get; set; } public string Overview { get; set; }
public DateTime ReleaseDate { get; set; } public DateTime ReleaseDate { get; set; }
public bool Approved { get; set; } public bool Approved { get; set; }
public bool Denied { get; set; }
public string MediaId { get; set; } public string MediaId { get; set; }
public string PosterPath { get; set; } public string PosterPath { get; set; }

View file

@ -5,6 +5,5 @@
public bool IsValid { get; set; } public bool IsValid { get; set; }
public string Version { get; set; } public string Version { get; set; }
public string ExpectedSubDir { get; set; } public string ExpectedSubDir { get; set; }
public string AdditionalInformation { 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

@ -36,7 +36,7 @@ namespace Ombi.Core.Rule.Rules.Request
{ {
if (obj is MovieRequests movie) if (obj is MovieRequests movie)
{ {
await ApproveMovie(movie); await Check4K(movie);
} }
else else
{ {
@ -45,14 +45,10 @@ namespace Ombi.Core.Rule.Rules.Request
return Success(); return Success();
} }
if (obj.RequestType == RequestType.Movie) if (obj.RequestType == RequestType.Movie && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMovie))
{ {
var movie = (MovieRequests)obj; var movie = (MovieRequests)obj;
var autoApproveRole = movie.Is4kRequest ? OmbiRoles.AutoApprove4KMovie : OmbiRoles.AutoApproveMovie; await Check4K(movie);
if (await _manager.IsInRoleAsync(user, autoApproveRole))
{
await ApproveMovie(movie);
}
} }
if (obj.RequestType == RequestType.TvShow && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveTv)) if (obj.RequestType == RequestType.TvShow && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveTv))
obj.Approved = true; obj.Approved = true;
@ -61,7 +57,7 @@ namespace Ombi.Core.Rule.Rules.Request
return Success(); // We don't really care, we just don't set the obj to approve return Success(); // We don't really care, we just don't set the obj to approve
} }
private async Task ApproveMovie(MovieRequests movie) private async Task Check4K(MovieRequests movie)
{ {
var featureEnabled = await _featureService.FeatureEnabled(FeatureNames.Movie4KRequests); var featureEnabled = await _featureService.FeatureEnabled(FeatureNames.Movie4KRequests);
if (movie.Is4kRequest && featureEnabled) if (movie.Is4kRequest && featureEnabled)

View file

@ -36,14 +36,22 @@ namespace Ombi.Core.Rule.Rules.Request
if (obj.RequestType == RequestType.Movie) if (obj.RequestType == RequestType.Movie)
{ {
var movie = (MovieRequests)obj; var movie = (MovieRequests)obj;
var hasAutoApprove = await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMovie);
var requestRole = movie.Is4kRequest ? OmbiRoles.Request4KMovie : OmbiRoles.RequestMovie; if (await _manager.IsInRoleAsync(user, OmbiRoles.RequestMovie) || hasAutoApprove)
var autoApproveRole = movie.Is4kRequest ? OmbiRoles.AutoApprove4KMovie : OmbiRoles.AutoApproveMovie; {
if (movie.Is4kRequest && !hasAutoApprove)
if (await _manager.IsInRoleAsync(user, requestRole) || await _manager.IsInRoleAsync(user, autoApproveRole)) {
var has4kPermission = await _manager.IsInRoleAsync(user, OmbiRoles.Request4KMovie);
if (has4kPermission)
{ {
return Success(); return Success();
} }
}
else
{
return Success();
}
}
return Fail(ErrorCode.NoPermissionsRequestMovie, "You do not have permissions to Request a Movie"); return Fail(ErrorCode.NoPermissionsRequestMovie, "You do not have permissions to Request a Movie");
} }

View file

@ -69,7 +69,7 @@ namespace Ombi.Core.Senders
} }
if (radarrSettings.Enabled) if (radarrSettings.Enabled)
{ {
return await SendToRadarr(model, radarrSettings, is4K); return await SendToRadarr(model, radarrSettings);
} }
var dogSettings = await _dogNzbSettings.GetSettingsAsync(); var dogSettings = await _dogNzbSettings.GetSettingsAsync();
@ -133,7 +133,7 @@ namespace Ombi.Core.Senders
return await _dogNzbApi.AddMovie(settings.ApiKey, id); return await _dogNzbApi.AddMovie(settings.ApiKey, id);
} }
private async Task<SenderResult> SendToRadarr(MovieRequests model, RadarrSettings settings, bool is4k) private async Task<SenderResult> SendToRadarr(MovieRequests model, RadarrSettings settings)
{ {
var qualityToUse = int.Parse(settings.DefaultQualityProfile); var qualityToUse = int.Parse(settings.DefaultQualityProfile);
@ -141,23 +141,6 @@ namespace Ombi.Core.Senders
var profiles = await _userProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId); var profiles = await _userProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId);
if (profiles != null) if (profiles != null)
{
if (is4k)
{
if (profiles.Radarr4KRootPath > 0)
{
var tempPath = await RadarrRootPath(profiles.Radarr4KRootPath, settings);
if (tempPath.HasValue())
{
rootFolderPath = tempPath;
}
}
if (profiles.Radarr4KQualityProfile > 0)
{
qualityToUse = profiles.Radarr4KQualityProfile;
}
}
else
{ {
if (profiles.RadarrRootPath > 0) if (profiles.RadarrRootPath > 0)
{ {
@ -172,7 +155,6 @@ namespace Ombi.Core.Senders
qualityToUse = profiles.RadarrQualityProfile; qualityToUse = profiles.RadarrQualityProfile;
} }
} }
}
var tags = new List<int>(); var tags = new List<int>();
if (settings.Tag.HasValue) if (settings.Tag.HasValue)
@ -182,11 +164,8 @@ 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
if (model.QualityOverride > 0) if (model.QualityOverride > 0)
@ -201,9 +180,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 +228,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

@ -100,7 +100,7 @@ namespace Ombi.Core.Senders
addOptions = new Addoptions addOptions = new Addoptions
{ {
monitored = true, monitored = true,
monitor = MonitorTypes.Existing, monitor = MonitorTypes.None,
searchForMissingAlbums = false, searchForMissingAlbums = false,
AlbumsToMonitor = new[] {model.ForeignAlbumId} AlbumsToMonitor = new[] {model.ForeignAlbumId}
}, },

View file

@ -1,9 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.VisualBasic;
using Ombi.Api.DogNzb;
using Ombi.Api.DogNzb.Models;
using Ombi.Api.SickRage; using Ombi.Api.SickRage;
using Ombi.Api.SickRage.Models; using Ombi.Api.SickRage.Models;
using Ombi.Api.Sonarr; using Ombi.Api.Sonarr;
@ -19,13 +23,15 @@ namespace Ombi.Core.Senders
{ {
public class TvSender : ITvSender public class TvSender : ITvSender
{ {
public TvSender(ISonarrV3Api sonarrV3Api, ILogger<TvSender> log, ISettingsService<SonarrSettings> sonarrSettings, public TvSender(ISonarrApi sonarrApi, ISonarrV3Api sonarrV3Api, ILogger<TvSender> log, ISettingsService<SonarrSettings> sonarrSettings,
ISettingsService<SickRageSettings> srSettings, ISettingsService<DogNzbSettings> dog, IDogNzbApi dogApi, ISettingsService<SickRageSettings> srSettings,
ISickRageApi srApi, IRepository<UserQualityProfiles> userProfiles, IRepository<RequestQueue> requestQueue, INotificationHelper notify) ISickRageApi srApi, IRepository<UserQualityProfiles> userProfiles, IRepository<RequestQueue> requestQueue, INotificationHelper notify)
{ {
SonarrApi = sonarrV3Api; SonarrApi = sonarrV3Api;
Logger = log; Logger = log;
SonarrSettings = sonarrSettings; SonarrSettings = sonarrSettings;
DogNzbSettings = dog;
DogNzbApi = dogApi;
SickRageSettings = srSettings; SickRageSettings = srSettings;
SickRageApi = srApi; SickRageApi = srApi;
UserQualityProfiles = userProfiles; UserQualityProfiles = userProfiles;
@ -34,9 +40,11 @@ namespace Ombi.Core.Senders
} }
private ISonarrV3Api SonarrApi { get; } private ISonarrV3Api SonarrApi { get; }
private IDogNzbApi DogNzbApi { get; }
private ISickRageApi SickRageApi { get; } private ISickRageApi SickRageApi { get; }
private ILogger<TvSender> Logger { get; } private ILogger<TvSender> Logger { get; }
private ISettingsService<SonarrSettings> SonarrSettings { get; } private ISettingsService<SonarrSettings> SonarrSettings { get; }
private ISettingsService<DogNzbSettings> DogNzbSettings { get; }
private ISettingsService<SickRageSettings> SickRageSettings { get; } private ISettingsService<SickRageSettings> SickRageSettings { get; }
private IRepository<UserQualityProfiles> UserQualityProfiles { get; } private IRepository<UserQualityProfiles> UserQualityProfiles { get; }
private readonly IRepository<RequestQueue> _requestQueueRepository; private readonly IRepository<RequestQueue> _requestQueueRepository;
@ -59,7 +67,23 @@ namespace Ombi.Core.Senders
}; };
} }
} }
var dog = await DogNzbSettings.GetSettingsAsync();
if (dog.Enabled)
{
var result = await SendToDogNzb(model, dog);
if (!result.Failure)
{
return new SenderResult
{
Sent = true,
Success = true
};
}
return new SenderResult
{
Message = result.ErrorMessage
};
}
var sr = await SickRageSettings.GetSettingsAsync(); var sr = await SickRageSettings.GetSettingsAsync();
if (sr.Enabled) if (sr.Enabled)
{ {
@ -113,6 +137,12 @@ namespace Ombi.Core.Senders
}; };
} }
private async Task<DogNzbAddResult> SendToDogNzb(ChildRequests model, DogNzbSettings settings)
{
var id = model.ParentRequest.ExternalProviderId;
return await DogNzbApi.AddTvShow(settings.ApiKey, id.ToString());
}
/// <summary> /// <summary>
/// Send the request to Sonarr to process /// Send the request to Sonarr to process
/// </summary> /// </summary>
@ -133,14 +163,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 +171,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 +184,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 +199,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)
@ -198,23 +216,31 @@ namespace Ombi.Core.Senders
} }
// Overrides on the request take priority // Overrides on the request take priority
if (model.ParentRequest.QualityOverride.HasValue && model.ParentRequest.QualityOverride.Value > 0) if (model.ParentRequest.QualityOverride.HasValue)
{ {
qualityToUse = model.ParentRequest.QualityOverride.Value; var qualityOverride = model.ParentRequest.QualityOverride.Value;
if (qualityOverride > 0)
{
qualityToUse = qualityOverride;
}
}
if (model.ParentRequest.RootFolder.HasValue)
{
var rootfolderOverride = model.ParentRequest.RootFolder.Value;
if (rootfolderOverride > 0)
{
rootFolderPath = await GetSonarrRootPath(rootfolderOverride, s);
}
} }
if (model.ParentRequest.RootFolder.HasValue && model.ParentRequest.RootFolder.Value > 0) if (model.ParentRequest.LanguageProfile.HasValue)
{ {
Logger.LogInformation("Using request root folder override: {RootFolder}", model.ParentRequest.RootFolder.Value); var languageProfile = model.ParentRequest.LanguageProfile.Value;
rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder.Value, s); if (languageProfile > 0)
}
if (model.ParentRequest.LanguageProfile.HasValue && model.ParentRequest.LanguageProfile.Value > 0)
{ {
languageProfileId = model.ParentRequest.LanguageProfile.Value; languageProfileId = languageProfile;
}
} }
Logger.LogInformation("Final root folder path: {RootFolderPath}", rootFolderPath);
try try
{ {
@ -398,10 +424,9 @@ namespace Ombi.Core.Senders
await SonarrApi.MonitorEpisode(epToUnmonitored.Select(x => x.id).ToArray(), false, s.ApiKey, s.FullUri); await SonarrApi.MonitorEpisode(epToUnmonitored.Select(x => x.id).ToArray(), false, s.ApiKey, s.FullUri);
} }
}
// Now update the episodes that need updating // Now update the episodes that need updating
await SonarrApi.MonitorEpisode(episodesToUpdate.Select(x => x.id).ToArray(), true, s.ApiKey, s.FullUri); await SonarrApi.MonitorEpisode(episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber).Select(x => x.id).ToArray(), true, s.ApiKey, s.FullUri);
}
if (!s.AddOnly) if (!s.AddOnly)
{ {
@ -535,36 +560,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

@ -73,10 +73,6 @@ namespace Ombi.Core.Services
var lang = await DefaultLanguageCode(); var lang = await DefaultLanguageCode();
foreach (var item in await recentMovieRequests.ToListAsync(cancellationToken)) foreach (var item in await recentMovieRequests.ToListAsync(cancellationToken))
{ {
if (hideUsers.Hide && item.RequestedUserId != hideUsers.UserId)
{
continue;
}
var images = await _cache.GetOrAddAsync($"{CacheKeys.TmdbImages}movie{item.TheMovieDbId}", () => _movieDbApi.GetMovieImages(item.TheMovieDbId.ToString(), cancellationToken), DateTimeOffset.Now.AddDays(1)); var images = await _cache.GetOrAddAsync($"{CacheKeys.TmdbImages}movie{item.TheMovieDbId}", () => _movieDbApi.GetMovieImages(item.TheMovieDbId.ToString(), cancellationToken), DateTimeOffset.Now.AddDays(1));
model.Add(new RecentlyRequestedModel model.Add(new RecentlyRequestedModel
{ {
@ -88,9 +84,8 @@ namespace Ombi.Core.Services
Title = item.Title, Title = item.Title,
Type = RequestType.Movie, Type = RequestType.Movie,
Approved = item.Approved, Approved = item.Approved,
Denied = item.Denied ?? false, UserId = hideUsers.Hide ? string.Empty : item.RequestedUserId,
UserId = item.RequestedUserId, Username = hideUsers.Hide ? string.Empty : item.RequestedUser.UserAlias,
Username = item.RequestedUser.UserAlias,
MediaId = item.TheMovieDbId.ToString(), MediaId = item.TheMovieDbId.ToString(),
PosterPath = images?.posters?.Where(x => lang.Equals(x?.iso_639_1, StringComparison.InvariantCultureIgnoreCase))?.OrderByDescending(x => x.vote_count)?.Select(x => x.file_path)?.FirstOrDefault(), PosterPath = images?.posters?.Where(x => lang.Equals(x?.iso_639_1, StringComparison.InvariantCultureIgnoreCase))?.OrderByDescending(x => x.vote_count)?.Select(x => x.file_path)?.FirstOrDefault(),
Background = images?.backdrops?.Where(x => lang.Equals(x?.iso_639_1, StringComparison.InvariantCultureIgnoreCase))?.OrderByDescending(x => x.vote_count)?.Select(x => x.file_path)?.FirstOrDefault(), Background = images?.backdrops?.Where(x => lang.Equals(x?.iso_639_1, StringComparison.InvariantCultureIgnoreCase))?.OrderByDescending(x => x.vote_count)?.Select(x => x.file_path)?.FirstOrDefault(),
@ -99,33 +94,24 @@ namespace Ombi.Core.Services
foreach (var item in await recentMusicRequests.ToListAsync(cancellationToken)) foreach (var item in await recentMusicRequests.ToListAsync(cancellationToken))
{ {
if (hideUsers.Hide && item.RequestedUserId != hideUsers.UserId)
{
continue;
}
model.Add(new RecentlyRequestedModel model.Add(new RecentlyRequestedModel
{ {
RequestId = item.Id, RequestId = item.Id,
Available = item.Available, Available = item.Available,
Overview = item.ArtistName, Overview = item.ArtistName,
Approved = item.Approved, Approved = item.Approved,
Denied = item.Denied ?? false,
ReleaseDate = item.ReleaseDate, ReleaseDate = item.ReleaseDate,
RequestDate = item.RequestedDate, RequestDate = item.RequestedDate,
Title = item.Title, Title = item.Title,
Type = RequestType.Album, Type = RequestType.Album,
UserId = item.RequestedUserId, UserId = hideUsers.Hide ? string.Empty : item.RequestedUserId,
Username = item.RequestedUser.UserAlias, Username = hideUsers.Hide ? string.Empty : item.RequestedUser.UserAlias,
MediaId = item.ForeignAlbumId, MediaId = item.ForeignAlbumId,
}); });
} }
foreach (var item in await recentTvRequests.ToListAsync(cancellationToken)) foreach (var item in await recentTvRequests.ToListAsync(cancellationToken))
{ {
if (hideUsers.Hide && item.RequestedUserId != hideUsers.UserId)
{
continue;
}
var providerId = item.ParentRequest.ExternalProviderId.ToString(); var providerId = item.ParentRequest.ExternalProviderId.ToString();
var images = await _cache.GetOrAddAsync($"{CacheKeys.TmdbImages}tv{providerId}", () => _movieDbApi.GetTvImages(providerId.ToString(), cancellationToken), DateTimeOffset.Now.AddDays(1)); var images = await _cache.GetOrAddAsync($"{CacheKeys.TmdbImages}tv{providerId}", () => _movieDbApi.GetTvImages(providerId.ToString(), cancellationToken), DateTimeOffset.Now.AddDays(1));
@ -137,13 +123,12 @@ namespace Ombi.Core.Services
Overview = item.ParentRequest.Overview, Overview = item.ParentRequest.Overview,
ReleaseDate = item.ParentRequest.ReleaseDate, ReleaseDate = item.ParentRequest.ReleaseDate,
Approved = item.Approved, Approved = item.Approved,
Denied = item.Denied ?? false,
RequestDate = item.RequestedDate, RequestDate = item.RequestedDate,
TvPartiallyAvailable = partialAvailability, TvPartiallyAvailable = partialAvailability,
Title = item.ParentRequest.Title, Title = item.ParentRequest.Title,
Type = RequestType.TvShow, Type = RequestType.TvShow,
UserId = item.RequestedUserId, UserId = hideUsers.Hide ? string.Empty : item.RequestedUserId,
Username = item.RequestedUser.UserAlias, Username = hideUsers.Hide ? string.Empty : item.RequestedUser.UserAlias,
MediaId = providerId.ToString(), MediaId = providerId.ToString(),
PosterPath = images?.posters?.Where(x => lang.Equals(x?.iso_639_1, StringComparison.InvariantCultureIgnoreCase))?.OrderByDescending(x => x.vote_count)?.Select(x => x.file_path)?.FirstOrDefault(), PosterPath = images?.posters?.Where(x => lang.Equals(x?.iso_639_1, StringComparison.InvariantCultureIgnoreCase))?.OrderByDescending(x => x.vote_count)?.Select(x => x.file_path)?.FirstOrDefault(),
Background = images?.backdrops?.Where(x => lang.Equals(x?.iso_639_1, StringComparison.InvariantCultureIgnoreCase))?.OrderByDescending(x => x.vote_count)?.Select(x => x.file_path)?.FirstOrDefault(), Background = images?.backdrops?.Where(x => lang.Equals(x?.iso_639_1, StringComparison.InvariantCultureIgnoreCase))?.OrderByDescending(x => x.vote_count)?.Select(x => x.file_path)?.FirstOrDefault(),

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>();
@ -198,8 +197,6 @@ namespace Ombi.DependencyInjection
services.AddScoped<IEmbyContentRepository, EmbyContentRepository>(); services.AddScoped<IEmbyContentRepository, EmbyContentRepository>();
services.AddScoped<IJellyfinContentRepository, JellyfinContentRepository>(); services.AddScoped<IJellyfinContentRepository, JellyfinContentRepository>();
services.AddScoped<INotificationTemplatesRepository, NotificationTemplatesRepository>(); services.AddScoped<INotificationTemplatesRepository, NotificationTemplatesRepository>();
services.AddScoped<IUserPlayedMovieRepository, UserPlayedMovieRepository>();
services.AddScoped<IUserPlayedEpisodeRepository, UserPlayedEpisodeRepository>();
services.AddScoped<ITvRequestRepository, TvRequestRepository>(); services.AddScoped<ITvRequestRepository, TvRequestRepository>();
services.AddScoped<IMovieRequestRepository, MovieRequestRepository>(); services.AddScoped<IMovieRequestRepository, MovieRequestRepository>();
@ -237,8 +234,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)
@ -249,7 +244,6 @@ namespace Ombi.DependencyInjection
services.AddTransient<IPlexContentSync, PlexContentSync>(); services.AddTransient<IPlexContentSync, PlexContentSync>();
services.AddTransient<IPlexWatchlistImport, PlexWatchlistImport>(); services.AddTransient<IPlexWatchlistImport, PlexWatchlistImport>();
services.AddTransient<IEmbyContentSync, EmbyContentSync>(); services.AddTransient<IEmbyContentSync, EmbyContentSync>();
services.AddTransient<IEmbyPlayedSync, EmbyPlayedSync>();
services.AddTransient<IEmbyEpisodeSync, EmbyEpisodeSync>(); services.AddTransient<IEmbyEpisodeSync, EmbyEpisodeSync>();
services.AddTransient<IEmbyAvaliabilityChecker, EmbyAvaliabilityChecker>(); services.AddTransient<IEmbyAvaliabilityChecker, EmbyAvaliabilityChecker>();
services.AddTransient<IJellyfinContentSync, JellyfinContentSync>(); services.AddTransient<IJellyfinContentSync, JellyfinContentSync>();

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,10 @@
</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.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,5 +1,8 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;

View file

@ -1,9 +1,17 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks;
using Ombi.Api.CouchPotato; using Ombi.Api.CouchPotato;
using Ombi.Api.Emby;
using Ombi.Api.Emby.Models;
using Ombi.Api.Jellyfin;
using Ombi.Api.Jellyfin.Models;
using Ombi.Api.Plex;
using Ombi.Api.Plex.Models.Status;
using Ombi.Core.Settings; using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External;
using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.External;
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;

View file

@ -2,6 +2,8 @@
using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks;
using Ombi.Api.Emby; using Ombi.Api.Emby;
using Ombi.Api.Emby.Models; using Ombi.Api.Emby.Models;
using Ombi.Api.Plex;
using Ombi.Api.Plex.Models.Status;
using Ombi.Core.Settings; using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External; using Ombi.Core.Settings.Models.External;
using System; using System;

View file

@ -2,6 +2,8 @@
using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks;
using Ombi.Api.Jellyfin; using Ombi.Api.Jellyfin;
using Ombi.Api.Jellyfin.Models; using Ombi.Api.Jellyfin.Models;
using Ombi.Api.Plex;
using Ombi.Api.Plex.Models.Status;
using Ombi.Core.Settings; using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External; using Ombi.Core.Settings.Models.External;
using System; using System;

View file

@ -0,0 +1,47 @@
using HealthChecks.Network;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Ombi.HealthChecks.Checks
{
public class OmbiPingHealthCheck
: IHealthCheck
{
private readonly OmbiPingHealthCheckOptions _options;
public OmbiPingHealthCheck(OmbiPingHealthCheckOptions options)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
}
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
var configuredHosts = _options.ConfiguredHosts.Values;
try
{
foreach (var (host, timeout, status) in configuredHosts)
{
using (var ping = new Ping())
{
var pingReply = await ping.SendPingAsync(host, timeout);
if (pingReply.Status != IPStatus.Success)
{
return new HealthCheckResult(status, description: $"Ping check for host {host} is failed with status reply:{pingReply.Status}");
}
}
}
return HealthCheckResult.Healthy();
}
catch (Exception ex)
{
return new HealthCheckResult(context.Registration.FailureStatus, exception: ex);
}
}
}
}

View file

@ -0,0 +1,16 @@
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Collections.Generic;
namespace Ombi.HealthChecks.Checks
{
public class OmbiPingHealthCheckOptions
{
internal Dictionary<string, (string Host, int TimeOut, HealthStatus status)> ConfiguredHosts { get; } = new Dictionary<string, (string, int, HealthStatus)>();
public OmbiPingHealthCheckOptions AddHost(string host, int timeout, HealthStatus status)
{
ConfiguredHosts.Add(host, (host, timeout, status));
return this;
}
}
}

View file

@ -27,7 +27,7 @@ namespace Ombi.HealthChecks.Checks
var settings = await settingsProvider.GetSettingsAsync(); var settings = await settingsProvider.GetSettingsAsync();
if (settings == null) if (settings == null)
{ {
return HealthCheckResult.Healthy("Plex is not configured."); return HealthCheckResult.Healthy("Plex is not confiured.");
} }
var taskResult = new List<Task<PlexStatus>>(); var taskResult = new List<Task<PlexStatus>>();

View file

@ -1,9 +1,18 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks;
using Ombi.Api.CouchPotato;
using Ombi.Api.Emby;
using Ombi.Api.Emby.Models;
using Ombi.Api.Jellyfin;
using Ombi.Api.Jellyfin.Models;
using Ombi.Api.Plex;
using Ombi.Api.Plex.Models.Status;
using Ombi.Api.SickRage; using Ombi.Api.SickRage;
using Ombi.Core.Settings; using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External;
using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.External;
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;

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