Merge pull request #5 from tidusjar/feature/v4

Update
pull/3895/head
Javier Pastor 4 years ago committed by GitHub
commit 10e35150df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,93 @@
variables:
- template: templates/variables.yml
stages:
- stage: build
jobs:
- job: Build
pool:
vmImage: ${{ variables.vmImage }}
steps:
- template: templates/build-steps.yml
- stage: publish
jobs:
- job:
strategy:
matrix:
win10-x64:
runtime: win10-x64
format: zip
compression: zip
win10-x86:
runtime: win10-x86
format: zip
compression: zip
osx-x64:
runtime: osx-x64
format: tar.gz
compression: tar
linux-x64:
runtime: linux-x64
format: tar.gz
compression: tar
linux-arm:
runtime: linux-arm
format: tar.gz
compression: tar
linux-arm64:
runtime: linux-arm64
format: tar.gz
compression: tar
pool:
vmImage: ${{ variables.vmImage }}
steps:
- template: templates/publish-os-steps.yml
- stage: deploy
jobs:
- job:
condition: and(succeeded(), eq(variables.isMain, true))
steps:
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
targetPath: '$(System.ArtifactsDirectory)'
- task: PowerShell@2
displayName: 'Get Release Notes'
inputs:
targetType: 'inline'
script: |
$response = Invoke-WebRequest -Uri "https://ombireleasenote.azurewebsites.net/api/ReleaseNotesFunction?buildId=$(Build.BuildId)"
Write-Host "##vso[task.setvariable variable=ReleaseNotes;]$response"
# - task: GitHubRelease@1
# inputs:
# gitHubConnection: 'github.com_tidusjar'
# repositoryName: 'tidusjar/Ombi'
# action: 'create'
# target: '$(Build.SourceVersion)'
# tagSource: 'userSpecifiedTag'
# tag: '$(gitTag)'
# isDraft: true
# changeLogCompareToRelease: 'lastNonDraftRelease'
# changeLogType: 'commitBased'
- task: GitHubRelease@1
inputs:
gitHubConnection: 'github.com_tidusjar'
repositoryName: 'tidusjar/Ombi.Releases'
action: 'create'
target: 'c7fcbb77b58aef1076d635a9ef99e4374abc8672'
tagSource: 'userSpecifiedTag'
tag: '$(gitTag)'
releaseNotesSource: 'inline'
releaseNotesInline: '$(ReleaseNotes)'
assets: |
$(System.ArtifactsDirectory)/**/*.zip
$(System.ArtifactsDirectory)/**/*.tar.gz
isPreRelease: true
changeLogCompareToRelease: 'lastNonDraftRelease'
changeLogType: 'commitBased'

@ -0,0 +1,34 @@
steps:
## This is needed due to https://github.com/microsoft/azure-pipelines-tasks/issues/8429
## For the set version tool...
- task: DotNetCoreInstaller@1
displayName: 'Use .NET Core sdk '
inputs:
packageType: 'sdk'
version: '5.x'
- task: Yarn@3
displayName: 'Install UI Dependancies'
inputs:
projectDirectory: '$(UiLocation)'
arguments: 'install'
- task: Yarn@3
displayName: 'Build and Publish Angular App'
inputs:
projectDirectory: '$(UiLocation)'
arguments: 'run build'
- task: PublishPipelineArtifact@1
inputs:
targetPath: '$(UiLocation)dist'
artifact: 'angular_dist'
publishLocation: 'pipeline'
- task: DotNetCoreCLI@2
displayName: Run Unit Tests
inputs:
command: 'custom'
projects: '$(TestProject)'
custom: 'test'
continueOnError: false

@ -0,0 +1,57 @@
steps:
- task: DotNetCoreInstaller@1
displayName: 'Use .NET Core sdk '
inputs:
packageType: 'sdk'
version: '5.x'
- task: DotNetCoreInstaller@1
displayName: 'Use .NET Core sdk for versioning'
inputs:
packageType: 'sdk'
version: '3.1.x'
- task: PowerShell@2
displayName: 'Set Version'
inputs:
targetType: 'inline'
script: |
dotnet tool install -g dotnet-setversion
setversion -r $(BuildVersion)
- task: DotNetCoreCLI@2
displayName: 'publish $(runtime)'
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-c $(BuildConfiguration) -r "$(runtime)" -o $(Build.ArtifactStagingDirectory)/$(runtime) --self-contained true -p:PublishSingleFile=true'
zipAfterPublish: false
modifyOutputPath: false
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: 'angular_dist'
targetPath: '$(Build.ArtifactStagingDirectory)/angular_dist'
- task: CopyFiles@2
displayName: 'Copy Angular App $(runtime)'
inputs:
SourceFolder: '$(Build.ArtifactStagingDirectory)/angular_dist'
Contents: '**'
TargetFolder: '$(Build.ArtifactStagingDirectory)/$(runtime)/ClientApp/dist'
- task: ArchiveFiles@2
displayName: 'Zip $(runtime)'
inputs:
rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/$(runtime)'
includeRootFolder: false
archiveType: $(compression)
archiveFile: '$(Build.ArtifactStagingDirectory)/$(runtime).$(format)'
replaceExistingArchive: true
- task: PublishPipelineArtifact@1
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/$(runtime).$(format)'
artifact: '$(runtime)'
publishLocation: 'pipeline'

@ -0,0 +1,30 @@
variables:
- name: "BuildConfiguration"
value: "Release"
- name: "vmImage"
value: "ubuntu-latest"
- name: "Solution"
value: "**/*.sln"
- name: "TestProject"
value: "**/*.Tests.csproj"
- name: "NetCoreVersion"
value: "5.0"
- name: "PublishLocation"
value: "$(Build.SourcesDirectory)/src/Ombi/bin/Release/netcoreapp$(NetCoreVersion)"
- name: "GitTag"
value: "v$(buildVersion)"
- name: "UiLocation"
value: "$(Build.SourcesDirectory)/src/Ombi/ClientApp/"
- name: "BuildVersion"
value: "4.0.$(Build.BuildId)"
- name: isMain
value: $[eq(variables['Build.SourceBranch'], 'refs/heads/feature/v4')]

@ -1,9 +1,6 @@
---
name: Bug report
name: "\U0001F41B Bug report"
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
@ -32,6 +29,7 @@ If applicable, a snippet of the logs that seems relevant to the bug if present.
**Ombi Version (please complete the following information):**
- Version [e.g. 3.0.1158]
- Media Server [e.g. Plex]
- Database Type: SQLite (Please change if using MySQL)
**Additional context**
Add any other context about the problem here.

@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Wiki
url: https://github.com/tidusjar/Ombi/wiki
about: The Ombi wiki should help guide you through installation and setup as well as help resolve common problems and answer frequently asked questions
- name: Reddit support
url: https://www.reddit.com/r/Ombi
about: Ask questions about Ombi
- name: Feature suggestions
url: https://ombifeatures.featureupvote.com
about: Share your suggestions or ideas to make Ombi better!

@ -1,18 +0,0 @@
name: ASP.NET Core CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 2.2.108
- name: Build Backend
run: ./build.sh --settings_skipverification=true

@ -1,9 +0,0 @@
workflow "New workflow" {
on = "push"
resolves = [".NET Core CLI"]
}
action ".NET Core CLI" {
uses = "baruchiro/github-actions@0.0.1"
args = "build src/Ombi.sln"
}

2
.gitignore vendored

@ -247,3 +247,5 @@ _Pvt_Extensions
# Ignore local vscode config
*.vscode
/src/Ombi/database.json
/src/Ombi/healthchecksdb
/src/Ombi/ClientApp/package-lock.json

@ -1,7 +0,0 @@
language: csharp
solution: src/Ombi.sln
install:
- mono Tools/nuget.exe restore Ombi.sln
- nuget install NUnit.Runners -OutputDirectory testrunner
script:
- xbuild /p:Configuration=Release Ombi.sln /p:TargetFrameworkVersion="v4.5"

@ -1,12 +0,0 @@

param([String]$env='local')
"Environment: " + $env | Write-Output;
"Build Version: " + $env:APPVEYOR_BUILD_VERSION | Write-Output;
"Base Path: " + $env:APPVEYOR_BUILD_FOLDER | Write-Output;
$appSettingsPath = $env:APPVEYOR_BUILD_FOLDER + '\src\Ombi\appsettings.json'
$appSettings = Get-Content $appSettingsPath -raw
$appSettings = $appSettings.Replace("{{VERSIONNUMBER}}",$env:APPVEYOR_BUILD_VERSION);
$appSettings = $appSettings.Replace("{{BRANCH}}",$env:APPVEYOR_REPO_BRANCH);
Set-Content -Path $appSettingsPath -Value $appSettings

@ -1,6 +1,77 @@
# Changelog
## v3.0.4817 (2019-10-15)
## (unreleased)
### **New Features**
- Update login.component.ts. [Jamie]
- Update CHANGELOG.md. [Jamie]
- Updated SlackNotification.cs. [Tim]
### **Fixes**
- Fixed the issue where we couldn't always pick up stuff on the sync. [tidusjar]
- Removed hangfire completly from Ombi. [tidusjar]
- Fixed the notifications issue. [tidusjar]
- Fixed the issues where the DB was being disposed too early. [tidusjar]
- Fixed an error with the newsletter with the new db structure. [tidusjar]
- Output some useful stuff to the about window regarding the databases. [tidusjar]
- Fixed the migration for combined databases. [tidusjar]
- Fixed the issue where exisitng databases would now fail due to the DB structure changes. [tidusjar]
- Finished it! [tidusjar]
- Got MySql working. [tidusjar]
- Got the new DB structure in place. [tidusjar]
- Fix for #3219. [tidusjar]
- Fixed the error in the newsletter. [tidusjar]
- Fixed #3208. [tidusjar]
- Use tags and autocomplete for excluded keywords. [Taylor Buchanan]
- Add comments to clarify filter decisions. [Taylor Buchanan]
- Fix TS import order. [Taylor Buchanan]
- Add adult movie filtering. [Taylor Buchanan]
- Fix search bar overlap on mobile. [Taylor Buchanan]
- New translations en.json (Slovak) [Jamie]
- New translations en.json (Slovak) [Jamie]
- New translations en.json (Slovak) [Jamie]
- New translations en.json (Slovak) [Jamie]
- New translations en.json (Slovak) [Jamie]
- New translations en.json (Slovak) [Jamie]
- New translations en.json (Slovak) [Jamie]
- Add SK lang. [Jamie Rees]
- Add the migration to the correct database... #3214. [tidusjar]
- Hopefully provide a fix now for #2998 Theory is that the refresh metadata was using stale data and then overriding the availbility that just happened on that media item. [tidusjar]
## v3.0.4817 (2019-10-12)
### **New Features**
@ -34,6 +105,8 @@
### **Fixes**
- Gitchangelog. [tidusjar]
- Fixed #3078. [tidusjar]
- Fixes issue #3195 The new string extension method ToHttpsUrl ensures that URLs starting with "https" are no longer turned into "httpss" The commit also replaces all occurances of the error prone .Replace("http", "https") in the whole solution. [msdeibel]

