mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-18 21:34:28 -04:00
Compare commits
226 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e39c7340c | |||
| f75add984f | |||
| f0f6c3eb35 | |||
| d94f866aeb | |||
| 618f07d138 | |||
| 3db33c988a | |||
| 28e38b7f17 | |||
| ca403e6f31 | |||
| 51351dee1d | |||
| 2081f2e321 | |||
| c10a32534c | |||
| 0e415c6ce3 | |||
| a8eb674071 | |||
| 7f8a1cf849 | |||
| a3c0d10240 | |||
| 3ddeaaefe2 | |||
| 6d99de4fe0 | |||
| 8b36a5ce92 | |||
| 1202a43466 | |||
| 82bc2d1aa4 | |||
| ed9af393b7 | |||
| 0799cfc885 | |||
| 331d0c9a9c | |||
| 03c93c9c84 | |||
| 60f6ed030b | |||
| cc70d61735 | |||
| a7b965100d | |||
| 8901118aef | |||
| 99c17d7698 | |||
| b84e83b082 | |||
| 4249f5324a | |||
| 9e1630e9a4 | |||
| 68b2773913 | |||
| ad446b358e | |||
| 423a8ecbe1 | |||
| 29a12aa3b0 | |||
| 695781dde5 | |||
| 4e8ddd3018 | |||
| eb67231a45 | |||
| 3d3a458828 | |||
| a11930a03f | |||
| abaf39d67e | |||
| 894a5943e4 | |||
| be26647afb | |||
| b319a4bce7 | |||
| f03fd7e95e | |||
| 7f25a3c4b1 | |||
| e3247dc505 | |||
| 3677fd6d34 | |||
| 4f6901b1ff | |||
| ce820f6f73 | |||
| 53e6cb24b7 | |||
| 7c1ca8acc1 | |||
| 5e9e578101 | |||
| 156407c541 | |||
| 1ef6c60318 | |||
| 73b3b1848b | |||
| 33fbd95707 | |||
| 704635f758 | |||
| 263e807de2 | |||
| 9ac9bd25c1 | |||
| 4ead5186ae | |||
| dea797c375 | |||
| 58ba24762b | |||
| fbd7b4fe33 | |||
| fee7fbbff6 | |||
| 18253a298e | |||
| 22f92150c3 | |||
| 4d7a762ee8 | |||
| b11517e2ac | |||
| d5af254f47 | |||
| f09da06f80 | |||
| d3443510b4 | |||
| d73eb1b5f9 | |||
| 39778a95bf | |||
| 9fccca1154 | |||
| e165663616 | |||
| b49d2312ab | |||
| 52221c7cf4 | |||
| ad7b110a0b | |||
| b04b483f86 | |||
| b79941e0a1 | |||
| 84d47b1f23 | |||
| 17df4d47fb | |||
| b9f89dddc9 | |||
| e3fc469cd3 | |||
| 4304685a65 | |||
| 7d77b1fbe5 | |||
| 1989174801 | |||
| ac4ae9bb4d | |||
| f399d27470 | |||
| c5fd2e3aa0 | |||
| e971d68d67 | |||
| af858ac4aa | |||
| 63ea253a6b | |||
| 484f2eb3ec | |||
| 15190aa61a | |||
| a3aac90bf7 | |||
| dd9cbc4f54 | |||
| 4bca0d77b7 | |||
| 1316b388ad | |||
| 243c88ce56 | |||
| 921f170234 | |||
| 3e102627f5 | |||
| f3b5f0c5cb | |||
| a53516e821 | |||
| f0f95be57f | |||
| f436d730fe | |||
| f7c135faaf | |||
| 8bb52105fd | |||
| e5a1b7a72e | |||
| 2f2a521391 | |||
| 304d1e3462 | |||
| 1d1cc6526d | |||
| 690e0b5d96 | |||
| 212eedd345 | |||
| 0b38743292 | |||
| 1def54f246 | |||
| 0eeaa1e443 | |||
| 7beee07a2c | |||
| 924f739d1f | |||
| b187fb23e3 | |||
| ca043b3820 | |||
| c3c9b9afbb | |||
| f225a742cc | |||
| f4fd36061c | |||
| 38e39449aa | |||
| 484c255fd4 | |||
| f341b5f449 | |||
| eb5654c634 | |||
| e843046d76 | |||
| ef57545221 | |||
| 09d44726a4 | |||
| 0e2d39f580 | |||
| dbcb0e77a8 | |||
| 0186900a54 | |||
| 941b30edac | |||
| 5c61b6ceb3 | |||
| 55959e1112 | |||
| 07451cbcde | |||
| 1ebdffcd26 | |||
| 75119ce9df | |||
| 668dc6dfde | |||
| ee989c9c67 | |||
| acac3bd680 | |||
| 3b18f3206d | |||
| fcf057a019 | |||
| c7399cdd2b | |||
| 08a3682b89 | |||
| 3da00f75dc | |||
| 60abb298b2 | |||
| c710b117ab | |||
| 816f53b36b | |||
| 749684e24a | |||
| 3a0ca45aa9 | |||
| 595efd498e | |||
| dea1060d61 | |||
| f6049b8bf2 | |||
| 53ced38221 | |||
| 3a3cf8511e | |||
| 9ec913337d | |||
| 9a2120ae92 | |||
| 818d3a94d5 | |||
| 4e493b74e6 | |||
| c7eaf1e85c | |||
| 31fe15c911 | |||
| 2c36a6c25f | |||
| 6af56f7a15 | |||
| 6e13191c25 | |||
| 921ddfc962 | |||
| 22f977401a | |||
| 113d9a07ef | |||
| 0560d65ea1 | |||
| 94ff105104 | |||
| 9bcf258aa9 | |||
| 54985bd4ca | |||
| 9e4d551f08 | |||
| 8390da1c2a | |||
| 44ae043c58 | |||
| bb7e2fc70c | |||
| b05938a9a8 | |||
| 1e42ac572e | |||
| 649dd0bda0 | |||
| de24aef059 | |||
| 10766dd227 | |||
| 257d279e43 | |||
| 1db333088a | |||
| d1aff31593 | |||
| 89dd4d3271 | |||
| fc6c78a54e | |||
| c98f4512df | |||
| 0a43481aed | |||
| df6c142250 | |||
| 58cf93e360 | |||
| 7be282ad12 | |||
| 7920378789 | |||
| bdcf336d94 | |||
| 3828492226 | |||
| 3cab98e2c0 | |||
| c977311227 | |||
| ca775df3d1 | |||
| fecb3895ed | |||
| 91fadd5430 | |||
| 79d4e1a89a | |||
| 149c18dd4f | |||
| 3171c6f195 | |||
| baf19897ff | |||
| accd16da71 | |||
| 9aa5bee493 | |||
| c22ed7172d | |||
| 8ddff3868d | |||
| 1307f8f5b1 | |||
| 985f0fa4a6 | |||
| e199e40621 | |||
| 4734cad9b1 | |||
| c744b00b55 | |||
| c88f4570b3 | |||
| c0e8a3e55a | |||
| 1ab34a1692 | |||
| 2dc5246180 | |||
| 234ad835af | |||
| b5a30af422 | |||
| 0463193127 | |||
| 8bec6c91c9 | |||
| cb511dc19d | |||
| 34f0c1820a |
+10
-4
@@ -36,12 +36,18 @@ dotnet_naming_style.instance_field_style.capitalization = camel_case
|
|||||||
dotnet_naming_style.instance_field_style.required_prefix = _
|
dotnet_naming_style.instance_field_style.required_prefix = _
|
||||||
|
|
||||||
# Prefer "var" everywhere
|
# Prefer "var" everywhere
|
||||||
csharp_style_var_for_built_in_types = true:suggestion
|
csharp_style_var_for_built_in_types = true
|
||||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
csharp_style_var_when_type_is_apparent = true
|
||||||
csharp_style_var_elsewhere = true:suggestion
|
csharp_style_var_elsewhere = true
|
||||||
|
# Prefer "out" variables to be declared inline
|
||||||
|
csharp_style_inlined_variable_declaration = true
|
||||||
|
|
||||||
# Using directive is unnecessary.
|
# Using directive is unnecessary.
|
||||||
dotnet_diagnostic.IDE0005.severity = error
|
dotnet_diagnostic.IDE0005.severity = error
|
||||||
|
# Use var instead of explicit type
|
||||||
|
dotnet_diagnostic.IDE0007.severity = error
|
||||||
|
# Inline variable declaration
|
||||||
|
dotnet_diagnostic.IDE0018.severity = error
|
||||||
|
|
||||||
# Stylecop Rules
|
# Stylecop Rules
|
||||||
dotnet_diagnostic.SA0001.severity = none
|
dotnet_diagnostic.SA0001.severity = none
|
||||||
@@ -269,7 +275,7 @@ dotnet_diagnostic.CA5397.severity = suggestion
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
[*.{js,html,js,hbs,less,css}]
|
[*.{js,html,hbs,less,css,ts,tsx}]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|||||||
+1
-2
@@ -3,8 +3,7 @@
|
|||||||
|
|
||||||
# Explicitly set bash scripts to have unix endings
|
# Explicitly set bash scripts to have unix endings
|
||||||
*.sh text eol=lf
|
*.sh text eol=lf
|
||||||
distribution/debian/* text eol=lf
|
distribution/osx/Readarr text eol=lf
|
||||||
macOS/Readarr text eol=lf
|
|
||||||
|
|
||||||
# Custom for Visual Studio
|
# Custom for Visual Studio
|
||||||
*.cs diff=csharp
|
*.cs diff=csharp
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ body:
|
|||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Trace Logs have been provided as applicable. Reports may be closed if the required logs are not provided.
|
label: Trace Logs have been provided as applicable. Reports may be closed if the required logs are not provided.
|
||||||
description: Trace logs are generally required for all bug reports
|
description: Trace logs are generally required for all bug reports and contain `trace`. Info logs are invalid for bug reports and do not contain `debug` nor `trace`
|
||||||
options:
|
options:
|
||||||
- label: I have followed the steps in the wiki link above and provided the required trace logs that are relevant and show this issue.
|
- label: I have read and followed the steps in the wiki link above and provided the required trace logs - the logs contain `trace` - that are relevant and show this issue.
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ Note that only one type of a given book is supported. If you want both an audiob
|
|||||||
|
|
||||||
[](https://wiki.servarr.com/readarr)
|
[](https://wiki.servarr.com/readarr)
|
||||||
[](https://readarr.com/discord)
|
[](https://readarr.com/discord)
|
||||||
[](https://www.reddit.com/r/readarr)
|
|
||||||
|
|
||||||
Note: GitHub Issues are for Bugs and Feature Requests Only
|
Note: GitHub Issues are for Bugs and Feature Requests Only
|
||||||
|
|
||||||
|
|||||||
+293
-140
@@ -9,7 +9,7 @@ variables:
|
|||||||
testsFolder: './_tests'
|
testsFolder: './_tests'
|
||||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||||
majorVersion: '0.1.5'
|
majorVersion: '0.3.3'
|
||||||
minorVersion: $[counter('minorVersion', 1)]
|
minorVersion: $[counter('minorVersion', 1)]
|
||||||
readarrVersion: '$(majorVersion).$(minorVersion)'
|
readarrVersion: '$(majorVersion).$(minorVersion)'
|
||||||
buildName: '$(Build.SourceBranchName).$(readarrVersion)'
|
buildName: '$(Build.SourceBranchName).$(readarrVersion)'
|
||||||
@@ -27,6 +27,10 @@ trigger:
|
|||||||
include:
|
include:
|
||||||
- develop
|
- develop
|
||||||
- master
|
- master
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- .github
|
||||||
|
- src/Readarr.Api.*/openapi.json
|
||||||
|
|
||||||
pr:
|
pr:
|
||||||
branches:
|
branches:
|
||||||
@@ -34,82 +38,37 @@ pr:
|
|||||||
- develop
|
- develop
|
||||||
paths:
|
paths:
|
||||||
exclude:
|
exclude:
|
||||||
|
- .github
|
||||||
- src/NzbDrone.Core/Localization/Core
|
- src/NzbDrone.Core/Localization/Core
|
||||||
|
- src/Readarr.Api.*/openapi.json
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
|
- stage: Setup
|
||||||
- stage: Build_Backend_Windows
|
displayName: Setup
|
||||||
displayName: Build Backend
|
|
||||||
dependsOn: []
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: Backend
|
- job:
|
||||||
strategy:
|
displayName: Build Variables
|
||||||
matrix:
|
|
||||||
Windows:
|
|
||||||
osName: 'Windows'
|
|
||||||
imageName: ${{ variables.windowsImage }}
|
|
||||||
enableAnalysis: 'false'
|
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: ${{ variables.linuxImage }}
|
||||||
variables:
|
|
||||||
# Disable stylecop here - linting errors get caught by the analyze task
|
|
||||||
EnableAnalyzers: $(enableAnalysis)
|
|
||||||
steps:
|
steps:
|
||||||
# Set the build name properly. The 'name' property won't recursively expand so hack here:
|
# Set the build name properly. The 'name' property won't recursively expand so hack here:
|
||||||
- bash: echo "##vso[build.updatebuildnumber]$READARRVERSION"
|
- bash: echo "##vso[build.updatebuildnumber]$READARRVERSION"
|
||||||
displayName: Set Build Name
|
displayName: Set Build Name
|
||||||
- checkout: self
|
|
||||||
submodules: true
|
|
||||||
fetchDepth: 1
|
|
||||||
- task: UseDotNet@2
|
|
||||||
displayName: 'Install .net core'
|
|
||||||
inputs:
|
|
||||||
version: $(dotnetVersion)
|
|
||||||
- bash: |
|
- bash: |
|
||||||
SDK_PATH="${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}"
|
if [[ $BUILD_REASON == "PullRequest" ]]; then
|
||||||
BUNDLEDVERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props"
|
git diff origin/develop...HEAD --name-only | grep -E "^(src/|azure-pipelines.yml)"
|
||||||
|
echo $? > not_backend_update
|
||||||
if ! grep -q freebsd-x64 $BUNDLEDVERSIONS; then
|
else
|
||||||
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
|
echo 0 > not_backend_update
|
||||||
fi
|
fi
|
||||||
displayName: Extra Platform Support
|
cat not_backend_update
|
||||||
- task: Cache@2
|
displayName: Check for Backend File Changes
|
||||||
inputs:
|
- publish: not_backend_update
|
||||||
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
|
artifact: not_backend_update
|
||||||
path: $(nugetCacheFolder)
|
displayName: Publish update type
|
||||||
displayName: Cache NuGet packages
|
- stage: Build_Backend
|
||||||
- bash: ./build.sh --backend --enable-bsd
|
displayName: Build Backend
|
||||||
displayName: Build Readarr Backend
|
dependsOn: Setup
|
||||||
env:
|
|
||||||
NUGET_PACKAGES: $(nugetCacheFolder)
|
|
||||||
- powershell: Get-ChildItem _output\net6.0*,_output\*.Update\* -Recurse | Where { $_.Fullname -notlike "*\publish\*" -and $_.attributes -notlike "*directory*" } | Remove-Item
|
|
||||||
displayName: Clean up intermediate output
|
|
||||||
- publish: $(outputFolder)
|
|
||||||
artifact: '$(osName)Backend'
|
|
||||||
displayName: Publish Backend
|
|
||||||
- publish: '$(testsFolder)/net6.0/win-x64/publish'
|
|
||||||
artifact: win-x64-tests
|
|
||||||
displayName: Publish win-x64 Test Package
|
|
||||||
- publish: '$(testsFolder)/net6.0/linux-x64/publish'
|
|
||||||
artifact: linux-x64-tests
|
|
||||||
displayName: Publish linux-x64 Test Package
|
|
||||||
- publish: '$(testsFolder)/net6.0/linux-x86/publish'
|
|
||||||
artifact: linux-x86-tests
|
|
||||||
displayName: Publish linux-x86 Test Package
|
|
||||||
- publish: '$(testsFolder)/net6.0/linux-musl-x64/publish'
|
|
||||||
artifact: linux-musl-x64-tests
|
|
||||||
displayName: Publish linux-musl-x64 Test Package
|
|
||||||
- publish: '$(testsFolder)/net6.0/freebsd-x64/publish'
|
|
||||||
artifact: freebsd-x64-tests
|
|
||||||
displayName: Publish freebsd-x64 Test Package
|
|
||||||
- publish: '$(testsFolder)/net6.0/osx-x64/publish'
|
|
||||||
artifact: osx-x64-tests
|
|
||||||
displayName: Publish osx-x64 Test Package
|
|
||||||
|
|
||||||
- stage: Build_Backend_Other
|
|
||||||
displayName: Build Backend (Other OS)
|
|
||||||
dependsOn: []
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: Backend
|
- job: Backend
|
||||||
strategy:
|
strategy:
|
||||||
@@ -122,6 +81,10 @@ stages:
|
|||||||
osName: 'Mac'
|
osName: 'Mac'
|
||||||
imageName: ${{ variables.macImage }}
|
imageName: ${{ variables.macImage }}
|
||||||
enableAnalysis: 'false'
|
enableAnalysis: 'false'
|
||||||
|
Windows:
|
||||||
|
osName: 'Windows'
|
||||||
|
imageName: ${{ variables.windowsImage }}
|
||||||
|
enableAnalysis: 'false'
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
@@ -137,22 +100,17 @@ stages:
|
|||||||
inputs:
|
inputs:
|
||||||
version: $(dotnetVersion)
|
version: $(dotnetVersion)
|
||||||
- bash: |
|
- bash: |
|
||||||
SDK_PATH="${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}"
|
BUNDLEDVERSIONS=${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}/Microsoft.NETCoreSdk.BundledVersions.props
|
||||||
BUNDLEDVERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props"
|
echo $BUNDLEDVERSIONS
|
||||||
|
if grep -q freebsd-x64 $BUNDLEDVERSIONS; then
|
||||||
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;linux-x86/' $BUNDLEDVERSIONS
|
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
|
||||||
fi
|
fi
|
||||||
displayName: Extra Platform Support
|
displayName: Enable Extra Platform Support
|
||||||
- task: Cache@2
|
|
||||||
inputs:
|
|
||||||
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
|
|
||||||
path: $(nugetCacheFolder)
|
|
||||||
displayName: Cache NuGet packages
|
|
||||||
- bash: ./build.sh --backend --enable-extra-platforms
|
- bash: ./build.sh --backend --enable-extra-platforms
|
||||||
displayName: Build Readarr Backend
|
displayName: Build Readarr Backend
|
||||||
env:
|
|
||||||
NUGET_PACKAGES: $(nugetCacheFolder)
|
|
||||||
- bash: |
|
- bash: |
|
||||||
find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
|
find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
|
||||||
find ${OUTPUTFOLDER} -depth -empty -type d -exec rm -r "{}" \;
|
find ${OUTPUTFOLDER} -depth -empty -type d -exec rm -r "{}" \;
|
||||||
@@ -160,10 +118,38 @@ stages:
|
|||||||
find ${TESTSFOLDER} -depth -empty -type d -exec rm -r "{}" \;
|
find ${TESTSFOLDER} -depth -empty -type d -exec rm -r "{}" \;
|
||||||
displayName: Clean up intermediate output
|
displayName: Clean up intermediate output
|
||||||
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
||||||
|
- publish: $(outputFolder)
|
||||||
|
artifact: '$(osName)Backend'
|
||||||
|
displayName: Publish Backend
|
||||||
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
|
- publish: '$(testsFolder)/net6.0/win-x64/publish'
|
||||||
|
artifact: win-x64-tests
|
||||||
|
displayName: Publish win-x64 Test Package
|
||||||
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
|
- publish: '$(testsFolder)/net6.0/linux-x64/publish'
|
||||||
|
artifact: linux-x64-tests
|
||||||
|
displayName: Publish linux-x64 Test Package
|
||||||
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
|
- publish: '$(testsFolder)/net6.0/linux-x86/publish'
|
||||||
|
artifact: linux-x86-tests
|
||||||
|
displayName: Publish linux-x86 Test Package
|
||||||
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
|
- publish: '$(testsFolder)/net6.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)/net6.0/freebsd-x64/publish'
|
||||||
|
artifact: freebsd-x64-tests
|
||||||
|
displayName: Publish freebsd-x64 Test Package
|
||||||
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
|
- publish: '$(testsFolder)/net6.0/osx-x64/publish'
|
||||||
|
artifact: osx-x64-tests
|
||||||
|
displayName: Publish osx-x64 Test Package
|
||||||
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
|
|
||||||
- stage: Build_Frontend
|
- stage: Build_Frontend
|
||||||
displayName: Frontend
|
displayName: Frontend
|
||||||
dependsOn: []
|
dependsOn: Setup
|
||||||
jobs:
|
jobs:
|
||||||
- job: Build
|
- job: Build
|
||||||
strategy:
|
strategy:
|
||||||
@@ -192,7 +178,6 @@ stages:
|
|||||||
key: 'yarn | "$(osName)" | yarn.lock'
|
key: 'yarn | "$(osName)" | yarn.lock'
|
||||||
restoreKeys: |
|
restoreKeys: |
|
||||||
yarn | "$(osName)"
|
yarn | "$(osName)"
|
||||||
yarn
|
|
||||||
path: $(yarnCacheFolder)
|
path: $(yarnCacheFolder)
|
||||||
displayName: Cache Yarn packages
|
displayName: Cache Yarn packages
|
||||||
- bash: ./build.sh --frontend
|
- bash: ./build.sh --frontend
|
||||||
@@ -204,10 +189,10 @@ stages:
|
|||||||
artifact: '$(osName)Frontend'
|
artifact: '$(osName)Frontend'
|
||||||
displayName: Publish Frontend
|
displayName: Publish Frontend
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
|
|
||||||
- stage: Installer
|
- stage: Installer
|
||||||
dependsOn:
|
dependsOn:
|
||||||
- Build_Backend_Windows
|
- Build_Backend
|
||||||
- Build_Frontend
|
- Build_Frontend
|
||||||
jobs:
|
jobs:
|
||||||
- job: Windows_Installer
|
- job: Windows_Installer
|
||||||
@@ -231,8 +216,8 @@ stages:
|
|||||||
displayName: Fetch Frontend
|
displayName: Fetch Frontend
|
||||||
- bash: |
|
- bash: |
|
||||||
./build.sh --packages --installer
|
./build.sh --packages --installer
|
||||||
cp setup/output/Readarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x64-installer.exe
|
cp distribution/windows/setup/output/Readarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x64-installer.exe
|
||||||
cp setup/output/Readarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x86-installer.exe
|
cp distribution/windows/setup/output/Readarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x86-installer.exe
|
||||||
displayName: Create Installers
|
displayName: Create Installers
|
||||||
- publish: $(Build.ArtifactStagingDirectory)
|
- publish: $(Build.ArtifactStagingDirectory)
|
||||||
artifact: 'WindowsInstaller'
|
artifact: 'WindowsInstaller'
|
||||||
@@ -240,7 +225,7 @@ stages:
|
|||||||
|
|
||||||
- stage: Packages
|
- stage: Packages
|
||||||
dependsOn:
|
dependsOn:
|
||||||
- Build_Backend_Windows
|
- Build_Backend
|
||||||
- Build_Frontend
|
- Build_Frontend
|
||||||
jobs:
|
jobs:
|
||||||
- job: Other_Packages
|
- job: Other_Packages
|
||||||
@@ -382,7 +367,7 @@ stages:
|
|||||||
- bash: |
|
- bash: |
|
||||||
echo "Uploading source maps to sentry"
|
echo "Uploading source maps to sentry"
|
||||||
curl -sL https://sentry.io/get-cli/ | bash
|
curl -sL https://sentry.io/get-cli/ | bash
|
||||||
RELEASENAME="${READARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
|
RELEASENAME="Readarr@${READARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
|
||||||
sentry-cli releases new --finalize -p readarr -p readarr-ui -p readarr-update "${RELEASENAME}"
|
sentry-cli releases new --finalize -p readarr -p readarr-ui -p readarr-update "${RELEASENAME}"
|
||||||
sentry-cli releases -p readarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
|
sentry-cli releases -p readarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
|
||||||
sentry-cli releases set-commits --auto "${RELEASENAME}"
|
sentry-cli releases set-commits --auto "${RELEASENAME}"
|
||||||
@@ -406,14 +391,29 @@ stages:
|
|||||||
SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
|
SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
|
||||||
SENTRY_ORG: $(sentryOrg)
|
SENTRY_ORG: $(sentryOrg)
|
||||||
SENTRY_URL: $(sentryUrl)
|
SENTRY_URL: $(sentryUrl)
|
||||||
|
|
||||||
- stage: Unit_Test
|
- stage: Unit_Test
|
||||||
displayName: Unit Tests
|
displayName: Unit Tests
|
||||||
dependsOn: Build_Backend_Windows
|
dependsOn: Build_Backend
|
||||||
condition: succeeded()
|
|
||||||
jobs:
|
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
|
- job: Unit
|
||||||
displayName: Unit Native
|
displayName: Unit Native
|
||||||
|
dependsOn: Prepare
|
||||||
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
workspace:
|
workspace:
|
||||||
clean: all
|
clean: all
|
||||||
|
|
||||||
@@ -479,6 +479,8 @@ stages:
|
|||||||
|
|
||||||
- job: Unit_Docker
|
- job: Unit_Docker
|
||||||
displayName: Unit Docker
|
displayName: Unit Docker
|
||||||
|
dependsOn: Prepare
|
||||||
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
alpine:
|
alpine:
|
||||||
@@ -492,11 +494,11 @@ stages:
|
|||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: ${{ variables.linuxImage }}
|
vmImage: ${{ variables.linuxImage }}
|
||||||
|
|
||||||
container: $[ variables['containerImage'] ]
|
container: $[ variables['containerImage'] ]
|
||||||
|
|
||||||
timeoutInMinutes: 10
|
timeoutInMinutes: 10
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Install .NET'
|
displayName: 'Install .NET'
|
||||||
@@ -530,12 +532,14 @@ stages:
|
|||||||
testResultsFiles: '**/TestResult.xml'
|
testResultsFiles: '**/TestResult.xml'
|
||||||
testRunTitle: '$(testName) Unit Tests'
|
testRunTitle: '$(testName) Unit Tests'
|
||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
|
|
||||||
- job: Unit_LinuxCore_Postgres
|
- job: Unit_LinuxCore_Postgres14
|
||||||
displayName: Unit Native LinuxCore with Postgres Database
|
displayName: Unit Native LinuxCore with Postgres14 Database
|
||||||
|
dependsOn: Prepare
|
||||||
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
variables:
|
variables:
|
||||||
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
||||||
artifactName: LinuxCoreTests
|
artifactName: linux-x64-tests
|
||||||
Readarr__Postgres__Host: 'localhost'
|
Readarr__Postgres__Host: 'localhost'
|
||||||
Readarr__Postgres__Port: '5432'
|
Readarr__Postgres__Port: '5432'
|
||||||
Readarr__Postgres__User: 'readarr'
|
Readarr__Postgres__User: 'readarr'
|
||||||
@@ -545,7 +549,7 @@ stages:
|
|||||||
vmImage: ${{ variables.linuxImage }}
|
vmImage: ${{ variables.linuxImage }}
|
||||||
|
|
||||||
timeoutInMinutes: 10
|
timeoutInMinutes: 10
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Install .net core'
|
displayName: 'Install .net core'
|
||||||
@@ -556,7 +560,7 @@ stages:
|
|||||||
displayName: Download Test Artifact
|
displayName: Download Test Artifact
|
||||||
inputs:
|
inputs:
|
||||||
buildType: 'current'
|
buildType: 'current'
|
||||||
artifactName: 'linux-x64-Tests'
|
artifactName: $(artifactName)
|
||||||
targetPath: $(testsFolder)
|
targetPath: $(testsFolder)
|
||||||
- bash: find ${TESTSFOLDER} -name "Readarr.Test.Dummy" -exec chmod a+x {} \;
|
- bash: find ${TESTSFOLDER} -name "Readarr.Test.Dummy" -exec chmod a+x {} \;
|
||||||
displayName: Make Test Dummy Executable
|
displayName: Make Test Dummy Executable
|
||||||
@@ -579,15 +583,84 @@ stages:
|
|||||||
inputs:
|
inputs:
|
||||||
testResultsFormat: 'NUnit'
|
testResultsFormat: 'NUnit'
|
||||||
testResultsFiles: '**/TestResult.xml'
|
testResultsFiles: '**/TestResult.xml'
|
||||||
testRunTitle: 'LinuxCore Postgres Unit Tests'
|
testRunTitle: 'LinuxCore Postgres14 Unit Tests'
|
||||||
|
failTaskOnFailedTests: 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: 'Readarr.*.linux-core-x64.tar.gz'
|
||||||
|
artifactName: linux-x64-tests
|
||||||
|
Readarr__Postgres__Host: 'localhost'
|
||||||
|
Readarr__Postgres__Port: '5432'
|
||||||
|
Readarr__Postgres__User: 'readarr'
|
||||||
|
Readarr__Postgres__Password: 'readarr'
|
||||||
|
|
||||||
|
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: find ${TESTSFOLDER} -name "Readarr.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=readarr \
|
||||||
|
-e POSTGRES_USER=readarr \
|
||||||
|
-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
|
failTaskOnFailedTests: true
|
||||||
|
|
||||||
- stage: Integration
|
- stage: Integration
|
||||||
displayName: Integration
|
displayName: Integration
|
||||||
dependsOn: Packages
|
dependsOn: Packages
|
||||||
|
|
||||||
jobs:
|
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
|
- job: Integration_Native
|
||||||
displayName: Integration Native
|
displayName: Integration Native
|
||||||
|
dependsOn: Prepare
|
||||||
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
MacCore:
|
MacCore:
|
||||||
@@ -608,7 +681,7 @@ stages:
|
|||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Install .net core'
|
displayName: 'Install .net core'
|
||||||
@@ -630,7 +703,7 @@ stages:
|
|||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
targetPath: $(Build.ArtifactStagingDirectory)
|
||||||
- task: ExtractFiles@1
|
- task: ExtractFiles@1
|
||||||
inputs:
|
inputs:
|
||||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||||
displayName: Extract Package
|
displayName: Extract Package
|
||||||
- bash: |
|
- bash: |
|
||||||
@@ -649,8 +722,10 @@ stages:
|
|||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: true
|
||||||
displayName: Publish Test Results
|
displayName: Publish Test Results
|
||||||
|
|
||||||
- job: Integration_LinuxCore_Postgres
|
- job: Integration_LinuxCore_Postgres14
|
||||||
displayName: Integration Native LinuxCore with Postgres Database
|
displayName: Integration Native LinuxCore with Postgres14 Database
|
||||||
|
dependsOn: Prepare
|
||||||
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
variables:
|
variables:
|
||||||
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
||||||
Readarr__Postgres__Host: 'localhost'
|
Readarr__Postgres__Host: 'localhost'
|
||||||
@@ -682,7 +757,7 @@ stages:
|
|||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
targetPath: $(Build.ArtifactStagingDirectory)
|
||||||
- task: ExtractFiles@1
|
- task: ExtractFiles@1
|
||||||
inputs:
|
inputs:
|
||||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||||
displayName: Extract Package
|
displayName: Extract Package
|
||||||
- bash: |
|
- bash: |
|
||||||
@@ -705,12 +780,77 @@ stages:
|
|||||||
inputs:
|
inputs:
|
||||||
testResultsFormat: 'NUnit'
|
testResultsFormat: 'NUnit'
|
||||||
testResultsFiles: '**/TestResult.xml'
|
testResultsFiles: '**/TestResult.xml'
|
||||||
testRunTitle: 'Integration LinuxCore Postgres Database Integration Tests'
|
testRunTitle: 'Integration LinuxCore Postgres14 Database Integration Tests'
|
||||||
|
failTaskOnFailedTests: 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: 'Readarr.*.linux-core-x64.tar.gz'
|
||||||
|
Readarr__Postgres__Host: 'localhost'
|
||||||
|
Readarr__Postgres__Port: '5432'
|
||||||
|
Readarr__Postgres__User: 'readarr'
|
||||||
|
Readarr__Postgres__Password: 'readarr'
|
||||||
|
|
||||||
|
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/Readarr/. ./bin/
|
||||||
|
displayName: Move Package Contents
|
||||||
|
- bash: |
|
||||||
|
docker run -d --name=postgres15 \
|
||||||
|
-e POSTGRES_PASSWORD=readarr \
|
||||||
|
-e POSTGRES_USER=readarr \
|
||||||
|
-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
|
failTaskOnFailedTests: true
|
||||||
displayName: Publish Test Results
|
displayName: Publish Test Results
|
||||||
|
|
||||||
- job: Integration_FreeBSD
|
- job: Integration_FreeBSD
|
||||||
displayName: Integration Native FreeBSD
|
displayName: Integration Native FreeBSD
|
||||||
|
dependsOn: Prepare
|
||||||
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
workspace:
|
workspace:
|
||||||
clean: all
|
clean: all
|
||||||
variables:
|
variables:
|
||||||
@@ -755,6 +895,8 @@ stages:
|
|||||||
|
|
||||||
- job: Integration_Docker
|
- job: Integration_Docker
|
||||||
displayName: Integration Docker
|
displayName: Integration Docker
|
||||||
|
dependsOn: Prepare
|
||||||
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
alpine:
|
alpine:
|
||||||
@@ -773,7 +915,7 @@ stages:
|
|||||||
container: $[ variables['containerImage'] ]
|
container: $[ variables['containerImage'] ]
|
||||||
|
|
||||||
timeoutInMinutes: 15
|
timeoutInMinutes: 15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Install .NET'
|
displayName: 'Install .NET'
|
||||||
@@ -801,7 +943,7 @@ stages:
|
|||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
targetPath: $(Build.ArtifactStagingDirectory)
|
||||||
- task: ExtractFiles@1
|
- task: ExtractFiles@1
|
||||||
inputs:
|
inputs:
|
||||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||||
displayName: Extract Package
|
displayName: Extract Package
|
||||||
- bash: |
|
- bash: |
|
||||||
@@ -823,7 +965,7 @@ stages:
|
|||||||
- stage: Automation
|
- stage: Automation
|
||||||
displayName: Automation
|
displayName: Automation
|
||||||
dependsOn: Packages
|
dependsOn: Packages
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: Automation
|
- job: Automation
|
||||||
strategy:
|
strategy:
|
||||||
@@ -833,20 +975,23 @@ stages:
|
|||||||
artifactName: 'linux-x64'
|
artifactName: 'linux-x64'
|
||||||
imageName: ${{ variables.linuxImage }}
|
imageName: ${{ variables.linuxImage }}
|
||||||
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
||||||
|
failBuild: true
|
||||||
Mac:
|
Mac:
|
||||||
osName: 'Mac'
|
osName: 'Mac'
|
||||||
artifactName: 'osx-x64'
|
artifactName: 'osx-x64'
|
||||||
imageName: ${{ variables.macImage }}
|
imageName: ${{ variables.macImage }}
|
||||||
pattern: 'Readarr.*.osx-core-x64.tar.gz'
|
pattern: 'Readarr.*.osx-core-x64.tar.gz'
|
||||||
|
failBuild: true
|
||||||
Windows:
|
Windows:
|
||||||
osName: 'Windows'
|
osName: 'Windows'
|
||||||
artifactName: 'win-x64'
|
artifactName: 'win-x64'
|
||||||
imageName: ${{ variables.windowsImage }}
|
imageName: ${{ variables.windowsImage }}
|
||||||
pattern: 'Readarr.*.windows-core-x64.zip'
|
pattern: 'Readarr.*.windows-core-x64.zip'
|
||||||
|
failBuild: true
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: $(imageName)
|
vmImage: $(imageName)
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Install .net core'
|
displayName: 'Install .net core'
|
||||||
@@ -868,7 +1013,7 @@ stages:
|
|||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
targetPath: $(Build.ArtifactStagingDirectory)
|
||||||
- task: ExtractFiles@1
|
- task: ExtractFiles@1
|
||||||
inputs:
|
inputs:
|
||||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||||
displayName: Extract Package
|
displayName: Extract Package
|
||||||
- bash: |
|
- bash: |
|
||||||
@@ -888,20 +1033,35 @@ stages:
|
|||||||
TargetFolder: '$(Build.ArtifactStagingDirectory)/screenshots'
|
TargetFolder: '$(Build.ArtifactStagingDirectory)/screenshots'
|
||||||
- publish: $(Build.ArtifactStagingDirectory)/screenshots
|
- publish: $(Build.ArtifactStagingDirectory)/screenshots
|
||||||
artifact: '$(osName)AutomationScreenshots'
|
artifact: '$(osName)AutomationScreenshots'
|
||||||
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
|
|
||||||
displayName: Publish Screenshot Bundle
|
displayName: Publish Screenshot Bundle
|
||||||
|
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
|
||||||
- task: PublishTestResults@2
|
- task: PublishTestResults@2
|
||||||
inputs:
|
inputs:
|
||||||
testResultsFormat: 'NUnit'
|
testResultsFormat: 'NUnit'
|
||||||
testResultsFiles: '**/TestResult.xml'
|
testResultsFiles: '**/TestResult.xml'
|
||||||
testRunTitle: '$(osName) Automation Tests'
|
testRunTitle: '$(osName) Automation Tests'
|
||||||
failTaskOnFailedTests: true
|
failTaskOnFailedTests: $(failBuild)
|
||||||
displayName: Publish Test Results
|
displayName: Publish Test Results
|
||||||
|
|
||||||
- stage: Analyze
|
- stage: Analyze
|
||||||
dependsOn: []
|
dependsOn:
|
||||||
|
- Setup
|
||||||
displayName: Analyze
|
displayName: Analyze
|
||||||
|
|
||||||
jobs:
|
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
|
- job: Lint_Frontend
|
||||||
displayName: Lint Frontend
|
displayName: Lint Frontend
|
||||||
strategy:
|
strategy:
|
||||||
@@ -927,7 +1087,6 @@ stages:
|
|||||||
key: 'yarn | "$(osName)" | yarn.lock'
|
key: 'yarn | "$(osName)" | yarn.lock'
|
||||||
restoreKeys: |
|
restoreKeys: |
|
||||||
yarn | "$(osName)"
|
yarn | "$(osName)"
|
||||||
yarn
|
|
||||||
path: $(yarnCacheFolder)
|
path: $(yarnCacheFolder)
|
||||||
displayName: Cache Yarn packages
|
displayName: Cache Yarn packages
|
||||||
- bash: ./build.sh --lint
|
- bash: ./build.sh --lint
|
||||||
@@ -956,11 +1115,16 @@ stages:
|
|||||||
cliProjectVersion: '$(readarrVersion)'
|
cliProjectVersion: '$(readarrVersion)'
|
||||||
cliSources: './frontend'
|
cliSources: './frontend'
|
||||||
- task: SonarCloudAnalyze@1
|
- task: SonarCloudAnalyze@1
|
||||||
|
|
||||||
- job: Api_Docs
|
- job: Api_Docs
|
||||||
displayName: API Docs
|
displayName: API Docs
|
||||||
|
dependsOn: Prepare
|
||||||
condition: |
|
condition: |
|
||||||
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))
|
and
|
||||||
|
(
|
||||||
|
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
|
||||||
|
and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
|
)
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: ${{ variables.windowsImage }}
|
vmImage: ${{ variables.windowsImage }}
|
||||||
@@ -973,7 +1137,7 @@ stages:
|
|||||||
- checkout: self
|
- checkout: self
|
||||||
submodules: true
|
submodules: true
|
||||||
persistCredentials: true
|
persistCredentials: true
|
||||||
fetchDepth: 1
|
fetchDepth: 1
|
||||||
- bash: ./docs.sh Windows
|
- bash: ./docs.sh Windows
|
||||||
displayName: Create openapi.json
|
displayName: Create openapi.json
|
||||||
- bash: |
|
- bash: |
|
||||||
@@ -981,8 +1145,7 @@ stages:
|
|||||||
git config --global user.name "Servarr"
|
git config --global user.name "Servarr"
|
||||||
git checkout -b api-docs
|
git checkout -b api-docs
|
||||||
git add .
|
git add .
|
||||||
git status
|
if git status | grep -q modified
|
||||||
if git status | grep modified
|
|
||||||
then
|
then
|
||||||
git commit -am 'Automated API Docs update'
|
git commit -am 'Automated API Docs update'
|
||||||
git push -f --set-upstream origin api-docs
|
git push -f --set-upstream origin api-docs
|
||||||
@@ -1008,33 +1171,25 @@ stages:
|
|||||||
|
|
||||||
- job: Analyze_Backend
|
- job: Analyze_Backend
|
||||||
displayName: Backend
|
displayName: Backend
|
||||||
|
dependsOn: Prepare
|
||||||
|
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
disable.coverage.autogenerate: 'true'
|
disable.coverage.autogenerate: 'true'
|
||||||
|
EnableAnalyzers: 'false'
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: ${{ variables.linuxImage }}
|
vmImage: ${{ variables.windowsImage }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Install .net core 2.1'
|
displayName: 'Install .net core'
|
||||||
inputs:
|
|
||||||
version: 2.1.815
|
|
||||||
- task: UseDotNet@2
|
|
||||||
displayName: 'Install .net core 3.1'
|
|
||||||
inputs:
|
|
||||||
version: 3.1.413
|
|
||||||
- task: UseDotNet@2
|
|
||||||
displayName: 'Install .net core 5.0'
|
|
||||||
inputs:
|
inputs:
|
||||||
version: $(dotnetVersion)
|
version: $(dotnetVersion)
|
||||||
- checkout: self # Need history for Sonar analysis
|
- checkout: self # Need history for Sonar analysis
|
||||||
submodules: true
|
submodules: true
|
||||||
- task: Cache@2
|
- powershell: Set-Service SCardSvr -StartupType Manual
|
||||||
inputs:
|
displayName: Enable Windows Test Service
|
||||||
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
|
|
||||||
path: $(nugetCacheFolder)
|
|
||||||
displayName: Cache NuGet packages
|
|
||||||
|
|
||||||
- task: SonarCloudPrepare@1
|
- task: SonarCloudPrepare@1
|
||||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||||
inputs:
|
inputs:
|
||||||
@@ -1045,16 +1200,14 @@ stages:
|
|||||||
projectName: 'Readarr'
|
projectName: 'Readarr'
|
||||||
projectVersion: '$(readarrVersion)'
|
projectVersion: '$(readarrVersion)'
|
||||||
extraProperties: |
|
extraProperties: |
|
||||||
sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,./src/Libraries/**
|
sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,**/ExternalModules/**,./src/Libraries/**
|
||||||
sonar.coverage.exclusions=**/Readarr.Api.V1/**/*
|
sonar.coverage.exclusions=**/Readarr.Api.V1/**/*
|
||||||
sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml
|
sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml
|
||||||
sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
|
sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
|
||||||
- bash: |
|
- bash: |
|
||||||
./build.sh --backend -f net6.0 -r linux-x64
|
./build.sh --backend -f net6.0 -r win-x64
|
||||||
TEST_DIR=_tests/net6.0/linux-x64/publish/ ./test.sh Linux Unit Coverage
|
TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage
|
||||||
displayName: Coverage Unit Tests
|
displayName: Coverage Unit Tests
|
||||||
env:
|
|
||||||
NUGET_PACKAGES: $(nugetCacheFolder)
|
|
||||||
- task: SonarCloudAnalyze@1
|
- task: SonarCloudAnalyze@1
|
||||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||||
displayName: Publish SonarCloud Results
|
displayName: Publish SonarCloud Results
|
||||||
@@ -1077,7 +1230,6 @@ stages:
|
|||||||
- Unit_Test
|
- Unit_Test
|
||||||
- Integration
|
- Integration
|
||||||
- Automation
|
- Automation
|
||||||
- Build_Backend_Other
|
|
||||||
condition: eq(variables['system.pullrequest.isfork'], false)
|
condition: eq(variables['system.pullrequest.isfork'], false)
|
||||||
displayName: Build Status Report
|
displayName: Build Status Report
|
||||||
jobs:
|
jobs:
|
||||||
@@ -1101,3 +1253,4 @@ stages:
|
|||||||
DISCORDCHANNELID: $(discordChannelId)
|
DISCORDCHANNELID: $(discordChannelId)
|
||||||
DISCORDWEBHOOKKEY: $(discordWebhookKey)
|
DISCORDWEBHOOKKEY: $(discordWebhookKey)
|
||||||
DISCORDTHREADID: $(discordThreadId)
|
DISCORDTHREADID: $(discordThreadId)
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ UpdateVersionNumber()
|
|||||||
echo "Updating Version Info"
|
echo "Updating Version Info"
|
||||||
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$READARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
|
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$READARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
|
||||||
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
|
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
|
||||||
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$READARRVERSION<\/string>/g" macOS/Readarr.app/Contents/Info.plist
|
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$READARRVERSION<\/string>/g" distribution/osx/Readarr.app/Contents/Info.plist
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ PackageMacOSApp()
|
|||||||
|
|
||||||
rm -rf $folder
|
rm -rf $folder
|
||||||
mkdir -p $folder
|
mkdir -p $folder
|
||||||
cp -r macOS/Readarr.app $folder
|
cp -r distribution/osx/Readarr.app $folder
|
||||||
mkdir -p $folder/Readarr.app/Contents/MacOS
|
mkdir -p $folder/Readarr.app/Contents/MacOS
|
||||||
|
|
||||||
echo "Copying Binaries"
|
echo "Copying Binaries"
|
||||||
@@ -245,7 +245,7 @@ BuildInstaller()
|
|||||||
local framework="$1"
|
local framework="$1"
|
||||||
local runtime="$2"
|
local runtime="$2"
|
||||||
|
|
||||||
./_inno/ISCC.exe setup/readarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
|
./_inno/ISCC.exe distribution/windows/setup/readarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallInno()
|
InstallInno()
|
||||||
@@ -391,22 +391,21 @@ then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$FRONTEND" = "YES" ];
|
if [[ "$LINT" = "YES" || "$FRONTEND" = "YES" ]];
|
||||||
then
|
then
|
||||||
YarnInstall
|
YarnInstall
|
||||||
RunWebpack
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$LINT" = "YES" ];
|
if [ "$LINT" = "YES" ];
|
||||||
then
|
then
|
||||||
if [ -z "$FRONTEND" ];
|
|
||||||
then
|
|
||||||
YarnInstall
|
|
||||||
fi
|
|
||||||
|
|
||||||
LintUI
|
LintUI
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$FRONTEND" = "YES" ];
|
||||||
|
then
|
||||||
|
RunWebpack
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$PACKAGES" = "YES" ];
|
if [ "$PACKAGES" = "YES" ];
|
||||||
then
|
then
|
||||||
UpdateVersionNumber
|
UpdateVersionNumber
|
||||||
|
|||||||
@@ -44,16 +44,16 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
|
|||||||
|
|
||||||
[Tasks]
|
[Tasks]
|
||||||
Name: "desktopIcon"; Description: "{cm:CreateDesktopIcon}"
|
Name: "desktopIcon"; Description: "{cm:CreateDesktopIcon}"
|
||||||
Name: "windowsService"; Description: "Install Windows Service (Starts when the computer starts as the LocalService user, you will need to change the user to access network shares)"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
Name: "windowsService"; Description: "Install Windows Service (Starts when the computer starts as the LocalService user, you will need to change the user to access network shares)"; GroupDescription: "Start automatically"; Flags: exclusive
|
||||||
Name: "startupShortcut"; Description: "Create shortcut in Startup folder (Starts when you log into Windows)"; GroupDescription: "Start automatically"; Flags: exclusive
|
Name: "startupShortcut"; Description: "Create shortcut in Startup folder (Starts when you log into Windows)"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
||||||
Name: "none"; Description: "Do not start automatically"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
Name: "none"; Description: "Do not start automatically"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
||||||
|
|
||||||
[Dirs]
|
[Dirs]
|
||||||
Name: "{app}"; Permissions: users-modify
|
Name: "{app}"; Permissions: users-modify
|
||||||
|
|
||||||
[Files]
|
[Files]
|
||||||
Source: "..\_artifacts\{#Runtime}\{#Framework}\Readarr\Readarr.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
|
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Readarr\Readarr.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
|
||||||
Source: "..\_artifacts\{#Runtime}\{#Framework}\Readarr\*"; Excludes: "Readarr.Update"; DestDir: "{app}\bin"; Flags: ignoreversion recursesubdirs createallsubdirs
|
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Readarr\*"; Excludes: "Readarr.Update"; DestDir: "{app}\bin"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||||
|
|
||||||
[Icons]
|
[Icons]
|
||||||
@@ -72,12 +72,13 @@ Filename: "{app}\bin\Readarr.exe"; Description: "Open Readarr Web UI"; Flags: po
|
|||||||
Filename: "{app}\bin\Readarr.exe"; Description: "Start Readarr"; Flags: postinstall skipifsilent nowait; Tasks: startupShortcut none;
|
Filename: "{app}\bin\Readarr.exe"; Description: "Start Readarr"; Flags: postinstall skipifsilent nowait; Tasks: startupShortcut none;
|
||||||
|
|
||||||
[UninstallRun]
|
[UninstallRun]
|
||||||
Filename: "{app}\bin\Readarr.Console.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist
|
Filename: "{app}\bin\readarr.console.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist
|
||||||
|
|
||||||
[Code]
|
[Code]
|
||||||
function PrepareToInstall(var NeedsRestart: Boolean): String;
|
function PrepareToInstall(var NeedsRestart: Boolean): String;
|
||||||
var
|
var
|
||||||
ResultCode: Integer;
|
ResultCode: Integer;
|
||||||
begin
|
begin
|
||||||
Exec(ExpandConstant('{commonappdata}\Readarr\bin\Readarr.Console.exe'), '/u', '', 0, ewWaitUntilTerminated, ResultCode)
|
Exec('net', 'stop readarr', '', 0, ewWaitUntilTerminated, ResultCode)
|
||||||
|
Exec('sc', 'delete readarr', '', 0, ewWaitUntilTerminated, ResultCode)
|
||||||
end;
|
end;
|
||||||
@@ -1 +1,2 @@
|
|||||||
**/JsLibraries/**
|
**/JsLibraries/**
|
||||||
|
**/*.css.d.ts
|
||||||
|
|||||||
+57
-3
@@ -1,5 +1,9 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const typescriptEslintRecommended = require('@typescript-eslint/eslint-plugin').configs.recommended;
|
||||||
|
|
||||||
const frontendFolder = __dirname;
|
const frontendFolder = __dirname;
|
||||||
|
|
||||||
@@ -44,7 +48,9 @@ module.exports = {
|
|||||||
'react',
|
'react',
|
||||||
'react-hooks',
|
'react-hooks',
|
||||||
'simple-import-sort',
|
'simple-import-sort',
|
||||||
'import'
|
'import',
|
||||||
|
'@typescript-eslint',
|
||||||
|
'prettier'
|
||||||
],
|
],
|
||||||
|
|
||||||
settings: {
|
settings: {
|
||||||
@@ -227,7 +233,7 @@ module.exports = {
|
|||||||
'consistent-this': ['error', 'self'],
|
'consistent-this': ['error', 'self'],
|
||||||
'eol-last': 'error',
|
'eol-last': 'error',
|
||||||
'func-names': 'off',
|
'func-names': 'off',
|
||||||
'func-style': ['error', 'declaration'],
|
'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
|
||||||
indent: ['error', 2, { SwitchCase: 1 }],
|
indent: ['error', 2, { SwitchCase: 1 }],
|
||||||
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
||||||
'keyword-spacing': ['error', { before: true, after: true }],
|
'keyword-spacing': ['error', { before: true, after: true }],
|
||||||
@@ -318,7 +324,9 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: ['*.js'],
|
files: [
|
||||||
|
'*.js'
|
||||||
|
],
|
||||||
rules: {
|
rules: {
|
||||||
'simple-import-sort/imports': [
|
'simple-import-sort/imports': [
|
||||||
'error',
|
'error',
|
||||||
@@ -333,6 +341,52 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'*.ts',
|
||||||
|
'*.tsx'
|
||||||
|
],
|
||||||
|
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
parserOptions: {
|
||||||
|
project: './tsconfig.json'
|
||||||
|
},
|
||||||
|
|
||||||
|
extends: [
|
||||||
|
'prettier'
|
||||||
|
],
|
||||||
|
|
||||||
|
rules: Object.assign(typescriptEslintRecommended.rules, {
|
||||||
|
'no-shadow': 'off',
|
||||||
|
// These should be enabled after cleaning things up
|
||||||
|
'@typescript-eslint/no-unused-vars': 'warn',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'react/prop-types': 'off',
|
||||||
|
'prettier/prettier': 'error',
|
||||||
|
'simple-import-sort/imports': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
groups: [
|
||||||
|
// Packages
|
||||||
|
// Absolute Paths
|
||||||
|
// Relative Paths
|
||||||
|
// Css
|
||||||
|
['^@?\\w', `^(${dirs})(/.*|$)`, '^\\.', '^\\..*css$']
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'*.css.d.ts'
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'filenames/match-exported': 'off',
|
||||||
|
'init-declarations': 'off',
|
||||||
|
'prettier/prettier': 'off'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Ignore everything recursively
|
||||||
|
*
|
||||||
|
|
||||||
|
# But not the .ts files
|
||||||
|
!*.ts*
|
||||||
|
|
||||||
|
*css.d.ts
|
||||||
|
|
||||||
|
# Check subdirectories too
|
||||||
|
!*/
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"arrowParens": "always",
|
||||||
|
"endOfLine": "auto",
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "es5"
|
||||||
|
}
|
||||||
Vendored
+21
-2
@@ -1,4 +1,23 @@
|
|||||||
// Place your settings in this file to overwrite default and user settings.
|
// Place your settings in this file to overwrite default and user settings.
|
||||||
{
|
{
|
||||||
"files.insertFinalNewline": true
|
"files.insertFinalNewline": true,
|
||||||
}
|
|
||||||
|
"files.exclude": {
|
||||||
|
"**/node_modules": true,
|
||||||
|
"**/*.d.css": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"editor.formatOnSave": false,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"typescript.preferences.quoteStyle": "single",
|
||||||
|
|
||||||
|
"eslint.validate": [
|
||||||
|
"javascript",
|
||||||
|
"javascriptreact",
|
||||||
|
"typescript",
|
||||||
|
"typescriptreact"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ module.exports = {
|
|||||||
env: {
|
env: {
|
||||||
development: {
|
development: {
|
||||||
presets: [
|
presets: [
|
||||||
['@babel/preset-react', { development: true }]
|
['@babel/preset-react', { development: true }],
|
||||||
|
'@babel/preset-typescript'
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
'babel-plugin-inline-classnames'
|
'babel-plugin-inline-classnames'
|
||||||
@@ -25,7 +26,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
production: {
|
production: {
|
||||||
presets: [
|
presets: [
|
||||||
'@babel/preset-react'
|
'@babel/preset-react',
|
||||||
|
'@babel/preset-typescript'
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
'babel-plugin-transform-react-remove-prop-types'
|
'babel-plugin-transform-react-remove-prop-types'
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const FileManagerPlugin = require('filemanager-webpack-plugin');
|
const FileManagerPlugin = require('filemanager-webpack-plugin');
|
||||||
@@ -5,6 +6,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|||||||
const LiveReloadPlugin = require('webpack-livereload-plugin');
|
const LiveReloadPlugin = require('webpack-livereload-plugin');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
|
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||||
|
|
||||||
module.exports = (env) => {
|
module.exports = (env) => {
|
||||||
const uiFolder = 'UI';
|
const uiFolder = 'UI';
|
||||||
@@ -34,10 +36,15 @@ module.exports = (env) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
entry: {
|
entry: {
|
||||||
index: 'index.js'
|
index: 'index.ts'
|
||||||
},
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
|
extensions: [
|
||||||
|
'.ts',
|
||||||
|
'.tsx',
|
||||||
|
'.js'
|
||||||
|
],
|
||||||
modules: [
|
modules: [
|
||||||
srcFolder,
|
srcFolder,
|
||||||
path.join(srcFolder, 'Shims'),
|
path.join(srcFolder, 'Shims'),
|
||||||
@@ -60,23 +67,23 @@ module.exports = (env) => {
|
|||||||
output: {
|
output: {
|
||||||
path: distFolder,
|
path: distFolder,
|
||||||
publicPath: '/',
|
publicPath: '/',
|
||||||
filename: '[name].js',
|
filename: '[name]-[contenthash].js',
|
||||||
sourceMapFilename: '[file].map'
|
sourceMapFilename: '[file].map'
|
||||||
},
|
},
|
||||||
|
|
||||||
optimization: {
|
optimization: {
|
||||||
moduleIds: 'deterministic',
|
moduleIds: 'deterministic',
|
||||||
chunkIds: 'named',
|
chunkIds: isProduction ? 'deterministic' : 'named'
|
||||||
splitChunks: {
|
|
||||||
chunks: 'initial',
|
|
||||||
name: 'vendors'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
performance: {
|
performance: {
|
||||||
hints: false
|
hints: false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
experiments: {
|
||||||
|
topLevelAwait: true
|
||||||
|
},
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
__DEV__: !isProduction,
|
__DEV__: !isProduction,
|
||||||
@@ -84,13 +91,15 @@ module.exports = (env) => {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
filename: 'Content/styles.css'
|
filename: 'Content/styles.css',
|
||||||
|
chunkFilename: 'Content/[id]-[chunkhash].css'
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
template: 'frontend/src/index.ejs',
|
template: 'frontend/src/index.ejs',
|
||||||
filename: 'index.html',
|
filename: 'index.html',
|
||||||
publicPath: '/'
|
publicPath: '/',
|
||||||
|
inject: false
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new FileManagerPlugin({
|
new FileManagerPlugin({
|
||||||
@@ -131,6 +140,8 @@ module.exports = (env) => {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
new ForkTsCheckerWebpackPlugin(),
|
||||||
|
|
||||||
new LiveReloadPlugin()
|
new LiveReloadPlugin()
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -154,7 +165,7 @@ module.exports = (env) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.js?$/,
|
test: [/\.jsx?$/, /\.tsx?$/],
|
||||||
exclude: /(node_modules|JsLibraries)/,
|
exclude: /(node_modules|JsLibraries)/,
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
@@ -185,6 +196,7 @@ module.exports = (env) => {
|
|||||||
exclude: /(node_modules|globals.css)/,
|
exclude: /(node_modules|globals.css)/,
|
||||||
use: [
|
use: [
|
||||||
{ loader: MiniCssExtractPlugin.loader },
|
{ loader: MiniCssExtractPlugin.loader },
|
||||||
|
{ loader: 'css-modules-typescript-loader' },
|
||||||
{
|
{
|
||||||
loader: 'css-loader',
|
loader: 'css-loader',
|
||||||
options: {
|
options: {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import Alert from 'Components/Alert';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
@@ -161,16 +162,16 @@ class Blocklist extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isAnyFetching && !!error &&
|
!isAnyFetching && !!error &&
|
||||||
<div>
|
<Alert kind={kinds.DANGER}>
|
||||||
{translate('UnableToLoadBlocklist')}
|
{translate('UnableToLoadBlocklist')}
|
||||||
</div>
|
</Alert>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isAllPopulated && !error && !items.length &&
|
isAllPopulated && !error && !items.length &&
|
||||||
<div>
|
<Alert kind={kinds.INFO}>
|
||||||
{translate('NoHistoryBlocklist')}
|
{translate('NoHistoryBlocklist')}
|
||||||
</div>
|
</Alert>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -214,7 +215,7 @@ class Blocklist extends Component {
|
|||||||
isOpen={isConfirmRemoveModalOpen}
|
isOpen={isConfirmRemoveModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title={translate('RemoveSelected')}
|
title={translate('RemoveSelected')}
|
||||||
message={translate('RemoveSelectedMessageText')}
|
message={translate('RemoveSelectedItemBlocklistMessageText')}
|
||||||
confirmLabel={translate('RemoveSelected')}
|
confirmLabel={translate('RemoveSelected')}
|
||||||
onConfirm={this.onRemoveSelectedConfirmed}
|
onConfirm={this.onRemoveSelectedConfirmed}
|
||||||
onCancel={this.onConfirmRemoveModalClose}
|
onCancel={this.onConfirmRemoveModalClose}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'actions': string;
|
||||||
|
'indexer': string;
|
||||||
|
'quality': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'description': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -9,7 +9,7 @@ import Link from 'Components/Link/Link';
|
|||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||||
import formatAge from 'Utilities/Number/formatAge';
|
import formatAge from 'Utilities/Number/formatAge';
|
||||||
import formatPreferredWordScore from 'Utilities/Number/formatPreferredWordScore';
|
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import styles from './HistoryDetails.css';
|
import styles from './HistoryDetails.css';
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ function HistoryDetails(props) {
|
|||||||
customFormatScore && customFormatScore !== '0' ?
|
customFormatScore && customFormatScore !== '0' ?
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('CustomFormatScore')}
|
title={translate('CustomFormatScore')}
|
||||||
data={formatPreferredWordScore(customFormatScore)}
|
data={formatCustomFormatScore(customFormatScore)}
|
||||||
/> :
|
/> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -225,7 +225,7 @@ function HistoryDetails(props) {
|
|||||||
customFormatScore && customFormatScore !== '0' ?
|
customFormatScore && customFormatScore !== '0' ?
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('CustomFormatScore')}
|
title={translate('CustomFormatScore')}
|
||||||
data={formatPreferredWordScore(customFormatScore)}
|
data={formatCustomFormatScore(customFormatScore)}
|
||||||
/> :
|
/> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -271,7 +271,7 @@ function HistoryDetails(props) {
|
|||||||
customFormatScore && customFormatScore !== '0' ?
|
customFormatScore && customFormatScore !== '0' ?
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title={translate('CustomFormatScore')}
|
title={translate('CustomFormatScore')}
|
||||||
data={formatPreferredWordScore(customFormatScore)}
|
data={formatCustomFormatScore(customFormatScore)}
|
||||||
/> :
|
/> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'markAsFailedButton': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import Alert from 'Components/Alert';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import FilterMenu from 'Components/Menu/FilterMenu';
|
import FilterMenu from 'Components/Menu/FilterMenu';
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
@@ -11,7 +12,7 @@ import Table from 'Components/Table/Table';
|
|||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||||
import TablePager from 'Components/Table/TablePager';
|
import TablePager from 'Components/Table/TablePager';
|
||||||
import { align, icons } from 'Helpers/Props';
|
import { align, icons, kinds } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import HistoryRowConnector from './HistoryRowConnector';
|
import HistoryRowConnector from './HistoryRowConnector';
|
||||||
|
|
||||||
@@ -85,9 +86,9 @@ class History extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isFetchingAny && hasError &&
|
!isFetchingAny && hasError &&
|
||||||
<div>
|
<Alert kind={kinds.DANGER}>
|
||||||
{translate('UnableToLoadHistory')}
|
{translate('UnableToLoadHistory')}
|
||||||
</div>
|
</Alert>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -95,9 +96,9 @@ class History extends Component {
|
|||||||
// wait for the books to populate because they are never coming.
|
// wait for the books to populate because they are never coming.
|
||||||
|
|
||||||
isPopulated && !hasError && !items.length &&
|
isPopulated && !hasError && !items.length &&
|
||||||
<div>
|
<Alert kind={kinds.INFO}>
|
||||||
No history found
|
{translate('NoHistory')}
|
||||||
</div>
|
</Alert>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'cell': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'customFormatScore': string;
|
||||||
|
'details': string;
|
||||||
|
'downloadClient': string;
|
||||||
|
'indexer': string;
|
||||||
|
'releaseGroup': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -8,8 +8,9 @@ import IconButton from 'Components/Link/IconButton';
|
|||||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import TableRow from 'Components/Table/TableRow';
|
import TableRow from 'Components/Table/TableRow';
|
||||||
import { icons } from 'Helpers/Props';
|
import Tooltip from 'Components/Tooltip/Tooltip';
|
||||||
import formatPreferredWordScore from 'Utilities/Number/formatPreferredWordScore';
|
import { icons, tooltipPositions } from 'Helpers/Props';
|
||||||
|
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
||||||
import HistoryDetailsModal from './Details/HistoryDetailsModal';
|
import HistoryDetailsModal from './Details/HistoryDetailsModal';
|
||||||
import HistoryEventTypeCell from './HistoryEventTypeCell';
|
import HistoryEventTypeCell from './HistoryEventTypeCell';
|
||||||
import styles from './HistoryRow.css';
|
import styles from './HistoryRow.css';
|
||||||
@@ -57,6 +58,7 @@ class HistoryRow extends Component {
|
|||||||
book,
|
book,
|
||||||
quality,
|
quality,
|
||||||
customFormats,
|
customFormats,
|
||||||
|
customFormatScore,
|
||||||
qualityCutoffNotMet,
|
qualityCutoffNotMet,
|
||||||
eventType,
|
eventType,
|
||||||
sourceTitle,
|
sourceTitle,
|
||||||
@@ -177,7 +179,14 @@ class HistoryRow extends Component {
|
|||||||
key={name}
|
key={name}
|
||||||
className={styles.customFormatScore}
|
className={styles.customFormatScore}
|
||||||
>
|
>
|
||||||
{formatPreferredWordScore(data.customFormatScore)}
|
<Tooltip
|
||||||
|
anchor={formatCustomFormatScore(
|
||||||
|
customFormatScore,
|
||||||
|
customFormats.length
|
||||||
|
)}
|
||||||
|
tooltip={<BookFormats formats={customFormats} />}
|
||||||
|
position={tooltipPositions.BOTTOM}
|
||||||
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -244,6 +253,7 @@ HistoryRow.propTypes = {
|
|||||||
book: PropTypes.object,
|
book: PropTypes.object,
|
||||||
quality: PropTypes.object.isRequired,
|
quality: PropTypes.object.isRequired,
|
||||||
customFormats: PropTypes.arrayOf(PropTypes.object),
|
customFormats: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
customFormatScore: PropTypes.number.isRequired,
|
||||||
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
||||||
eventType: PropTypes.string.isRequired,
|
eventType: PropTypes.string.isRequired,
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
sourceTitle: PropTypes.string.isRequired,
|
||||||
@@ -257,4 +267,8 @@ HistoryRow.propTypes = {
|
|||||||
onMarkAsFailedPress: PropTypes.func.isRequired
|
onMarkAsFailedPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HistoryRow.defaultProps = {
|
||||||
|
customFormats: []
|
||||||
|
};
|
||||||
|
|
||||||
export default HistoryRow;
|
export default HistoryRow;
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'torrent': string;
|
||||||
|
'usenet': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import Alert from 'Components/Alert';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
import PageContentBody from 'Components/Page/PageContentBody';
|
import PageContentBody from 'Components/Page/PageContentBody';
|
||||||
@@ -12,7 +13,7 @@ import Table from 'Components/Table/Table';
|
|||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||||
import TablePager from 'Components/Table/TablePager';
|
import TablePager from 'Components/Table/TablePager';
|
||||||
import { align, icons } from 'Helpers/Props';
|
import { align, icons, kinds } from 'Helpers/Props';
|
||||||
import getRemovedItems from 'Utilities/Object/getRemovedItems';
|
import getRemovedItems from 'Utilities/Object/getRemovedItems';
|
||||||
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
@@ -233,17 +234,17 @@ class Queue extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isRefreshing && hasError ?
|
!isRefreshing && hasError ?
|
||||||
<div>
|
<Alert kind={kinds.DANGER}>
|
||||||
{translate('FailedToLoadQueue')}
|
{translate('FailedToLoadQueue')}
|
||||||
</div> :
|
</Alert> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isAllPopulated && !hasError && !items.length ?
|
isAllPopulated && !hasError && !items.length ?
|
||||||
<div>
|
<Alert kind={kinds.INFO}>
|
||||||
{translate('QueueIsEmpty')}
|
{translate('QueueIsEmpty')}
|
||||||
</div> :
|
</Alert> :
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,12 @@
|
|||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.customFormatScore {
|
||||||
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
|
width: 55px;
|
||||||
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'actions': string;
|
||||||
|
'customFormatScore': string;
|
||||||
|
'progress': string;
|
||||||
|
'protocol': string;
|
||||||
|
'quality': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -14,9 +14,11 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
|||||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||||
import TableRow from 'Components/Table/TableRow';
|
import TableRow from 'Components/Table/TableRow';
|
||||||
import Popover from 'Components/Tooltip/Popover';
|
import Popover from 'Components/Tooltip/Popover';
|
||||||
|
import Tooltip from 'Components/Tooltip/Tooltip';
|
||||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||||
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
|
||||||
import formatBytes from 'Utilities/Number/formatBytes';
|
import formatBytes from 'Utilities/Number/formatBytes';
|
||||||
|
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import QueueStatusCell from './QueueStatusCell';
|
import QueueStatusCell from './QueueStatusCell';
|
||||||
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
||||||
@@ -44,14 +46,14 @@ class QueueRow extends Component {
|
|||||||
this.setState({ isRemoveQueueItemModalOpen: true });
|
this.setState({ isRemoveQueueItemModalOpen: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
onRemoveQueueItemModalConfirmed = (blocklist, skipredownload) => {
|
onRemoveQueueItemModalConfirmed = (blocklist, skipRedownload) => {
|
||||||
const {
|
const {
|
||||||
onRemoveQueueItemPress,
|
onRemoveQueueItemPress,
|
||||||
onQueueRowModalOpenOrClose
|
onQueueRowModalOpenOrClose
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
onQueueRowModalOpenOrClose(false);
|
onQueueRowModalOpenOrClose(false);
|
||||||
onRemoveQueueItemPress(blocklist, skipredownload);
|
onRemoveQueueItemPress(blocklist, skipRedownload);
|
||||||
|
|
||||||
this.setState({ isRemoveQueueItemModalOpen: false });
|
this.setState({ isRemoveQueueItemModalOpen: false });
|
||||||
};
|
};
|
||||||
@@ -91,6 +93,7 @@ class QueueRow extends Component {
|
|||||||
book,
|
book,
|
||||||
quality,
|
quality,
|
||||||
customFormats,
|
customFormats,
|
||||||
|
customFormatScore,
|
||||||
protocol,
|
protocol,
|
||||||
indexer,
|
indexer,
|
||||||
outputPath,
|
outputPath,
|
||||||
@@ -222,6 +225,24 @@ class QueueRow extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name === 'customFormatScore') {
|
||||||
|
return (
|
||||||
|
<TableRowCell
|
||||||
|
key={name}
|
||||||
|
className={styles.customFormatScore}
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
anchor={formatCustomFormatScore(
|
||||||
|
customFormatScore,
|
||||||
|
customFormats.length
|
||||||
|
)}
|
||||||
|
tooltip={<BookFormats formats={customFormats} />}
|
||||||
|
position={tooltipPositions.BOTTOM}
|
||||||
|
/>
|
||||||
|
</TableRowCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (name === 'protocol') {
|
if (name === 'protocol') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
@@ -392,6 +413,7 @@ QueueRow.propTypes = {
|
|||||||
book: PropTypes.object,
|
book: PropTypes.object,
|
||||||
quality: PropTypes.object.isRequired,
|
quality: PropTypes.object.isRequired,
|
||||||
customFormats: PropTypes.arrayOf(PropTypes.object),
|
customFormats: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
customFormatScore: PropTypes.number.isRequired,
|
||||||
protocol: PropTypes.string.isRequired,
|
protocol: PropTypes.string.isRequired,
|
||||||
indexer: PropTypes.string,
|
indexer: PropTypes.string,
|
||||||
outputPath: PropTypes.string,
|
outputPath: PropTypes.string,
|
||||||
@@ -416,6 +438,7 @@ QueueRow.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
QueueRow.defaultProps = {
|
QueueRow.defaultProps = {
|
||||||
|
customFormats: [],
|
||||||
isGrabbing: false,
|
isGrabbing: false,
|
||||||
isRemoving: false
|
isRemoving: false
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'status': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -23,7 +23,7 @@ class RemoveQueueItemModal extends Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
remove: true,
|
remove: true,
|
||||||
blocklist: false,
|
blocklist: false,
|
||||||
skipredownload: false
|
skipRedownload: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ class RemoveQueueItemModal extends Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
remove: true,
|
remove: true,
|
||||||
blocklist: false,
|
blocklist: false,
|
||||||
skipredownload: false
|
skipRedownload: false
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -49,8 +49,8 @@ class RemoveQueueItemModal extends Component {
|
|||||||
this.setState({ blocklist: value });
|
this.setState({ blocklist: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
onSkipReDownloadChange = ({ value }) => {
|
onSkipRedownloadChange = ({ value }) => {
|
||||||
this.setState({ skipredownload: value });
|
this.setState({ skipRedownload: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
onRemoveConfirmed = () => {
|
onRemoveConfirmed = () => {
|
||||||
@@ -76,7 +76,7 @@ class RemoveQueueItemModal extends Component {
|
|||||||
isPending
|
isPending
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { remove, blocklist, skipredownload } = this.state;
|
const { remove, blocklist, skipRedownload } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -124,7 +124,7 @@ class RemoveQueueItemModal extends Component {
|
|||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="blocklist"
|
name="blocklist"
|
||||||
value={blocklist}
|
value={blocklist}
|
||||||
helpText={translate('BlocklistHelpText')}
|
helpText={translate('BlocklistReleaseHelpText')}
|
||||||
onChange={this.onBlocklistChange}
|
onChange={this.onBlocklistChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
@@ -137,10 +137,10 @@ class RemoveQueueItemModal extends Component {
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="skipredownload"
|
name="skipRedownload"
|
||||||
value={skipredownload}
|
value={skipRedownload}
|
||||||
helpText={translate('SkipredownloadHelpText')}
|
helpText={translate('SkipRedownloadHelpText')}
|
||||||
onChange={this.onSkipReDownloadChange}
|
onChange={this.onSkipRedownloadChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'message': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -24,7 +24,7 @@ class RemoveQueueItemsModal extends Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
remove: true,
|
remove: true,
|
||||||
blocklist: false,
|
blocklist: false,
|
||||||
skipredownload: false
|
skipRedownload: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ class RemoveQueueItemsModal extends Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
remove: true,
|
remove: true,
|
||||||
blocklist: false,
|
blocklist: false,
|
||||||
skipredownload: false
|
skipRedownload: false
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -50,8 +50,8 @@ class RemoveQueueItemsModal extends Component {
|
|||||||
this.setState({ blocklist: value });
|
this.setState({ blocklist: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
onSkipReDownloadChange = ({ value }) => {
|
onSkipRedownloadChange = ({ value }) => {
|
||||||
this.setState({ skipredownload: value });
|
this.setState({ skipRedownload: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
onRemoveConfirmed = () => {
|
onRemoveConfirmed = () => {
|
||||||
@@ -77,7 +77,7 @@ class RemoveQueueItemsModal extends Component {
|
|||||||
allPending
|
allPending
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { remove, blocklist, skipredownload } = this.state;
|
const { remove, blocklist, skipRedownload } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -89,12 +89,12 @@ class RemoveQueueItemsModal extends Component {
|
|||||||
onModalClose={this.onModalClose}
|
onModalClose={this.onModalClose}
|
||||||
>
|
>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
Remove Selected Item{selectedCount > 1 ? 's' : ''}
|
{selectedCount > 1 ? translate('RemoveSelectedItems') : translate('RemoveSelectedItem')}
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div className={styles.message}>
|
<div className={styles.message}>
|
||||||
Are you sure you want to remove {selectedCount} item{selectedCount > 1 ? 's' : ''} from the queue?
|
{selectedCount > 1 ? translate('RemoveSelectedItemsQueueMessageText', selectedCount) : translate('RemoveSelectedItemQueueMessageText')}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -118,14 +118,14 @@ class RemoveQueueItemsModal extends Component {
|
|||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Add Release{selectedCount > 1 ? 's' : ''} To Blocklist
|
{selectedCount > 1 ? translate('BlocklistReleases') : translate('BlocklistRelease')}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="blocklist"
|
name="blocklist"
|
||||||
value={blocklist}
|
value={blocklist}
|
||||||
helpText={translate('BlocklistHelpText')}
|
helpText={translate('BlocklistReleaseHelpText')}
|
||||||
onChange={this.onBlocklistChange}
|
onChange={this.onBlocklistChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
@@ -138,10 +138,10 @@ class RemoveQueueItemsModal extends Component {
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="skipredownload"
|
name="skipRedownload"
|
||||||
value={skipredownload}
|
value={skipRedownload}
|
||||||
helpText={translate('SkipredownloadHelpText')}
|
helpText={translate('SkipRedownloadHelpText')}
|
||||||
onChange={this.onSkipReDownloadChange}
|
onChange={this.onSkipRedownloadChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
}
|
}
|
||||||
@@ -150,14 +150,14 @@ class RemoveQueueItemsModal extends Component {
|
|||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button onPress={this.onModalClose}>
|
<Button onPress={this.onModalClose}>
|
||||||
Close
|
{translate('Close')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
onPress={this.onRemoveConfirmed}
|
onPress={this.onRemoveConfirmed}
|
||||||
>
|
>
|
||||||
Remove
|
{translate('Remove')}
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'timeleft': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
.version {
|
.version {
|
||||||
margin: 0 3px;
|
margin: 0 3px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
font-family: var(--defaultFontFamily);
|
||||||
}
|
}
|
||||||
|
|
||||||
.maintenance {
|
.maintenance {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'changes': string;
|
||||||
|
'maintenance': string;
|
||||||
|
'version': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Button from 'Components/Link/Button';
|
import Button from 'Components/Link/Button';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
|
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
|
||||||
import ModalBody from 'Components/Modal/ModalBody';
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
import ModalContent from 'Components/Modal/ModalContent';
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
@@ -64,12 +65,12 @@ function AppUpdatedModalContent(props) {
|
|||||||
return (
|
return (
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
Readarr Updated
|
{translate('AppUpdated', { appName: 'Readarr' })}
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div>
|
<div>
|
||||||
Version <span className={styles.version}>{version}</span> of Readarr has been installed, in order to get the latest changes you'll need to reload Readarr.
|
<InlineMarkdown data={translate('AppUpdatedVersion', { appName: 'Readarr', version })} blockClassName={styles.version} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -77,16 +78,14 @@ function AppUpdatedModalContent(props) {
|
|||||||
<div>
|
<div>
|
||||||
{
|
{
|
||||||
!update.changes &&
|
!update.changes &&
|
||||||
<div className={styles.maintenance}>
|
<div className={styles.maintenance}>{translate('MaintenanceRelease')}</div>
|
||||||
{translate('MaintenanceRelease')}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
!!update.changes &&
|
!!update.changes &&
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.changes}>
|
<div className={styles.changes}>
|
||||||
What's new?
|
{translate('WhatsNew')}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UpdateChanges
|
<UpdateChanges
|
||||||
@@ -113,14 +112,14 @@ function AppUpdatedModalContent(props) {
|
|||||||
<Button
|
<Button
|
||||||
onPress={onSeeChangesPress}
|
onPress={onSeeChangesPress}
|
||||||
>
|
>
|
||||||
Recent Changes
|
{translate('RecentChanges')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
kind={kinds.PRIMARY}
|
kind={kinds.PRIMARY}
|
||||||
onPress={onModalClose}
|
onPress={onModalClose}
|
||||||
>
|
>
|
||||||
Reload
|
{translate('Reload')}
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'automatic': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -7,6 +7,7 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
import { kinds } from 'Helpers/Props';
|
import { kinds } from 'Helpers/Props';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
import styles from './ConnectionLostModal.css';
|
import styles from './ConnectionLostModal.css';
|
||||||
|
|
||||||
function ConnectionLostModal(props) {
|
function ConnectionLostModal(props) {
|
||||||
@@ -22,16 +23,16 @@ function ConnectionLostModal(props) {
|
|||||||
>
|
>
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
Connection Lost
|
{translate('ConnectionLost')}
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div>
|
<div>
|
||||||
Readarr has lost its connection to the backend and will need to be reloaded to restore functionality.
|
{translate('ConnectionLostToBackend', { appName: 'Readarr' })}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.automatic}>
|
<div className={styles.automatic}>
|
||||||
Readarr will try to connect automatically, or you can click reload below.
|
{translate('ConnectionLostReconnect', { appName: 'Readarr' })}
|
||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
@@ -39,7 +40,7 @@ function ConnectionLostModal(props) {
|
|||||||
kind={kinds.PRIMARY}
|
kind={kinds.PRIMARY}
|
||||||
onPress={onModalClose}
|
onPress={onModalClose}
|
||||||
>
|
>
|
||||||
Reload
|
{translate('Reload')}
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface ModelBase {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModelBase;
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import SortDirection from 'Helpers/Props/SortDirection';
|
||||||
|
|
||||||
|
export interface Error {
|
||||||
|
responseJSON: {
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppSectionDeleteState {
|
||||||
|
isDeleting: boolean;
|
||||||
|
deleteError: Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppSectionSaveState {
|
||||||
|
isSaving: boolean;
|
||||||
|
saveError: Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PagedAppSectionState {
|
||||||
|
pageSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppSectionSchemaState<T> {
|
||||||
|
isSchemaFetching: boolean;
|
||||||
|
isSchemaPopulated: boolean;
|
||||||
|
schemaError: Error;
|
||||||
|
schema: {
|
||||||
|
items: T[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppSectionItemState<T> {
|
||||||
|
isFetching: boolean;
|
||||||
|
isPopulated: boolean;
|
||||||
|
error: Error;
|
||||||
|
item: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AppSectionState<T> {
|
||||||
|
isFetching: boolean;
|
||||||
|
isPopulated: boolean;
|
||||||
|
error: Error;
|
||||||
|
items: T[];
|
||||||
|
sortKey: string;
|
||||||
|
sortDirection: SortDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppSectionState;
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import SettingsAppState from './SettingsAppState';
|
||||||
|
import TagsAppState from './TagsAppState';
|
||||||
|
|
||||||
|
interface FilterBuilderPropOption {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterBuilderProp<T> {
|
||||||
|
name: string;
|
||||||
|
label: string;
|
||||||
|
type: string;
|
||||||
|
valueType?: string;
|
||||||
|
optionsSelector?: (items: T[]) => FilterBuilderPropOption[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PropertyFilter {
|
||||||
|
key: string;
|
||||||
|
value: boolean | string | number | string[] | number[];
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Filter {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
filers: PropertyFilter[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomFilter {
|
||||||
|
id: number;
|
||||||
|
type: string;
|
||||||
|
label: string;
|
||||||
|
filers: PropertyFilter[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AppState {
|
||||||
|
settings: SettingsAppState;
|
||||||
|
tags: TagsAppState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppState;
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import AppSectionState, {
|
||||||
|
AppSectionDeleteState,
|
||||||
|
AppSectionSaveState,
|
||||||
|
} from 'App/State/AppSectionState';
|
||||||
|
import DownloadClient from 'typings/DownloadClient';
|
||||||
|
import ImportList from 'typings/ImportList';
|
||||||
|
import Indexer from 'typings/Indexer';
|
||||||
|
import Notification from 'typings/Notification';
|
||||||
|
import { UiSettings } from 'typings/UiSettings';
|
||||||
|
|
||||||
|
export interface DownloadClientAppState
|
||||||
|
extends AppSectionState<DownloadClient>,
|
||||||
|
AppSectionDeleteState,
|
||||||
|
AppSectionSaveState {}
|
||||||
|
|
||||||
|
export interface ImportListAppState
|
||||||
|
extends AppSectionState<ImportList>,
|
||||||
|
AppSectionDeleteState,
|
||||||
|
AppSectionSaveState {}
|
||||||
|
|
||||||
|
export interface IndexerAppState
|
||||||
|
extends AppSectionState<Indexer>,
|
||||||
|
AppSectionDeleteState,
|
||||||
|
AppSectionSaveState {}
|
||||||
|
|
||||||
|
export interface NotificationAppState
|
||||||
|
extends AppSectionState<Notification>,
|
||||||
|
AppSectionDeleteState {}
|
||||||
|
|
||||||
|
export type UiSettingsAppState = AppSectionState<UiSettings>;
|
||||||
|
|
||||||
|
interface SettingsAppState {
|
||||||
|
downloadClients: DownloadClientAppState;
|
||||||
|
importLists: ImportListAppState;
|
||||||
|
indexers: IndexerAppState;
|
||||||
|
notifications: NotificationAppState;
|
||||||
|
uiSettings: UiSettingsAppState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SettingsAppState;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import ModelBase from 'App/ModelBase';
|
||||||
|
import AppSectionState, {
|
||||||
|
AppSectionDeleteState,
|
||||||
|
} from 'App/State/AppSectionState';
|
||||||
|
|
||||||
|
export interface Tag extends ModelBase {
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TagsAppState extends AppSectionState<Tag>, AppSectionDeleteState {}
|
||||||
|
|
||||||
|
export default TagsAppState;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'deleteFilesMessage': string;
|
||||||
|
'pathContainer': string;
|
||||||
|
'pathIcon': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'alternateTitle': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'authorNavigationButton': string;
|
||||||
|
'authorNavigationButtons': string;
|
||||||
|
'authorUpButton': string;
|
||||||
|
'contentContainer': string;
|
||||||
|
'errorMessage': string;
|
||||||
|
'innerContentBody': string;
|
||||||
|
'metadataMessage': string;
|
||||||
|
'selectedTab': string;
|
||||||
|
'tab': string;
|
||||||
|
'tabContent': string;
|
||||||
|
'tabList': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -392,10 +392,7 @@ class AuthorDetails extends Component {
|
|||||||
name={icons.ARROW_UP}
|
name={icons.ARROW_UP}
|
||||||
size={30}
|
size={30}
|
||||||
title={translate('GoToAuthorListing')}
|
title={translate('GoToAuthorListing')}
|
||||||
to={{
|
to={'/'}
|
||||||
pathname: '/',
|
|
||||||
state: { restoreScrollPosition: true }
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'alternateTitlesIconContainer': string;
|
||||||
|
'authorNavigationButton': string;
|
||||||
|
'authorNavigationButtons': string;
|
||||||
|
'authorUpButton': string;
|
||||||
|
'backdrop': string;
|
||||||
|
'backdropOverlay': string;
|
||||||
|
'details': string;
|
||||||
|
'detailsLabel': string;
|
||||||
|
'header': string;
|
||||||
|
'headerContent': string;
|
||||||
|
'info': string;
|
||||||
|
'links': string;
|
||||||
|
'monitorToggleButton': string;
|
||||||
|
'overview': string;
|
||||||
|
'path': string;
|
||||||
|
'poster': string;
|
||||||
|
'qualityProfileName': string;
|
||||||
|
'sizeOnDisk': string;
|
||||||
|
'tags': string;
|
||||||
|
'title': string;
|
||||||
|
'titleContainer': string;
|
||||||
|
'titleRow': string;
|
||||||
|
'toggleMonitoredContainer': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -92,6 +92,7 @@ class AuthorDetailsHeader extends Component {
|
|||||||
titleWidth
|
titleWidth
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
|
const fanartUrl = getFanartUrl(images);
|
||||||
const marqueeWidth = titleWidth - (isSmallScreen ? 85 : 160);
|
const marqueeWidth = titleWidth - (isSmallScreen ? 85 : 160);
|
||||||
|
|
||||||
const continuing = status === 'continuing';
|
const continuing = status === 'continuing';
|
||||||
@@ -108,9 +109,11 @@ class AuthorDetailsHeader extends Component {
|
|||||||
<div className={styles.header} style={{ width }} >
|
<div className={styles.header} style={{ width }} >
|
||||||
<div
|
<div
|
||||||
className={styles.backdrop}
|
className={styles.backdrop}
|
||||||
style={{
|
style={
|
||||||
backgroundImage: `url(${getFanartUrl(images)})`
|
fanartUrl ?
|
||||||
}}
|
{ backgroundImage: `url(${fanartUrl})` } :
|
||||||
|
null
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div className={styles.backdropOverlay} />
|
<div className={styles.backdropOverlay} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'link': string;
|
||||||
|
'linkLabel': string;
|
||||||
|
'links': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'actionButton': string;
|
||||||
|
'actionMenuIcon': string;
|
||||||
|
'actions': string;
|
||||||
|
'actionsMenu': string;
|
||||||
|
'actionsMenuContent': string;
|
||||||
|
'bookCount': string;
|
||||||
|
'bookType': string;
|
||||||
|
'bookTypeLabel': string;
|
||||||
|
'books': string;
|
||||||
|
'collapseButtonContainer': string;
|
||||||
|
'collapseButtonIcon': string;
|
||||||
|
'episodeCountTooltip': string;
|
||||||
|
'expandButton': string;
|
||||||
|
'expandButtonIcon': string;
|
||||||
|
'header': string;
|
||||||
|
'left': string;
|
||||||
|
'noBooks': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'actionButton': string;
|
||||||
|
'actionMenuIcon': string;
|
||||||
|
'actions': string;
|
||||||
|
'actionsMenu': string;
|
||||||
|
'actionsMenuContent': string;
|
||||||
|
'bookCount': string;
|
||||||
|
'bookType': string;
|
||||||
|
'bookTypeLabel': string;
|
||||||
|
'books': string;
|
||||||
|
'collapseButtonContainer': string;
|
||||||
|
'collapseButtonIcon': string;
|
||||||
|
'episodeCountTooltip': string;
|
||||||
|
'expandButton': string;
|
||||||
|
'expandButtonIcon': string;
|
||||||
|
'header': string;
|
||||||
|
'left': string;
|
||||||
|
'noBooks': string;
|
||||||
|
'seriesTitle': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'monitored': string;
|
||||||
|
'pageCount': string;
|
||||||
|
'position': string;
|
||||||
|
'rating': string;
|
||||||
|
'releaseDate': string;
|
||||||
|
'status': string;
|
||||||
|
'title': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'center': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'deleteButton': string;
|
||||||
|
'labelIcon': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'message': string;
|
||||||
|
'retagIcon': string;
|
||||||
|
'searchForNewBookContainer': string;
|
||||||
|
'searchForNewBookInput': string;
|
||||||
|
'searchForNewBookLabel': string;
|
||||||
|
'searchForNewBookLabelContainer': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'buttonContainer': string;
|
||||||
|
'buttonContainerContent': string;
|
||||||
|
'buttons': string;
|
||||||
|
'deleteSelectedButton': string;
|
||||||
|
'dropdownContainer': string;
|
||||||
|
'footer': string;
|
||||||
|
'inputContainer': string;
|
||||||
|
'organizeSelectedButton': string;
|
||||||
|
'selectedAuthorLabel': string;
|
||||||
|
'tagsButton': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
import MoveAuthorModal from 'Author/MoveAuthor/MoveAuthorModal';
|
import MoveAuthorModal from 'Author/MoveAuthor/MoveAuthorModal';
|
||||||
import MetadataProfileSelectInputConnector from 'Components/Form/MetadataProfileSelectInputConnector';
|
import MetadataProfileSelectInputConnector from 'Components/Form/MetadataProfileSelectInputConnector';
|
||||||
import MonitorNewItemsSelectInput from 'Components/Form/MonitorNewItemsSelectInput';
|
import MonitorNewItemsSelectInput from 'Components/Form/MonitorNewItemsSelectInput';
|
||||||
@@ -9,6 +10,7 @@ import SelectInput from 'Components/Form/SelectInput';
|
|||||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||||
import PageContentFooter from 'Components/Page/PageContentFooter';
|
import PageContentFooter from 'Components/Page/PageContentFooter';
|
||||||
import { kinds } from 'Helpers/Props';
|
import { kinds } from 'Helpers/Props';
|
||||||
|
import { fetchRootFolders } from 'Store/Actions/Settings/rootFolders';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import AuthorEditorFooterLabel from './AuthorEditorFooterLabel';
|
import AuthorEditorFooterLabel from './AuthorEditorFooterLabel';
|
||||||
import DeleteAuthorModal from './Delete/DeleteAuthorModal';
|
import DeleteAuthorModal from './Delete/DeleteAuthorModal';
|
||||||
@@ -17,6 +19,10 @@ import styles from './AuthorEditorFooter.css';
|
|||||||
|
|
||||||
const NO_CHANGE = 'noChange';
|
const NO_CHANGE = 'noChange';
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
dispatchFetchRootFolders: fetchRootFolders
|
||||||
|
};
|
||||||
|
|
||||||
class AuthorEditorFooter extends Component {
|
class AuthorEditorFooter extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -39,6 +45,13 @@ class AuthorEditorFooter extends Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.dispatchFetchRootFolders();
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
const {
|
const {
|
||||||
isSaving,
|
isSaving,
|
||||||
@@ -160,9 +173,9 @@ class AuthorEditorFooter extends Component {
|
|||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const monitoredOptions = [
|
const monitoredOptions = [
|
||||||
{ key: NO_CHANGE, value: 'No Change', disabled: true },
|
{ key: NO_CHANGE, value: translate('NoChange'), disabled: true },
|
||||||
{ key: 'monitored', value: 'Monitored' },
|
{ key: 'monitored', value: translate('Monitored') },
|
||||||
{ key: 'unmonitored', value: 'Unmonitored' }
|
{ key: 'unmonitored', value: translate('Unmonitored') }
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -341,7 +354,8 @@ AuthorEditorFooter.propTypes = {
|
|||||||
showMetadataProfile: PropTypes.bool.isRequired,
|
showMetadataProfile: PropTypes.bool.isRequired,
|
||||||
onSaveSelected: PropTypes.func.isRequired,
|
onSaveSelected: PropTypes.func.isRequired,
|
||||||
onOrganizeAuthorPress: PropTypes.func.isRequired,
|
onOrganizeAuthorPress: PropTypes.func.isRequired,
|
||||||
onRetagAuthorPress: PropTypes.func.isRequired
|
onRetagAuthorPress: PropTypes.func.isRequired,
|
||||||
|
dispatchFetchRootFolders: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AuthorEditorFooter;
|
export default connect(undefined, mapDispatchToProps)(AuthorEditorFooter);
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'label': string;
|
||||||
|
'savingIcon': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'message': string;
|
||||||
|
'path': string;
|
||||||
|
'pathContainer': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'message': string;
|
||||||
|
'renameIcon': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'message': string;
|
||||||
|
'renameIcon': string;
|
||||||
|
'result': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -98,10 +98,10 @@ class TagsModalContent extends Component {
|
|||||||
value={applyTags}
|
value={applyTags}
|
||||||
values={applyTagsOptions}
|
values={applyTagsOptions}
|
||||||
helpTexts={[
|
helpTexts={[
|
||||||
translate('ApplyTagsHelpTexts1'),
|
translate('ApplyTagsHelpTextHowToApplyAuthors'),
|
||||||
translate('ApplyTagsHelpTexts2'),
|
translate('ApplyTagsHelpTextAdd'),
|
||||||
translate('ApplyTagsHelpTexts3'),
|
translate('ApplyTagsHelpTextRemove'),
|
||||||
translate('ApplyTagsHelpTexts4')
|
translate('ApplyTagsHelpTextReplace')
|
||||||
]}
|
]}
|
||||||
onChange={this.onInputChange}
|
onChange={this.onInputChange}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'actions': string;
|
||||||
|
'details': string;
|
||||||
|
'sourceTitle': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import Alert from 'Components/Alert';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
import Table from 'Components/Table/Table';
|
import Table from 'Components/Table/Table';
|
||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
|
import { kinds } from 'Helpers/Props';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import AuthorHistoryRowConnector from './AuthorHistoryRowConnector';
|
import AuthorHistoryRowConnector from './AuthorHistoryRowConnector';
|
||||||
|
|
||||||
@@ -70,9 +72,9 @@ class AuthorHistoryTableContent extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!isFetching && !!error &&
|
!isFetching && !!error &&
|
||||||
<div>
|
<Alert kind={kinds.DANGER}>
|
||||||
{translate('UnableToLoadHistory')}
|
{translate('UnableToLoadHistory')}
|
||||||
</div>
|
</Alert>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'bannersInnerContentBody': string;
|
||||||
|
'contentBody': string;
|
||||||
|
'contentBodyContainer': string;
|
||||||
|
'errorMessage': string;
|
||||||
|
'pageContentBodyWrapper': string;
|
||||||
|
'postersInnerContentBody': string;
|
||||||
|
'tableInnerContentBody': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'continuing': string;
|
||||||
|
'ended': string;
|
||||||
|
'footer': string;
|
||||||
|
'legendItem': string;
|
||||||
|
'legendItemColor': string;
|
||||||
|
'missingMonitored': string;
|
||||||
|
'missingUnmonitored': string;
|
||||||
|
'statistics': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'actions': string;
|
||||||
|
'container': string;
|
||||||
|
'content': string;
|
||||||
|
'details': string;
|
||||||
|
'editorSelect': string;
|
||||||
|
'ended': string;
|
||||||
|
'info': string;
|
||||||
|
'link': string;
|
||||||
|
'overview': string;
|
||||||
|
'poster': string;
|
||||||
|
'posterContainer': string;
|
||||||
|
'title': string;
|
||||||
|
'titleRow': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'infos': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'icon': string;
|
||||||
|
'infoRow': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'grid': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
+30
-5
@@ -14,14 +14,39 @@ import { inputTypes } from 'Helpers/Props';
|
|||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
const nameOptions = [
|
const nameOptions = [
|
||||||
{ key: 'firstLast', value: translate('NameFirstLast') },
|
{
|
||||||
{ key: 'lastFirst', value: translate('NameLastFirst') }
|
key: 'firstLast',
|
||||||
|
get value() {
|
||||||
|
return translate('NameFirstLast');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'lastFirst',
|
||||||
|
get value() {
|
||||||
|
return translate('NameLastFirst');
|
||||||
|
}
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const posterSizeOptions = [
|
const posterSizeOptions = [
|
||||||
{ key: 'small', value: 'Small' },
|
{
|
||||||
{ key: 'medium', value: 'Medium' },
|
key: 'small',
|
||||||
{ key: 'large', value: 'Large' }
|
get value() {
|
||||||
|
return translate('Small');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'medium',
|
||||||
|
get value() {
|
||||||
|
return translate('Medium');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'large',
|
||||||
|
get value() {
|
||||||
|
return translate('Large');
|
||||||
|
}
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
class AuthorIndexOverviewOptionsModalContent extends Component {
|
class AuthorIndexOverviewOptionsModalContent extends Component {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'action': string;
|
||||||
|
'container': string;
|
||||||
|
'content': string;
|
||||||
|
'controls': string;
|
||||||
|
'editorSelect': string;
|
||||||
|
'ended': string;
|
||||||
|
'link': string;
|
||||||
|
'nextAiring': string;
|
||||||
|
'overlayTitle': string;
|
||||||
|
'poster': string;
|
||||||
|
'posterContainer': string;
|
||||||
|
'title': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'info': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'grid': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -14,15 +14,45 @@ import { inputTypes } from 'Helpers/Props';
|
|||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
const posterSizeOptions = [
|
const posterSizeOptions = [
|
||||||
{ key: 'small', value: 'Small' },
|
{
|
||||||
{ key: 'medium', value: 'Medium' },
|
key: 'small',
|
||||||
{ key: 'large', value: 'Large' }
|
get value() {
|
||||||
|
return translate('Small');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'medium',
|
||||||
|
get value() {
|
||||||
|
return translate('Medium');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'large',
|
||||||
|
get value() {
|
||||||
|
return translate('Large');
|
||||||
|
}
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const nameOptions = [
|
const nameOptions = [
|
||||||
{ key: 'no', value: translate('NoName') },
|
{
|
||||||
{ key: 'firstLast', value: translate('NameFirstLast') },
|
key: 'no',
|
||||||
{ key: 'lastFirst', value: translate('NameLastFirst') }
|
get value() {
|
||||||
|
return translate('NoName');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'firstLast',
|
||||||
|
get value() {
|
||||||
|
return translate('NameFirstLast');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'lastFirst',
|
||||||
|
get value() {
|
||||||
|
return translate('NameLastFirst');
|
||||||
|
}
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
class AuthorIndexPosterOptionsModalContent extends Component {
|
class AuthorIndexPosterOptionsModalContent extends Component {
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'progress': string;
|
||||||
|
'progressBar': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -17,8 +17,8 @@ function AuthorIndexProgressBar(props) {
|
|||||||
detailedProgressBar
|
detailedProgressBar
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const progress = bookCount ? bookFileCount / bookCount * 100 : 100;
|
const progress = bookCount ? bookCount / totalBookCount * 100 : 100;
|
||||||
const text = `${bookFileCount} / ${bookCount}`;
|
const text = `${bookCount} / ${totalBookCount}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'actions': string;
|
||||||
|
'added': string;
|
||||||
|
'banner': string;
|
||||||
|
'bannerGrow': string;
|
||||||
|
'bookCount': string;
|
||||||
|
'bookProgress': string;
|
||||||
|
'genres': string;
|
||||||
|
'lastBook': string;
|
||||||
|
'latestBook': string;
|
||||||
|
'metadataProfileId': string;
|
||||||
|
'nextBook': string;
|
||||||
|
'path': string;
|
||||||
|
'qualityProfileId': string;
|
||||||
|
'ratings': string;
|
||||||
|
'sizeOnDisk': string;
|
||||||
|
'sortName': string;
|
||||||
|
'status': string;
|
||||||
|
'tags': string;
|
||||||
|
'useSceneNumbering': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'actions': string;
|
||||||
|
'added': string;
|
||||||
|
'banner': string;
|
||||||
|
'bannerGrow': string;
|
||||||
|
'bannerImage': string;
|
||||||
|
'bookProgress': string;
|
||||||
|
'cell': string;
|
||||||
|
'checkInput': string;
|
||||||
|
'genres': string;
|
||||||
|
'lastBook': string;
|
||||||
|
'link': string;
|
||||||
|
'metadataProfileId': string;
|
||||||
|
'nextBook': string;
|
||||||
|
'overlayTitle': string;
|
||||||
|
'path': string;
|
||||||
|
'qualityProfileId': string;
|
||||||
|
'ratings': string;
|
||||||
|
'sizeOnDisk': string;
|
||||||
|
'sortName': string;
|
||||||
|
'status': string;
|
||||||
|
'tags': string;
|
||||||
|
'useSceneNumbering': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -297,7 +297,7 @@ class AuthorIndexRow extends Component {
|
|||||||
progress={progress}
|
progress={progress}
|
||||||
kind={getProgressBarKind(status, monitored, progress)}
|
kind={getProgressBarKind(status, monitored, progress)}
|
||||||
showText={true}
|
showText={true}
|
||||||
text={`${bookFileCount} / ${bookCount}`}
|
text={`${bookCount} / ${totalBookCount}`}
|
||||||
title={translate('BookFileCountBookCountTotalTotalBookCountInterp', [bookFileCount, bookCount, totalBookCount])}
|
title={translate('BookFileCountBookCountTotalTotalBookCountInterp', [bookFileCount, bookCount, totalBookCount])}
|
||||||
width={125}
|
width={125}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'tableContainer': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -7,8 +7,18 @@ import { inputTypes } from 'Helpers/Props';
|
|||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
const nameOptions = [
|
const nameOptions = [
|
||||||
{ key: 'firstLast', value: translate('NameFirstLast') },
|
{
|
||||||
{ key: 'lastFirst', value: translate('NameLastFirst') }
|
key: 'firstLast',
|
||||||
|
get value() {
|
||||||
|
return translate('NameFirstLast');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'lastFirst',
|
||||||
|
get value() {
|
||||||
|
return translate('NameLastFirst');
|
||||||
|
}
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
class AuthorIndexTableOptions extends Component {
|
class AuthorIndexTableOptions extends Component {
|
||||||
|
|||||||
@@ -6,4 +6,5 @@
|
|||||||
|
|
||||||
.statusIcon {
|
.statusIcon {
|
||||||
width: 20px !important;
|
width: 20px !important;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'status': string;
|
||||||
|
'statusIcon': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -33,7 +33,7 @@ class MonitoringOptionsModalContent extends Component {
|
|||||||
const {
|
const {
|
||||||
isSaving,
|
isSaving,
|
||||||
saveError
|
saveError
|
||||||
} = prevProps;
|
} = this.props;
|
||||||
|
|
||||||
if (prevProps.isSaving && !isSaving && !saveError) {
|
if (prevProps.isSaving && !isSaving && !saveError) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'doNotMoveButton': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'buttonContainer': string;
|
||||||
|
'message': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'BookSearchCell': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'deleteFilesMessage': string;
|
||||||
|
'pathContainer': string;
|
||||||
|
'pathIcon': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'bookNavigationButton': string;
|
||||||
|
'bookNavigationButtons': string;
|
||||||
|
'bookUpButton': string;
|
||||||
|
'contentContainer': string;
|
||||||
|
'filterIcon': string;
|
||||||
|
'innerContentBody': string;
|
||||||
|
'selectedTab': string;
|
||||||
|
'tab': string;
|
||||||
|
'tabContent': string;
|
||||||
|
'tabList': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'alternateTitlesIconContainer': string;
|
||||||
|
'backdrop': string;
|
||||||
|
'backdropOverlay': string;
|
||||||
|
'cover': string;
|
||||||
|
'details': string;
|
||||||
|
'detailsLabel': string;
|
||||||
|
'duration': string;
|
||||||
|
'header': string;
|
||||||
|
'headerContent': string;
|
||||||
|
'info': string;
|
||||||
|
'links': string;
|
||||||
|
'monitorToggleButton': string;
|
||||||
|
'overview': string;
|
||||||
|
'qualityProfileName': string;
|
||||||
|
'sizeOnDisk': string;
|
||||||
|
'tags': string;
|
||||||
|
'title': string;
|
||||||
|
'titleContainer': string;
|
||||||
|
'titleRow': string;
|
||||||
|
'toggleMonitoredContainer': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -83,15 +83,18 @@ class BookDetailsHeader extends Component {
|
|||||||
titleWidth
|
titleWidth
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
|
const fanartUrl = getFanartUrl(author.images);
|
||||||
const marqueeWidth = titleWidth - (isSmallScreen ? 85 : 160);
|
const marqueeWidth = titleWidth - (isSmallScreen ? 85 : 160);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.header} style={{ width }}>
|
<div className={styles.header} style={{ width }}>
|
||||||
<div
|
<div
|
||||||
className={styles.backdrop}
|
className={styles.backdrop}
|
||||||
style={{
|
style={
|
||||||
backgroundImage: `url(${getFanartUrl(author.images)})`
|
fanartUrl ?
|
||||||
}}
|
{ backgroundImage: `url(${fanartUrl})` } :
|
||||||
|
null
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div className={styles.backdropOverlay} />
|
<div className={styles.backdropOverlay} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'link': string;
|
||||||
|
'linkLabel': string;
|
||||||
|
'links': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'buttonContainer': string;
|
||||||
|
'buttonContainerContent': string;
|
||||||
|
'buttons': string;
|
||||||
|
'deleteSelectedButton': string;
|
||||||
|
'inputContainer': string;
|
||||||
|
'organizeSelectedButton': string;
|
||||||
|
'selectedAuthorLabel': string;
|
||||||
|
'tagsButton': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -89,9 +89,9 @@ class BookEditorFooter extends Component {
|
|||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const monitoredOptions = [
|
const monitoredOptions = [
|
||||||
{ key: NO_CHANGE, value: 'No Change', disabled: true },
|
{ key: NO_CHANGE, value: translate('NoChange'), disabled: true },
|
||||||
{ key: 'monitored', value: 'Monitored' },
|
{ key: 'monitored', value: translate('Monitored') },
|
||||||
{ key: 'unmonitored', value: 'Unmonitored' }
|
{ key: 'unmonitored', value: translate('Unmonitored') }
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'label': string;
|
||||||
|
'savingIcon': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'deleteFilesMessage': string;
|
||||||
|
'message': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user