diff --git a/.vscode/extensions.json b/.vscode/extensions.json
deleted file mode 100644
index 7a36fefe19..0000000000
--- a/.vscode/extensions.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "recommendations": [
- "esbenp.prettier-vscode",
- "ms-dotnettools.csdevkit",
- "ms-vscode-remote.remote-containers"
- ]
-}
diff --git a/.vscode/launch.json b/.vscode/launch.json
deleted file mode 100644
index 832711c354..0000000000
--- a/.vscode/launch.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "version": "0.2.0",
- "configurations": [
- {
- // Use IntelliSense to find out which attributes exist for C# debugging
- // Use hover for the description of the existing attributes
- // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
- "name": "Run Radarr",
- "type": "coreclr",
- "request": "launch",
- "preLaunchTask": "build dotnet",
- // If you have changed target frameworks, make sure to update the program path.
- "program": "${workspaceFolder}/_output/net8.0/Radarr",
- "args": [],
- "cwd": "${workspaceFolder}",
- // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
- "console": "integratedTerminal",
- "stopAtEntry": false
- },
- {
- "name": ".NET Core Attach",
- "type": "coreclr",
- "request": "attach"
- }
- ]
-}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
deleted file mode 100644
index 13b0a6254c..0000000000
--- a/.vscode/tasks.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
- "version": "2.0.0",
- "tasks": [
- {
- "label": "build dotnet",
- "command": "dotnet",
- "type": "process",
- "args": [
- "msbuild",
- "-restore",
- "${workspaceFolder}/src/Radarr.sln",
- "-p:GenerateFullPaths=true",
- "-p:Configuration=Debug",
- "-p:Platform=Posix",
- "-consoleloggerparameters:NoSummary;ForceNoAlign"
- ],
- "problemMatcher": "$msCompile"
- },
- {
- "label": "publish",
- "command": "dotnet",
- "type": "process",
- "args": [
- "publish",
- "${workspaceFolder}/src/Radarr.sln",
- "-property:GenerateFullPaths=true",
- "-consoleloggerparameters:NoSummary;ForceNoAlign"
- ],
- "problemMatcher": "$msCompile"
- },
- {
- "label": "watch",
- "command": "dotnet",
- "type": "process",
- "args": [
- "watch",
- "run",
- "--project",
- "${workspaceFolder}/src/Radarr.sln"
- ],
- "problemMatcher": "$msCompile"
- }
- ]
-}
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
deleted file mode 100644
index f9852aee16..0000000000
--- a/azure-pipelines.yml
+++ /dev/null
@@ -1,1244 +0,0 @@
-# Starter pipeline
-# Start with a minimal pipeline that you can customize to build and deploy your code.
-# Add steps that build, run tests, deploy, and more:
-# https://aka.ms/yaml
-
-variables:
- outputFolder: './_output'
- artifactsFolder: './_artifacts'
- testsFolder: './_tests'
- yarnCacheFolder: $(Pipeline.Workspace)/.yarn
- nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
- majorVersion: '6.1.0'
- minorVersion: $[counter('minorVersion', 2000)]
- radarrVersion: '$(majorVersion).$(minorVersion)'
- buildName: '$(Build.SourceBranchName).$(radarrVersion)'
- sentryOrg: 'servarr'
- sentryUrl: 'https://sentry.servarr.com'
- dotnetVersion: '8.0.405'
- nodeVersion: '20.X'
- innoVersion: '6.2.2'
- windowsImage: 'windows-2025'
- linuxImage: 'ubuntu-24.04'
- macImage: 'macOS-15'
-
-trigger:
- branches:
- include:
- - develop
- - master
- paths:
- exclude:
- - .github
- - src/Radarr.Api.*/openapi.json
-
-pr:
- branches:
- include:
- - develop
- paths:
- exclude:
- - .github
- - src/NzbDrone.Core/Localization/Core
- - src/Radarr.Api.*/openapi.json
-
-stages:
- - stage: Setup
- displayName: Setup
- jobs:
- - job:
- displayName: Build Variables
- pool:
- vmImage: ${{ variables.linuxImage }}
- steps:
- # Set the build name properly. The 'name' property won't recursively expand so hack here:
- - bash: echo "##vso[build.updatebuildnumber]$RADARRVERSION"
- displayName: Set Build Name
- - bash: |
- if [[ $BUILD_REASON == "PullRequest" ]]; then
- git diff origin/develop...HEAD --name-only | grep -E "^(src/|azure-pipelines.yml)"
- echo $? > not_backend_update
- else
- echo 0 > not_backend_update
- fi
- cat not_backend_update
- displayName: Check for Backend File Changes
- - publish: not_backend_update
- artifact: not_backend_update
- displayName: Publish update type
- - stage: Build_Backend
- displayName: Build Backend
- dependsOn: Setup
- jobs:
- - job: Backend
- strategy:
- matrix:
- Linux:
- osName: 'Linux'
- imageName: ${{ variables.linuxImage }}
- enableAnalysis: 'true'
- Mac:
- osName: 'Mac'
- imageName: ${{ variables.macImage }}
- enableAnalysis: 'false'
- Windows:
- osName: 'Windows'
- imageName: ${{ variables.windowsImage }}
- enableAnalysis: 'false'
-
- pool:
- vmImage: $(imageName)
- variables:
- # Disable stylecop here - linting errors get caught by the analyze task
- EnableAnalyzers: $(enableAnalysis)
- steps:
- - checkout: self
- submodules: true
- fetchDepth: 1
- - task: UseDotNet@2
- displayName: 'Install .net core'
- inputs:
- version: $(dotnetVersion)
- - bash: |
- BUNDLEDVERSIONS=${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}/Microsoft.NETCoreSdk.BundledVersions.props
- echo $BUNDLEDVERSIONS
- if grep -q freebsd-x64 $BUNDLEDVERSIONS; then
- echo "Extra platforms already enabled"
- else
- echo "Enabling extra platform support"
- sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64/' "$BUNDLEDVERSIONS"
- fi
- displayName: Enable Extra Platform Support
- - bash: ./build.sh --backend --enable-extra-platforms
- displayName: Build Radarr Backend
- - bash: |
- find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
- find ${OUTPUTFOLDER} -depth -empty -type d -exec rm -r "{}" \;
- find ${TESTSFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
- find ${TESTSFOLDER} -depth -empty -type d -exec rm -r "{}" \;
- displayName: Clean up intermediate output
- condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- - publish: $(outputFolder)
- artifact: '$(osName)Backend'
- displayName: Publish Backend
- condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- - publish: '$(testsFolder)/net8.0/win-x64/publish'
- artifact: win-x64-tests
- displayName: Publish win-x64 Test Package
- condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- - publish: '$(testsFolder)/net8.0/linux-x64/publish'
- artifact: linux-x64-tests
- displayName: Publish linux-x64 Test Package
- condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- - publish: '$(testsFolder)/net8.0/linux-musl-x64/publish'
- artifact: linux-musl-x64-tests
- displayName: Publish linux-musl-x64 Test Package
- condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- - publish: '$(testsFolder)/net8.0/freebsd-x64/publish'
- artifact: freebsd-x64-tests
- displayName: Publish freebsd-x64 Test Package
- condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- - publish: '$(testsFolder)/net8.0/osx-x64/publish'
- artifact: osx-x64-tests
- displayName: Publish osx-x64 Test Package
- condition: and(succeeded(), eq(variables['osName'], 'Windows'))
-
- - stage: Build_Frontend
- displayName: Frontend
- dependsOn: Setup
- jobs:
- - job: Build
- strategy:
- matrix:
- Linux:
- osName: 'Linux'
- imageName: ${{ variables.linuxImage }}
- Mac:
- osName: 'Mac'
- imageName: ${{ variables.macImage }}
- Windows:
- osName: 'Windows'
- imageName: ${{ variables.windowsImage }}
- pool:
- vmImage: $(imageName)
- steps:
- - task: UseNode@1
- displayName: Set Node.js version
- inputs:
- version: $(nodeVersion)
- - checkout: self
- submodules: true
- fetchDepth: 1
- - task: Cache@2
- inputs:
- key: 'yarn | "$(osName)" | yarn.lock'
- restoreKeys: |
- yarn | "$(osName)"
- path: $(yarnCacheFolder)
- displayName: Cache Yarn packages
- - bash: ./build.sh --frontend
- displayName: Build Radarr Frontend
- env:
- FORCE_COLOR: 0
- YARN_CACHE_FOLDER: $(yarnCacheFolder)
- - publish: $(outputFolder)
- artifact: '$(osName)Frontend'
- displayName: Publish Frontend
- condition: and(succeeded(), eq(variables['osName'], 'Windows'))
-
- - stage: Installer
- dependsOn:
- - Build_Backend
- - Build_Frontend
- jobs:
- - job: Windows_Installer
- displayName: Create Installer
- pool:
- vmImage: ${{ variables.windowsImage }}
- steps:
- - checkout: self
- fetchDepth: 1
- - task: DownloadPipelineArtifact@2
- inputs:
- buildType: 'current'
- artifactName: WindowsBackend
- targetPath: _output
- displayName: Fetch Backend
- - task: DownloadPipelineArtifact@2
- inputs:
- buildType: 'current'
- artifactName: WindowsFrontend
- targetPath: _output
- displayName: Fetch Frontend
- - bash: |
- ./build.sh --packages --installer
- cp distribution/windows/setup/output/Radarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x64-installer.exe
- cp distribution/windows/setup/output/Radarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x86-installer.exe
- displayName: Create Installers
- - publish: $(Build.ArtifactStagingDirectory)
- artifact: 'WindowsInstaller'
- displayName: Publish Installer
-
- - stage: Packages
- dependsOn:
- - Build_Backend
- - Build_Frontend
- jobs:
- - job: Other_Packages
- displayName: Create Standard Packages
- pool:
- vmImage: ${{ variables.linuxImage }}
- steps:
- - checkout: self
- fetchDepth: 1
- - task: DownloadPipelineArtifact@2
- inputs:
- buildType: 'current'
- artifactName: WindowsBackend
- targetPath: _output
- displayName: Fetch Backend
- - task: DownloadPipelineArtifact@2
- inputs:
- buildType: 'current'
- artifactName: WindowsFrontend
- targetPath: _output
- displayName: Fetch Frontend
- - bash: ./build.sh --packages --enable-extra-platforms
- displayName: Create Packages
- - bash: |
- find . -name "ffprobe" -exec chmod a+x {} \;
- find . -name "Radarr" -exec chmod a+x {} \;
- find . -name "Radarr.Update" -exec chmod a+x {} \;
- displayName: Set executable bits
- - task: ArchiveFiles@2
- displayName: Create win-x64 zip
- inputs:
- archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).windows-core-x64.zip'
- archiveType: 'zip'
- includeRootFolder: false
- rootFolderOrFile: $(artifactsFolder)/win-x64/net8.0
- - task: ArchiveFiles@2
- displayName: Create win-x86 zip
- inputs:
- archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).windows-core-x86.zip'
- archiveType: 'zip'
- includeRootFolder: false
- rootFolderOrFile: $(artifactsFolder)/win-x86/net8.0
- - task: ArchiveFiles@2
- displayName: Create osx-x64 app
- inputs:
- archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).osx-app-core-x64.zip'
- archiveType: 'zip'
- includeRootFolder: false
- rootFolderOrFile: $(artifactsFolder)/osx-x64-app/net8.0
- - task: ArchiveFiles@2
- displayName: Create osx-x64 tar
- inputs:
- archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).osx-core-x64.tar.gz'
- archiveType: 'tar'
- tarCompression: 'gz'
- includeRootFolder: false
- rootFolderOrFile: $(artifactsFolder)/osx-x64/net8.0
- - task: ArchiveFiles@2
- displayName: Create osx-arm64 app
- inputs:
- archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).osx-app-core-arm64.zip'
- archiveType: 'zip'
- includeRootFolder: false
- rootFolderOrFile: $(artifactsFolder)/osx-arm64-app/net8.0
- - task: ArchiveFiles@2
- displayName: Create osx-arm64 tar
- inputs:
- archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).osx-core-arm64.tar.gz'
- archiveType: 'tar'
- tarCompression: 'gz'
- includeRootFolder: false
- rootFolderOrFile: $(artifactsFolder)/osx-arm64/net8.0
- - task: ArchiveFiles@2
- displayName: Create linux-x64 tar
- inputs:
- archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-core-x64.tar.gz'
- archiveType: 'tar'
- tarCompression: 'gz'
- includeRootFolder: false
- rootFolderOrFile: $(artifactsFolder)/linux-x64/net8.0
- - task: ArchiveFiles@2
- displayName: Create linux-musl-x64 tar
- inputs:
- archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-musl-core-x64.tar.gz'
- archiveType: 'tar'
- tarCompression: 'gz'
- includeRootFolder: false
- rootFolderOrFile: $(artifactsFolder)/linux-musl-x64/net8.0
- - task: ArchiveFiles@2
- displayName: Create linux-arm tar
- inputs:
- archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-core-arm.tar.gz'
- archiveType: 'tar'
- tarCompression: 'gz'
- includeRootFolder: false
- rootFolderOrFile: $(artifactsFolder)/linux-arm/net8.0
- - task: ArchiveFiles@2
- displayName: Create linux-musl-arm tar
- inputs:
- archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-musl-core-arm.tar.gz'
- archiveType: 'tar'
- tarCompression: 'gz'
- includeRootFolder: false
- rootFolderOrFile: $(artifactsFolder)/linux-musl-arm/net8.0
- - task: ArchiveFiles@2
- displayName: Create linux-arm64 tar
- inputs:
- archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-core-arm64.tar.gz'
- archiveType: 'tar'
- tarCompression: 'gz'
- includeRootFolder: false
- rootFolderOrFile: $(artifactsFolder)/linux-arm64/net8.0
- - task: ArchiveFiles@2
- displayName: Create linux-musl-arm64 tar
- inputs:
- archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-musl-core-arm64.tar.gz'
- archiveType: 'tar'
- tarCompression: 'gz'
- includeRootFolder: false
- rootFolderOrFile: $(artifactsFolder)/linux-musl-arm64/net8.0
- - task: ArchiveFiles@2
- displayName: Create freebsd-x64 tar
- inputs:
- archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).freebsd-core-x64.tar.gz'
- archiveType: 'tar'
- tarCompression: 'gz'
- includeRootFolder: false
- rootFolderOrFile: $(artifactsFolder)/freebsd-x64/net8.0
- - publish: $(Build.ArtifactStagingDirectory)
- artifact: 'Packages'
- displayName: Publish Packages
- - bash: |
- echo "Uploading source maps to sentry"
- curl -sL https://sentry.io/get-cli/ | bash
- RELEASENAME="Radarr@${RADARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
- sentry-cli releases new --finalize -p radarr -p radarr-ui -p radarr-update "${RELEASENAME}"
- sentry-cli releases -p radarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
- sentry-cli releases set-commits --auto "${RELEASENAME}"
- if [[ ${BUILD_SOURCEBRANCH} == "refs/heads/develop" ]]; then
- sentry-cli releases deploys "${RELEASENAME}" new -e nightly
- else
- sentry-cli releases deploys "${RELEASENAME}" new -e production
- fi
- if [ $? -gt 0 ]; then
- echo "##vso[task.logissue type=warning]Error uploading source maps."
- fi
- exit 0
- displayName: Publish Sentry Source Maps
- condition: |
- or
- (
- and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
- and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
- )
- env:
- SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
- SENTRY_ORG: $(sentryOrg)
- SENTRY_URL: $(sentryUrl)
-
- - stage: Unit_Test
- displayName: Unit Tests
- dependsOn: Build_Backend
-
- jobs:
- - job: Prepare
- pool:
- vmImage: ${{ variables.linuxImage }}
- steps:
- - checkout: none
- - task: DownloadPipelineArtifact@2
- inputs:
- buildType: 'current'
- artifactName: 'not_backend_update'
- targetPath: '.'
- - bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
- name: setVar
-
- - job: Unit
- displayName: Unit Native
- dependsOn: Prepare
- condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
- workspace:
- clean: all
-
- strategy:
- matrix:
- MacCore:
- osName: 'Mac'
- testName: 'osx-x64'
- poolName: 'Azure Pipelines'
- imageName: ${{ variables.macImage }}
- WindowsCore:
- osName: 'Windows'
- testName: 'win-x64'
- poolName: 'Azure Pipelines'
- imageName: ${{ variables.windowsImage }}
- LinuxCore:
- osName: 'Linux'
- testName: 'linux-x64'
- poolName: 'Azure Pipelines'
- imageName: ${{ variables.linuxImage }}
- FreebsdCore:
- osName: 'Linux'
- testName: 'freebsd-x64'
- poolName: 'FreeBSD'
- imageName:
-
- pool:
- name: $(poolName)
- vmImage: $(imageName)
-
- steps:
- - checkout: none
- - task: UseDotNet@2
- displayName: 'Install .net core'
- inputs:
- version: $(dotnetVersion)
- condition: ne(variables['poolName'], 'FreeBSD')
- - task: DownloadPipelineArtifact@2
- displayName: Download Test Artifact
- inputs:
- buildType: 'current'
- artifactName: '$(testName)-tests'
- targetPath: $(testsFolder)
- - powershell: Set-Service SCardSvr -StartupType Manual
- displayName: Enable Windows Test Service
- condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- - bash: |
- chmod a+x _tests/ffprobe
- displayName: Make ffprobe Executable
- condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- - bash: find ${TESTSFOLDER} -name "Radarr.Test.Dummy" -exec chmod a+x {} \;
- displayName: Make Test Dummy Executable
- condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- - bash: |
- chmod a+x ${TESTSFOLDER}/test.sh
- ${TESTSFOLDER}/test.sh ${OSNAME} Unit Test
- displayName: Run Tests
- env:
- TEST_DIR: $(Build.SourcesDirectory)/_tests
- - task: PublishTestResults@2
- displayName: Publish Test Results
- inputs:
- testResultsFormat: 'NUnit'
- testResultsFiles: '**/TestResult.xml'
- testRunTitle: '$(testName) Unit Tests'
- failTaskOnFailedTests: true
- failTaskOnMissingResultsFile: ne(variables['testName'], 'freebsd-x64')
-
- - job: Unit_Docker
- displayName: Unit Docker
- dependsOn: Prepare
- condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
- strategy:
- matrix:
- alpine:
- testName: 'Musl Net Core'
- artifactName: linux-musl-x64-tests
- containerImage: ghcr.io/servarr/testimages:alpine
-
- pool:
- vmImage: ${{ variables.linuxImage }}
-
- container: $[ variables['containerImage'] ]
-
- timeoutInMinutes: 10
-
- steps:
- - task: UseDotNet@2
- displayName: 'Install .NET'
- inputs:
- version: $(dotnetVersion)
- - checkout: none
- - task: DownloadPipelineArtifact@2
- displayName: Download Test Artifact
- inputs:
- buildType: 'current'
- artifactName: $(artifactName)
- targetPath: $(testsFolder)
- - bash: |
- chmod a+x _tests/ffprobe
- displayName: Make ffprobe Executable
- - bash: find ${TESTSFOLDER} -name "Radarr.Test.Dummy" -exec chmod a+x {} \;
- displayName: Make Test Dummy Executable
- condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- - bash: |
- chmod a+x ${TESTSFOLDER}/test.sh
- ls -lR ${TESTSFOLDER}
- ${TESTSFOLDER}/test.sh Linux Unit Test
- displayName: Run Tests
- - task: PublishTestResults@2
- displayName: Publish Test Results
- inputs:
- testResultsFormat: 'NUnit'
- testResultsFiles: '**/TestResult.xml'
- testRunTitle: '$(testName) Unit Tests'
- failTaskOnFailedTests: true
- failTaskOnMissingResultsFile: true
-
- - job: Unit_LinuxCore_Postgres14
- displayName: Unit Native LinuxCore with Postgres14 Database
- dependsOn: Prepare
- condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
- variables:
- pattern: 'Radarr.*.linux-core-x64.tar.gz'
- artifactName: linux-x64-tests
- Radarr__Postgres__Host: 'localhost'
- Radarr__Postgres__Port: '5432'
- Radarr__Postgres__User: 'radarr'
- Radarr__Postgres__Password: 'radarr'
-
- pool:
- vmImage: ${{ variables.linuxImage }}
-
- timeoutInMinutes: 10
-
- steps:
- - task: UseDotNet@2
- displayName: 'Install .net core'
- inputs:
- version: $(dotnetVersion)
- - checkout: none
- - task: DownloadPipelineArtifact@2
- displayName: Download Test Artifact
- inputs:
- buildType: 'current'
- artifactName: $(artifactName)
- targetPath: $(testsFolder)
- - bash: |
- chmod a+x _tests/ffprobe
- displayName: Make ffprobe Executable
- - bash: find ${TESTSFOLDER} -name "Radarr.Test.Dummy" -exec chmod a+x {} \;
- displayName: Make Test Dummy Executable
- condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- - bash: |
- docker run -d --name=postgres14 \
- -e POSTGRES_PASSWORD=radarr \
- -e POSTGRES_USER=radarr \
- -p 5432:5432/tcp \
- -v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
- postgres:14
- displayName: Start postgres
- - bash: |
- chmod a+x ${TESTSFOLDER}/test.sh
- ls -lR ${TESTSFOLDER}
- ${TESTSFOLDER}/test.sh Linux Unit Test
- displayName: Run Tests
- - task: PublishTestResults@2
- displayName: Publish Test Results
- inputs:
- testResultsFormat: 'NUnit'
- testResultsFiles: '**/TestResult.xml'
- testRunTitle: 'LinuxCore Postgres14 Unit Tests'
- failTaskOnFailedTests: true
- failTaskOnMissingResultsFile: true
-
- - job: Unit_LinuxCore_Postgres15
- displayName: Unit Native LinuxCore with Postgres15 Database
- dependsOn: Prepare
- condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
- variables:
- pattern: 'Radarr.*.linux-core-x64.tar.gz'
- artifactName: linux-x64-tests
- Radarr__Postgres__Host: 'localhost'
- Radarr__Postgres__Port: '5432'
- Radarr__Postgres__User: 'radarr'
- Radarr__Postgres__Password: 'radarr'
-
- pool:
- vmImage: ${{ variables.linuxImage }}
-
- timeoutInMinutes: 10
-
- steps:
- - task: UseDotNet@2
- displayName: 'Install .net core'
- inputs:
- version: $(dotnetVersion)
- - checkout: none
- - task: DownloadPipelineArtifact@2
- displayName: Download Test Artifact
- inputs:
- buildType: 'current'
- artifactName: $(artifactName)
- targetPath: $(testsFolder)
- - bash: |
- chmod a+x _tests/ffprobe
- displayName: Make ffprobe Executable
- - bash: find ${TESTSFOLDER} -name "Radarr.Test.Dummy" -exec chmod a+x {} \;
- displayName: Make Test Dummy Executable
- condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- - bash: |
- docker run -d --name=postgres15 \
- -e POSTGRES_PASSWORD=radarr \
- -e POSTGRES_USER=radarr \
- -p 5432:5432/tcp \
- -v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
- postgres:15
- displayName: Start postgres
- - bash: |
- chmod a+x ${TESTSFOLDER}/test.sh
- ls -lR ${TESTSFOLDER}
- ${TESTSFOLDER}/test.sh Linux Unit Test
- displayName: Run Tests
- - task: PublishTestResults@2
- displayName: Publish Test Results
- inputs:
- testResultsFormat: 'NUnit'
- testResultsFiles: '**/TestResult.xml'
- testRunTitle: 'LinuxCore Postgres15 Unit Tests'
- failTaskOnFailedTests: true
- failTaskOnMissingResultsFile: true
-
- - stage: Integration
- displayName: Integration
- dependsOn: Packages
-
- jobs:
- - job: Prepare
- pool:
- vmImage: ${{ variables.linuxImage }}
- steps:
- - checkout: none
- - task: DownloadPipelineArtifact@2
- inputs:
- buildType: 'current'
- artifactName: 'not_backend_update'
- targetPath: '.'
- - bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
- name: setVar
-
- - job: Integration_Native
- displayName: Integration Native
- dependsOn: Prepare
- condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
- strategy:
- matrix:
- MacCore:
- osName: 'Mac'
- testName: 'osx-x64'
- imageName: ${{ variables.macImage }}
- pattern: 'Radarr.*.osx-core-x64.tar.gz'
- WindowsCore:
- osName: 'Windows'
- testName: 'win-x64'
- imageName: ${{ variables.windowsImage }}
- pattern: 'Radarr.*.windows-core-x64.zip'
- LinuxCore:
- osName: 'Linux'
- testName: 'linux-x64'
- imageName: ${{ variables.linuxImage }}
- pattern: 'Radarr.*.linux-core-x64.tar.gz'
-
- pool:
- vmImage: $(imageName)
-
- steps:
- - task: UseDotNet@2
- displayName: 'Install .net core'
- inputs:
- version: $(dotnetVersion)
- - checkout: none
- - task: DownloadPipelineArtifact@2
- displayName: Download Test Artifact
- inputs:
- buildType: 'current'
- artifactName: '$(testName)-tests'
- targetPath: $(testsFolder)
- - task: DownloadPipelineArtifact@2
- displayName: Download Build Artifact
- inputs:
- buildType: 'current'
- artifactName: Packages
- itemPattern: '**/$(pattern)'
- targetPath: $(Build.ArtifactStagingDirectory)
- - task: ExtractFiles@1
- inputs:
- archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
- destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
- displayName: Extract Package
- - bash: |
- mkdir -p ./bin/
- cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
- displayName: Move Package Contents
- - bash: |
- chmod a+x ${TESTSFOLDER}/test.sh
- ${TESTSFOLDER}/test.sh ${OSNAME} Integration Test
- displayName: Run Integration Tests
- - task: PublishTestResults@2
- inputs:
- testResultsFormat: 'NUnit'
- testResultsFiles: '**/TestResult.xml'
- testRunTitle: '$(testName) Integration Tests'
- failTaskOnFailedTests: true
- failTaskOnMissingResultsFile: true
- displayName: Publish Test Results
-
- - job: Integration_LinuxCore_Postgres14
- displayName: Integration Native LinuxCore with Postgres14 Database
- dependsOn: Prepare
- condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
- variables:
- pattern: 'Radarr.*.linux-core-x64.tar.gz'
- Radarr__Postgres__Host: 'localhost'
- Radarr__Postgres__Port: '5432'
- Radarr__Postgres__User: 'radarr'
- Radarr__Postgres__Password: 'radarr'
-
- pool:
- vmImage: ${{ variables.linuxImage }}
-
- steps:
- - task: UseDotNet@2
- displayName: 'Install .net core'
- inputs:
- version: $(dotnetVersion)
- - checkout: none
- - task: DownloadPipelineArtifact@2
- displayName: Download Test Artifact
- inputs:
- buildType: 'current'
- artifactName: 'linux-x64-tests'
- targetPath: $(testsFolder)
- - task: DownloadPipelineArtifact@2
- displayName: Download Build Artifact
- inputs:
- buildType: 'current'
- artifactName: Packages
- itemPattern: '**/$(pattern)'
- targetPath: $(Build.ArtifactStagingDirectory)
- - task: ExtractFiles@1
- inputs:
- archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
- destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
- displayName: Extract Package
- - bash: |
- mkdir -p ./bin/
- cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
- displayName: Move Package Contents
- - bash: |
- docker run -d --name=postgres14 \
- -e POSTGRES_PASSWORD=radarr \
- -e POSTGRES_USER=radarr \
- -p 5432:5432/tcp \
- -v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
- postgres:14
- displayName: Start postgres
- - bash: |
- chmod a+x ${TESTSFOLDER}/test.sh
- ${TESTSFOLDER}/test.sh Linux Integration Test
- displayName: Run Integration Tests
- - task: PublishTestResults@2
- inputs:
- testResultsFormat: 'NUnit'
- testResultsFiles: '**/TestResult.xml'
- testRunTitle: 'Integration LinuxCore Postgres14 Database Integration Tests'
- failTaskOnFailedTests: true
- failTaskOnMissingResultsFile: true
- displayName: Publish Test Results
-
-
- - job: Integration_LinuxCore_Postgres15
- displayName: Integration Native LinuxCore with Postgres Database
- dependsOn: Prepare
- condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
- variables:
- pattern: 'Radarr.*.linux-core-x64.tar.gz'
- Radarr__Postgres__Host: 'localhost'
- Radarr__Postgres__Port: '5432'
- Radarr__Postgres__User: 'radarr'
- Radarr__Postgres__Password: 'radarr'
-
- pool:
- vmImage: ${{ variables.linuxImage }}
-
- steps:
- - task: UseDotNet@2
- displayName: 'Install .net core'
- inputs:
- version: $(dotnetVersion)
- - checkout: none
- - task: DownloadPipelineArtifact@2
- displayName: Download Test Artifact
- inputs:
- buildType: 'current'
- artifactName: 'linux-x64-tests'
- targetPath: $(testsFolder)
- - task: DownloadPipelineArtifact@2
- displayName: Download Build Artifact
- inputs:
- buildType: 'current'
- artifactName: Packages
- itemPattern: '**/$(pattern)'
- targetPath: $(Build.ArtifactStagingDirectory)
- - task: ExtractFiles@1
- inputs:
- archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
- destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
- displayName: Extract Package
- - bash: |
- mkdir -p ./bin/
- cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
- displayName: Move Package Contents
- - bash: |
- docker run -d --name=postgres15 \
- -e POSTGRES_PASSWORD=radarr \
- -e POSTGRES_USER=radarr \
- -p 5432:5432/tcp \
- -v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
- postgres:15
- displayName: Start postgres
- - bash: |
- chmod a+x ${TESTSFOLDER}/test.sh
- ${TESTSFOLDER}/test.sh Linux Integration Test
- displayName: Run Integration Tests
- - task: PublishTestResults@2
- inputs:
- testResultsFormat: 'NUnit'
- testResultsFiles: '**/TestResult.xml'
- testRunTitle: 'Integration LinuxCore Postgres15 Database Integration Tests'
- failTaskOnFailedTests: true
- failTaskOnMissingResultsFile: true
- displayName: Publish Test Results
-
- - job: Integration_FreeBSD
- displayName: Integration Native FreeBSD
- dependsOn: Prepare
- condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
- workspace:
- clean: all
- variables:
- pattern: 'Radarr.*.freebsd-core-x64.tar.gz'
- pool:
- name: 'FreeBSD'
-
- steps:
- - checkout: none
- - task: DownloadPipelineArtifact@2
- displayName: Download Test Artifact
- inputs:
- buildType: 'current'
- artifactName: 'freebsd-x64-tests'
- targetPath: $(testsFolder)
- - task: DownloadPipelineArtifact@2
- displayName: Download Build Artifact
- inputs:
- buildType: 'current'
- artifactName: Packages
- itemPattern: '**/$(pattern)'
- targetPath: $(Build.ArtifactStagingDirectory)
- - bash: |
- mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
- tar xf ${BUILD_ARTIFACTSTAGINGDIRECTORY}/$(pattern) -C ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
- displayName: Extract Package
- - bash: |
- mkdir -p ./bin/
- cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
- displayName: Move Package Contents
- - bash: |
- chmod a+x ${TESTSFOLDER}/test.sh
- ${TESTSFOLDER}/test.sh Linux Integration Test
- displayName: Run Integration Tests
- - task: PublishTestResults@2
- inputs:
- testResultsFormat: 'NUnit'
- testResultsFiles: '**/TestResult.xml'
- testRunTitle: 'FreeBSD Integration Tests'
- failTaskOnFailedTests: true
- failTaskOnMissingResultsFile: false
- displayName: Publish Test Results
-
- - job: Integration_Docker
- displayName: Integration Docker
- dependsOn: Prepare
- condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
- strategy:
- matrix:
- alpine:
- testName: 'linux-musl-x64'
- artifactName: linux-musl-x64-tests
- containerImage: ghcr.io/servarr/testimages:alpine
- pattern: 'Radarr.*.linux-musl-core-x64.tar.gz'
- pool:
- vmImage: ${{ variables.linuxImage }}
-
- container: $[ variables['containerImage'] ]
-
- timeoutInMinutes: 15
-
- steps:
- - task: UseDotNet@2
- displayName: 'Install .NET'
- inputs:
- version: $(dotnetVersion)
- - checkout: none
- - task: DownloadPipelineArtifact@2
- displayName: Download Test Artifact
- inputs:
- buildType: 'current'
- artifactName: $(artifactName)
- targetPath: $(testsFolder)
- - task: DownloadPipelineArtifact@2
- displayName: Download Build Artifact
- inputs:
- buildType: 'current'
- artifactName: Packages
- itemPattern: '**/$(pattern)'
- targetPath: $(Build.ArtifactStagingDirectory)
- - task: ExtractFiles@1
- inputs:
- archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
- destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
- displayName: Extract Package
- - bash: |
- mkdir -p ./bin/
- cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
- displayName: Move Package Contents
- - bash: |
- chmod a+x ${TESTSFOLDER}/test.sh
- ${TESTSFOLDER}/test.sh Linux Integration Test
- displayName: Run Integration Tests
- - task: PublishTestResults@2
- inputs:
- testResultsFormat: 'NUnit'
- testResultsFiles: '**/TestResult.xml'
- testRunTitle: '$(testName) Integration Tests'
- failTaskOnFailedTests: true
- failTaskOnMissingResultsFile: true
- displayName: Publish Test Results
-
- - stage: Automation
- displayName: Automation
- dependsOn: Packages
-
- jobs:
- - job: Automation
- strategy:
- matrix:
- Linux:
- osName: 'Linux'
- artifactName: 'linux-x64'
- imageName: ${{ variables.linuxImage }}
- pattern: 'Radarr.*.linux-core-x64.tar.gz'
- failBuild: true
- Mac:
- osName: 'Mac'
- artifactName: 'osx-x64'
- imageName: ${{ variables.macImage }}
- pattern: 'Radarr.*.osx-core-x64.tar.gz'
- failBuild: true
- Windows:
- osName: 'Windows'
- artifactName: 'win-x64'
- imageName: ${{ variables.windowsImage }}
- pattern: 'Radarr.*.windows-core-x64.zip'
- failBuild: true
-
- pool:
- vmImage: $(imageName)
-
- steps:
- - task: UseDotNet@2
- displayName: 'Install .net core'
- inputs:
- version: $(dotnetVersion)
- - checkout: none
- - task: DownloadPipelineArtifact@2
- displayName: Download Test Artifact
- inputs:
- buildType: 'current'
- artifactName: '$(artifactName)-tests'
- targetPath: $(testsFolder)
- - task: DownloadPipelineArtifact@2
- displayName: Download Build Artifact
- inputs:
- buildType: 'current'
- artifactName: Packages
- itemPattern: '**/$(pattern)'
- targetPath: $(Build.ArtifactStagingDirectory)
- - task: ExtractFiles@1
- inputs:
- archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
- destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
- displayName: Extract Package
- - bash: |
- mkdir -p ./bin/
- cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
- displayName: Move Package Contents
- - bash: |
- chmod a+x ${TESTSFOLDER}/test.sh
- ${TESTSFOLDER}/test.sh ${OSNAME} Automation Test
- displayName: Run Automation Tests
- - task: CopyFiles@2
- displayName: 'Copy Screenshot to: $(Build.ArtifactStagingDirectory)'
- inputs:
- SourceFolder: '$(Build.SourcesDirectory)'
- Contents: |
- **/*_test_screenshot.png
- TargetFolder: '$(Build.ArtifactStagingDirectory)/screenshots'
- - publish: $(Build.ArtifactStagingDirectory)/screenshots
- artifact: '$(osName)AutomationScreenshots'
- displayName: Publish Screenshot Bundle
- condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
- - task: PublishTestResults@2
- inputs:
- testResultsFormat: 'NUnit'
- testResultsFiles: '**/TestResult.xml'
- testRunTitle: '$(osName) Automation Tests'
- failTaskOnFailedTests: $(failBuild)
- failTaskOnMissingResultsFile: $(failBuild)
- displayName: Publish Test Results
-
- - stage: Analyze
- dependsOn:
- - Setup
- displayName: Analyze
-
- jobs:
- - job: Prepare
- pool:
- vmImage: ${{ variables.linuxImage }}
- steps:
- - checkout: none
- - task: DownloadPipelineArtifact@2
- inputs:
- buildType: 'current'
- artifactName: 'not_backend_update'
- targetPath: '.'
- - bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
- name: setVar
-
- - job: Lint_Frontend
- displayName: Lint Frontend
- strategy:
- matrix:
- Linux:
- osName: 'Linux'
- imageName: ${{ variables.linuxImage }}
- Windows:
- osName: 'Windows'
- imageName: ${{ variables.windowsImage }}
- pool:
- vmImage: $(imageName)
- steps:
- - task: UseNode@1
- displayName: Set Node.js version
- inputs:
- version: $(nodeVersion)
- - checkout: self
- submodules: true
- fetchDepth: 1
- - task: Cache@2
- inputs:
- key: 'yarn | "$(osName)" | yarn.lock'
- restoreKeys: |
- yarn | "$(osName)"
- path: $(yarnCacheFolder)
- displayName: Cache Yarn packages
- - bash: ./build.sh --lint
- displayName: Lint Radarr Frontend
- env:
- FORCE_COLOR: 0
- YARN_CACHE_FOLDER: $(yarnCacheFolder)
-
- - job: Analyze_Frontend
- displayName: Frontend
- condition: eq(variables['System.PullRequest.IsFork'], 'False')
- pool:
- vmImage: ${{ variables.windowsImage }}
- steps:
- - checkout: self # Need history for Sonar analysis
- - task: SonarCloudPrepare@3
- env:
- SONAR_SCANNER_OPTS: ''
- inputs:
- SonarCloud: 'SonarCloud'
- organization: 'radarr'
- scannerMode: 'cli'
- configMode: 'manual'
- cliProjectKey: 'Radarr_Radarr.UI'
- cliProjectName: 'RadarrUI'
- cliProjectVersion: '$(radarrVersion)'
- cliSources: './frontend'
- - task: SonarCloudAnalyze@3
-
- - job: Api_Docs
- displayName: API Docs
- dependsOn: Prepare
- condition: |
- and
- (
- and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
- and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
- )
-
- pool:
- vmImage: ${{ variables.windowsImage }}
-
- steps:
- - task: UseDotNet@2
- displayName: 'Install .net core'
- inputs:
- version: $(dotnetVersion)
- - checkout: self
- submodules: true
- persistCredentials: true
- fetchDepth: 1
- - bash: ./docs.sh Windows
- displayName: Create openapi.json
- - bash: |
- git config --global user.email "development@lidarr.audio"
- git config --global user.name "Servarr"
- git checkout -b api-docs
- git add .
- git status
- if git status | grep modified
- then
- git commit -am 'Automated API Docs update'
- git push -f --set-upstream origin api-docs
- curl -X POST -H "Authorization: token ${GITHUBTOKEN}" -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/radarr/radarr/pulls -d '{"head":"api-docs","base":"develop","title":"Update API docs"}'
- else
- echo "No changes since last run"
- fi
- displayName: Commit API Doc Change
- continueOnError: true
- env:
- GITHUBTOKEN: $(githubToken)
- - task: CopyFiles@2
- displayName: 'Copy openapi.json to: $(Build.ArtifactStagingDirectory)'
- inputs:
- SourceFolder: '$(Build.SourcesDirectory)'
- Contents: |
- **/*openapi.json
- TargetFolder: '$(Build.ArtifactStagingDirectory)/api_docs'
- - publish: $(Build.ArtifactStagingDirectory)/api_docs
- artifact: 'APIDocs'
- displayName: Publish API Docs Bundle
- condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
-
- - job: Analyze_Backend
- displayName: Backend
- dependsOn: Prepare
- condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
-
- variables:
- disable.coverage.autogenerate: 'true'
- EnableAnalyzers: 'false'
-
- pool:
- vmImage: ${{ variables.windowsImage }}
-
- steps:
- - task: UseDotNet@2
- displayName: 'Install .net core'
- inputs:
- version: $(dotnetVersion)
- - checkout: self # Need history for Sonar analysis
- submodules: true
- - powershell: Set-Service SCardSvr -StartupType Manual
- displayName: Enable Windows Test Service
- - task: SonarCloudPrepare@3
- condition: eq(variables['System.PullRequest.IsFork'], 'False')
- inputs:
- SonarCloud: 'SonarCloud'
- organization: 'radarr'
- scannerMode: 'dotnet'
- projectKey: 'Radarr_Radarr'
- projectName: 'Radarr'
- projectVersion: '$(radarrVersion)'
- extraProperties: |
- sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,**/ExternalModules/**,./src/Libraries/**
- sonar.coverage.exclusions=**/Radarr.Api.V3/**/*
- sonar.cs.cobertura.reportsPaths=$(Build.SourcesDirectory)/CoverageResults/**/coverage.cobertura.xml
- sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
- - bash: |
- ./build.sh --backend -f net8.0 -r win-x64
- TEST_DIR=_tests/net8.0/win-x64/publish/ ./test.sh Windows Unit Coverage
- displayName: Coverage Unit Tests
- - task: SonarCloudAnalyze@3
- condition: eq(variables['System.PullRequest.IsFork'], 'False')
- displayName: Publish SonarCloud Results
- - task: reportgenerator@5
- displayName: Generate Coverage Report
- inputs:
- reports: '$(Build.SourcesDirectory)/CoverageResults/**/coverage.cobertura.xml'
- targetdir: '$(Build.SourcesDirectory)/CoverageResults/combined'
- reporttypes: 'HtmlInline_AzurePipelines;Cobertura;Badges'
- publishCodeCoverageResults: true
- sourcedirs: src
-
- - stage: Report_Out
- dependsOn:
- - Analyze
- - Installer
- - Unit_Test
- - Integration
- - Automation
- condition: eq(variables['system.pullrequest.isfork'], false)
- displayName: Build Status Report
- jobs:
- - job:
- displayName: Discord Notification
- pool:
- vmImage: ${{ variables.linuxImage }}
- steps:
- - task: DownloadPipelineArtifact@2
- continueOnError: true
- displayName: Download Screenshot Artifact
- inputs:
- buildType: 'current'
- artifactName: 'WindowsAutomationScreenshots'
- targetPath: $(Build.SourcesDirectory)
- - checkout: none
- - pwsh: |
- iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/Servarr/AzureDiscordNotify/master/DiscordNotify.ps1'))
- env:
- SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- DISCORDCHANNELID: $(discordChannelId)
- DISCORDWEBHOOKKEY: $(discordWebhookKey)
- DISCORDTHREADID: $(discordThreadId)
diff --git a/frontend/.vscode/extensions.json b/frontend/.vscode/extensions.json
deleted file mode 100644
index 0e005a3cd8..0000000000
--- a/frontend/.vscode/extensions.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "recommendations": [
- "stylelint.vscode-stylelint",
- "dbaeumer.vscode-eslint",
- "esbenp.prettier-vscode"
- ]
-}
\ No newline at end of file
diff --git a/frontend/.vscode/settings.json b/frontend/.vscode/settings.json
deleted file mode 100644
index 8da95337f6..0000000000
--- a/frontend/.vscode/settings.json
+++ /dev/null
@@ -1,23 +0,0 @@
-// Place your settings in this file to overwrite default and user settings.
-{
- "files.insertFinalNewline": true,
-
- "files.exclude": {
- "**/node_modules": true,
- "**/*.d.css": true
- },
-
- "editor.formatOnSave": false,
- "editor.codeActionsOnSave": {
- "source.fixAll": "explicit"
- },
-
- "typescript.preferences.quoteStyle": "single",
-
- "eslint.validate": [
- "javascript",
- "javascriptreact",
- "typescript",
- "typescriptreact"
- ],
-}
diff --git a/package.json b/package.json
index f3d02bcdc6..56618bffe9 100644
--- a/package.json
+++ b/package.json
@@ -55,8 +55,6 @@
"prop-types": "15.8.1",
"qs": "6.13.0",
"react": "18.3.1",
- "react-addons-shallow-compare": "15.6.3",
- "react-async-script": "1.2.0",
"react-autosuggest": "10.1.0",
"react-custom-scrollbars-2": "4.5.0",
"react-dnd": "14.0.4",
diff --git a/src/.idea/.idea.NzbDrone/.idea/.name b/src/.idea/.idea.NzbDrone/.idea/.name
deleted file mode 100644
index 37baec0ab3..0000000000
--- a/src/.idea/.idea.NzbDrone/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-NzbDrone
\ No newline at end of file
diff --git a/src/.idea/.idea.NzbDrone/.idea/encodings.xml b/src/.idea/.idea.NzbDrone/.idea/encodings.xml
deleted file mode 100644
index 15a15b218a..0000000000
--- a/src/.idea/.idea.NzbDrone/.idea/encodings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/src/.idea/.idea.NzbDrone/.idea/indexLayout.xml b/src/.idea/.idea.NzbDrone/.idea/indexLayout.xml
deleted file mode 100644
index 27ba142e96..0000000000
--- a/src/.idea/.idea.NzbDrone/.idea/indexLayout.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/.idea/.idea.NzbDrone/.idea/misc.xml b/src/.idea/.idea.NzbDrone/.idea/misc.xml
deleted file mode 100644
index 1d8c84d0af..0000000000
--- a/src/.idea/.idea.NzbDrone/.idea/misc.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/.idea/.idea.NzbDrone/.idea/vcs.xml b/src/.idea/.idea.NzbDrone/.idea/vcs.xml
deleted file mode 100644
index ea6ca8297f..0000000000
--- a/src/.idea/.idea.NzbDrone/.idea/vcs.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/NzbDrone.Console/ConsoleApp.cs b/src/NzbDrone.Console/ConsoleApp.cs
index 65c22338ee..6107e22554 100644
--- a/src/NzbDrone.Console/ConsoleApp.cs
+++ b/src/NzbDrone.Console/ConsoleApp.cs
@@ -53,7 +53,7 @@ public static void Main(string[] args)
{
System.Console.WriteLine("");
System.Console.WriteLine("");
- Logger.Fatal(ex.Message + " This can happen if another instance of Radarr is already running another application is using the same port (default: 7878) or the user has insufficient permissions");
+ Logger.Fatal(ex.Message + " This can happen if another instance of Aletheia is already running another application is using the same port (default: 7878) or the user has insufficient permissions");
Exit(ExitCodes.RecoverableFailure, startupArgs);
}
catch (IOException ex)
@@ -62,7 +62,7 @@ public static void Main(string[] args)
{
System.Console.WriteLine("");
System.Console.WriteLine("");
- Logger.Fatal(ex.Message + " This can happen if another instance of Radarr is already running another application is using the same port (default: 7878) or the user has insufficient permissions");
+ Logger.Fatal(ex.Message + " This can happen if another instance of Aletheia is already running another application is using the same port (default: 7878) or the user has insufficient permissions");
Exit(ExitCodes.RecoverableFailure, startupArgs);
}
else
diff --git a/src/NzbDrone.Core/Audiobooks/AudiobookService.cs b/src/NzbDrone.Core/Audiobooks/AudiobookService.cs
index c6c1ed2d37..fa949c88c5 100644
--- a/src/NzbDrone.Core/Audiobooks/AudiobookService.cs
+++ b/src/NzbDrone.Core/Audiobooks/AudiobookService.cs
@@ -1,17 +1,16 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using NzbDrone.Core.Audiobooks.Events;
using NzbDrone.Core.Datastore;
+using NzbDrone.Core.MediaItems;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Audiobooks
{
- public interface IAudiobookService
+ public interface IAudiobookService : IBaseMediaService
{
Audiobook GetAudiobook(int audiobookId);
List GetAudiobooks(IEnumerable audiobookIds);
- PagingSpec Paged(PagingSpec pagingSpec);
Audiobook AddAudiobook(Audiobook newAudiobook);
List AddAudiobooks(List newAudiobooks);
Audiobook FindByIsbn(string isbn);
@@ -34,159 +33,61 @@ public interface IAudiobookService
bool AudiobookPathExists(string folder);
}
- public class AudiobookService : IAudiobookService
+ public class AudiobookService : BaseMediaService, IAudiobookService
{
private readonly IAudiobookRepository _audiobookRepository;
private readonly IEventAggregator _eventAggregator;
- public AudiobookService(IAudiobookRepository audiobookRepository,
- IEventAggregator eventAggregator)
+ public AudiobookService(IAudiobookRepository audiobookRepository, IEventAggregator eventAggregator)
{
_audiobookRepository = audiobookRepository;
_eventAggregator = eventAggregator;
}
- public Audiobook GetAudiobook(int audiobookId)
- {
- return _audiobookRepository.Get(audiobookId);
- }
+ protected override IBasicRepository Repository => _audiobookRepository;
+ protected override IEventAggregator EventAggregator => _eventAggregator;
- public List GetAudiobooks(IEnumerable audiobookIds)
- {
- return _audiobookRepository.Get(audiobookIds).ToList();
- }
-
- public PagingSpec Paged(PagingSpec pagingSpec)
- {
- return _audiobookRepository.GetPaged(pagingSpec);
- }
-
- public Audiobook AddAudiobook(Audiobook newAudiobook)
- {
- newAudiobook.Added = DateTime.UtcNow;
- var audiobook = _audiobookRepository.Insert(newAudiobook);
-
- _eventAggregator.PublishEvent(new AudiobookAddedEvent(GetAudiobook(audiobook.Id)));
-
- return audiobook;
- }
-
- public List AddAudiobooks(List newAudiobooks)
- {
- var now = DateTime.UtcNow;
- foreach (var audiobook in newAudiobooks)
- {
- audiobook.Added = now;
- }
-
- _audiobookRepository.InsertMany(newAudiobooks);
-
- _eventAggregator.PublishEvent(new AudiobooksImportedEvent(newAudiobooks));
-
- return newAudiobooks;
- }
-
- public Audiobook FindByIsbn(string isbn)
- {
- return _audiobookRepository.FindByIsbn(isbn);
- }
-
- public Audiobook FindByIsbn13(string isbn13)
- {
- return _audiobookRepository.FindByIsbn13(isbn13);
- }
-
- public Audiobook FindByAsin(string asin)
- {
- return _audiobookRepository.FindByAsin(asin);
- }
-
- public Audiobook FindByForeignId(string foreignAudiobookId)
- {
- return _audiobookRepository.FindByForeignId(foreignAudiobookId);
- }
-
- public Audiobook FindByPath(string path)
- {
- return _audiobookRepository.FindByPath(path);
- }
-
- public List FindByAuthorId(int authorId)
- {
- return _audiobookRepository.FindByAuthorId(authorId);
- }
-
- public List FindBySeriesId(int seriesId)
- {
- return _audiobookRepository.FindBySeriesId(seriesId);
- }
-
- public List FindByBookId(int bookId)
- {
- return _audiobookRepository.FindByBookId(bookId);
- }
-
- public List FindByNarrator(string narrator)
- {
- return _audiobookRepository.FindByNarrator(narrator);
- }
-
- public Dictionary AllAudiobookPaths()
- {
- return _audiobookRepository.AllAudiobookPaths();
- }
+ public Audiobook GetAudiobook(int audiobookId) => Get(audiobookId);
+ public List GetAudiobooks(IEnumerable audiobookIds) => Get(audiobookIds);
+ public Audiobook AddAudiobook(Audiobook newAudiobook) => Add(newAudiobook);
+ public List AddAudiobooks(List newAudiobooks) => AddMany(newAudiobooks);
+ public void DeleteAudiobook(int audiobookId, bool deleteFiles) => Delete(audiobookId, deleteFiles);
+ public void DeleteAudiobooks(List audiobookIds, bool deleteFiles) => DeleteMany(audiobookIds, deleteFiles);
+ public List GetAllAudiobooks() => GetAll();
+ public Audiobook UpdateAudiobook(Audiobook audiobook) => Update(audiobook);
+ public List UpdateAudiobooks(List audiobooks) => UpdateMany(audiobooks);
+ public Audiobook FindByIsbn(string isbn) => _audiobookRepository.FindByIsbn(isbn);
+ public Audiobook FindByIsbn13(string isbn13) => _audiobookRepository.FindByIsbn13(isbn13);
+ public Audiobook FindByAsin(string asin) => _audiobookRepository.FindByAsin(asin);
+ public Audiobook FindByForeignId(string foreignAudiobookId) => _audiobookRepository.FindByForeignId(foreignAudiobookId);
+ public Audiobook FindByPath(string path) => _audiobookRepository.FindByPath(path);
+ public List FindByAuthorId(int authorId) => _audiobookRepository.FindByAuthorId(authorId);
+ public List FindBySeriesId(int seriesId) => _audiobookRepository.FindBySeriesId(seriesId);
+ public List FindByBookId(int bookId) => _audiobookRepository.FindByBookId(bookId);
+ public List FindByNarrator(string narrator) => _audiobookRepository.FindByNarrator(narrator);
+ public Dictionary AllAudiobookPaths() => _audiobookRepository.AllAudiobookPaths();
public List GetAudiobooksBetweenDates(DateTime start, DateTime end, bool includeUnmonitored)
- {
- return _audiobookRepository.AudiobooksBetweenDates(start, end, includeUnmonitored);
- }
+ => _audiobookRepository.AudiobooksBetweenDates(start, end, includeUnmonitored);
+ public Dictionary> AllAudiobookTags() => _audiobookRepository.AllAudiobookTags();
+ public bool AudiobookPathExists(string folder) => _audiobookRepository.AudiobookPathExists(folder);
- public void DeleteAudiobook(int audiobookId, bool deleteFiles)
- {
- var audiobook = _audiobookRepository.Get(audiobookId);
- _audiobookRepository.Delete(audiobookId);
- _eventAggregator.PublishEvent(new AudiobookDeletedEvent(audiobook, deleteFiles));
- }
+ protected override void OnItemAdded(Audiobook item)
+ => _eventAggregator.PublishEvent(new AudiobookAddedEvent(item));
- public void DeleteAudiobooks(List audiobookIds, bool deleteFiles)
- {
- var audiobooks = _audiobookRepository.Get(audiobookIds).ToList();
- _audiobookRepository.DeleteMany(audiobookIds);
- _eventAggregator.PublishEvent(new AudiobooksDeletedEvent(audiobooks, deleteFiles));
- }
+ protected override void OnItemsImported(List items)
+ => _eventAggregator.PublishEvent(new AudiobooksImportedEvent(items));
- public List GetAllAudiobooks()
- {
- return _audiobookRepository.All().ToList();
- }
+ protected override void OnItemDeleted(Audiobook item, bool deleteFiles)
+ => _eventAggregator.PublishEvent(new AudiobookDeletedEvent(item, deleteFiles));
- public Dictionary> AllAudiobookTags()
- {
- return _audiobookRepository.AllAudiobookTags();
- }
+ protected override void OnItemsDeleted(List items, bool deleteFiles)
+ => _eventAggregator.PublishEvent(new AudiobooksDeletedEvent(items, deleteFiles));
- public Audiobook UpdateAudiobook(Audiobook audiobook)
- {
- var storedAudiobook = GetAudiobook(audiobook.Id);
- var updatedAudiobook = _audiobookRepository.Update(audiobook);
+ protected override void OnItemEdited(Audiobook updated, Audiobook stored)
+ => _eventAggregator.PublishEvent(new AudiobookEditedEvent(updated, stored));
- _eventAggregator.PublishEvent(new AudiobookEditedEvent(updatedAudiobook, storedAudiobook));
-
- return updatedAudiobook;
- }
-
- public List UpdateAudiobooks(List audiobooks)
- {
- _audiobookRepository.UpdateMany(audiobooks);
-
- _eventAggregator.PublishEvent(new AudiobooksBulkEditedEvent(audiobooks));
-
- return audiobooks;
- }
-
- public bool AudiobookPathExists(string folder)
- {
- return _audiobookRepository.AudiobookPathExists(folder);
- }
+ protected override void OnItemsBulkEdited(List items)
+ => _eventAggregator.PublishEvent(new AudiobooksBulkEditedEvent(items));
}
}
diff --git a/src/NzbDrone.Core/Books/BookService.cs b/src/NzbDrone.Core/Books/BookService.cs
index 2f779f5baa..14e5e9ece1 100644
--- a/src/NzbDrone.Core/Books/BookService.cs
+++ b/src/NzbDrone.Core/Books/BookService.cs
@@ -1,17 +1,16 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using NzbDrone.Core.Books.Events;
using NzbDrone.Core.Datastore;
+using NzbDrone.Core.MediaItems;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Books
{
- public interface IBookService
+ public interface IBookService : IBaseMediaService
{
Book GetBook(int bookId);
List GetBooks(IEnumerable bookIds);
- PagingSpec Paged(PagingSpec pagingSpec);
Book AddBook(Book newBook);
List AddBooks(List newBooks);
Book FindByIsbn(string isbn);
@@ -32,149 +31,59 @@ public interface IBookService
bool BookPathExists(string folder);
}
- public class BookService : IBookService
+ public class BookService : BaseMediaService, IBookService
{
private readonly IBookRepository _bookRepository;
private readonly IEventAggregator _eventAggregator;
- public BookService(IBookRepository bookRepository,
- IEventAggregator eventAggregator)
+ public BookService(IBookRepository bookRepository, IEventAggregator eventAggregator)
{
_bookRepository = bookRepository;
_eventAggregator = eventAggregator;
}
- public Book GetBook(int bookId)
- {
- return _bookRepository.Get(bookId);
- }
+ protected override IBasicRepository Repository => _bookRepository;
+ protected override IEventAggregator EventAggregator => _eventAggregator;
- public List GetBooks(IEnumerable bookIds)
- {
- return _bookRepository.Get(bookIds).ToList();
- }
-
- public PagingSpec Paged(PagingSpec pagingSpec)
- {
- return _bookRepository.GetPaged(pagingSpec);
- }
-
- public Book AddBook(Book newBook)
- {
- newBook.Added = DateTime.UtcNow;
- var book = _bookRepository.Insert(newBook);
-
- _eventAggregator.PublishEvent(new BookAddedEvent(GetBook(book.Id)));
-
- return book;
- }
-
- public List AddBooks(List newBooks)
- {
- var now = DateTime.UtcNow;
- foreach (var book in newBooks)
- {
- book.Added = now;
- }
-
- _bookRepository.InsertMany(newBooks);
-
- _eventAggregator.PublishEvent(new BooksImportedEvent(newBooks));
-
- return newBooks;
- }
-
- public Book FindByIsbn(string isbn)
- {
- return _bookRepository.FindByIsbn(isbn);
- }
-
- public Book FindByIsbn13(string isbn13)
- {
- return _bookRepository.FindByIsbn13(isbn13);
- }
-
- public Book FindByAsin(string asin)
- {
- return _bookRepository.FindByAsin(asin);
- }
-
- public Book FindByForeignId(string foreignBookId)
- {
- return _bookRepository.FindByForeignId(foreignBookId);
- }
-
- public Book FindByPath(string path)
- {
- return _bookRepository.FindByPath(path);
- }
-
- public List FindByAuthorId(int authorId)
- {
- return _bookRepository.FindByAuthorId(authorId);
- }
-
- public List FindBySeriesId(int seriesId)
- {
- return _bookRepository.FindBySeriesId(seriesId);
- }
-
- public Dictionary AllBookPaths()
- {
- return _bookRepository.AllBookPaths();
- }
+ public Book GetBook(int bookId) => Get(bookId);
+ public List GetBooks(IEnumerable bookIds) => Get(bookIds);
+ public Book AddBook(Book newBook) => Add(newBook);
+ public List AddBooks(List newBooks) => AddMany(newBooks);
+ public void DeleteBook(int bookId, bool deleteFiles) => Delete(bookId, deleteFiles);
+ public void DeleteBooks(List bookIds, bool deleteFiles) => DeleteMany(bookIds, deleteFiles);
+ public List GetAllBooks() => GetAll();
+ public Book UpdateBook(Book book) => Update(book);
+ public List UpdateBooks(List books) => UpdateMany(books);
+ public Book FindByIsbn(string isbn) => _bookRepository.FindByIsbn(isbn);
+ public Book FindByIsbn13(string isbn13) => _bookRepository.FindByIsbn13(isbn13);
+ public Book FindByAsin(string asin) => _bookRepository.FindByAsin(asin);
+ public Book FindByForeignId(string foreignBookId) => _bookRepository.FindByForeignId(foreignBookId);
+ public Book FindByPath(string path) => _bookRepository.FindByPath(path);
+ public List FindByAuthorId(int authorId) => _bookRepository.FindByAuthorId(authorId);
+ public List FindBySeriesId(int seriesId) => _bookRepository.FindBySeriesId(seriesId);
+ public Dictionary AllBookPaths() => _bookRepository.AllBookPaths();
public List GetBooksBetweenDates(DateTime start, DateTime end, bool includeUnmonitored)
- {
- return _bookRepository.BooksBetweenDates(start, end, includeUnmonitored);
- }
+ => _bookRepository.BooksBetweenDates(start, end, includeUnmonitored);
+ public Dictionary> AllBookTags() => _bookRepository.AllBookTags();
+ public bool BookPathExists(string folder) => _bookRepository.BookPathExists(folder);
- public void DeleteBook(int bookId, bool deleteFiles)
- {
- var book = _bookRepository.Get(bookId);
- _bookRepository.Delete(bookId);
- _eventAggregator.PublishEvent(new BookDeletedEvent(book, deleteFiles));
- }
+ protected override void OnItemAdded(Book item)
+ => _eventAggregator.PublishEvent(new BookAddedEvent(item));
- public void DeleteBooks(List bookIds, bool deleteFiles)
- {
- var books = _bookRepository.Get(bookIds).ToList();
- _bookRepository.DeleteMany(bookIds);
- _eventAggregator.PublishEvent(new BooksDeletedEvent(books, deleteFiles));
- }
+ protected override void OnItemsImported(List items)
+ => _eventAggregator.PublishEvent(new BooksImportedEvent(items));
- public List GetAllBooks()
- {
- return _bookRepository.All().ToList();
- }
+ protected override void OnItemDeleted(Book item, bool deleteFiles)
+ => _eventAggregator.PublishEvent(new BookDeletedEvent(item, deleteFiles));
- public Dictionary> AllBookTags()
- {
- return _bookRepository.AllBookTags();
- }
+ protected override void OnItemsDeleted(List items, bool deleteFiles)
+ => _eventAggregator.PublishEvent(new BooksDeletedEvent(items, deleteFiles));
- public Book UpdateBook(Book book)
- {
- var storedBook = GetBook(book.Id);
- var updatedBook = _bookRepository.Update(book);
+ protected override void OnItemEdited(Book updated, Book stored)
+ => _eventAggregator.PublishEvent(new BookEditedEvent(updated, stored));
- _eventAggregator.PublishEvent(new BookEditedEvent(updatedBook, storedBook));
-
- return updatedBook;
- }
-
- public List UpdateBooks(List books)
- {
- _bookRepository.UpdateMany(books);
-
- _eventAggregator.PublishEvent(new BooksBulkEditedEvent(books));
-
- return books;
- }
-
- public bool BookPathExists(string folder)
- {
- return _bookRepository.BookPathExists(folder);
- }
+ protected override void OnItemsBulkEdited(List items)
+ => _eventAggregator.PublishEvent(new BooksBulkEditedEvent(items));
}
}
diff --git a/src/NzbDrone.Core/Localization/Core/bs.json b/src/NzbDrone.Core/Localization/Core/bs.json
deleted file mode 100644
index 0967ef424b..0000000000
--- a/src/NzbDrone.Core/Localization/Core/bs.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/src/NzbDrone.Core/Localization/Core/es_MX.json b/src/NzbDrone.Core/Localization/Core/es_MX.json
deleted file mode 100644
index 0967ef424b..0000000000
--- a/src/NzbDrone.Core/Localization/Core/es_MX.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/src/NzbDrone.Core/Localization/Core/et.json b/src/NzbDrone.Core/Localization/Core/et.json
deleted file mode 100644
index 0967ef424b..0000000000
--- a/src/NzbDrone.Core/Localization/Core/et.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/src/NzbDrone.Core/Localization/Core/lt.json b/src/NzbDrone.Core/Localization/Core/lt.json
deleted file mode 100644
index 0967ef424b..0000000000
--- a/src/NzbDrone.Core/Localization/Core/lt.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/src/NzbDrone.Core/Localization/Core/sr.json b/src/NzbDrone.Core/Localization/Core/sr.json
deleted file mode 100644
index 0967ef424b..0000000000
--- a/src/NzbDrone.Core/Localization/Core/sr.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/src/NzbDrone.Core/Localization/Core/ta.json b/src/NzbDrone.Core/Localization/Core/ta.json
deleted file mode 100644
index 0967ef424b..0000000000
--- a/src/NzbDrone.Core/Localization/Core/ta.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/src/NzbDrone.Core/MediaItems/BaseMediaService.cs b/src/NzbDrone.Core/MediaItems/BaseMediaService.cs
new file mode 100644
index 0000000000..3492df9a94
--- /dev/null
+++ b/src/NzbDrone.Core/MediaItems/BaseMediaService.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NzbDrone.Core.Datastore;
+using NzbDrone.Core.Messaging.Events;
+
+namespace NzbDrone.Core.MediaItems
+{
+ public interface IBaseMediaService where T : ModelBase
+ {
+ T Get(int id);
+ List Get(IEnumerable ids);
+ PagingSpec Paged(PagingSpec pagingSpec);
+ T Add(T item);
+ List AddMany(List items);
+ void Delete(int id, bool deleteFiles);
+ void DeleteMany(List ids, bool deleteFiles);
+ List GetAll();
+ T Update(T item);
+ List UpdateMany(List items);
+ }
+
+ public abstract class BaseMediaService : IBaseMediaService where T : ModelBase
+ {
+ protected abstract IBasicRepository Repository { get; }
+ protected virtual IEventAggregator EventAggregator => null;
+
+ public T Get(int id)
+ {
+ return Repository.Get(id);
+ }
+
+ public List Get(IEnumerable ids)
+ {
+ return Repository.Get(ids).ToList();
+ }
+
+ public PagingSpec Paged(PagingSpec pagingSpec)
+ {
+ return Repository.GetPaged(pagingSpec);
+ }
+
+ public virtual T Add(T item)
+ {
+ SetAddedTimestamp(item);
+ var inserted = Repository.Insert(item);
+ OnItemAdded(Get(inserted.Id));
+ return inserted;
+ }
+
+ public virtual List AddMany(List items)
+ {
+ var now = DateTime.UtcNow;
+ foreach (var item in items)
+ {
+ SetAddedTimestamp(item, now);
+ }
+
+ Repository.InsertMany(items);
+ OnItemsImported(items);
+ return items;
+ }
+
+ public virtual void Delete(int id, bool deleteFiles)
+ {
+ var item = Repository.Get(id);
+ Repository.Delete(id);
+ OnItemDeleted(item, deleteFiles);
+ }
+
+ public virtual void DeleteMany(List ids, bool deleteFiles)
+ {
+ var items = Repository.Get(ids).ToList();
+ Repository.DeleteMany(ids);
+ OnItemsDeleted(items, deleteFiles);
+ }
+
+ public List GetAll()
+ {
+ return Repository.All().ToList();
+ }
+
+ public virtual T Update(T item)
+ {
+ var stored = Get(item.Id);
+ var updated = Repository.Update(item);
+ OnItemEdited(updated, stored);
+ return updated;
+ }
+
+ public virtual List UpdateMany(List items)
+ {
+ Repository.UpdateMany(items);
+ OnItemsBulkEdited(items);
+ return items;
+ }
+
+ protected virtual void SetAddedTimestamp(T item, DateTime? timestamp = null)
+ {
+ var ts = timestamp ?? DateTime.UtcNow;
+
+ if (item is MediaItem mediaItem)
+ {
+ mediaItem.Added = ts;
+ return;
+ }
+
+ var addedProperty = item.GetType().GetProperty("Added");
+ if (addedProperty != null && addedProperty.PropertyType == typeof(DateTime) && addedProperty.CanWrite)
+ {
+ addedProperty.SetValue(item, ts);
+ }
+ }
+
+ protected virtual void OnItemAdded(T item) { }
+ protected virtual void OnItemsImported(List items) { }
+ protected virtual void OnItemDeleted(T item, bool deleteFiles) { }
+ protected virtual void OnItemsDeleted(List items, bool deleteFiles) { }
+ protected virtual void OnItemEdited(T updated, T stored) { }
+ protected virtual void OnItemsBulkEdited(List items) { }
+ }
+}
diff --git a/src/NzbDrone.Core/Music/AlbumService.cs b/src/NzbDrone.Core/Music/AlbumService.cs
index 7edc4cf132..791e4adf3a 100644
--- a/src/NzbDrone.Core/Music/AlbumService.cs
+++ b/src/NzbDrone.Core/Music/AlbumService.cs
@@ -1,15 +1,14 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using NzbDrone.Core.Datastore;
+using NzbDrone.Core.MediaItems;
namespace NzbDrone.Core.Music
{
- public interface IAlbumService
+ public interface IAlbumService : IBaseMediaService
{
Album GetAlbum(int albumId);
List GetAlbums(IEnumerable albumIds);
- PagingSpec Paged(PagingSpec pagingSpec);
Album AddAlbum(Album newAlbum);
List AddAlbums(List newAlbums);
Album FindByForeignId(string foreignAlbumId);
@@ -26,7 +25,7 @@ public interface IAlbumService
bool AlbumPathExists(string folder);
}
- public class AlbumService : IAlbumService
+ public class AlbumService : BaseMediaService, IAlbumService
{
private readonly IAlbumRepository _albumRepository;
@@ -35,98 +34,25 @@ public AlbumService(IAlbumRepository albumRepository)
_albumRepository = albumRepository;
}
- public Album GetAlbum(int albumId)
- {
- return _albumRepository.Get(albumId);
- }
+ protected override IBasicRepository Repository => _albumRepository;
- public List GetAlbums(IEnumerable albumIds)
- {
- return _albumRepository.Get(albumIds).ToList();
- }
-
- public PagingSpec Paged(PagingSpec pagingSpec)
- {
- return _albumRepository.GetPaged(pagingSpec);
- }
-
- public Album AddAlbum(Album newAlbum)
- {
- newAlbum.Added = DateTime.UtcNow;
- return _albumRepository.Insert(newAlbum);
- }
-
- public List AddAlbums(List newAlbums)
- {
- var now = DateTime.UtcNow;
- foreach (var album in newAlbums)
- {
- album.Added = now;
- }
-
- _albumRepository.InsertMany(newAlbums);
- return newAlbums;
- }
-
- public Album FindByForeignId(string foreignAlbumId)
- {
- return _albumRepository.FindByForeignId(foreignAlbumId);
- }
-
- public Album FindByPath(string path)
- {
- return _albumRepository.FindByPath(path);
- }
-
- public List FindByArtistId(int artistId)
- {
- return _albumRepository.FindByArtistId(artistId);
- }
-
- public Dictionary AllAlbumPaths()
- {
- return _albumRepository.AllAlbumPaths();
- }
+ public Album GetAlbum(int albumId) => Get(albumId);
+ public List GetAlbums(IEnumerable albumIds) => Get(albumIds);
+ public Album AddAlbum(Album newAlbum) => Add(newAlbum);
+ public List AddAlbums(List newAlbums) => AddMany(newAlbums);
+ public void DeleteAlbum(int albumId, bool deleteFiles) => Delete(albumId, deleteFiles);
+ public void DeleteAlbums(List albumIds, bool deleteFiles) => DeleteMany(albumIds, deleteFiles);
+ public List GetAllAlbums() => GetAll();
+ public Album UpdateAlbum(Album album) => Update(album);
+ public List UpdateAlbums(List albums) => UpdateMany(albums);
+ public Album FindByForeignId(string foreignAlbumId) => _albumRepository.FindByForeignId(foreignAlbumId);
+ public Album FindByPath(string path) => _albumRepository.FindByPath(path);
+ public List FindByArtistId(int artistId) => _albumRepository.FindByArtistId(artistId);
+ public Dictionary AllAlbumPaths() => _albumRepository.AllAlbumPaths();
public List GetAlbumsBetweenDates(DateTime start, DateTime end, bool includeUnmonitored)
- {
- return _albumRepository.AlbumsBetweenDates(start, end, includeUnmonitored);
- }
-
- public void DeleteAlbum(int albumId, bool deleteFiles)
- {
- _albumRepository.Delete(albumId);
- }
-
- public void DeleteAlbums(List albumIds, bool deleteFiles)
- {
- _albumRepository.DeleteMany(albumIds);
- }
-
- public List GetAllAlbums()
- {
- return _albumRepository.All().ToList();
- }
-
- public Dictionary> AllAlbumTags()
- {
- return _albumRepository.AllAlbumTags();
- }
-
- public Album UpdateAlbum(Album album)
- {
- return _albumRepository.Update(album);
- }
-
- public List UpdateAlbums(List albums)
- {
- _albumRepository.UpdateMany(albums);
- return albums;
- }
-
- public bool AlbumPathExists(string folder)
- {
- return _albumRepository.AlbumPathExists(folder);
- }
+ => _albumRepository.AlbumsBetweenDates(start, end, includeUnmonitored);
+ public Dictionary> AllAlbumTags() => _albumRepository.AllAlbumTags();
+ public bool AlbumPathExists(string folder) => _albumRepository.AlbumPathExists(folder);
}
}
diff --git a/src/NzbDrone.Core/Music/ArtistService.cs b/src/NzbDrone.Core/Music/ArtistService.cs
index 21aed54b5d..a2b5c44469 100644
--- a/src/NzbDrone.Core/Music/ArtistService.cs
+++ b/src/NzbDrone.Core/Music/ArtistService.cs
@@ -1,10 +1,10 @@
-using System;
using System.Collections.Generic;
-using System.Linq;
+using NzbDrone.Core.Datastore;
+using NzbDrone.Core.MediaItems;
namespace NzbDrone.Core.Music
{
- public interface IArtistService
+ public interface IArtistService : IBaseMediaService
{
Artist GetArtist(int artistId);
List GetArtists(IEnumerable artistIds);
@@ -21,7 +21,7 @@ public interface IArtistService
bool ArtistPathExists(string path);
}
- public class ArtistService : IArtistService
+ public class ArtistService : BaseMediaService, IArtistService
{
private readonly IArtistRepository _artistRepository;
@@ -30,78 +30,21 @@ public ArtistService(IArtistRepository artistRepository)
_artistRepository = artistRepository;
}
- public Artist GetArtist(int artistId)
- {
- return _artistRepository.Get(artistId);
- }
+ protected override IBasicRepository Repository => _artistRepository;
- public List GetArtists(IEnumerable artistIds)
- {
- return _artistRepository.Get(artistIds).ToList();
- }
+ public Artist GetArtist(int artistId) => Get(artistId);
+ public List GetArtists(IEnumerable artistIds) => Get(artistIds);
+ public Artist AddArtist(Artist newArtist) => Add(newArtist);
+ public List AddArtists(List newArtists) => AddMany(newArtists);
+ public void DeleteArtist(int artistId, bool deleteFiles) => Delete(artistId, deleteFiles);
+ public void DeleteArtists(List artistIds, bool deleteFiles) => DeleteMany(artistIds, deleteFiles);
+ public List GetAllArtists() => GetAll();
+ public Artist UpdateArtist(Artist artist) => Update(artist);
+ public List UpdateArtists(List artists) => UpdateMany(artists);
- public Artist AddArtist(Artist newArtist)
- {
- newArtist.Added = DateTime.UtcNow;
- return _artistRepository.Insert(newArtist);
- }
-
- public List AddArtists(List newArtists)
- {
- var now = DateTime.UtcNow;
- foreach (var artist in newArtists)
- {
- artist.Added = now;
- }
-
- _artistRepository.InsertMany(newArtists);
- return newArtists;
- }
-
- public Artist FindByName(string name)
- {
- return _artistRepository.FindByName(name);
- }
-
- public Artist FindByForeignId(string foreignArtistId)
- {
- return _artistRepository.FindByForeignId(foreignArtistId);
- }
-
- public void DeleteArtist(int artistId, bool deleteFiles)
- {
- _artistRepository.Delete(artistId);
- }
-
- public void DeleteArtists(List artistIds, bool deleteFiles)
- {
- _artistRepository.DeleteMany(artistIds);
- }
-
- public List GetAllArtists()
- {
- return _artistRepository.All().ToList();
- }
-
- public List GetMonitoredArtists()
- {
- return _artistRepository.GetMonitored();
- }
-
- public Artist UpdateArtist(Artist artist)
- {
- return _artistRepository.Update(artist);
- }
-
- public List UpdateArtists(List artists)
- {
- _artistRepository.UpdateMany(artists);
- return artists;
- }
-
- public bool ArtistPathExists(string path)
- {
- return _artistRepository.ArtistPathExists(path);
- }
+ public Artist FindByName(string name) => _artistRepository.FindByName(name);
+ public Artist FindByForeignId(string foreignArtistId) => _artistRepository.FindByForeignId(foreignArtistId);
+ public List GetMonitoredArtists() => _artistRepository.GetMonitored();
+ public bool ArtistPathExists(string path) => _artistRepository.ArtistPathExists(path);
}
}
diff --git a/src/NzbDrone.Core/Music/TrackService.cs b/src/NzbDrone.Core/Music/TrackService.cs
index db0db14b5a..fd23134903 100644
--- a/src/NzbDrone.Core/Music/TrackService.cs
+++ b/src/NzbDrone.Core/Music/TrackService.cs
@@ -1,15 +1,13 @@
-using System;
using System.Collections.Generic;
-using System.Linq;
using NzbDrone.Core.Datastore;
+using NzbDrone.Core.MediaItems;
namespace NzbDrone.Core.Music
{
- public interface ITrackService
+ public interface ITrackService : IBaseMediaService