@ -9,8 +9,16 @@ ____
[![Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://patreon.com/tidusjar/Ombi)
[![Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://paypal.me/PlexRequestsNet)
# Feature Requests
Feature requests are handled on Feature Upvote.
Search the existing requests to see if your suggestion has already been submitted.
(If a similar request exists, please vote, or add additional comments to the request)
#### [![Feature Requests](https://cloud.githubusercontent.com/assets/390379/10127973/045b3a96-6560-11e5-9b20-31a2032956b2.png)](https://features.ombi.io)
___
[![Twitter](https://img.shields.io/twitter/follow/tidusjar.svg?style=social)](https://twitter.com/intent/follow?screen_name=tidusjar)
Follow me developing Ombi!
@ -33,10 +41,10 @@ We also now have merch up on Teespring!
___
| Service | Stable | Develop |
|----------|:---------------------------:|:----------------------------:|
| AppVeyor | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/master?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/develop?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop) |
| Download |[![Download](http://i.imgur.com/odToka3.png)](https://github.com/tidusjar/Ombi/releases) | [![Download](http://i.imgur.com/odToka3.png)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop/artifacts) |
| Service | Stable | Develop | V4 |
|----------|:---------------------------:|:----------------------------:|:----------------------------:|
| Build Status | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/master?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn/branch/develop?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop) | [![Build Status](https://dev.azure.com/tidusjar/Ombi/_apis/build/status/Ombi%20CI?branchName=feature%2Fv4)](https://dev.azure.com/tidusjar/Ombi/_build/latest?definitionId=18&branchName=feature%2Fv4)
| Download |[![Download](http://i.imgur.com/odToka3.png)](https://github.com/tidusjar/Ombi/releases) | [![Download](http://i.imgur.com/odToka3.png)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop/artifacts) | [![Download](http://i.imgur.com/odToka3.png)](https://github.com/tidusjar/ombi.releases/releases) |
# Features
Here are some of the features Ombi V3 has:
* Now working without crashes on Linux.
@ -57,8 +65,9 @@ Here are some of the features Ombi V3 has:
We integrate with the following applications:
* Plex Media Server
* Emby
* Sonarr
* Radarr
* Jellyfin
* Sonarr V2 and V3
* Radarr V2
* Lidarr
* DogNzb
* Couch Potato
@ -66,47 +75,45 @@ We integrate with the following applications:
### Notifications
Supported notifications:
* Mobile
* SMTP Notifications (Email)
* Discord
* Gotify
* Slack
* Pushbullet
* Pushover
* Mattermost
* Telegram
* Gotify
* Twilio
* Webhook
### The difference between Version 3 and 2
### The difference between Version 4 and 3
Over the last year, we focused on the main functions on Ombi, a complete rewrite while making it better, faster and more stable.
We have already done most of the work, but some features are still be missing in this first version.
We are planning to bring back these features in V3 but for now you can find a list below with a quick comparison of features between v2 and v3.
We are planning to bring back these features in V3 but for now you can find a list below with a quick comparison of features between v4 and v3.
| Service | Version 3 | Version 2 |
| Service | Version 4 (Beta) | Version 3 (Stable)|
|----------|:----------:|:----------:|
| Multiple Plex/Emby Servers| Yes | No |
| Emby & Plex support | Yes | Yes |
| Mono dependency | No | Yes |
| Notifications support | Yes| Yes |
| Landing page | Yes (brand new) | Yes |
| Multiple Plex/Emby/Jellyfin Servers | Yes | Yes |
| Emby/Jellyfin & Plex support | Yes | Yes |
| Mono dependency | No | No |
| Plex OAuth support | Yes | Yes |
| Login page | Yes (brand new) | Yes |
| Custom Notification Messages | Yes | No |
| Discovery page | Yes (brand new) | No |
| Request a movie collection | Yes (brand new) | No |
| Auto Delete Available Requests | Yes (brand new) | No |
| Report issues | Yes | Yes |
| Notifications support | Yes | Yes |
| Custom Notification Messages | Yes | Yes |
| Sending newsletters | Yes | Yes |
| Send a Mass Email | Yes | Yes |
| SickRage | Yes | Yes |
| CouchPotato | Yes | Yes |
| DogNzb | Yes | No |
| Issues | Yes | Yes |
| DogNzb | Yes | Yes |
| Headphones | No | Yes |
| Lidarr | Yes | No |
# Feature Requests
Feature requests are handled on FeatHub.
Search the existing requests to see if your suggestion has already been submitted.
(If a similar request exists, give it a thumbs up (+1), or add additional comments to the request)
#### [![Feature Requests](https://cloud.githubusercontent.com/assets/390379/10127973/045b3a96-6560-11e5-9b20-31a2032956b2.png)](http://feathub.com/tidusjar/Ombi)
| Lidarr | Yes | Yes |
# Preview

@ -1,72 +0,0 @@
version: 4.0.{build}
configuration: Release
os: Visual Studio 2019
environment:
nodejs_version: "11.5.0"
typescript_version: "3.0.1"
github_auth_token:
secure: H/7uCrjmWHGJxgN3l9fbhhdVjvvWI8VVF4ZzQqeXuJwAf+PgSNBdxv4SS+rMQ+RH
# Do not build on tags (GitHub and BitBucket)
skip_tags: true
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
- cmd: set path=%programfiles(x86)%\\Microsoft SDKs\TypeScript\3.6;%path%
- cmd: tsc -v
build_script:
- ps: |
$deployBranches =
"feature/v4",
"develop",
"master";
If(($env:APPVEYOR_REPO_BRANCH -in $deployBranches -Or $env:APPVEYOR_REPO_COMMIT_MESSAGE -Match '!deploy') -And $env:APPVEYOR_REPO_COMMIT_MESSAGE -NotMatch '!build') {
Write-Output "This is a deployment build"
$env:Deploy = 'true'
./build.ps1
}
Else
{
$env:Deploy = 'false'
Write-Output "This is a not a deployment build"
./build.ps1 --target=build
}
skip_commits:
files:
- '**/*.md'
after_build:
- ps: |
$deployBranches =
"feature/v4",
"develop",
"master";
If(($env:APPVEYOR_REPO_BRANCH -in $deployBranches -Or $env:APPVEYOR_REPO_COMMIT_MESSAGE -Match '!deploy') -And $env:APPVEYOR_REPO_COMMIT_MESSAGE -NotMatch '!build')
{
Write-Output "Deploying!"
Get-ChildItem -Recurse .\*.zip | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
Get-ChildItem -Recurse .\*.gz | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
}
Else
{
Write-Output "No Deployment"
}
#cache:
#- '%USERPROFILE%\.nuget\packages'
deploy:
- provider: GitHub
release: Ombi v$(appveyor_build_version)
auth_token:
secure: jDpp1/WUQl3uN41fNI3VeZoRZbDiDfs3GPQ1v+C5ZNE3cWdnUvuJfCCfUbYUV1Rp
draft: true
on:
branch: master

@ -1,83 +0,0 @@
# ASP.NET Core
# Build and test ASP.NET Core projects targeting .NET Core.
# Add steps that run tests, create a NuGet package, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core
trigger:
- master
- feature/v4
- develop
variables:
solution: '**/*.sln'
testProj: '**/*.Tests.csproj'
csProj: '**/*.csproj'
buildConfiguration: 'Release'
publishLocation: '$(Build.SourcesDirectory)/src/Ombi/bin/Release/netcoreapp2.2'
pool:
vmImage: 'ubuntu-latest'
steps:
- task: CmdLine@2
displayName: Run Build Script
inputs:
script: './build.sh'
- task: CmdLine@2
inputs:
script: |
cd src/Ombi/bin/Release/netcoreapp2.2
ls
workingDirectory: '$(Build.SourcesDirectory)'
- task: CopyFiles@2
displayName: Upload Windows Build
inputs:
SourceFolder: '$(publishLocation)/windows.zip'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
OverWrite: true
- task: CopyFiles@2
displayName: Upload OSX Build
inputs:
SourceFolder: '**/osx.tar.gz'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
OverWrite: true
- task: CopyFiles@2
displayName: Upload Linux Build
inputs:
SourceFolder: '$(publishLocation)/linux.tar.gz'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
OverWrite: true
- task: CopyFiles@2
displayName: Upload Linux-ARM Build
inputs:
SourceFolder: '$(publishLocation)/linux-arm.tar.gz'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
OverWrite: true
- task: CopyFiles@2
displayName: Upload Windows 32Bit Build
inputs:
SourceFolder: '$(publishLocation)/windows-32bit.zip'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
OverWrite: true
- task: CopyFiles@2
displayName: Upload Linux-ARM64 Build
inputs:
SourceFolder: '$(publishLocation)/linux-arm64.tar.gz'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
OverWrite: true
- task: PublishTestResults@2
displayName: Upload Test Results
inputs:
testResultsFormat: 'VSTest'
testResultsFiles: '**/Test.trx'
mergeTestResults: true
failTaskOnFailedTests: true
testRunTitle: 'Unit Tests'

@ -1,306 +0,0 @@
#tool "nuget:?package=GitVersion.CommandLine&version=5.0.1"
#addin nuget:?package=SharpZipLib&version=1.2.0
#addin nuget:?package=Cake.Compression&version=0.2.3
#addin "Cake.Incubator&version=5.1.0"
#addin nuget:?package=Cake.Yarn&version=0.4.6
//////////////////////////////////////////////////////////////////////
// ARGUMENTS
//////////////////////////////////////////////////////////////////////
var target = Argument("target", "Default");
var configuration = Argument("configuration", "Release");
//////////////////////////////////////////////////////////////////////
// PREPARATION
//////////////////////////////////////////////////////////////////////
var buildDir = "./src/Ombi/bin/" + configuration;
var nodeModulesDir ="./src/Ombi/ClientApp/node_modules/";
var wwwRootDistDir = "./src/Ombi/wwwroot/dist/";
var projDir = "./src/"; // Project Directory
var webProjDir = "./src/Ombi";
var uiProjectDir = "./src/Ombi/ClientApp";
var csProj = "./src/Ombi/Ombi.csproj"; // Path to the project.csproj
var solutionFile = "Ombi.sln"; // Solution file if needed
GitVersion versionInfo = null;
var frameworkVer = "netcoreapp3.0";
var buildSettings = new DotNetCoreBuildSettings
{
Framework = frameworkVer,
Configuration = "Release",
OutputDirectory = Directory(buildDir),
};
var publishSettings = new DotNetCorePublishSettings
{
Framework = frameworkVer,
Configuration = "Release",
OutputDirectory = Directory(buildDir),
};
var artifactsFolder = buildDir + "/"+frameworkVer+"/";
var windowsArtifactsFolder = artifactsFolder + "win10-x64/published";
var windows32BitArtifactsFolder = artifactsFolder + "win10-x86/published";
var osxArtifactsFolder = artifactsFolder + "osx-x64/published";
var linuxArtifactsFolder = artifactsFolder + "linux-x64/published";
var linuxArmArtifactsFolder = artifactsFolder + "linux-arm/published";
var linuxArm64BitArtifactsFolder = artifactsFolder + "linux-arm64/published";
//////////////////////////////////////////////////////////////////////
// TASKS
//////////////////////////////////////////////////////////////////////
Task("Clean")
.Does(() =>
{
CleanDirectory(buildDir);
//CleanDirectory(nodeModulesDir);
CleanDirectory(wwwRootDistDir);
});
Task("SetVersionInfo")
.IsDependentOn("Clean")
.Does(() =>
{
var settings = new GitVersionSettings {
RepositoryPath = ".",
};
if (AppVeyor.IsRunningOnAppVeyor) {
settings.Branch = AppVeyor.Environment.Repository.Branch;
} else {
settings.Branch = "master";
}
versionInfo = GitVersion(settings);
// Information("GitResults -> {0}", versionInfo.Dump());
//Information(@"Build:{0}",AppVeyor.Environment.Build.Dump());
var buildVersion = string.Empty;
if(string.IsNullOrEmpty(AppVeyor.Environment.Build.Version))
{
buildVersion = "3.0.000";
} else{
buildVersion = AppVeyor.Environment.Build.Version;
}
if(versionInfo.BranchName.Contains("_"))
{
versionInfo.BranchName = versionInfo.BranchName.Replace("_","-");
}
var fullVer = buildVersion + "-" + versionInfo.BranchName;
if(versionInfo.PreReleaseTag.Contains("PullRequest"))
{
fullVer = buildVersion + "-PR";
}
if(fullVer.Contains("_"))
{
fullVer = fullVer.Replace("_","");
}
if(fullVer.Contains("/"))
{
fullVer = fullVer.Replace("/","");
}
buildSettings.ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.AssemblySemVer);
buildSettings.ArgumentCustomization = args => args.Append("/p:FullVer=" + fullVer);
publishSettings.ArgumentCustomization = args => args.Append("/p:SemVer=" + versionInfo.AssemblySemVer);
publishSettings.ArgumentCustomization = args => args.Append("/p:FullVer=" + fullVer);
//buildSettings.VersionSuffix = versionInfo.BranchName;
//publishSettings.VersionSuffix = versionInfo.BranchName;
});
Task("NPM")
.Does(() => {
Yarn.FromPath(uiProjectDir).Install();
});
Task("Gulp Publish")
.IsDependentOn("NPM")
.Does(() => {
Yarn.FromPath(uiProjectDir).RunScript("build");
});
Task("TSLint")
.Does(() =>
{
//Yarn.FromPath(uiProjectDir).RunScript("lint");
});
Task("PrePublish")
.IsDependentOn("SetVersionInfo");
//.IsDependentOn("Gulp Publish") // these are done in the main csproj
//.IsDependentOn("TSLint");
Task("Package")
.Does(() =>
{
Zip(windowsArtifactsFolder +"/",artifactsFolder + "windows.zip");
Zip(windows32BitArtifactsFolder +"/",artifactsFolder + "windows-32bit.zip");
GZipCompress(osxArtifactsFolder, artifactsFolder + "osx.tar.gz");
GZipCompress(linuxArtifactsFolder, artifactsFolder + "linux.tar.gz");
GZipCompress(linuxArmArtifactsFolder, artifactsFolder + "linux-arm.tar.gz");
GZipCompress(linuxArm64BitArtifactsFolder, artifactsFolder + "linux-arm64.tar.gz");
});
Task("Publish")
.IsDependentOn("Upload-Test-Results")
.IsDependentOn("PrePublish")
.IsDependentOn("Publish-Windows")
.IsDependentOn("Publish-Windows-32bit")
.IsDependentOn("Publish-OSX")
.IsDependentOn("Publish-Linux")
.IsDependentOn("Publish-Linux-ARM")
.IsDependentOn("Publish-Linux-ARM-64Bit")
.IsDependentOn("Package");
Task("Publish-Windows")
.Does(() =>
{
publishSettings.Runtime = "win10-x64";
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/win10-x64/published");
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
CopyFile(buildDir + "/"+frameworkVer+"/win10-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/win10-x64/published/Swagger.xml");
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/win10-x64/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
});
Task("Publish-Windows-32bit")
.Does(() =>
{
publishSettings.Runtime = "win10-x86";
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/win10-x86/published");
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
CopyFile(buildDir + "/"+frameworkVer+"/win10-x86/Swagger.xml", buildDir + "/"+frameworkVer+"/win10-x86/published/Swagger.xml");
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/win10-x86/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
});
Task("Publish-OSX")
.Does(() =>
{
publishSettings.Runtime = "osx-x64";
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/osx-x64/published");
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
CopyFile(buildDir + "/"+frameworkVer+"/osx-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/osx-x64/published/Swagger.xml");
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/osx-x64/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
});
Task("Publish-Linux")
.Does(() =>
{
publishSettings.Runtime = "linux-x64";
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/linux-x64/published");
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
CopyFile(buildDir + "/"+frameworkVer+"/linux-x64/Swagger.xml", buildDir + "/"+frameworkVer+"/linux-x64/published/Swagger.xml");
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-x64/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
});
Task("Publish-Linux-ARM")
.Does(() =>
{
publishSettings.Runtime = "linux-arm";
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/linux-arm/published");
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
CopyFile(
buildDir + "/"+frameworkVer+"/linux-arm/Swagger.xml",
buildDir + "/"+frameworkVer+"/linux-arm/published/Swagger.xml");
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-arm/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
});
Task("Publish-Linux-ARM-64Bit")
.Does(() =>
{
publishSettings.Runtime = "linux-arm64";
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer+"/linux-arm64/published");
DotNetCorePublish("./src/Ombi/Ombi.csproj", publishSettings);
CopyFile(
buildDir + "/"+frameworkVer+"/linux-arm64/Swagger.xml",
buildDir + "/"+frameworkVer+"/linux-arm64/published/Swagger.xml");
publishSettings.OutputDirectory = Directory(buildDir) + Directory(frameworkVer +"/linux-arm64/published/updater");
DotNetCorePublish("./src/Ombi.Updater/Ombi.Updater.csproj", publishSettings);
});
Task("Run-Unit-Tests")
.Does(() =>
{
var settings = new DotNetCoreTestSettings
{
ArgumentCustomization = args => args.Append("--logger \"trx;LogFileName=Test.trx\""),
Configuration = "Release"
};
var projectFiles = GetFiles("./**/*Tests.csproj");
foreach(var file in projectFiles)
{
DotNetCoreTest(file.FullPath, settings);
}
});
Task("Upload-Test-Results")
.IsDependentOn("Run-Unit-Tests")
.ContinueOnError()
.Does(() => {
});
Task("Run-Server-Build")
.Does(() =>
{
var settings = new DotNetCoreBuildSettings
{
Framework = frameworkVer,
Configuration = "Release",
OutputDirectory = Directory(buildDir)
};
DotNetCoreBuild(csProj, settings);
});
Task("Run-UI-Build")
.IsDependentOn("PrePublish");
//////////////////////////////////////////////////////////////////////
// TASK TARGETS
//////////////////////////////////////////////////////////////////////
Task("Default")
.IsDependentOn("Publish");
Task("Build")
.IsDependentOn("SetVersionInfo")
.IsDependentOn("Upload-Test-Results")
.IsDependentOn("Run-Server-Build");
// .IsDependentOn("Run-UI-Build");
//////////////////////////////////////////////////////////////////////
// EXECUTION
//////////////////////////////////////////////////////////////////////
RunTarget(target);

@ -1,242 +0,0 @@
##########################################################################
# This is the Cake bootstrapper script for PowerShell.
# This file was downloaded from https://github.com/cake-build/resources
# Feel free to change this file to fit your needs.
##########################################################################
<#
.SYNOPSIS
This is a Powershell script to bootstrap a Cake build.
.DESCRIPTION
This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
and execute your Cake build script with the parameters you provide.
.PARAMETER Script
The build script to execute.
.PARAMETER Target
The build script target to run.
.PARAMETER Configuration
The build configuration to use.
.PARAMETER Verbosity
Specifies the amount of information to be displayed.
.PARAMETER ShowDescription
Shows description about tasks.
.PARAMETER DryRun
Performs a dry run.
.PARAMETER SkipToolPackageRestore
Skips restoring of packages.
.PARAMETER ScriptArgs
Remaining arguments are added here.
.LINK
https://cakebuild.net
#>
[CmdletBinding()]
Param(
[string]$Script = "build.cake",
[string]$Target,
[string]$Configuration,
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
[string]$Verbosity,
[switch]$ShowDescription,
[Alias("WhatIf", "Noop")]
[switch]$DryRun,
[switch]$SkipToolPackageRestore,
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[string[]]$ScriptArgs
)
# Attempt to set highest encryption available for SecurityProtocol.
# PowerShell will not set this by default (until maybe .NET 4.6.x). This
# will typically produce a message for PowerShell v2 (just an info
# message though)
try {
# Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48)
# Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't
# exist in .NET 4.0, even though they are addressable if .NET 4.5+ is
# installed (.NET 4.5 is an in-place upgrade).
[System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48
} catch {
Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3'
}
[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
function MD5HashFile([string] $filePath)
{
if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf))
{
return $null
}
[System.IO.Stream] $file = $null;
[System.Security.Cryptography.MD5] $md5 = $null;
try
{
$md5 = [System.Security.Cryptography.MD5]::Create()
$file = [System.IO.File]::OpenRead($filePath)
return [System.BitConverter]::ToString($md5.ComputeHash($file))
}
finally
{
if ($file -ne $null)
{
$file.Dispose()
}
}
}
function GetProxyEnabledWebClient
{
$wc = New-Object System.Net.WebClient
$proxy = [System.Net.WebRequest]::GetSystemWebProxy()
$proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
$wc.Proxy = $proxy
return $wc
}
Write-Host "Preparing to run build script..."
if(!$PSScriptRoot){
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
}
$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins"
$MODULES_DIR = Join-Path $TOOLS_DIR "Modules"
$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config"
$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config"
# Make sure tools folder exists
if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
Write-Verbose -Message "Creating tools directory..."
New-Item -Path $TOOLS_DIR -Type directory | out-null
}
# Make sure that packages.config exist.
if (!(Test-Path $PACKAGES_CONFIG)) {
Write-Verbose -Message "Downloading packages.config..."
try {
$wc = GetProxyEnabledWebClient
$wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG)
} catch {
Throw "Could not download packages.config."
}
}
# Try find NuGet.exe in path if not exists
if (!(Test-Path $NUGET_EXE)) {
Write-Verbose -Message "Trying to find nuget.exe in PATH..."
$existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) }
$NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1
if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)."
$NUGET_EXE = $NUGET_EXE_IN_PATH.FullName
}
}
# Try download NuGet.exe if not exists
if (!(Test-Path $NUGET_EXE)) {
Write-Verbose -Message "Downloading NuGet.exe..."
try {
$wc = GetProxyEnabledWebClient
$wc.DownloadFile($NUGET_URL, $NUGET_EXE)
} catch {
Throw "Could not download NuGet.exe."
}
}
# Save nuget.exe path to environment to be available to child processed
$ENV:NUGET_EXE = $NUGET_EXE
# Restore tools from NuGet?
if(-Not $SkipToolPackageRestore.IsPresent) {
Push-Location
Set-Location $TOOLS_DIR
# Check for changes in packages.config and remove installed tools if true.
[string] $md5Hash = MD5HashFile($PACKAGES_CONFIG)
if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
Write-Verbose -Message "Missing or changed package.config hash..."
Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery |
Remove-Item -Recurse
}
Write-Verbose -Message "Restoring tools from NuGet..."
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
if ($LASTEXITCODE -ne 0) {
Throw "An error occurred while restoring NuGet tools."
}
else
{
$md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
}
Write-Verbose -Message ($NuGetOutput | out-string)
Pop-Location
}
# Restore addins from NuGet
if (Test-Path $ADDINS_PACKAGES_CONFIG) {
Push-Location
Set-Location $ADDINS_DIR
Write-Verbose -Message "Restoring addins from NuGet..."
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`""
if ($LASTEXITCODE -ne 0) {
Throw "An error occurred while restoring NuGet addins."
}
Write-Verbose -Message ($NuGetOutput | out-string)
Pop-Location
}
# Restore modules from NuGet
if (Test-Path $MODULES_PACKAGES_CONFIG) {
Push-Location
Set-Location $MODULES_DIR
Write-Verbose -Message "Restoring modules from NuGet..."
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`""
if ($LASTEXITCODE -ne 0) {
Throw "An error occurred while restoring NuGet modules."
}
Write-Verbose -Message ($NuGetOutput | out-string)
Pop-Location
}
# Make sure that Cake has been installed.
if (!(Test-Path $CAKE_EXE)) {
Throw "Could not find Cake.exe at $CAKE_EXE"
}
# Build Cake arguments
$cakeArguments = @("$Script");
if ($Target) { $cakeArguments += "-target=$Target" }
if ($Configuration) { $cakeArguments += "-configuration=$Configuration" }
if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" }
if ($ShowDescription) { $cakeArguments += "-showdescription" }
if ($DryRun) { $cakeArguments += "-dryrun" }
$cakeArguments += $ScriptArgs
# Start Cake
Write-Host "Running build script..."
&$CAKE_EXE $cakeArguments
exit $LASTEXITCODE

@ -1,101 +0,0 @@
#!/usr/bin/env bash
##########################################################################
# This is the Cake bootstrapper script for Linux and OS X.
# This file was downloaded from https://github.com/cake-build/resources
# Feel free to change this file to fit your needs.
##########################################################################
# Define directories.
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
TOOLS_DIR=$SCRIPT_DIR/tools
NUGET_EXE=$TOOLS_DIR/nuget.exe
CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe
PACKAGES_CONFIG=$TOOLS_DIR/packages.config
PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum
# Define md5sum or md5 depending on Linux/OSX
MD5_EXE=
if [[ "$(uname -s)" == "Darwin" ]]; then
MD5_EXE="md5 -r"
else
MD5_EXE="md5sum"
fi
# Define default arguments.
SCRIPT="build.cake"
TARGET="Default"
CONFIGURATION="Release"
VERBOSITY="verbose"
DRYRUN=
SHOW_VERSION=false
SCRIPT_ARGUMENTS=()
# Parse arguments.
for i in "$@"; do
case $1 in
-s|--script) SCRIPT="$2"; shift ;;
-t|--target) TARGET="$2"; shift ;;
-c|--configuration) CONFIGURATION="$2"; shift ;;
-v|--verbosity) VERBOSITY="$2"; shift ;;
-d|--dryrun) DRYRUN="-dryrun" ;;
--version) SHOW_VERSION=true ;;
--) shift; SCRIPT_ARGUMENTS+=("$@"); break ;;
*) SCRIPT_ARGUMENTS+=("$1") ;;
esac
shift
done
# Make sure the tools folder exist.
if [ ! -d "$TOOLS_DIR" ]; then
mkdir "$TOOLS_DIR"
fi
# Make sure that packages.config exist.
if [ ! -f "$TOOLS_DIR/packages.config" ]; then
echo "Downloading packages.config..."
curl -Lsfo "$TOOLS_DIR/packages.config" https://cakebuild.net/download/bootstrapper/packages
if [ $? -ne 0 ]; then
echo "An error occurred while downloading packages.config."
exit 1
fi
fi
# Download NuGet if it does not exist.
if [ ! -f "$NUGET_EXE" ]; then
echo "Downloading NuGet..."
curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
if [ $? -ne 0 ]; then
echo "An error occurred while downloading nuget.exe."
exit 1
fi
fi
# Restore tools from NuGet.
pushd "$TOOLS_DIR" >/dev/null
if [ ! -f $PACKAGES_CONFIG_MD5 ] || [ "$( cat $PACKAGES_CONFIG_MD5 | sed 's/\r$//' )" != "$( $MD5_EXE $PACKAGES_CONFIG | awk '{ print $1 }' )" ]; then
find . -type d ! -name . | xargs rm -rf
fi
mono "$NUGET_EXE" install -ExcludeVersion
if [ $? -ne 0 ]; then
echo "Could not restore NuGet packages."
exit 1
fi
$MD5_EXE $PACKAGES_CONFIG | awk '{ print $1 }' >| $PACKAGES_CONFIG_MD5
popd >/dev/null
# Make sure that Cake has been installed.
if [ ! -f "$CAKE_EXE" ]; then
echo "Could not find Cake.exe at '$CAKE_EXE'."
exit 1
fi
# Start Cake
if $SHOW_VERSION; then
exec mono "$CAKE_EXE" -version
else
exec mono "$CAKE_EXE" $SCRIPT -verbosity=$VERBOSITY -configuration=$CONFIGURATION -target=$TARGET $DRYRUN "${SCRIPT_ARGUMENTS[@]}"
fi

@ -1,38 +0,0 @@
# ASP.NET Core
# Build and test ASP.NET Core projects targeting .NET Core.
# Add steps that run tests, create a NuGet package, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core
trigger:
branches:
include:
- feature/*
exclude:
- feature/v4
variables:
solution: '**/*.sln'
testProj: '**/*.Tests.csproj'
csProj: '**/*.csproj'
buildConfiguration: 'Release'
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
displayName: Use dotnet sdk
inputs:
packageType: 'sdk'
version: '2.2.401'
- task: DotNetCoreCLI@2
displayName: Run Unit Tests
inputs:
command: 'test'
projects: '$(testProj)'
- task: Yarn@3
displayName: Build UI
inputs:
projectDirectory: '$(Build.SourcesDirectory)/src/Ombi/ClientApp/'
arguments: 'run build'

@ -0,0 +1,51 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Ombi.Helpers;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
namespace Ombi.Api.CloudService
{
public interface ICloudMobileNotification
{
Task<bool> SendMessage(MobileNotificationRequest notification);
}
public class CloudMobileNotification : ICloudMobileNotification
{
private readonly IApi _api;
private readonly ILogger _logger;
private readonly string _baseUrl;
public CloudMobileNotification(IApi api, ILogger<CloudMobileNotification> logger, IOptions<ApplicationSettings> settings)
{
_api = api;
_baseUrl = settings.Value.NotificationService;
_logger = logger;
}
public async Task<bool> SendMessage(MobileNotificationRequest notification)
{
var request = new Request("MobileNotification", _baseUrl, HttpMethod.Post);
request.AddJsonBody(notification);
var response = await _api.Request(request);
if (!response.IsSuccessStatusCode)
{
_logger.LogError($"Error when sending mobile notification message, status code: {response.StatusCode}. Please raise an issue on Github, might be a problem with" +
$" the notification service!");
return false;
}
return true;
}
}
public class MobileNotificationRequest
{
public string Title { get; set; }
public string Body { get; set; }
public string To { get; set; }
public Dictionary<string, string> Data { get; set; }
}
}

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
</ItemGroup>
</Project>

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,5 +1,6 @@
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Ombi.Api.Discord.Models;
namespace Ombi.Api.Discord
@ -17,7 +18,7 @@ namespace Ombi.Api.Discord
public async Task SendMessage(DiscordWebhookBody body, string webhookId, string webhookToken)
{
var request = new Request($"webhooks/{webhookId}/{webhookToken}", BaseUrl, HttpMethod.Post);
request.AddJsonBody(body);
request.ApplicationJsonContentType();

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace Ombi.Api.Discord.Models
{
@ -13,8 +14,32 @@ namespace Ombi.Api.Discord.Models
{
public string title { get; set; }
public string type => "rich"; // Always rich or embedded content
public string description { get; set; } // Don't really need to set this
public DiscordImage image { get; set; }
public string description { get; set; }
public DateTime timestamp => DateTime.Now;
public string color { get; set; }
public DiscordFooter footer { get; set; }
public DiscordImage thumbnail { get; set; }
public DiscordAuthor author { get; set; }
public List<DiscordField> fields { get; set; }
}
public class DiscordFooter
{
public string text { get; set; }
}
public class DiscordAuthor
{
public string name { get; set; }
public string url { get; set; }
public string iconurl { get; set; }
}
public class DiscordField
{
public string name { get; set; }
public string value { get; set; }
public bool inline { get; set; }
}
public class DiscordImage

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -0,0 +1,41 @@
using Ombi.Api;
using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External;
using System.Threading.Tasks;
namespace Ombi.Api.Emby
{
public class EmbyApiFactory : IEmbyApiFactory
{
private readonly ISettingsService<EmbySettings> _embySettings;
private readonly IApi _api;
// TODO, if we need to derive futher, need to rework
public EmbyApiFactory(ISettingsService<EmbySettings> embySettings, IApi api)
{
_embySettings = embySettings;
_api = api;
}
public async Task<IEmbyApi> CreateClient()
{
var settings = await _embySettings.GetSettingsAsync();
return CreateClient(settings);
}
public IEmbyApi CreateClient(EmbySettings settings)
{
if (settings.IsJellyfin)
{
return new JellyfinApi(_api);
}
return new EmbyApi(_api);
}
}
public interface IEmbyApiFactory
{
Task<IEmbyApi> CreateClient();
IEmbyApi CreateClient(EmbySettings settings);
}
}

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Api.Emby.Models;
using Ombi.Api.Emby.Models.Media.Tv;
using Ombi.Api.Emby.Models.Movie;
namespace Ombi.Api.Emby
{
public interface IBaseEmbyApi
{
Task<EmbySystemInfo> GetSystemInformation(string apiKey, string baseUrl);
Task<List<EmbyUser>> GetUsers(string baseUri, string apiKey);
Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri);
Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbyMovie>> GetCollection(string mediaId,
string apiKey, string userId, string baseUrl);
Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<MovieInformation> GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<EpisodeInformation> GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<PublicInfo> GetPublicInformation(string baseUrl);
}
}

@ -1,34 +1,10 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Ombi.Api.Emby.Models;
using Ombi.Api.Emby.Models.Media.Tv;
using Ombi.Api.Emby.Models.Movie;
namespace Ombi.Api.Emby
{
public interface IEmbyApi
{
Task<EmbySystemInfo> GetSystemInformation(string apiKey, string baseUrl);
Task<List<EmbyUser>> GetUsers(string baseUri, string apiKey);
Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri);
public interface IEmbyApi : IBaseEmbyApi
{
Task<EmbyConnectUser> LoginConnectUser(string username, string password);
Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, int startIndex, int count, string userId,
string baseUri);
Task<EmbyItemContainer<EmbyMovie>> GetCollection(string mediaId,
string apiKey, string userId, string baseUrl);
Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<MovieInformation> GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<EpisodeInformation> GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<PublicInfo> GetPublicInformation(string baseUrl);
}
}

@ -0,0 +1,180 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Internal;
using Newtonsoft.Json;
using Ombi.Api.Emby.Models;
using Ombi.Api.Emby.Models.Media.Tv;
using Ombi.Api.Emby.Models.Movie;
using Ombi.Helpers;
namespace Ombi.Api.Emby
{
public class JellyfinApi : IEmbyApi
{
public JellyfinApi(IApi api)
{
Api = api;
}
private IApi Api { get; }
/// <summary>
/// Returns all users from the Emby Instance
/// </summary>
/// <param name="baseUri"></param>
/// <param name="apiKey"></param>
public async Task<List<EmbyUser>> GetUsers(string baseUri, string apiKey)
{
var request = new Request("users", baseUri, HttpMethod.Get);
AddHeaders(request, apiKey);
var obj = await Api.Request<List<EmbyUser>>(request);
return obj;
}
public async Task<EmbySystemInfo> GetSystemInformation(string apiKey, string baseUrl)
{
var request = new Request("System/Info", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
var obj = await Api.Request<EmbySystemInfo>(request);
return obj;
}
public async Task<PublicInfo> GetPublicInformation(string baseUrl)
{
var request = new Request("System/Info/public", baseUrl, HttpMethod.Get);
AddHeaders(request, string.Empty);
var obj = await Api.Request<PublicInfo>(request);
return obj;
}
public async Task<EmbyUser> LogIn(string username, string password, string apiKey, string baseUri)
{
var request = new Request("users/authenticatebyname", baseUri, HttpMethod.Post);
var body = new
{
username,
pw = password,
};
request.AddJsonBody(body);
request.AddHeader("X-Emby-Authorization",
$"MediaBrowser Client=\"Ombi\", Device=\"Ombi\", DeviceId=\"v3\", Version=\"v3\"");
AddHeaders(request, apiKey);
var obj = await Api.Request<EmbyUser>(request);
return obj;
}
public async Task<EmbyItemContainer<EmbyMovie>> GetCollection(string mediaId, string apiKey, string userId, string baseUrl)
{
var request = new Request($"users/{userId}/items?parentId={mediaId}", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
request.AddQueryString("Fields", "ProviderIds,Overview");
request.AddQueryString("IsVirtualItem", "False");
return await Api.Request<EmbyItemContainer<EmbyMovie>>(request);
}
public async Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, int startIndex, int count, string userId, string baseUri)
{
return await GetAll<EmbyMovie>("Movie", apiKey, userId, baseUri, true, startIndex, count);
}
public async Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, string baseUri)
{
return await GetAll<EmbyEpisodes>("Episode", apiKey, userId, baseUri, false, startIndex, count);
}
public async Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, int startIndex, int count, string userId, string baseUri)
{
return await GetAll<EmbySeries>("Series", apiKey, userId, baseUri, false, startIndex, count);
}
public async Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl)
{
return await GetInformation<SeriesInformation>(mediaId, apiKey, userId, baseUrl);
}
public async Task<MovieInformation> GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl)
{
return await GetInformation<MovieInformation>(mediaId, apiKey, userId, baseUrl);
}
public async Task<EpisodeInformation> GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl)
{
return await GetInformation<EpisodeInformation>(mediaId, apiKey, userId, baseUrl);
}
private async Task<T> GetInformation<T>(string mediaId, string apiKey, string userId, string baseUrl)
{
var request = new Request($"users/{userId}/items/{mediaId}", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
var response = await Api.RequestContent(request);
return JsonConvert.DeserializeObject<T>(response);
}
private async Task<EmbyItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview = false)
{
var request = new Request($"users/{userId}/items", baseUri, HttpMethod.Get);
request.AddQueryString("Recursive", true.ToString());
request.AddQueryString("IncludeItemTypes", type);
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds");
request.AddQueryString("IsVirtualItem", "False");
AddHeaders(request, apiKey);
var obj = await Api.Request<EmbyItemContainer<T>>(request);
return obj;
}
private async Task<EmbyItemContainer<T>> GetAll<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview, int startIndex, int count)
{
var request = new Request($"users/{userId}/items", baseUri, HttpMethod.Get);
request.AddQueryString("Recursive", true.ToString());
request.AddQueryString("IncludeItemTypes", type);
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds");
request.AddQueryString("startIndex", startIndex.ToString());
request.AddQueryString("limit", count.ToString());
request.AddQueryString("IsVirtualItem", "False");
AddHeaders(request, apiKey);
var obj = await Api.Request<EmbyItemContainer<T>>(request);
return obj;
}
private static void AddHeaders(Request req, string apiKey)
{
if (!string.IsNullOrEmpty(apiKey))
{
req.AddHeader("X-MediaBrowser-Token", apiKey);
}
req.AddHeader("Accept", "application/json");
req.AddContentHeader("Content-Type", "application/json");
req.AddHeader("Device", "Ombi");
}
public Task<EmbyConnectUser> LoginConnectUser(string username, string password)
{
throw new System.NotImplementedException();
}
}
}

@ -34,7 +34,6 @@ namespace Ombi.Api.Emby.Models
public string Name { get; set; }
public string ServerId { get; set; }
public string ConnectUserName { get; set; }
public string ConnectUserId { get; set; }
public string ConnectLinkType { get; set; }
public string Id { get; set; }
public bool HasPassword { get; set; }

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Ombi.Api.Lidarr.Models;
@ -11,7 +12,7 @@ namespace Ombi.Api.Lidarr
Task<List<LidarrProfile>> GetProfiles(string apiKey, string baseUrl);
Task<List<LidarrRootFolder>> GetRootFolders(string apiKey, string baseUrl);
Task<ArtistResult> GetArtist(int artistId, string apiKey, string baseUrl);
Task<ArtistResult> GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl);
Task<ArtistResult> GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl, CancellationToken token = default);
Task<AlbumByArtistResponse> GetAlbumsByArtist(string foreignArtistId);
Task<AlbumLookup> GetAlbumByForeignId(string foreignArtistId, string apiKey, string baseUrl);
Task<List<ArtistResult>> GetArtists(string apiKey, string baseUrl);

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Ombi.Api.Lidarr.Models;
@ -10,14 +11,12 @@ namespace Ombi.Api.Lidarr
{
public class LidarrApi : ILidarrApi
{
public LidarrApi(ILogger<LidarrApi> logger, IApi api)
public LidarrApi(IApi api)
{
Api = api;
Logger = logger;
_api = api;
}
private IApi Api { get; }
private ILogger Logger { get; }
private IApi _api { get; }
private const string ApiVersion = "/api/v1";
@ -26,7 +25,7 @@ namespace Ombi.Api.Lidarr
var request = new Request($"{ApiVersion}/qualityprofile", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
return Api.Request<List<LidarrProfile>>(request);
return _api.Request<List<LidarrProfile>>(request);
}
public Task<List<LidarrRootFolder>> GetRootFolders(string apiKey, string baseUrl)
@ -34,7 +33,7 @@ namespace Ombi.Api.Lidarr
var request = new Request($"{ApiVersion}/rootfolder", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
return Api.Request<List<LidarrRootFolder>>(request);
return _api.Request<List<LidarrRootFolder>>(request);
}
public async Task<List<ArtistLookup>> ArtistLookup(string searchTerm, string apiKey, string baseUrl)
@ -43,7 +42,7 @@ namespace Ombi.Api.Lidarr
request.AddQueryString("term", searchTerm);
AddHeaders(request, apiKey);
return await Api.Request<List<ArtistLookup>>(request);
return await _api.Request<List<ArtistLookup>>(request);
}
public Task<List<AlbumLookup>> AlbumLookup(string searchTerm, string apiKey, string baseUrl)
@ -52,7 +51,7 @@ namespace Ombi.Api.Lidarr
request.AddQueryString("term", searchTerm);
AddHeaders(request, apiKey);
return Api.Request<List<AlbumLookup>>(request);
return _api.Request<List<AlbumLookup>>(request);
}
public Task<ArtistResult> GetArtist(int artistId, string apiKey, string baseUrl)
@ -60,16 +59,16 @@ namespace Ombi.Api.Lidarr
var request = new Request($"{ApiVersion}/artist/{artistId}", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
return Api.Request<ArtistResult>(request);
return _api.Request<ArtistResult>(request);
}
public async Task<ArtistResult> GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl)
public async Task<ArtistResult> GetArtistByForeignId(string foreignArtistId, string apiKey, string baseUrl, CancellationToken token = default)
{
var request = new Request($"{ApiVersion}/artist/lookup", baseUrl, HttpMethod.Get);
request.AddQueryString("term", $"lidarr:{foreignArtistId}");
AddHeaders(request, apiKey);
return (await Api.Request<List<ArtistResult>>(request)).FirstOrDefault();
return (await _api.Request<List<ArtistResult>>(request, token)).FirstOrDefault();
}
public async Task<AlbumLookup> GetAlbumByForeignId(string foreignArtistId, string apiKey, string baseUrl)
@ -78,7 +77,7 @@ namespace Ombi.Api.Lidarr
request.AddQueryString("term", $"lidarr:{foreignArtistId}");
AddHeaders(request, apiKey);
var albums = await Api.Request<List<AlbumLookup>>(request);
var albums = await _api.Request<List<AlbumLookup>>(request);
return albums.FirstOrDefault();
}
@ -86,7 +85,7 @@ namespace Ombi.Api.Lidarr
{
var request = new Request(string.Empty, $"https://api.lidarr.audio/api/v0.4/artist/{foreignArtistId}",
HttpMethod.Get) {IgnoreBaseUrlAppend = true};
return Api.Request<AlbumByArtistResponse>(request);
return _api.Request<AlbumByArtistResponse>(request);
}
public Task<List<ArtistResult>> GetArtists(string apiKey, string baseUrl)
@ -94,7 +93,7 @@ namespace Ombi.Api.Lidarr
var request = new Request($"{ApiVersion}/artist", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
return Api.Request<List<ArtistResult>>(request);
return _api.Request<List<ArtistResult>>(request);
}
public Task<List<AlbumResponse>> GetAllAlbums(string apiKey, string baseUrl)
@ -102,7 +101,7 @@ namespace Ombi.Api.Lidarr
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
return Api.Request<List<AlbumResponse>>(request);
return _api.Request<List<AlbumResponse>>(request);
}
public async Task<AlbumByForeignId> AlbumInformation(string albumId, string apiKey, string baseUrl)
@ -110,7 +109,7 @@ namespace Ombi.Api.Lidarr
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
request.AddQueryString("foreignAlbumId", albumId);
AddHeaders(request, apiKey);
var albums = await Api.Request<List<AlbumByForeignId>>(request);
var albums = await _api.Request<List<AlbumByForeignId>>(request);
return albums.FirstOrDefault();
}
@ -127,7 +126,7 @@ namespace Ombi.Api.Lidarr
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
request.AddQueryString("albumId", albumId.ToString());
AddHeaders(request, apiKey);
return Api.Request<List<LidarrTrack>>(request);
return _api.Request<List<LidarrTrack>>(request);
}
public Task<ArtistResult> AddArtist(ArtistAdd artist, string apiKey, string baseUrl)
@ -135,7 +134,7 @@ namespace Ombi.Api.Lidarr
var request = new Request($"{ApiVersion}/artist", baseUrl, HttpMethod.Post);
request.AddJsonBody(artist);
AddHeaders(request, apiKey);
return Api.Request<ArtistResult>(request);
return _api.Request<ArtistResult>(request);
}
public async Task<AlbumResponse> MontiorAlbum(int albumId, string apiKey, string baseUrl)
@ -147,7 +146,7 @@ namespace Ombi.Api.Lidarr
monitored = true
});
AddHeaders(request, apiKey);
return (await Api.Request<List<AlbumResponse>>(request)).FirstOrDefault();
return (await _api.Request<List<AlbumResponse>>(request)).FirstOrDefault();
}
public Task<List<AlbumResponse>> GetAllAlbumsByArtistId(int artistId, string apiKey, string baseUrl)
@ -155,21 +154,21 @@ namespace Ombi.Api.Lidarr
var request = new Request($"{ApiVersion}/album", baseUrl, HttpMethod.Get);
request.AddQueryString("artistId", artistId.ToString());
AddHeaders(request, apiKey);
return Api.Request<List<AlbumResponse>>(request);
return _api.Request<List<AlbumResponse>>(request);
}
public Task<List<MetadataProfile>> GetMetadataProfile(string apiKey, string baseUrl)
{
var request = new Request($"{ApiVersion}/metadataprofile", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
return Api.Request<List<MetadataProfile>>(request);
return _api.Request<List<MetadataProfile>>(request);
}
public Task<LidarrStatus> Status(string apiKey, string baseUrl)
{
var request = new Request($"{ApiVersion}/system/status", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
return Api.Request<LidarrStatus>(request);
return _api.Request<LidarrStatus>(request);
}
public Task<CommandResult> AlbumSearch(int[] albumIds, string apiKey, string baseUrl)
@ -177,7 +176,7 @@ namespace Ombi.Api.Lidarr
var request = new Request($"{ApiVersion}/command/", baseUrl, HttpMethod.Post);
request.AddJsonBody(new { name = "AlbumSearch", albumIds });
AddHeaders(request, apiKey);
return Api.Request<CommandResult>(request);
return _api.Request<CommandResult>(request);
}
private void AddHeaders(Request request, string key)

@ -32,17 +32,21 @@ namespace Ombi.Api.Lidarr.Models
public class Addoptions
{
/// <summary>
/// Future = 1
/// Missing = 2
/// Existing = 3
/// First = 5
/// Latest = 4
/// None = 6
/// </summary>
public int selectedOption { get; set; }
public MonitorTypes monitor { get; set; }
public bool monitored { get; set; }
public bool searchForMissingAlbums { get; set; }
public bool searchForMissingAlbums { get; set; } // Only for Artists add
public string[] AlbumsToMonitor { get; set; } // Uses the MusicBrainzAlbumId!
}
public enum MonitorTypes
{
All,
Future,
Missing,
Existing,
Latest,
First,
None,
Unknown
}
}

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,15 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -44,7 +44,13 @@ namespace Ombi.Api.Plex.Models
public string grandparentTheme { get; set; }
public string chapterSource { get; set; }
public Medium[] Media { get; set; }
public PlexGuids[] Guid { get; set; }
// public Director[] Director { get; set; }
// public Writer[] Writer { get; set; }
}
public class PlexGuids
{
public string Id { get; set; }
}
}

@ -1,17 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
</ItemGroup>

@ -135,16 +135,15 @@ namespace Ombi.Api.Plex
}
/// <summary>
// 192.168.1.69:32400/library/metadata/3662/allLeaves
// The metadata ratingkey should be in the Cache
// Search for it and then call the above with the Directory.RatingKey
// THEN! We need the episode metadata using result.Vide.Key ("/library/metadata/3664")
// We then have the GUID which contains the TVDB ID plus the season and episode number: guid="com.plexapp.agents.thetvdb://269586/2/8?lang=en"
/// 192.168.1.69:32400/library/metadata/3662/allLeaves
/// The metadata ratingkey should be in the Cache
/// Search for it and then call the above with the Directory.RatingKey
/// THEN! We need the episode metadata using result.Vide.Key ("/library/metadata/3664")
/// We then have the GUID which contains the TVDB ID plus the season and episode number: guid="com.plexapp.agents.thetvdb://269586/2/8?lang=en"
/// </summary>
/// <param name="authToken"></param>
/// <param name="plexFullHost"></param>
/// <param name="ratingKey"></param>
/// <returns></returns>
public async Task<PlexMetadata> GetEpisodeMetaData(string authToken, string plexFullHost, int ratingKey)
{
var request = new Request($"/library/metadata/{ratingKey}", plexFullHost, HttpMethod.Get);
@ -308,7 +307,7 @@ namespace Ombi.Api.Plex
}
private async Task CheckInstallId(PlexSettings s)
{
if (s.InstallId == null || s.InstallId == Guid.Empty)
if (s?.InstallId == Guid.Empty || s.InstallId == Guid.Empty)
{
s.InstallId = Guid.NewGuid();
await _plexSettings.SaveSettingsAsync(s);

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -13,7 +13,7 @@ namespace Ombi.Api.Radarr
Task<MovieResponse> GetMovie(int id, string apiKey, string baseUrl);
Task<MovieResponse> UpdateMovie(MovieResponse movie, string apiKey, string baseUrl);
Task<bool> MovieSearch(int[] movieIds, string apiKey, string baseUrl);
Task<RadarrAddMovieResponse> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath,string apiKey, string baseUrl, bool searchNow, string minimumAvailability);
Task<RadarrAddMovie> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath,string apiKey, string baseUrl, bool searchNow, string minimumAvailability);
Task<List<Tag>> GetTags(string apiKey, string baseUrl);
}
}

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Ombi.Api.Radarr.Models;
using Ombi.Api.Radarr.Models.V3;
namespace Ombi.Api.Radarr
{
public interface IRadarrV3Api
{
Task<List<MovieResponse>> GetMovies(string apiKey, string baseUrl);
Task<List<RadarrV3QualityProfile>> GetProfiles(string apiKey, string baseUrl);
Task<List<RadarrRootFolder>> GetRootFolders(string apiKey, string baseUrl);
Task<SystemStatus> SystemStatus(string apiKey, string baseUrl);
Task<MovieResponse> GetMovie(int id, string apiKey, string baseUrl);
Task<MovieResponse> UpdateMovie(MovieResponse movie, string apiKey, string baseUrl);
Task<bool> MovieSearch(int[] movieIds, string apiKey, string baseUrl);
Task<RadarrAddMovie> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath,string apiKey, string baseUrl, bool searchNow, string minimumAvailability);
Task<List<Tag>> GetTags(string apiKey, string baseUrl);
}
}

@ -1,25 +0,0 @@
namespace Ombi.Api.Radarr.Models
{
public class SystemStatus
{
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 runtimeVersion { get; set; }
}
}

@ -2,13 +2,22 @@
namespace Ombi.Api.Radarr.Models
{
public class RadarrAddMovieResponse
public class RadarrAddMovieResponse : RadarrAddMovie
{
public RadarrAddMovieResponse()
{
images = new List<string>();
}
public List<string> images { get; set; }
}
public class RadarrAddMovie
{
public RadarrAddMovie()
{
}
public RadarrError Error { get; set; }
public RadarrAddOptions addOptions { get; set; }
public string title { get; set; }
@ -16,7 +25,6 @@ namespace Ombi.Api.Radarr.Models
public int qualityProfileId { get; set; }
public bool monitored { get; set; }
public int tmdbId { get; set; }
public List<string> images { get; set; }
public string titleSlug { get; set; }
public int year { get; set; }
public string minimumAvailability { get; set; }

@ -0,0 +1,7 @@
namespace Ombi.Api.Radarr.Models
{
public class SystemStatus
{
public string version { get; set; }
}
}

@ -0,0 +1,29 @@
namespace Ombi.Api.Radarr.Models.V3
{
public class RadarrV3QualityProfile
{
public string name { get; set; }
public bool upgradeAllowed { get; set; }
public int cutoff { get; set; }
public string preferredTags { get; set; }
public Item[] items { get; set; }
public int id { get; set; }
}
public class Item
{
public Quality quality { get; set; }
public object[] items { get; set; }
public bool allowed { get; set; }
}
public class Quality
{
public int id { get; set; }
public string name { get; set; }
public string source { get; set; }
public int resolution { get; set; }
public string modifier { get; set; }
}
}

@ -1,15 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
</ItemGroup>
<ItemGroup>

@ -69,7 +69,7 @@ namespace Ombi.Api.Radarr
return await Api.Request<MovieResponse>(request);
}
public async Task<RadarrAddMovieResponse> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath, string apiKey, string baseUrl, bool searchNow, string minimumAvailability)
public async Task<RadarrAddMovie> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath, string apiKey, string baseUrl, bool searchNow, string minimumAvailability)
{
var request = new Request("/api/movie", baseUrl, HttpMethod.Post);
@ -110,7 +110,7 @@ namespace Ombi.Api.Radarr
var error = JsonConvert.DeserializeObject<List<RadarrErrorResponse>>(response).FirstOrDefault();
return new RadarrAddMovieResponse { Error = new RadarrError { message = error?.errorMessage } };
}
return JsonConvert.DeserializeObject<RadarrAddMovieResponse>(response);
return JsonConvert.DeserializeObject<RadarrAddMovie>(response);
}
catch (JsonSerializationException jse)
{

@ -0,0 +1,159 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Ombi.Api.Radarr.Models;
using Ombi.Api.Radarr.Models.V3;
using Ombi.Helpers;
namespace Ombi.Api.Radarr
{
//https://radarr.video/docs/api/
public class RadarrV3Api : IRadarrV3Api
{
public RadarrV3Api(ILogger<RadarrV3Api> logger, IApi api)
{
Api = api;
Logger = logger;
}
private IApi Api { get; }
private ILogger Logger { get; }
public async Task<List<RadarrV3QualityProfile>> GetProfiles(string apiKey, string baseUrl)
{
var request = new Request("/api/v3/qualityProfile", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
return await Api.Request<List<RadarrV3QualityProfile>>(request);
}
// TODO
public async Task<List<RadarrRootFolder>> GetRootFolders(string apiKey, string baseUrl)
{
var request = new Request("/api/v3/rootfolder", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
return await Api.Request<List<RadarrRootFolder>>(request);
}
public async Task<SystemStatus> SystemStatus(string apiKey, string baseUrl)
{
var request = new Request("/api/v3/status", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
return await Api.Request<SystemStatus>(request);
}
public async Task<List<MovieResponse>> GetMovies(string apiKey, string baseUrl)
{
var request = new Request("/api/v3/movie", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
return await Api.Request<List<MovieResponse>>(request);
}
public async Task<MovieResponse> GetMovie(int id, string apiKey, string baseUrl)
{
var request = new Request($"/api/v3/movie/{id}", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
return await Api.Request<MovieResponse>(request);
}
public async Task<MovieResponse> UpdateMovie(MovieResponse movie, string apiKey, string baseUrl)
{
var request = new Request($"/api/v3/movie/", baseUrl, HttpMethod.Put);
AddHeaders(request, apiKey);
request.AddJsonBody(movie);
return await Api.Request<MovieResponse>(request);
}
public async Task<RadarrAddMovie> AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath, string apiKey, string baseUrl, bool searchNow, string minimumAvailability)
{
var request = new Request("/api/v3/movie", baseUrl, HttpMethod.Post);
var options = new RadarrAddMovieResponse
{
title = title,
tmdbId = tmdbId,
qualityProfileId = qualityId,
rootFolderPath = rootPath,
titleSlug = title + year,
monitored = true,
year = year,
minimumAvailability = minimumAvailability
};
if (searchNow)
{
options.addOptions = new RadarrAddOptions
{
searchForMovie = true
};
}
request.AddHeader("X-Api-Key", apiKey);
request.AddJsonBody(options);
var response = await Api.RequestContent(request);
// TODO check if this is still correct, new API docs show validation as a 405 now
try
{
if (response.Contains("\"message\":"))
{
var error = JsonConvert.DeserializeObject<RadarrError>(response);
return new RadarrAddMovieResponse { Error = error };
}
if (response.Contains("\"errorMessage\":"))
{
var error = JsonConvert.DeserializeObject<List<RadarrErrorResponse>>(response).FirstOrDefault();
return new RadarrAddMovieResponse { Error = new RadarrError { message = error?.errorMessage } };
}
return JsonConvert.DeserializeObject<RadarrAddMovie>(response);
}
catch (JsonSerializationException jse)
{
Logger.LogError(LoggingEvents.RadarrApi, jse, "Error When adding movie to Radarr, Reponse: {0}", response);
}
return null;
}
public async Task<bool> MovieSearch(int[] movieIds, string apiKey, string baseUrl)
{
var result = await Command(apiKey, baseUrl, new { name = "MoviesSearch", movieIds });
return result != null;
}
public async Task<List<Tag>> GetTags(string apiKey, string baseUrl)
{
var request = new Request("/api/v3/tag", baseUrl, HttpMethod.Get);
AddHeaders(request, apiKey);
return await Api.Request<List<Tag>>(request);
}
private async Task<CommandResult> Command(string apiKey, string baseUrl, object body)
{
var request = new Request($"/api/v3/Command/", baseUrl, HttpMethod.Post);
request.AddHeader("X-Api-Key", apiKey);
request.AddJsonBody(body);
return await Api.Request<CommandResult>(request);
}
/// <summary>
/// Adds the required headers and also the authorization header
/// </summary>
/// <param name="request"></param>
/// <param name="key"></param>
private void AddHeaders(Request request, string key)
{
request.AddHeader("X-Api-Key", key);
}
}
}

@ -1,17 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<AssemblyName>Ombi.Api.Service</AssemblyName>
<RootNamespace>Ombi.Api.Service</RootNamespace>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
</ItemGroup>
<ItemGroup>

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -1,4 +1,5 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -48,7 +49,18 @@ namespace Ombi.Api.Trakt
public async Task<TraktShow> GetTvExtendedInfo(string imdbId)
{
return await Client.Shows.GetShowAsync(imdbId, TraktExtendedOption.Full);
try
{
return await Client.Shows.GetShowAsync(imdbId, TraktExtendedOption.Full);
}
catch (Exception e)
{
// Ignore the exception since the information returned from this API is optional.
Console.WriteLine($"Failed to retrieve extended tv information from Trakt. IMDbId: '{imdbId}'.");
Console.WriteLine(e);
}
return null;
}
}
}

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Ombi.Api.Twilio
{
public interface IWhatsAppApi
{
Task<string> SendMessage(WhatsAppModel message, string accountSid, string authToken);
}
}

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Twilio" Version="5.37.2" />
</ItemGroup>
</Project>

@ -0,0 +1,28 @@
using System;
using System.Threading.Tasks;
using Twilio;
using Twilio.Rest.Api.V2010.Account;
using Twilio.Types;
namespace Ombi.Api.Twilio
{
public class WhatsAppApi : IWhatsAppApi
{
public async Task<string> SendMessage(WhatsAppModel message, string accountSid, string authToken)
{
TwilioClient.Init(accountSid, authToken);
if(string.IsNullOrEmpty(message.To))
{
return string.Empty;
}
var response =await MessageResource.CreateAsync(
body: message.Message,
from: new PhoneNumber($"whatsapp:{message.From}"),
to: new PhoneNumber($"whatsapp:{message.To}")
);
return response.Sid;
}
}
}

@ -0,0 +1,9 @@
namespace Ombi.Api.Twilio
{
public class WhatsAppModel
{
public string Message { get; set; }
public string To { get; set; }
public string From { get; set; }
}
}

@ -0,0 +1,10 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ombi.Api.Webhook
{
public interface IWebhookApi
{
Task PushAsync(string endpoint, string accessToken, IDictionary<string, string> parameters);
}
}

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,40 @@
using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
namespace Ombi.Api.Webhook
{
public class WebhookApi : IWebhookApi
{
private static readonly CamelCasePropertyNamesContractResolver _nameResolver = new CamelCasePropertyNamesContractResolver();
public WebhookApi(IApi api)
{
_api = api;
}
private readonly IApi _api;
public async Task PushAsync(string baseUrl, string accessToken, IDictionary<string, string> parameters)
{
var request = new Request("/", baseUrl, HttpMethod.Post);
if (!string.IsNullOrWhiteSpace(accessToken))
{
request.AddHeader("Access-Token", accessToken);
}
var body = parameters.ToDictionary(
x => _nameResolver.GetResolvedPropertyName(x.Key),
x => x.Value
);
request.ApplicationJsonContentType();
request.AddJsonBody(body);
await _api.Request(request);
}
}
}

@ -5,7 +5,7 @@ using System.Threading.Tasks;
namespace Ombi.Api
{
public static class HttpRequestExtnesions
public static class HttpRequestExtensions
{
public static async Task<HttpRequestMessage> Clone(this HttpRequestMessage request)
{
@ -14,9 +14,9 @@ namespace Ombi.Api
Content = await request.Content.Clone(),
Version = request.Version
};
foreach (KeyValuePair<string, object> prop in request.Properties)
foreach (KeyValuePair<string, object> prop in request.Options)
{
clone.Properties.Add(prop);
clone.Options.TryAdd(prop.Key, prop.Value);
}
foreach (KeyValuePair<string, IEnumerable<string>> header in request.Headers)
{

@ -1,18 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
<Version></Version>
<PackageVersion></PackageVersion>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Polly" Version="7.1.0" />
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
</ItemGroup>
<ItemGroup>

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Security.Principal;
using System.Threading.Tasks;
using System.Linq;
using System.Threading;
using AutoFixture;
using Hqub.MusicBrainz.API.Entities;
using Moq;
@ -172,7 +173,7 @@ namespace Ombi.Core.Tests.Engine.V2
ApiKey = "dasdsa",
Ip = "192.168.1.7"
});
_lidarrApi.Setup(x => x.GetArtistByForeignId(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
_lidarrApi.Setup(x => x.GetArtistByForeignId(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), CancellationToken.None))
.ReturnsAsync(new ArtistResult
{
images = new Image[]

@ -34,7 +34,7 @@ namespace Ombi.Core.Tests.Engine
MovieRequestEngine = new Mock<IMovieRequestEngine>();
User = new Mock<IPrincipal>();
User.Setup(x => x.Identity.Name).Returns("abc");
UserManager = MockHelper.MockUserManager(new List<OmbiUser> { new OmbiUser { Id = "abc", UserName = "abc" } });
UserManager = MockHelper.MockUserManager(new List<OmbiUser> { new OmbiUser { Id = "abc", UserName = "abc", NormalizedUserName = "ABC" } });
Rule = new Mock<IRuleEvaluator>();
Engine = new VoteEngine(VoteRepository.Object, User.Object, UserManager.Object, Rule.Object, VoteSettings.Object, MusicRequestEngine.Object,
TvRequestEngine.Object, MovieRequestEngine.Object);

@ -1,17 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.5.0" />
<PackageReference Include="Moq" Version="4.10.0" />
<PackageReference Include="Nunit" Version="3.11.0" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="AutoFixture" Version="4.11.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" />
<PackageReference Include="Moq" Version="4.14.1" />
<PackageReference Include="Nunit" Version="3.12.0" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.11.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="16.0.1"></packagereference>
<packagereference Include="Microsoft.NET.Test.Sdk" Version="16.8.0"></packagereference>
</ItemGroup>
<ItemGroup>

@ -18,7 +18,8 @@ namespace Ombi.Core.Tests.Rule.Request
{
private List<OmbiUser> _users = new List<OmbiUser>
{
new OmbiUser { Id = Guid.NewGuid().ToString("N"), UserName="abc",UserType = UserType.LocalUser}
new OmbiUser { Id = Guid.NewGuid().ToString("N"), UserName="abc", NormalizedUserName = "ABC", UserType = UserType.LocalUser},
new OmbiUser { Id = Guid.NewGuid().ToString("N"), UserName="Sys", NormalizedUserName = "SYS", UserType = UserType.SystemUser}
};
[SetUp]
@ -92,6 +93,18 @@ namespace Ombi.Core.Tests.Rule.Request
Assert.True(request.Approved);
}
[Test]
public async Task Should_ReturnSuccess_WhenSystemUserAndRequestTV()
{
PrincipalMock.Setup(x => x.Identity.Name).Returns("sys");
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.AutoApproveTv)).ReturnsAsync(false);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.TvShow };
var result = await Rule.Execute(request);
Assert.True(result.Success);
Assert.True(request.Approved);
}
[Test]
public async Task Should_ReturnFail_WhenAutoApproveTV_And_RequestMovie()
{

@ -18,7 +18,8 @@ namespace Ombi.Core.Tests.Rule.Request
{
private List<OmbiUser> _users = new List<OmbiUser>
{
new OmbiUser { Id = Guid.NewGuid().ToString("N"), UserName="abc", UserType = UserType.LocalUser}
new OmbiUser { Id = Guid.NewGuid().ToString("N"), UserName="abc", NormalizedUserName = "ABC", UserType = UserType.LocalUser},
new OmbiUser { Id = Guid.NewGuid().ToString("N"), UserName="sys", NormalizedUserName = "SYS", UserType = UserType.SystemUser}
};
[SetUp]
@ -68,6 +69,17 @@ namespace Ombi.Core.Tests.Rule.Request
Assert.True(result.Success);
}
[Test]
public async Task Should_ReturnSuccess_WhenRequestingMovieWithSystemRole()
{
PrincipalMock.Setup(x => x.Identity.Name).Returns("sys");
UserManager.Setup(x => x.IsInRoleAsync(It.IsAny<OmbiUser>(), OmbiRoles.Admin)).ReturnsAsync(false);
var request = new BaseRequest() { RequestType = Store.Entities.RequestType.Movie };
var result = await Rule.Execute(request);
Assert.True(result.Success);
}
[Test]
public async Task Should_ReturnSuccess_WhenRequestingTVWithAdminRole()
{

@ -54,14 +54,15 @@ namespace Ombi.Core.Tests.Rule.Search
{
new EmbyServers
{
ServerHostname = "http://test.com/"
ServerHostname = "http://test.com/",
ServerId = "8"
}
}
});
ContextMock.Setup(x => x.GetByTheMovieDbId(It.IsAny<string>())).ReturnsAsync(new EmbyContent
{
ProviderId = "123",
EmbyId = 1.ToString()
EmbyId = 1.ToString(),
});
var search = new SearchMovieViewModel()
{
@ -70,7 +71,7 @@ namespace Ombi.Core.Tests.Rule.Search
var result = await Rule.Execute(search);
Assert.True(result.Success);
Assert.That(search.EmbyUrl, Is.EqualTo("http://test.com/#!/item/item.html?id=1"));
Assert.That(search.EmbyUrl, Is.EqualTo("http://test.com/web/index.html#!/item?id=1&serverId=8"));
}
[Test]
@ -83,7 +84,8 @@ namespace Ombi.Core.Tests.Rule.Search
{
new EmbyServers
{
ServerHostname = string.Empty
ServerHostname = string.Empty,
ServerId = "8"
}
}
});
@ -99,7 +101,7 @@ namespace Ombi.Core.Tests.Rule.Search
var result = await Rule.Execute(search);
Assert.True(result.Success);
Assert.That(search.EmbyUrl, Is.EqualTo("https://app.emby.media/#!/item/item.html?id=1"));
Assert.That(search.EmbyUrl, Is.EqualTo("https://app.emby.media/web/index.html#!/item?id=1&serverId=8"));
}
[Test]

@ -113,7 +113,7 @@ namespace Ombi.Core.Tests.Rule.Search
PercentOfTracks = 100
}
}.AsQueryable());
var request = new SearchAlbumViewModel { ForeignAlbumId = "ABC" };
var request = new SearchAlbumViewModel { ForeignAlbumId = "abc" };
var result = await Rule.Execute(request);
Assert.True(result.Success);

@ -63,7 +63,7 @@ namespace Ombi.Core.Tests.Rule.Search
ForeignArtistId = "abc",
}
}.AsQueryable());
var request = new SearchArtistViewModel { ForignArtistId = "ABC" };
var request = new SearchArtistViewModel { ForignArtistId = "abc" };
var result = await Rule.Execute(request);
Assert.True(result.Success);

@ -49,7 +49,7 @@ namespace Ombi.Core.Authentication
IPasswordHasher<OmbiUser> passwordHasher, IEnumerable<IUserValidator<OmbiUser>> userValidators,
IEnumerable<IPasswordValidator<OmbiUser>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<OmbiUser>> logger, IPlexApi plexApi,
IEmbyApi embyApi, ISettingsService<EmbySettings> embySettings, ISettingsService<AuthenticationSettings> auth)
IEmbyApiFactory embyApi, ISettingsService<EmbySettings> embySettings, ISettingsService<AuthenticationSettings> auth)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
_plexApi = plexApi;
@ -59,7 +59,7 @@ namespace Ombi.Core.Authentication
}
private readonly IPlexApi _plexApi;
private readonly IEmbyApi _embyApi;
private readonly IEmbyApiFactory _embyApi;
private readonly ISettingsService<EmbySettings> _embySettings;
private readonly ISettingsService<AuthenticationSettings> _authSettings;
@ -79,7 +79,7 @@ namespace Ombi.Core.Authentication
{
return await CheckPlexPasswordAsync(user, password);
}
if (user.UserType == UserType.EmbyUser)
if (user.UserType == UserType.EmbyUser || user.UserType == UserType.EmbyConnectUser)
{
return await CheckEmbyPasswordAsync(user, password);
}
@ -146,9 +146,12 @@ namespace Ombi.Core.Authentication
/// <returns></returns>
private async Task<bool> CheckEmbyPasswordAsync(OmbiUser user, string password)
{
var embySettings = await _embySettings.GetSettingsAsync();
var client = _embyApi.CreateClient(embySettings);
if (user.IsEmbyConnect)
{
var result = await _embyApi.LoginConnectUser(user.UserName, password);
var result = await client.LoginConnectUser(user.UserName, password);
if (result.AccessToken.HasValue())
{
// We cannot update the email address in the user importer due to there is no way
@ -165,12 +168,11 @@ namespace Ombi.Core.Authentication
}
}
var embySettings = await _embySettings.GetSettingsAsync();
foreach (var server in embySettings.Servers)
{
try
{
var result = await _embyApi.LogIn(user.UserName, password, server.ApiKey, server.FullUri);
var result = await client.LogIn(user.UserName, password, server.ApiKey, server.FullUri);
if (result != null)
{
return true;

@ -136,7 +136,7 @@ namespace Ombi.Core.Engine
{
var user = await GetUser();
var existingSub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(x =>
x.UserId.Equals(user.Id) && x.RequestId == requestId && x.RequestType == type);
x.UserId == user.Id && x.RequestId == requestId && x.RequestType == type);
if (existingSub != null)
{
return;
@ -155,23 +155,28 @@ namespace Ombi.Core.Engine
{
var user = await GetUser();
var existingSub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(x =>
x.UserId.Equals(user.Id) && x.RequestId == requestId && x.RequestType == type);
x.UserId == user.Id && x.RequestId == requestId && x.RequestType == type);
if (existingSub != null)
{
await _subscriptionRepository.Delete(existingSub);
}
}
private string defaultLangCode;
protected async Task<string> DefaultLanguageCode(string currentCode)
{
if (currentCode.HasValue())
{
return currentCode;
}
var user = await GetUser();
if (string.IsNullOrEmpty(user.Language))
{
var s = await GetOmbiSettings();
return s.DefaultLanguageCode;
}
var s = await GetOmbiSettings();
return s.DefaultLanguageCode;
return user.Language;
}
private OmbiSettings ombiSettings;

@ -50,7 +50,7 @@ namespace Ombi.Core.Engine.Demo
return null;
}
public async Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies()
new public async Task<IEnumerable<SearchMovieViewModel>> NowPlayingMovies()
{
var rand = new Random();
var responses = new List<SearchMovieViewModel>();
@ -72,18 +72,18 @@ namespace Ombi.Core.Engine.Demo
return responses;
}
public async Task<IEnumerable<SearchMovieViewModel>> PopularMovies()
new public async Task<IEnumerable<SearchMovieViewModel>> PopularMovies()
{
return await NowPlayingMovies();
}
public async Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies()
new public async Task<IEnumerable<SearchMovieViewModel>> TopRatedMovies()
{
return await NowPlayingMovies();
}
public async Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies()
new public async Task<IEnumerable<SearchMovieViewModel>> UpcomingMovies()
{
return await NowPlayingMovies();

@ -26,15 +26,16 @@ namespace Ombi.Core.Engine.Demo
public DemoTvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache,
ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub, IOptions<DemoLists> lists)
: base(identity, service, tvMaze, mapper, trakt, r, um, memCache, s, sub)
ISettingsService<OmbiSettings> s, IRepository<RequestSubscription> sub, IOptions<DemoLists> lists, IImageService imageService,
ISettingsService<CustomizationSettings> custom)
: base(identity, service, tvMaze, mapper, trakt, r, um, custom, memCache, s, sub, imageService)
{
_demoLists = lists.Value;
}
private readonly DemoLists _demoLists;
public async Task<IEnumerable<SearchTvShowViewModel>> Search(string search)
new public async Task<IEnumerable<SearchTvShowViewModel>> Search(string search)
{
var searchResult = await TvMazeApi.Search(search);
@ -55,7 +56,7 @@ namespace Ombi.Core.Engine.Demo
{
continue;
}
retVal.Add(await ProcessResult(tvMazeSearch));
retVal.Add(await ProcessResult(tvMazeSearch, false));
}
return retVal;
}
@ -77,7 +78,7 @@ namespace Ombi.Core.Engine.Demo
}
var movieResult = await TvMazeApi.ShowLookup(tv);
responses.Add(await ProcessResult(movieResult));
responses.Add(await ProcessResult(movieResult, false));
}
return responses;

@ -23,5 +23,7 @@ namespace Ombi.Core.Engine
Task<IEnumerable<AlbumRequest>> SearchAlbumRequest(string search);
Task<bool> UserHasRequest(string userId);
Task<RequestQuotaCountModel> GetRemainingRequests(OmbiUser user = null);
Task<RequestsViewModel<AlbumRequest>> GetRequestsByStatus(int count, int position, string sort, string sortOrder, RequestStatus available);
Task<RequestsViewModel<AlbumRequest>> GetRequests(int count, int position, string sort, string sortOrder);
}
}

@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Identity;
using Ombi.Store.Entities;
using System.Threading.Tasks;
namespace Ombi.Core.Engine
{
public interface IUserDeletionEngine
{
Task<IdentityResult> DeleteUser(OmbiUser userToDelete);
}
}

@ -29,7 +29,8 @@ namespace Ombi.Core.Engine.Interfaces
private OmbiUser _user;
protected async Task<OmbiUser> GetUser()
{
return _user ?? (_user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName.Equals(Username, StringComparison.CurrentCultureIgnoreCase)));
var username = Username.ToUpper();
return _user ?? (_user = await UserManager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username));
}
protected async Task<string> UserAlias()

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

Loading…
Cancel
Save