Compare commits

..

1 Commits

Author SHA1 Message Date
Qstick
fa304dcaca Random 2022-01-02 23:39:55 -06:00
1920 changed files with 42545 additions and 137989 deletions

View File

@@ -1,13 +0,0 @@
// This file is used to open the backend and frontend in the same workspace, which is necessary as
// the frontend has vscode settings that are distinct from the backend
{
"folders": [
{
"path": ".."
},
{
"path": "../frontend"
}
],
"settings": {}
}

View File

@@ -1,19 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet
{
"name": "Prowlarr",
"image": "mcr.microsoft.com/devcontainers/dotnet:1-6.0",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"nodeGypDependencies": true,
"version": "16",
"nvmVersion": "latest"
}
},
"forwardPorts": [9696],
"customizations": {
"vscode": {
"extensions": ["esbenp.prettier-vscode"]
}
}
}

View File

@@ -19,10 +19,10 @@ indent_size = 4
dotnet_sort_system_directives_first = true dotnet_sort_system_directives_first = true
# Avoid "this." and "Me." if not necessary # Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false:warning dotnet_style_qualification_for_field = false:refactoring
dotnet_style_qualification_for_property = false:warning dotnet_style_qualification_for_property = false:refactoring
dotnet_style_qualification_for_method = false:warning dotnet_style_qualification_for_method = false:refactoring
dotnet_style_qualification_for_event = false:warning dotnet_style_qualification_for_event = false:refactoring
# Indentation preferences # Indentation preferences
csharp_indent_block_contents = true csharp_indent_block_contents = true
@@ -32,22 +32,17 @@ csharp_indent_case_contents_when_block = true
csharp_indent_switch_labels = true csharp_indent_switch_labels = true
csharp_indent_labels = flush_left csharp_indent_labels = flush_left
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
dotnet_naming_style.instance_field_style.capitalization = camel_case 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 csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true csharp_style_var_elsewhere = true:suggestion
# Prefer "out" variables to be declared inline
csharp_style_inlined_variable_declaration = true
# Using directive is unnecessary.
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
@@ -126,6 +121,7 @@ dotnet_diagnostic.CA1003.severity = suggestion
dotnet_diagnostic.CA1008.severity = suggestion dotnet_diagnostic.CA1008.severity = suggestion
dotnet_diagnostic.CA1010.severity = suggestion dotnet_diagnostic.CA1010.severity = suggestion
dotnet_diagnostic.CA1012.severity = suggestion dotnet_diagnostic.CA1012.severity = suggestion
dotnet_diagnostic.CA1014.severity = suggestion
dotnet_diagnostic.CA1016.severity = suggestion dotnet_diagnostic.CA1016.severity = suggestion
dotnet_diagnostic.CA1017.severity = suggestion dotnet_diagnostic.CA1017.severity = suggestion
dotnet_diagnostic.CA1018.severity = suggestion dotnet_diagnostic.CA1018.severity = suggestion
@@ -171,7 +167,6 @@ dotnet_diagnostic.CA1309.severity = suggestion
dotnet_diagnostic.CA1310.severity = suggestion dotnet_diagnostic.CA1310.severity = suggestion
dotnet_diagnostic.CA1401.severity = suggestion dotnet_diagnostic.CA1401.severity = suggestion
dotnet_diagnostic.CA1416.severity = suggestion dotnet_diagnostic.CA1416.severity = suggestion
dotnet_diagnostic.CA1419.severity = suggestion
dotnet_diagnostic.CA1507.severity = suggestion dotnet_diagnostic.CA1507.severity = suggestion
dotnet_diagnostic.CA1508.severity = suggestion dotnet_diagnostic.CA1508.severity = suggestion
dotnet_diagnostic.CA1707.severity = suggestion dotnet_diagnostic.CA1707.severity = suggestion
@@ -187,6 +182,9 @@ dotnet_diagnostic.CA1720.severity = suggestion
dotnet_diagnostic.CA1721.severity = suggestion dotnet_diagnostic.CA1721.severity = suggestion
dotnet_diagnostic.CA1724.severity = suggestion dotnet_diagnostic.CA1724.severity = suggestion
dotnet_diagnostic.CA1725.severity = suggestion dotnet_diagnostic.CA1725.severity = suggestion
dotnet_diagnostic.CA1801.severity = suggestion
dotnet_diagnostic.CA1802.severity = suggestion
dotnet_diagnostic.CA1805.severity = suggestion
dotnet_diagnostic.CA1806.severity = suggestion dotnet_diagnostic.CA1806.severity = suggestion
dotnet_diagnostic.CA1810.severity = suggestion dotnet_diagnostic.CA1810.severity = suggestion
dotnet_diagnostic.CA1812.severity = suggestion dotnet_diagnostic.CA1812.severity = suggestion
@@ -198,14 +196,13 @@ dotnet_diagnostic.CA1819.severity = suggestion
dotnet_diagnostic.CA1822.severity = suggestion dotnet_diagnostic.CA1822.severity = suggestion
dotnet_diagnostic.CA1823.severity = suggestion dotnet_diagnostic.CA1823.severity = suggestion
dotnet_diagnostic.CA1824.severity = suggestion dotnet_diagnostic.CA1824.severity = suggestion
dotnet_diagnostic.CA1835.severity = suggestion
dotnet_diagnostic.CA1845.severity = suggestion
dotnet_diagnostic.CA1848.severity = suggestion
dotnet_diagnostic.CA1849.severity = suggestion
dotnet_diagnostic.CA2000.severity = suggestion dotnet_diagnostic.CA2000.severity = suggestion
dotnet_diagnostic.CA2002.severity = suggestion dotnet_diagnostic.CA2002.severity = suggestion
dotnet_diagnostic.CA2007.severity = suggestion dotnet_diagnostic.CA2007.severity = suggestion
dotnet_diagnostic.CA2008.severity = suggestion dotnet_diagnostic.CA2008.severity = suggestion
dotnet_diagnostic.CA2009.severity = suggestion
dotnet_diagnostic.CA2010.severity = suggestion
dotnet_diagnostic.CA2011.severity = suggestion
dotnet_diagnostic.CA2012.severity = suggestion dotnet_diagnostic.CA2012.severity = suggestion
dotnet_diagnostic.CA2013.severity = suggestion dotnet_diagnostic.CA2013.severity = suggestion
dotnet_diagnostic.CA2100.severity = suggestion dotnet_diagnostic.CA2100.severity = suggestion
@@ -236,7 +233,6 @@ dotnet_diagnostic.CA2243.severity = suggestion
dotnet_diagnostic.CA2244.severity = suggestion dotnet_diagnostic.CA2244.severity = suggestion
dotnet_diagnostic.CA2245.severity = suggestion dotnet_diagnostic.CA2245.severity = suggestion
dotnet_diagnostic.CA2246.severity = suggestion dotnet_diagnostic.CA2246.severity = suggestion
dotnet_diagnostic.CA2254.severity = suggestion
dotnet_diagnostic.CA3061.severity = suggestion dotnet_diagnostic.CA3061.severity = suggestion
dotnet_diagnostic.CA3075.severity = suggestion dotnet_diagnostic.CA3075.severity = suggestion
dotnet_diagnostic.CA3076.severity = suggestion dotnet_diagnostic.CA3076.severity = suggestion
@@ -263,11 +259,10 @@ dotnet_diagnostic.CA5385.severity = suggestion
dotnet_diagnostic.CA5392.severity = suggestion dotnet_diagnostic.CA5392.severity = suggestion
dotnet_diagnostic.CA5394.severity = suggestion dotnet_diagnostic.CA5394.severity = suggestion
dotnet_diagnostic.CA5397.severity = suggestion dotnet_diagnostic.CA5397.severity = suggestion
dotnet_diagnostic.CA5401.severity = suggestion
dotnet_diagnostic.SYSLIB0014.severity = none dotnet_diagnostic.SYSLIB0014.severity = none
[*.{js,jsx,ts,tsx,html,hbs,less,css}] [*.{js,html,js,hbs,less,css}]
charset = utf-8 charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true

9
.esprintrc Normal file
View File

@@ -0,0 +1,9 @@
{
"paths": [
"frontend/src/**/*.js"
],
"ignored": [
"**/node_modules/**/*"
],
"port": 5004
}

View File

@@ -1,13 +1,13 @@
name: Bug Report name: Bug Report
description: 'Report a new bug, if you are not 100% certain this is a bug please go to our Discord first' description: 'Report a new bug, if you are not 100% certain this is a bug please go to our Reddit or Discord first'
labels: ['Type: Bug', 'Status: Needs Triage'] labels: ['Type: Bug', 'Status: Needs Triage']
body: body:
- type: checkboxes - type: checkboxes
attributes: attributes:
label: Is there an existing issue for this? label: Is there an existing issue for this?
description: Please search to see if an open or closed issue already exists for the bug you encountered. If a bug exists and is closed note that it may only be fixed in an unstable branch. description: Please search to see if an issue already exists for the bug you encountered.
options: options:
- label: I have searched the existing open and closed issues - label: I have searched the existing issues
required: true required: true
- type: textarea - type: textarea
attributes: attributes:
@@ -71,10 +71,3 @@ body:
Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering! Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering!
validations: validations:
required: true required: true
- type: checkboxes
attributes:
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 and contain `trace`. Info logs are invalid for bug reports and do not contain `debug` nor `trace`
options:
- 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

View File

@@ -6,3 +6,6 @@ contact_links:
- name: Support via Discord - name: Support via Discord
url: https://prowlarr.com/discord url: https://prowlarr.com/discord
about: Chat with users and devs on support and setup related topics. about: Chat with users and devs on support and setup related topics.
- name: Support via Reddit
url: https://reddit.com/r/prowlarr
about: Discuss and search thru support topics.

View File

@@ -5,9 +5,9 @@ body:
- type: checkboxes - type: checkboxes
attributes: attributes:
label: Is there an existing issue for this? label: Is there an existing issue for this?
description: Please search to see if an open or closed issue already exists for the feature you are requesting. If a request exists and is closed note that it may only be fixed in an unstable branch. description: Please search to see if an issue already exists for the feature you are requesting.
options: options:
- label: I have searched the existing open and closed issues - label: I have searched the existing issues
required: true required: true
- type: textarea - type: textarea
attributes: attributes:

View File

@@ -1,12 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for more information:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
# https://containers.dev/guide/dependabot
version: 2
updates:
- package-ecosystem: "devcontainers"
directory: "/"
schedule:
interval: weekly

View File

@@ -4,21 +4,13 @@
comment: > comment: >
:wave: @{issue-author}, we use the issue tracker exclusively :wave: @{issue-author}, we use the issue tracker exclusively
for bug reports and feature requests. However, this issue appears for bug reports and feature requests. However, this issue appears
to be a support request. Please hop over onto our [Discord](https://prowlarr.com/discord). to be a support request. Please hop over onto our [Discord](https://prowlarr.com/discord)
or [Subreddit](https://reddit.com/r/prowlarr)
close: true close: true
close-reason: 'not planned'
'Type: Indexer Request': 'Type: Indexer Request':
comment: > comment: >
:wave: @{issue-author}, we use the issue tracker exclusively :wave: @{issue-author}, we use the issue tracker exclusively
for bug reports and feature requests. However, this issue appears for bug reports and feature requests. However, this issue appears
to be a indexer request. Please use our Indexer request [site](https://requests.prowlarr.com/) to be a indexer request. Please use our Indexer request [site](https://requests.prowlarr.com/)
close: true close: true
close-reason: 'not planned'
'Status: Logs Needed':
comment: >
:wave: @{issue-author}, in order to help you further we'll need to see logs.
You'll need to enable trace logging and replicate the problem that you encountered.
Guidance on how to enable trace logging can be found in
our [troubleshooting guide](https://wiki.servarr.com/prowlarr/troubleshooting#logging-and-log-files).

31
.github/labeler.yml vendored
View File

@@ -1,31 +0,0 @@
'Area: API':
- changed-files:
- any-glob-to-any-file:
- src/Prowlarr.Api.V1/**/*
'Area: Db-migration':
- changed-files:
- any-glob-to-any-file:
- src/NzbDrone.Core/Datastore/Migration/*
'Area: Download Clients':
- changed-files:
- any-glob-to-any-file:
- src/NzbDrone.Core/Download/Clients/**/*
'Area: Indexer':
- changed-files:
- any-glob-to-any-file:
- src/NzbDrone.Core/Indexers/**/*
'Area: Notifications':
- changed-files:
- any-glob-to-any-file:
- src/NzbDrone.Core/Notifications/**/*
'Area: UI':
- changed-files:
- any-glob-to-any-file:
- frontend/**/*
- package.json
- yarn.lock

41
.github/workflows/azuresync.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: Sync issue to Azure DevOps work item
on:
issues:
types:
[opened, edited, deleted, closed, reopened, labeled, unlabeled, assigned]
concurrency: azuresync-${{ github.event.issue.number }}
jobs:
alert:
runs-on: ubuntu-latest
steps:
- uses: danhellem/github-actions-issue-to-work-item@master
if: "${{ contains(github.event.issue.labels.*.name, 'Type: Bug') == true }}"
env:
ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}"
github_token: "${{ github.token }}"
ado_organization: "Servarr"
ado_project: "Servarr"
ado_area_path: "Servarr\\Prowlarr"
ado_wit: "Bug"
ado_new_state: "New"
ado_active_state: "Active"
ado_close_state: "Closed"
ado_bypassrules: true
log_level: 100
- uses: danhellem/github-actions-issue-to-work-item@master
if: "${{ contains(github.event.issue.labels.*.name, 'Type: Bug') == false }}"
env:
ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}"
github_token: "${{ github.token }}"
ado_organization: "Servarr"
ado_project: "Servarr"
ado_area_path: "Servarr\\Prowlarr"
ado_wit: "User Story"
ado_new_state: "New"
ado_active_state: "Active"
ado_close_state: "Closed"
ado_bypassrules: true
log_level: 100

View File

@@ -18,6 +18,6 @@ jobs:
action: action:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/label-actions@v4 - uses: dessant/label-actions@v2
with: with:
process-only: 'issues, prs' process-only: 'issues, prs'

View File

@@ -1,12 +0,0 @@
name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
triage:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5

View File

@@ -9,13 +9,13 @@ jobs:
lock: lock:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/lock-threads@v5 - uses: dessant/lock-threads@v2
with: with:
github-token: ${{ github.token }} github-token: ${{ github.token }}
issue-inactive-days: '90' issue-lock-inactive-days: '90'
exclude-issue-created-before: '' issue-exclude-created-before: ''
exclude-any-issue-labels: '' issue-exclude-labels: ''
add-issue-labels: '' issue-lock-labels: ''
issue-comment: '' issue-lock-comment: ''
issue-lock-reason: 'resolved' issue-lock-reason: 'resolved'
process-only: '' process-only: ''

1
.gitignore vendored
View File

@@ -127,7 +127,6 @@ coverage*.xml
coverage*.json coverage*.json
setup/Output/ setup/Output/
*.~is *.~is
.mono
# VS outout folders # VS outout folders
bin bin

View File

@@ -1,7 +0,0 @@
{
"recommendations": [
"esbenp.prettier-vscode",
"ms-dotnettools.csdevkit",
"ms-vscode-remote.remote-containers"
]
}

26
.vscode/launch.json vendored
View File

@@ -1,26 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
"name": "Run Prowlarr",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build dotnet",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/_output/net6.0/Prowlarr",
"args": [],
"cwd": "${workspaceFolder}",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "integratedTerminal",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}

44
.vscode/tasks.json vendored
View File

@@ -1,44 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build dotnet",
"command": "dotnet",
"type": "process",
"args": [
"msbuild",
"-restore",
"${workspaceFolder}/src/Prowlarr.sln",
"-p:GenerateFullPaths=true",
"-p:Configuration=Debug",
"-p:Platform=Posix",
"-consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/src/Prowlarr.sln",
"-property:GenerateFullPaths=true",
"-consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/src/Prowlarr.sln"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@@ -1,132 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
<development@prowlarr.com>.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

33
Logo/dottrace.svg Normal file
View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="70px" height="70px" viewBox="0 0 70 70" style="enable-background:new 0 0 70 70;" xml:space="preserve">
<g>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-1.3318" y1="43.7371" x2="67.0419" y2="26.0967">
<stop offset="0.1237" style="stop-color:#7866FF"/>
<stop offset="0.5376" style="stop-color:#FE2EB6"/>
<stop offset="0.8548" style="stop-color:#FD0486"/>
</linearGradient>
<polygon style="fill:url(#SVGID_1_);" points="67.3,16 43.7,0 0,31.1 11.1,70 58.9,60.3 "/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="45.9148" y1="38.9098" x2="67.6577" y2="9.0989">
<stop offset="0.1237" style="stop-color:#FF0080"/>
<stop offset="0.2587" style="stop-color:#FE0385"/>
<stop offset="0.4109" style="stop-color:#FA0C92"/>
<stop offset="0.5713" style="stop-color:#F41BA9"/>
<stop offset="0.7363" style="stop-color:#EB2FC8"/>
<stop offset="0.8656" style="stop-color:#E343E6"/>
</linearGradient>
<polygon style="fill:url(#SVGID_2_);" points="67.3,16 43.7,0 38,15.7 38,47.8 70,47.8 "/>
</g>
<g>
<rect x="13.4" y="13.4" style="fill:#000000;" width="43.2" height="43.2"/>
<rect x="17.4" y="48.5" style="fill:#FFFFFF;" width="16.2" height="2.7"/>
<g>
<path style="fill:#FFFFFF;" d="M17.4,19.1h6.9c5.6,0,9.5,3.8,9.5,8.9V28c0,5-3.9,8.9-9.5,8.9h-6.9V19.1z M21.4,22.7v10.7h3
c3.2,0,5.4-2.2,5.4-5.3V28c0-3.2-2.2-5.4-5.4-5.4H21.4z"/>
<polygon style="fill:#FFFFFF;" points="40.3,22.7 34.9,22.7 34.9,19.1 49.6,19.1 49.6,22.7 44.2,22.7 44.2,37 40.3,37 "/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

66
Logo/jetbrains.svg Normal file
View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="120.1px" height="130.2px" viewBox="0 0 120.1 130.2" style="enable-background:new 0 0 120.1 130.2;" xml:space="preserve"
>
<g>
<linearGradient id="XMLID_2_" gradientUnits="userSpaceOnUse" x1="31.8412" y1="120.5578" x2="110.2402" y2="73.24">
<stop offset="0" style="stop-color:#FCEE39"/>
<stop offset="1" style="stop-color:#F37B3D"/>
</linearGradient>
<path id="XMLID_3041_" style="fill:url(#XMLID_2_);" d="M118.6,71.8c0.9-0.8,1.4-1.9,1.5-3.2c0.1-2.6-1.8-4.7-4.4-4.9
c-1.2-0.1-2.4,0.4-3.3,1.1l0,0l-83.8,45.9c-1.9,0.8-3.6,2.2-4.7,4.1c-2.9,4.8-1.3,11,3.6,13.9c3.4,2,7.5,1.8,10.7-0.2l0,0l0,0
c0.2-0.2,0.5-0.3,0.7-0.5l78-54.8C117.3,72.9,118.4,72.1,118.6,71.8L118.6,71.8L118.6,71.8z"/>
<linearGradient id="XMLID_3_" gradientUnits="userSpaceOnUse" x1="48.3607" y1="6.9083" x2="119.9179" y2="69.5546">
<stop offset="0" style="stop-color:#EF5A6B"/>
<stop offset="0.57" style="stop-color:#F26F4E"/>
<stop offset="1" style="stop-color:#F37B3D"/>
</linearGradient>
<path id="XMLID_3049_" style="fill:url(#XMLID_3_);" d="M118.8,65.1L118.8,65.1L55,2.5C53.6,1,51.6,0,49.3,0
c-4.3,0-7.7,3.5-7.7,7.7v0c0,2.1,0.8,3.9,2.1,5.3l0,0l0,0c0.4,0.4,0.8,0.7,1.2,1l67.4,57.7l0,0c0.8,0.7,1.8,1.2,3,1.3
c2.6,0.1,4.7-1.8,4.9-4.4C120.2,67.3,119.7,66,118.8,65.1z"/>
<linearGradient id="XMLID_4_" gradientUnits="userSpaceOnUse" x1="52.9467" y1="63.6407" x2="10.5379" y2="37.1562">
<stop offset="0" style="stop-color:#7C59A4"/>
<stop offset="0.3852" style="stop-color:#AF4C92"/>
<stop offset="0.7654" style="stop-color:#DC4183"/>
<stop offset="0.957" style="stop-color:#ED3D7D"/>
</linearGradient>
<path id="XMLID_3042_" style="fill:url(#XMLID_4_);" d="M57.1,59.5C57,59.5,17.7,28.5,16.9,28l0,0l0,0c-0.6-0.3-1.2-0.6-1.8-0.9
c-5.8-2.2-12.2,0.8-14.4,6.6c-1.9,5.1,0.2,10.7,4.6,13.4l0,0l0,0C6,47.5,6.6,47.8,7.3,48c0.4,0.2,45.4,18.8,45.4,18.8l0,0
c1.8,0.8,3.9,0.3,5.1-1.2C59.3,63.7,59,61,57.1,59.5z"/>
<linearGradient id="XMLID_5_" gradientUnits="userSpaceOnUse" x1="52.1736" y1="3.7019" x2="10.7706" y2="37.8971">
<stop offset="0" style="stop-color:#EF5A6B"/>
<stop offset="0.364" style="stop-color:#EE4E72"/>
<stop offset="1" style="stop-color:#ED3D7D"/>
</linearGradient>
<path id="XMLID_3057_" style="fill:url(#XMLID_5_);" d="M49.3,0c-1.7,0-3.3,0.6-4.6,1.5L4.9,28.3c-0.1,0.1-0.2,0.1-0.2,0.2l-0.1,0
l0,0c-1.7,1.2-3.1,3-3.9,5.1C-1.5,39.4,1.5,45.9,7.3,48c3.6,1.4,7.5,0.7,10.4-1.4l0,0l0,0c0.7-0.5,1.3-1,1.8-1.6l34.6-31.2l0,0
c1.8-1.4,3-3.6,3-6.1v0C57.1,3.5,53.6,0,49.3,0z"/>
<g id="XMLID_3008_">
<rect id="XMLID_3033_" x="34.6" y="37.4" style="fill:#000000;" width="51" height="51"/>
<rect id="XMLID_3032_" x="39" y="78.8" style="fill:#FFFFFF;" width="19.1" height="3.2"/>
<g id="XMLID_3009_">
<path id="XMLID_3030_" style="fill:#FFFFFF;" d="M38.8,50.8l1.5-1.4c0.4,0.5,0.8,0.8,1.3,0.8c0.6,0,0.9-0.4,0.9-1.2l0-5.3l2.3,0
l0,5.3c0,1-0.3,1.8-0.8,2.3c-0.5,0.5-1.3,0.8-2.3,0.8C40.2,52.2,39.4,51.6,38.8,50.8z"/>
<path id="XMLID_3028_" style="fill:#FFFFFF;" d="M45.3,43.8l6.7,0v1.9l-4.4,0V47l4,0l0,1.8l-4,0l0,1.3l4.5,0l0,2l-6.7,0
L45.3,43.8z"/>
<path id="XMLID_3026_" style="fill:#FFFFFF;" d="M55,45.8l-2.5,0l0-2l7.3,0l0,2l-2.5,0l0,6.3l-2.3,0L55,45.8z"/>
<path id="XMLID_3022_" style="fill:#FFFFFF;" d="M39,54l4.3,0c1,0,1.8,0.3,2.3,0.7c0.3,0.3,0.5,0.8,0.5,1.4v0
c0,1-0.5,1.5-1.3,1.9c1,0.3,1.6,0.9,1.6,2v0c0,1.4-1.2,2.3-3.1,2.3l-4.3,0L39,54z M43.8,56.6c0-0.5-0.4-0.7-1-0.7l-1.5,0l0,1.5
l1.4,0C43.4,57.3,43.8,57.1,43.8,56.6L43.8,56.6z M43,59l-1.8,0l0,1.5H43c0.7,0,1.1-0.3,1.1-0.8v0C44.1,59.2,43.7,59,43,59z"/>
<path id="XMLID_3019_" style="fill:#FFFFFF;" d="M46.8,54l3.9,0c1.3,0,2.1,0.3,2.7,0.9c0.5,0.5,0.7,1.1,0.7,1.9v0
c0,1.3-0.7,2.1-1.7,2.6l2,2.9l-2.6,0l-1.7-2.5h-1l0,2.5l-2.3,0L46.8,54z M50.6,58c0.8,0,1.2-0.4,1.2-1v0c0-0.7-0.5-1-1.2-1
l-1.5,0v2H50.6z"/>
<path id="XMLID_3016_" style="fill:#FFFFFF;" d="M56.8,54l2.2,0l3.5,8.4l-2.5,0l-0.6-1.5l-3.2,0l-0.6,1.5l-2.4,0L56.8,54z
M58.8,59l-0.9-2.3L57,59L58.8,59z"/>
<path id="XMLID_3014_" style="fill:#FFFFFF;" d="M62.8,54l2.3,0l0,8.3l-2.3,0L62.8,54z"/>
<path id="XMLID_3012_" style="fill:#FFFFFF;" d="M65.7,54l2.1,0l3.4,4.4l0-4.4l2.3,0l0,8.3l-2,0L68,57.8l0,4.6l-2.3,0L65.7,54z"
/>
<path id="XMLID_3010_" style="fill:#FFFFFF;" d="M73.7,61.1l1.3-1.5c0.8,0.7,1.7,1,2.7,1c0.6,0,1-0.2,1-0.6v0
c0-0.4-0.3-0.5-1.4-0.8c-1.8-0.4-3.1-0.9-3.1-2.6v0c0-1.5,1.2-2.7,3.2-2.7c1.4,0,2.5,0.4,3.4,1.1l-1.2,1.6
c-0.8-0.5-1.6-0.8-2.3-0.8c-0.6,0-0.8,0.2-0.8,0.5v0c0,0.4,0.3,0.5,1.4,0.8c1.9,0.4,3.1,1,3.1,2.6v0c0,1.7-1.3,2.7-3.4,2.7
C76.1,62.5,74.7,62,73.7,61.1z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

50
Logo/resharper.svg Normal file
View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="70px" height="70px" viewBox="0 0 70 70" style="enable-background:new 0 0 70 70;" xml:space="preserve">
<g>
<g>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="22.9451" y1="75.7869" x2="74.7868" y2="20.6415">
<stop offset="1.612903e-002" style="stop-color:#B35BA3"/>
<stop offset="0.4044" style="stop-color:#C41E57"/>
<stop offset="0.4677" style="stop-color:#C41E57"/>
<stop offset="0.6505" style="stop-color:#EB8523"/>
<stop offset="0.9516" style="stop-color:#FEBD11"/>
</linearGradient>
<polygon style="fill:url(#SVGID_1_);" points="49.8,15.2 36,36.7 58.4,70 70,23.1 "/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="17.7187" y1="73.2922" x2="69.5556" y2="18.1519">
<stop offset="1.612903e-002" style="stop-color:#B35BA3"/>
<stop offset="0.4044" style="stop-color:#C41E57"/>
<stop offset="0.4677" style="stop-color:#C41E57"/>
<stop offset="0.7043" style="stop-color:#EB8523"/>
</linearGradient>
<polygon style="fill:url(#SVGID_2_);" points="51.1,15.7 49,0 18.8,33.6 27.6,42.3 20.8,70 58.4,70 "/>
</g>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="1.8281" y1="53.4275" x2="48.8245" y2="9.2255">
<stop offset="1.612903e-002" style="stop-color:#B35BA3"/>
<stop offset="0.6613" style="stop-color:#C41E57"/>
</linearGradient>
<polygon style="fill:url(#SVGID_3_);" points="49,0 11.6,0 0,47.1 55.6,47.1 "/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="49.8935" y1="-11.5569" x2="48.8588" y2="24.0352">
<stop offset="0.5" style="stop-color:#C41E57"/>
<stop offset="0.6668" style="stop-color:#D13F48"/>
<stop offset="0.7952" style="stop-color:#D94F39"/>
<stop offset="0.8656" style="stop-color:#DD5433"/>
</linearGradient>
<polygon style="fill:url(#SVGID_4_);" points="55.3,47.1 51.1,15.7 49,0 41.7,23 "/>
</g>
<g>
<rect x="13.4" y="13.5" transform="matrix(-1 2.577289e-003 -2.577289e-003 -1 70.0288 70.081)" style="fill:#000000;" width="43.2" height="43.2"/>
<rect x="17.6" y="48.6" transform="matrix(1 -2.577289e-003 2.577289e-003 1 -0.1287 6.634109e-002)" style="fill:#FFFFFF;" width="16.2" height="2.7"/>
<path style="fill:#FFFFFF;" d="M17.4,19.1l8.2,0c2.3,0,4,0.6,5.2,1.8c1,1,1.5,2.4,1.5,4.1l0,0.1c0,1.5-0.3,2.6-1.1,3.5
c-0.7,0.9-1.6,1.6-2.8,2l4.4,6.4l-4.6,0l-3.7-5.5l-3.3,0l0,5.5l-3.9,0L17.4,19.1z M25.3,27.8c1,0,1.7-0.2,2.2-0.7
c0.5-0.5,0.8-1.1,0.8-1.8l0-0.1c0-0.9-0.3-1.5-0.8-1.9c-0.5-0.4-1.3-0.6-2.3-0.6l-3.9,0l0,5.1L25.3,27.8z"/>
<path style="fill:#FFFFFF;" d="M36,33.2l-1.9,0l0-3.3l2.5,0l0.6-3.8l-2.3,0l0-3.3l2.8,0l0.6-3.7l3.4,0l-0.6,3.7l3.7,0l0.6-3.7
l3.4,0l-0.6,3.7l1.9,0l0,3.3l-2.5,0L47,29.9l2.3,0l0,3.3l-2.8,0L45.8,37l-3.4,0l0.7-3.8l-3.7,0L38.7,37l-3.4,0L36,33.2z
M43.7,29.9l0.6-3.8l-3.7,0L40,29.9L43.7,29.9z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

42
Logo/rider.svg Normal file
View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="70px" height="70px" viewBox="0 0 70 70" style="enable-background:new 0 0 70 70;" xml:space="preserve">
<defs>
<linearGradient id="linear-gradient" x1="70.22612" y1="27.79912" x2="-5.13024" y2="63.12242" gradientTransform="matrix(1, 0, 0, -1, 0, 71.27997)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#c90f5e"/>
<stop offset="0.22111" stop-color="#c90f5e"/>
<stop offset="0.2356" stop-color="#c90f5e"/>
<stop offset="0.35559" stop-color="#ca135c"/>
<stop offset="0.46633" stop-color="#ce1e57"/>
<stop offset="0.5735" stop-color="#d4314e"/>
<stop offset="0.67844" stop-color="#dc4b41"/>
<stop offset="0.78179" stop-color="#e66d31"/>
<stop offset="0.88253" stop-color="#f3961d"/>
<stop offset="0.94241" stop-color="#fcb20f"/>
</linearGradient>
<linearGradient id="linear-gradient-2" x1="24.65904" y1="61.99608" x2="46.04762" y2="2.93445" gradientTransform="matrix(1, 0, 0, -1, 0, 71.27997)" gradientUnits="userSpaceOnUse">
<stop offset="0.04188" stop-color="#077cfb"/>
<stop offset="0.44503" stop-color="#c90f5e"/>
<stop offset="0.95812" stop-color="#077cfb"/>
</linearGradient>
<linearGradient id="linear-gradient-3" x1="17.39552" y1="63.34592" x2="33.19389" y2="7.20092" gradientTransform="matrix(1, 0, 0, -1, 0, 71.27997)" gradientUnits="userSpaceOnUse">
<stop offset="0.27749" stop-color="#c90f5e"/>
<stop offset="0.97382" stop-color="#fcb20f"/>
</linearGradient>
</defs>
<title>rider</title>
<g>
<polygon points="70 27.237 63.391 23.75 20.926 0 3.827 17.921 21.619 41.068 60.537 44.397 70 27.237" fill="url(#linear-gradient)"/>
<polygon points="50.423 16.132 44.271 1.107 27.643 17.471 11.768 50.194 49.411 70 70 57.98 50.423 16.132" fill="url(#linear-gradient-2)"/>
<polygon points="20.926 0 0 14.095 7.779 62.172 27.848 69.889 53.78 48.823 20.926 0" fill="url(#linear-gradient-3)"/>
</g>
<g>
<rect x="13.30219" y="13.19311" width="43.61371" height="43.61371"/>
<g>
<path d="M17.22741,18.86293h8.39564a7.38416,7.38416,0,0,1,5.34268,1.85358,5.86989,5.86989,0,0,1,1.52648,4.1433h0A5.74339,5.74339,0,0,1,28.567,30.5296l4.47041,6.54206H28.34891L24.42368,31.1838h-3.162v5.88785H17.22741V18.86293h0ZM25.296,27.69471c1.96262,0,3.053-1.09034,3.053-2.61682h0c0-1.74455-1.19938-2.61682-3.162-2.61682H21.15265v5.23365H25.296Z" fill="#fff"/>
<path d="M36.09034,18.86293H43.2866c5.77882,0,9.70405,3.92523,9.70405,9.15888h0c0,5.12461-3.92523,9.15888-9.70405,9.15888H36.09034V18.86293Zm4.03427,3.59813V33.47352h3.162a5.23727,5.23727,0,0,0,5.56075-5.45171h0a5.26493,5.26493,0,0,0-5.56075-5.56075h-3.162Z" fill="#fff"/>
</g>
<rect x="17.22741" y="48.62925" width="16.35514" height="2.72586" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

36
Logo/webstorm.svg Normal file
View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="70px" height="70px" viewBox="0 0 70 70" style="enable-background:new 0 0 70 70;" xml:space="preserve">
<g>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="25.0676" y1="1.4599" x2="43.1829" y2="66.675">
<stop offset="0.2849" style="stop-color:#00CDD7"/>
<stop offset="0.9409" style="stop-color:#2086D7"/>
</linearGradient>
<polygon style="fill:url(#SVGID_1_);" points="9.4,63.3 0,7.3 17.5,0.1 28.6,6.7 38.8,1.2 60.1,9.4 48.1,70 "/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="30.7199" y1="9.7343" x2="61.365" y2="54.6713">
<stop offset="0.1398" style="stop-color:#FFF045"/>
<stop offset="0.3656" style="stop-color:#00CDD7"/>
</linearGradient>
<polygon style="fill:url(#SVGID_2_);" points="70,23.7 61,1.4 44.6,0 19.3,24.3 26.1,55.6 38.8,64.6 70,46 62.3,31.7 "/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="61.0819" y1="15.2899" x2="65.1065" y2="29.5436">
<stop offset="0.2849" style="stop-color:#00CDD7"/>
<stop offset="0.9409" style="stop-color:#2086D7"/>
</linearGradient>
<polygon style="fill:url(#SVGID_3_);" points="56,20.4 62.3,31.7 70,23.7 64.4,9.8 "/>
</g>
<g>
<g>
<rect x="13.4" y="13.4" style="fill:#000000;" width="43.2" height="43.2"/>
<rect x="17.5" y="48.5" style="fill:#FFFFFF;" width="16.2" height="2.7"/>
<path style="fill:#FFFFFF;" d="M38.7,34.3l2.3-2.8c1.6,1.3,3.3,2.2,5.3,2.2c1.6,0,2.5-0.6,2.5-1.7v-0.1c0-1-0.6-1.5-3.6-2.3
c-3.6-0.9-5.8-1.9-5.8-5.5v-0.1c0-3.3,2.6-5.4,6.2-5.4c2.6,0,4.8,0.8,6.6,2.3l-2,3c-1.6-1.1-3.1-1.8-4.6-1.8
c-1.5,0-2.3,0.7-2.3,1.6v0.1c0,1.2,0.8,1.6,3.8,2.4c3.6,1,5.6,2.3,5.6,5.4v0.1c0,3.6-2.7,5.6-6.5,5.6
C43.5,37.2,40.8,36.2,38.7,34.3"/>
</g>
<polygon style="fill:#FFFFFF;" points="35.2,19 32.5,29.4 29.5,19 26.5,19 23.4,29.4 20.7,19 16.6,19 21.7,36.9 25,36.9 28,26.5
30.9,36.9 34.3,36.9 39.4,19 "/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,8 +1,8 @@
# Prowlarr # Prowlarr
[![Build Status](https://dev.azure.com/Prowlarr/Prowlarr/_apis/build/status/Prowlarr.Prowlarr?branchName=develop)](https://dev.azure.com/Prowlarr/Prowlarr/_build/latest?definitionId=1&branchName=develop) [![Build Status](https://dev.azure.com/Prowlarr/Prowlarr/_apis/build/status/Prowlarr.Prowlarr?branchName=develop)](https://dev.azure.com/Prowlarr/Prowlarr/_build/latest?definitionId=1&branchName=develop)
[![Translation status](https://translate.servarr.com/widget/servarr/prowlarr/svg-badge.svg)](https://translate.servarr.com/engage/servarr/?utm_source=widget) [![Translated](https://translate.servarr.com/widgets/servarr/-/prowlarr/svg-badge.svg)](https://translate.servarr.com/engage/prowlarr/?utm_source=widget)
[![Docker Pulls](https://img.shields.io/docker/pulls/hotio/prowlarr.svg)](https://wiki.servarr.com/prowlarr/installation/docker) [![Docker Pulls](https://img.shields.io/docker/pulls/hotio/prowlarr.svg)](https://wiki.servarr.com/prowlarr/installation#docker)
![Github Downloads](https://img.shields.io/github/downloads/Prowlarr/Prowlarr/total.svg) ![Github Downloads](https://img.shields.io/github/downloads/Prowlarr/Prowlarr/total.svg)
[![Backers on Open Collective](https://opencollective.com/Prowlarr/backers/badge.svg)](#backers) [![Backers on Open Collective](https://opencollective.com/Prowlarr/backers/badge.svg)](#backers)
[![Sponsors on Open Collective](https://opencollective.com/Prowlarr/sponsors/badge.svg)](#sponsors) [![Sponsors on Open Collective](https://opencollective.com/Prowlarr/sponsors/badge.svg)](#sponsors)
@@ -27,8 +27,12 @@ Prowlarr is an indexer manager/proxy built on the popular \*arr .net/reactjs bas
## Support ## Support
Note: Prowlarr is currently early in life, thus bugs should be expected
[![Wiki](https://img.shields.io/badge/servarr-wiki-181717.svg?maxAge=60)](https://wiki.servarr.com/prowlarr) [![Wiki](https://img.shields.io/badge/servarr-wiki-181717.svg?maxAge=60)](https://wiki.servarr.com/prowlarr)
[![Discord](https://img.shields.io/badge/discord-chat-7289DA.svg?maxAge=60)](https://prowlarr.com/discord) [![Discord](https://img.shields.io/badge/discord-chat-7289DA.svg?maxAge=60)](https://prowlarr.com/discord)
[![Reddit](https://img.shields.io/badge/reddit-discussion-FF4500.svg?maxAge=60)](https://www.reddit.com/r/Prowlarr)
Note: GitHub Issues are for Bugs and Feature Requests Only Note: GitHub Issues are for Bugs and Feature Requests Only
@@ -68,16 +72,16 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
## JetBrains ## JetBrains
Thank you to [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.png" alt="JetBrains" width="96">](http://www.jetbrains.com/) for providing us with free licenses to their great tools. Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools.
* [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/ReSharper_icon.png" alt="ReSharper" width="32"> ReSharper](http://www.jetbrains.com/resharper/) - [<img src="/Logo/resharper.svg" alt="ReSharper" width="32"> ReSharper](http://www.jetbrains.com/resharper/)
* [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/WebStorm_icon.png" alt="WebStorm" width="32"> WebStorm](http://www.jetbrains.com/webstorm/) - [<img src="/Logo/webstorm.svg" alt="WebStorm" width="32"> WebStorm](http://www.jetbrains.com/webstorm/)
* [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/Rider_icon.png" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/) - [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
* [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/dotTrace_icon.png" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/) - [<img src="/Logo/dottrace.svg" alt="dotTrace" width="32"> dotTrace](http://www.jetbrains.com/dottrace/)
### License ### License
- [GNU GPL v3](http://www.gnu.org/licenses/gpl.html) - [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
- Copyright 2010-2024 - Copyright 2010-2022
Icon Credit - [Box vector created by freepik - www.freepik.com](https://www.freepik.com/vectors/box) Icon Credit - [Box vector created by freepik - www.freepik.com](https://www.freepik.com/vectors/box)

View File

@@ -7,30 +7,20 @@ variables:
outputFolder: './_output' outputFolder: './_output'
artifactsFolder: './_artifacts' artifactsFolder: './_artifacts'
testsFolder: './_tests' testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn majorVersion: '0.1.10'
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '1.30.2'
minorVersion: $[counter('minorVersion', 1)] minorVersion: $[counter('minorVersion', 1)]
prowlarrVersion: '$(majorVersion).$(minorVersion)' prowlarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)' buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
sentryOrg: 'servarr' sentryOrg: 'servarr'
sentryUrl: 'https://sentry.servarr.com' sentryUrl: 'https://sentry.servarr.com'
dotnetVersion: '6.0.427' dotnetVersion: '6.0.100'
nodeVersion: '20.X' yarnCacheFolder: $(Pipeline.Workspace)/.yarn
innoVersion: '6.2.2'
windowsImage: 'windows-2022'
linuxImage: 'ubuntu-20.04'
macImage: 'macOS-13'
trigger: trigger:
branches: branches:
include: include:
- develop - develop
- master - master
paths:
exclude:
- .github
- src/Prowlarr.Api.*/openapi.json
pr: pr:
branches: branches:
@@ -38,9 +28,8 @@ pr:
- develop - develop
paths: paths:
exclude: exclude:
- .github
- src/NzbDrone.Core/Localization/Core - src/NzbDrone.Core/Localization/Core
- src/Prowlarr.Api.*/openapi.json - src/Prowlarr.API.*/openapi.json
stages: stages:
- stage: Setup - stage: Setup
@@ -49,7 +38,7 @@ stages:
- job: - job:
displayName: Build Variables displayName: Build Variables
pool: pool:
vmImage: ${{ variables.linuxImage }} vmImage: 'ubuntu-18.04'
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]$PROWLARRVERSION" - bash: echo "##vso[build.updatebuildnumber]$PROWLARRVERSION"
@@ -75,15 +64,15 @@ stages:
matrix: matrix:
Linux: Linux:
osName: 'Linux' osName: 'Linux'
imageName: ${{ variables.linuxImage }} imageName: 'ubuntu-18.04'
enableAnalysis: 'true' enableAnalysis: 'true'
Mac: Mac:
osName: 'Mac' osName: 'Mac'
imageName: ${{ variables.macImage }} imageName: 'macos-10.15'
enableAnalysis: 'false' enableAnalysis: 'false'
Windows: Windows:
osName: 'Windows' osName: 'Windows'
imageName: ${{ variables.windowsImage }} imageName: 'windows-2019'
enableAnalysis: 'false' enableAnalysis: 'false'
pool: pool:
@@ -102,14 +91,15 @@ stages:
- bash: | - bash: |
BUNDLEDVERSIONS=${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}/Microsoft.NETCoreSdk.BundledVersions.props BUNDLEDVERSIONS=${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}/Microsoft.NETCoreSdk.BundledVersions.props
echo $BUNDLEDVERSIONS echo $BUNDLEDVERSIONS
grep osx-x64 $BUNDLEDVERSIONS
if grep -q freebsd-x64 $BUNDLEDVERSIONS; then if grep -q freebsd-x64 $BUNDLEDVERSIONS; then
echo "Extra platforms already enabled" echo "BSD already enabled"
else else
echo "Enabling extra platform support" echo "Enabling BSD support"
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64/' $BUNDLEDVERSIONS
fi fi
displayName: Enable Extra Platform Support displayName: Enable FreeBSD Support
- bash: ./build.sh --backend --enable-extra-platforms - bash: ./build.sh --backend --enable-bsd
displayName: Build Prowlarr Backend displayName: Build Prowlarr Backend
- bash: | - bash: |
find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \; find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
@@ -123,28 +113,24 @@ stages:
displayName: Publish Backend displayName: Publish Backend
condition: and(succeeded(), eq(variables['osName'], 'Windows')) condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/net6.0/win-x64/publish' - publish: '$(testsFolder)/net6.0/win-x64/publish'
artifact: win-x64-tests artifact: WindowsCoreTests
displayName: Publish win-x64 Test Package displayName: Publish Windows Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows')) condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/net6.0/linux-x64/publish' - publish: '$(testsFolder)/net6.0/linux-x64/publish'
artifact: linux-x64-tests artifact: LinuxCoreTests
displayName: Publish linux-x64 Test Package displayName: Publish Linux 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')) condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/net6.0/linux-musl-x64/publish' - publish: '$(testsFolder)/net6.0/linux-musl-x64/publish'
artifact: linux-musl-x64-tests artifact: LinuxMuslCoreTests
displayName: Publish linux-musl-x64 Test Package displayName: Publish Linux Musl Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows')) condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/net6.0/freebsd-x64/publish' - publish: '$(testsFolder)/net6.0/freebsd-x64/publish'
artifact: freebsd-x64-tests artifact: FreebsdCoreTests
displayName: Publish freebsd-x64 Test Package displayName: Publish FreeBSD Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows')) condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/net6.0/osx-x64/publish' - publish: '$(testsFolder)/net6.0/osx-x64/publish'
artifact: osx-x64-tests artifact: MacCoreTests
displayName: Publish osx-x64 Test Package displayName: Publish MacOS Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows')) condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- stage: Build_Frontend - stage: Build_Frontend
@@ -156,20 +142,20 @@ stages:
matrix: matrix:
Linux: Linux:
osName: 'Linux' osName: 'Linux'
imageName: ${{ variables.linuxImage }} imageName: 'ubuntu-18.04'
Mac: Mac:
osName: 'Mac' osName: 'Mac'
imageName: ${{ variables.macImage }} imageName: 'macos-10.15'
Windows: Windows:
osName: 'Windows' osName: 'Windows'
imageName: ${{ variables.windowsImage }} imageName: 'windows-2019'
pool: pool:
vmImage: $(imageName) vmImage: $(imageName)
steps: steps:
- task: UseNode@1 - task: NodeTool@0
displayName: Set Node.js version displayName: Set Node.js version
inputs: inputs:
version: $(nodeVersion) versionSpec: '12.x'
- checkout: self - checkout: self
submodules: true submodules: true
fetchDepth: 1 fetchDepth: 1
@@ -198,7 +184,7 @@ stages:
- job: Windows_Installer - job: Windows_Installer
displayName: Create Installer displayName: Create Installer
pool: pool:
vmImage: ${{ variables.windowsImage }} vmImage: 'windows-2019'
steps: steps:
- checkout: self - checkout: self
fetchDepth: 1 fetchDepth: 1
@@ -214,11 +200,16 @@ stages:
artifactName: WindowsFrontend artifactName: WindowsFrontend
targetPath: _output targetPath: _output
displayName: Fetch Frontend displayName: Fetch Frontend
- bash: ./build.sh --packages
displayName: Create Packages
- bash: | - bash: |
./build.sh --packages --installer distribution/windows/setup/inno/ISCC.exe distribution/windows/setup/prowlarr.iss //DFramework=net6.0 //DRuntime=win-x86
cp distribution/windows/setup/output/Prowlarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Prowlarr.${BUILDNAME}.windows-core-x64-installer.exe cp distribution/windows/setup/output/Prowlarr.*windows.net6.0.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Prowlarr.${BUILDNAME}.windows-core-x86-installer.exe
cp distribution/windows/setup/output/Prowlarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Prowlarr.${BUILDNAME}.windows-core-x86-installer.exe displayName: Create x86 .NET Core Windows installer
displayName: Create Installers - bash: |
distribution/windows/setup/inno/ISCC.exe distribution/windows/setup/prowlarr.iss //DFramework=net6.0 //DRuntime=win-x64
cp distribution/windows/setup/output/Prowlarr.*windows.net6.0.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Prowlarr.${BUILDNAME}.windows-core-x64-installer.exe
displayName: Create x64 .NET Core Windows installer
- publish: $(Build.ArtifactStagingDirectory) - publish: $(Build.ArtifactStagingDirectory)
artifact: 'WindowsInstaller' artifact: 'WindowsInstaller'
displayName: Publish Installer displayName: Publish Installer
@@ -231,7 +222,7 @@ stages:
- job: Other_Packages - job: Other_Packages
displayName: Create Standard Packages displayName: Create Standard Packages
pool: pool:
vmImage: ${{ variables.linuxImage }} vmImage: 'ubuntu-18.04'
steps: steps:
- checkout: self - checkout: self
fetchDepth: 1 fetchDepth: 1
@@ -247,35 +238,35 @@ stages:
artifactName: WindowsFrontend artifactName: WindowsFrontend
targetPath: _output targetPath: _output
displayName: Fetch Frontend displayName: Fetch Frontend
- bash: ./build.sh --packages --enable-extra-platforms - bash: ./build.sh --packages --enable-bsd
displayName: Create Packages displayName: Create Packages
- bash: | - bash: |
find . -name "Prowlarr" -exec chmod a+x {} \; find . -name "Prowlarr" -exec chmod a+x {} \;
find . -name "Prowlarr.Update" -exec chmod a+x {} \; find . -name "Prowlarr.Update" -exec chmod a+x {} \;
displayName: Set executable bits displayName: Set executable bits
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create win-x64 zip displayName: Create Windows Core zip
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).windows-core-x64.zip' archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).windows-core-x64.zip'
archiveType: 'zip' archiveType: 'zip'
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/win-x64/net6.0 rootFolderOrFile: $(artifactsFolder)/win-x64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create win-x86 zip displayName: Create Windows x86 Core zip
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).windows-core-x86.zip' archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).windows-core-x86.zip'
archiveType: 'zip' archiveType: 'zip'
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/win-x86/net6.0 rootFolderOrFile: $(artifactsFolder)/win-x86/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create osx-x64 app displayName: Create MacOS x64 Core app
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).osx-app-core-x64.zip' archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).osx-app-core-x64.zip'
archiveType: 'zip' archiveType: 'zip'
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/osx-x64-app/net6.0 rootFolderOrFile: $(artifactsFolder)/osx-x64-app/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create osx-x64 tar displayName: Create MacOS x64 Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).osx-core-x64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).osx-core-x64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -283,14 +274,14 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/osx-x64/net6.0 rootFolderOrFile: $(artifactsFolder)/osx-x64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create osx-arm64 app displayName: Create MacOS arm64 Core app
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).osx-app-core-arm64.zip' archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).osx-app-core-arm64.zip'
archiveType: 'zip' archiveType: 'zip'
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/osx-arm64-app/net6.0 rootFolderOrFile: $(artifactsFolder)/osx-arm64-app/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create osx-arm64 tar displayName: Create MacOS arm64 Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).osx-core-arm64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).osx-core-arm64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -298,7 +289,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/osx-arm64/net6.0 rootFolderOrFile: $(artifactsFolder)/osx-arm64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create linux-x64 tar displayName: Create Linux Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-core-x64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-core-x64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -306,7 +297,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-x64/net6.0 rootFolderOrFile: $(artifactsFolder)/linux-x64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create linux-musl-x64 tar displayName: Create Linux Musl Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-musl-core-x64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-musl-core-x64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -314,15 +305,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-musl-x64/net6.0 rootFolderOrFile: $(artifactsFolder)/linux-musl-x64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create linux-x86 tar displayName: Create ARM32 Linux Core tar
inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-core-x86.tar.gz'
archiveType: 'tar'
tarCompression: 'gz'
includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-x86/net6.0
- task: ArchiveFiles@2
displayName: Create linux-arm tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-core-arm.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-core-arm.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -330,7 +313,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-arm/net6.0 rootFolderOrFile: $(artifactsFolder)/linux-arm/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create linux-musl-arm tar displayName: Create ARM32 Linux Musl Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-musl-core-arm.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-musl-core-arm.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -338,7 +321,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-musl-arm/net6.0 rootFolderOrFile: $(artifactsFolder)/linux-musl-arm/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create linux-arm64 tar displayName: Create ARM64 Linux Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-core-arm64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-core-arm64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -346,7 +329,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-arm64/net6.0 rootFolderOrFile: $(artifactsFolder)/linux-arm64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create linux-musl-arm64 tar displayName: Create ARM64 Linux Musl Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-musl-core-arm64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).linux-musl-core-arm64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -354,7 +337,7 @@ stages:
includeRootFolder: false includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-musl-arm64/net6.0 rootFolderOrFile: $(artifactsFolder)/linux-musl-arm64/net6.0
- task: ArchiveFiles@2 - task: ArchiveFiles@2
displayName: Create freebsd-x64 tar displayName: Create FreeBSD Core Core tar
inputs: inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).freebsd-core-x64.tar.gz' archiveFile: '$(Build.ArtifactStagingDirectory)/Prowlarr.$(buildName).freebsd-core-x64.tar.gz'
archiveType: 'tar' archiveType: 'tar'
@@ -367,7 +350,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="Prowlarr@${PROWLARRVERSION}-${BUILD_SOURCEBRANCHNAME}" RELEASENAME="${PROWLARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
sentry-cli releases new --finalize -p prowlarr -p prowlarr-ui -p prowlarr-update "${RELEASENAME}" sentry-cli releases new --finalize -p prowlarr -p prowlarr-ui -p prowlarr-update "${RELEASENAME}"
sentry-cli releases -p prowlarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite sentry-cli releases -p prowlarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
sentry-cli releases set-commits --auto "${RELEASENAME}" sentry-cli releases set-commits --auto "${RELEASENAME}"
@@ -399,7 +382,7 @@ stages:
jobs: jobs:
- job: Prepare - job: Prepare
pool: pool:
vmImage: ${{ variables.linuxImage }} vmImage: 'ubuntu-18.04'
steps: steps:
- checkout: none - checkout: none
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
@@ -421,22 +404,22 @@ stages:
matrix: matrix:
MacCore: MacCore:
osName: 'Mac' osName: 'Mac'
testName: 'osx-x64' testName: 'MacCore'
poolName: 'Azure Pipelines' poolName: 'Azure Pipelines'
imageName: ${{ variables.macImage }} imageName: 'macos-10.15'
WindowsCore: WindowsCore:
osName: 'Windows' osName: 'Windows'
testName: 'win-x64' testName: 'WindowsCore'
poolName: 'Azure Pipelines' poolName: 'Azure Pipelines'
imageName: ${{ variables.windowsImage }} imageName: 'windows-2019'
LinuxCore: LinuxCore:
osName: 'Linux' osName: 'Linux'
testName: 'linux-x64' testName: 'LinuxCore'
poolName: 'Azure Pipelines' poolName: 'Azure Pipelines'
imageName: ${{ variables.linuxImage }} imageName: 'ubuntu-18.04'
FreebsdCore: FreebsdCore:
osName: 'Linux' osName: 'Linux'
testName: 'freebsd-x64' testName: 'FreebsdCore'
poolName: 'FreeBSD' poolName: 'FreeBSD'
imageName: imageName:
@@ -455,7 +438,7 @@ stages:
displayName: Download Test Artifact displayName: Download Test Artifact
inputs: inputs:
buildType: 'current' buildType: 'current'
artifactName: '$(testName)-tests' artifactName: '$(testName)Tests'
targetPath: $(testsFolder) targetPath: $(testsFolder)
- powershell: Set-Service SCardSvr -StartupType Manual - powershell: Set-Service SCardSvr -StartupType Manual
displayName: Enable Windows Test Service displayName: Enable Windows Test Service
@@ -485,15 +468,11 @@ stages:
matrix: matrix:
alpine: alpine:
testName: 'Musl Net Core' testName: 'Musl Net Core'
artifactName: linux-musl-x64-tests artifactName: LinuxMuslCoreTests
containerImage: ghcr.io/servarr/testimages:alpine containerImage: ghcr.io/servarr/testimages:alpine
linux-x86:
testName: 'linux-x86'
artifactName: linux-x86-tests
containerImage: ghcr.io/servarr/testimages:linux-x86
pool: pool:
vmImage: ${{ variables.linuxImage }} vmImage: 'ubuntu-18.04'
container: $[ variables['containerImage'] ] container: $[ variables['containerImage'] ]
@@ -501,15 +480,9 @@ stages:
steps: steps:
- task: UseDotNet@2 - task: UseDotNet@2
displayName: 'Install .NET' displayName: 'Install .net core'
inputs: inputs:
version: $(dotnetVersion) version: $(dotnetVersion)
condition: and(succeeded(), ne(variables['testName'], 'linux-x86'))
- bash: |
SDKURL=$(curl -s https://api.github.com/repos/Servarr/dotnet-linux-x86/releases | jq -rc '.[].assets[].browser_download_url' | grep sdk-${DOTNETVERSION}.*gz$)
curl -fsSL $SDKURL | tar xzf - -C /opt/dotnet
displayName: 'Install .NET'
condition: and(succeeded(), eq(variables['testName'], 'linux-x86'))
- checkout: none - checkout: none
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
displayName: Download Test Artifact displayName: Download Test Artifact
@@ -532,112 +505,6 @@ stages:
testResultsFiles: '**/TestResult.xml' testResultsFiles: '**/TestResult.xml'
testRunTitle: '$(testName) Unit Tests' testRunTitle: '$(testName) Unit Tests'
failTaskOnFailedTests: true failTaskOnFailedTests: true
- job: Unit_LinuxCore_Postgres14
displayName: Unit Native LinuxCore with Postgres14 Database
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
variables:
pattern: 'Prowlarr.*.linux-core-x64.tar.gz'
artifactName: linux-x64-tests
Prowlarr__Postgres__Host: 'localhost'
Prowlarr__Postgres__Port: '5432'
Prowlarr__Postgres__User: 'prowlarr'
Prowlarr__Postgres__Password: 'prowlarr'
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 "Prowlarr.Test.Dummy" -exec chmod a+x {} \;
displayName: Make Test Dummy Executable
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- bash: |
docker run -d --name=postgres14 \
-e POSTGRES_PASSWORD=prowlarr \
-e POSTGRES_USER=prowlarr \
-p 5432:5432/tcp \
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
postgres:14
displayName: Start postgres
- bash: |
chmod a+x ${TESTSFOLDER}/test.sh
ls -lR ${TESTSFOLDER}
${TESTSFOLDER}/test.sh Linux Unit Test
displayName: Run Tests
- task: PublishTestResults@2
displayName: Publish Test Results
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
testRunTitle: 'LinuxCore Postgres14 Unit Tests'
failTaskOnFailedTests: true
- 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: 'Prowlarr.*.linux-core-x64.tar.gz'
artifactName: linux-x64-tests
Prowlarr__Postgres__Host: 'localhost'
Prowlarr__Postgres__Port: '5432'
Prowlarr__Postgres__User: 'prowlarr'
Prowlarr__Postgres__Password: 'prowlarr'
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 "Prowlarr.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=prowlarr \
-e POSTGRES_USER=prowlarr \
-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
- stage: Integration - stage: Integration
displayName: Integration displayName: Integration
@@ -646,7 +513,7 @@ stages:
jobs: jobs:
- job: Prepare - job: Prepare
pool: pool:
vmImage: ${{ variables.linuxImage }} vmImage: 'ubuntu-18.04'
steps: steps:
- checkout: none - checkout: none
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
@@ -665,18 +532,18 @@ stages:
matrix: matrix:
MacCore: MacCore:
osName: 'Mac' osName: 'Mac'
testName: 'osx-x64' testName: 'MacCore'
imageName: ${{ variables.macImage }} imageName: 'macos-10.15'
pattern: 'Prowlarr.*.osx-core-x64.tar.gz' pattern: 'Prowlarr.*.osx-core-x64.tar.gz'
WindowsCore: WindowsCore:
osName: 'Windows' osName: 'Windows'
testName: 'win-x64' testName: 'WindowsCore'
imageName: ${{ variables.windowsImage }} imageName: 'windows-2019'
pattern: 'Prowlarr.*.windows-core-x64.zip' pattern: 'Prowlarr.*.windows-core-x64.zip'
LinuxCore: LinuxCore:
osName: 'Linux' osName: 'Linux'
testName: 'linux-x64' testName: 'LinuxCore'
imageName: ${{ variables.linuxImage }} imageName: 'ubuntu-18.04'
pattern: 'Prowlarr.*.linux-core-x64.tar.gz' pattern: 'Prowlarr.*.linux-core-x64.tar.gz'
pool: pool:
@@ -692,7 +559,7 @@ stages:
displayName: Download Test Artifact displayName: Download Test Artifact
inputs: inputs:
buildType: 'current' buildType: 'current'
artifactName: '$(testName)-tests' artifactName: '$(testName)Tests'
targetPath: $(testsFolder) targetPath: $(testsFolder)
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
displayName: Download Build Artifact displayName: Download Build Artifact
@@ -722,131 +589,6 @@ stages:
failTaskOnFailedTests: true failTaskOnFailedTests: true
displayName: Publish Test Results displayName: Publish Test Results
- job: Integration_LinuxCore_Postgres14
displayName: Integration Native LinuxCore with Postgres14 Database
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
variables:
pattern: 'Prowlarr.*.linux-core-x64.tar.gz'
Prowlarr__Postgres__Host: 'localhost'
Prowlarr__Postgres__Port: '5432'
Prowlarr__Postgres__User: 'prowlarr'
Prowlarr__Postgres__Password: 'prowlarr'
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/Prowlarr/. ./bin/
displayName: Move Package Contents
- bash: |
docker run -d --name=postgres14 \
-e POSTGRES_PASSWORD=prowlarr \
-e POSTGRES_USER=prowlarr \
-p 5432:5432/tcp \
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
postgres:14
displayName: Start postgres
- bash: |
chmod a+x ${TESTSFOLDER}/test.sh
${TESTSFOLDER}/test.sh Linux Integration Test
displayName: Run Integration Tests
- task: PublishTestResults@2
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
testRunTitle: 'Integration LinuxCore Postgres14 Database Integration Tests'
failTaskOnFailedTests: true
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: 'Prowlarr.*.linux-core-x64.tar.gz'
Prowlarr__Postgres__Host: 'localhost'
Prowlarr__Postgres__Port: '5432'
Prowlarr__Postgres__User: 'prowlarr'
Prowlarr__Postgres__Password: 'prowlarr'
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/Prowlarr/. ./bin/
displayName: Move Package Contents
- bash: |
docker run -d --name=postgres15 \
-e POSTGRES_PASSWORD=prowlarr \
-e POSTGRES_USER=prowlarr \
-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
displayName: Publish Test Results
- job: Integration_FreeBSD - job: Integration_FreeBSD
displayName: Integration Native FreeBSD displayName: Integration Native FreeBSD
dependsOn: Prepare dependsOn: Prepare
@@ -864,14 +606,14 @@ stages:
displayName: Download Test Artifact displayName: Download Test Artifact
inputs: inputs:
buildType: 'current' buildType: 'current'
artifactName: 'freebsd-x64-tests' artifactName: 'FreebsdCoreTests'
targetPath: $(testsFolder) targetPath: $(testsFolder)
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
displayName: Download Build Artifact displayName: Download Build Artifact
inputs: inputs:
buildType: 'current' buildType: 'current'
artifactName: Packages artifactName: Packages
itemPattern: '**/$(pattern)' itemPattern: '/$(pattern)'
targetPath: $(Build.ArtifactStagingDirectory) targetPath: $(Build.ArtifactStagingDirectory)
- bash: | - bash: |
mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
@@ -900,17 +642,13 @@ stages:
strategy: strategy:
matrix: matrix:
alpine: alpine:
testName: 'linux-musl-x64' testName: 'Musl Net Core'
artifactName: linux-musl-x64-tests artifactName: LinuxMuslCoreTests
containerImage: ghcr.io/servarr/testimages:alpine containerImage: ghcr.io/servarr/testimages:alpine
pattern: 'Prowlarr.*.linux-musl-core-x64.tar.gz' pattern: 'Prowlarr.*.linux-musl-core-x64.tar.gz'
linux-x86:
testName: 'linux-x86'
artifactName: linux-x86-tests
containerImage: ghcr.io/servarr/testimages:linux-x86
pattern: 'Prowlarr.*.linux-core-x86.tar.gz'
pool: pool:
vmImage: ${{ variables.linuxImage }} vmImage: 'ubuntu-18.04'
container: $[ variables['containerImage'] ] container: $[ variables['containerImage'] ]
@@ -918,15 +656,9 @@ stages:
steps: steps:
- task: UseDotNet@2 - task: UseDotNet@2
displayName: 'Install .NET' displayName: 'Install .net core'
inputs: inputs:
version: $(dotnetVersion) version: $(dotnetVersion)
condition: and(succeeded(), ne(variables['testName'], 'linux-x86'))
- bash: |
SDKURL=$(curl -s https://api.github.com/repos/Servarr/dotnet-linux-x86/releases | jq -rc '.[].assets[].browser_download_url' | grep sdk-${DOTNETVERSION}.*gz$)
curl -fsSL $SDKURL | tar xzf - -C /opt/dotnet
displayName: 'Install .NET'
condition: and(succeeded(), eq(variables['testName'], 'linux-x86'))
- checkout: none - checkout: none
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
displayName: Download Test Artifact displayName: Download Test Artifact
@@ -972,22 +704,19 @@ stages:
matrix: matrix:
Linux: Linux:
osName: 'Linux' osName: 'Linux'
artifactName: 'linux-x64' imageName: 'ubuntu-18.04'
imageName: ${{ variables.linuxImage }}
pattern: 'Prowlarr.*.linux-core-x64.tar.gz' pattern: 'Prowlarr.*.linux-core-x64.tar.gz'
failBuild: true failBuild: false
Mac: Mac:
osName: 'Mac' osName: 'Mac'
artifactName: 'osx-x64' imageName: 'macos-10.15'
imageName: ${{ variables.macImage }}
pattern: 'Prowlarr.*.osx-core-x64.tar.gz' pattern: 'Prowlarr.*.osx-core-x64.tar.gz'
failBuild: true failBuild: false
Windows: Windows:
osName: 'Windows' osName: 'Windows'
artifactName: 'win-x64' imageName: 'windows-2019'
imageName: ${{ variables.windowsImage }}
pattern: 'Prowlarr.*.windows-core-x64.zip' pattern: 'Prowlarr.*.windows-core-x64.zip'
failBuild: true failBuild: false
pool: pool:
vmImage: $(imageName) vmImage: $(imageName)
@@ -1002,7 +731,7 @@ stages:
displayName: Download Test Artifact displayName: Download Test Artifact
inputs: inputs:
buildType: 'current' buildType: 'current'
artifactName: '$(artifactName)-tests' artifactName: '$(osName)CoreTests'
targetPath: $(testsFolder) targetPath: $(testsFolder)
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
displayName: Download Build Artifact displayName: Download Build Artifact
@@ -1051,7 +780,7 @@ stages:
jobs: jobs:
- job: Prepare - job: Prepare
pool: pool:
vmImage: ${{ variables.linuxImage }} vmImage: 'ubuntu-18.04'
steps: steps:
- checkout: none - checkout: none
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
@@ -1068,17 +797,17 @@ stages:
matrix: matrix:
Linux: Linux:
osName: 'Linux' osName: 'Linux'
imageName: ${{ variables.linuxImage }} imageName: 'ubuntu-18.04'
Windows: Windows:
osName: 'Windows' osName: 'Windows'
imageName: ${{ variables.windowsImage }} imageName: 'windows-2019'
pool: pool:
vmImage: $(imageName) vmImage: $(imageName)
steps: steps:
- task: UseNode@1 - task: NodeTool@0
displayName: Set Node.js version displayName: Set Node.js version
inputs: inputs:
version: $(nodeVersion) versionSpec: '12.x'
- checkout: self - checkout: self
submodules: true submodules: true
fetchDepth: 1 fetchDepth: 1
@@ -1106,7 +835,7 @@ stages:
) )
pool: pool:
vmImage: ${{ variables.windowsImage }} vmImage: windows-2019
steps: steps:
- task: UseDotNet@2 - task: UseDotNet@2
@@ -1158,7 +887,7 @@ stages:
EnableAnalyzers: 'false' EnableAnalyzers: 'false'
pool: pool:
vmImage: ${{ variables.windowsImage }} vmImage: windows-2019
steps: steps:
- task: UseDotNet@2 - task: UseDotNet@2
@@ -1169,12 +898,12 @@ stages:
submodules: true submodules: true
- powershell: Set-Service SCardSvr -StartupType Manual - powershell: Set-Service SCardSvr -StartupType Manual
displayName: Enable Windows Test Service displayName: Enable Windows Test Service
- task: SonarCloudPrepare@3 - task: SonarCloudPrepare@1
condition: eq(variables['System.PullRequest.IsFork'], 'False') condition: eq(variables['System.PullRequest.IsFork'], 'False')
inputs: inputs:
SonarCloud: 'SonarCloud' SonarCloud: 'SonarCloud'
organization: 'prowlarr' organization: 'prowlarr'
scannerMode: 'dotnet' scannerMode: 'MSBuild'
projectKey: 'Prowlarr_Prowlarr' projectKey: 'Prowlarr_Prowlarr'
projectName: 'Prowlarr' projectName: 'Prowlarr'
projectVersion: '$(prowlarrVersion)' projectVersion: '$(prowlarrVersion)'
@@ -1187,21 +916,25 @@ stages:
./build.sh --backend -f net6.0 -r win-x64 ./build.sh --backend -f net6.0 -r win-x64
TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage
displayName: Coverage Unit Tests displayName: Coverage Unit Tests
- task: SonarCloudAnalyze@3 - 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
- task: reportgenerator@5.3.11 - task: reportgenerator@4
displayName: Generate Coverage Report displayName: Generate Coverage Report
inputs: inputs:
reports: '$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml' reports: '$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml'
targetdir: '$(Build.SourcesDirectory)/CoverageResults/combined' targetdir: '$(Build.SourcesDirectory)/CoverageResults/combined'
reporttypes: 'HtmlInline_AzurePipelines;Cobertura;Badges' reporttypes: 'HtmlInline_AzurePipelines;Cobertura;Badges'
publishCodeCoverageResults: true - task: PublishCodeCoverageResults@1
displayName: Publish Coverage Report
inputs:
codeCoverageTool: 'cobertura'
summaryFileLocation: './CoverageResults/combined/Cobertura.xml'
reportDirectory: './CoverageResults/combined/'
- stage: Report_Out - stage: Report_Out
dependsOn: dependsOn:
- Analyze - Analyze
- Installer
- Unit_Test - Unit_Test
- Integration - Integration
- Automation - Automation
@@ -1211,7 +944,7 @@ stages:
- job: - job:
displayName: Discord Notification displayName: Discord Notification
pool: pool:
vmImage: ${{ variables.linuxImage }} vmImage: 'windows-2019'
steps: steps:
- task: DownloadPipelineArtifact@2 - task: DownloadPipelineArtifact@2
continueOnError: true continueOnError: true
@@ -1227,5 +960,4 @@ stages:
SYSTEM_ACCESSTOKEN: $(System.AccessToken) SYSTEM_ACCESSTOKEN: $(System.AccessToken)
DISCORDCHANNELID: $(discordChannelId) DISCORDCHANNELID: $(discordChannelId)
DISCORDWEBHOOKKEY: $(discordWebhookKey) DISCORDWEBHOOKKEY: $(discordWebhookKey)
DISCORDTHREADID: $(discordThreadId)

View File

@@ -25,22 +25,15 @@ UpdateVersionNumber()
fi fi
} }
EnableExtraPlatformsInSDK() EnableBsdSupport()
{ {
SDK_PATH=$(dotnet --list-sdks | grep -P '6\.\d\.\d+' | head -1 | sed 's/\(6\.[0-9]*\.[0-9]*\).*\[\(.*\)\]/\2\/\1/g') #todo enable sdk with
BUNDLEDVERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props" #SDK_PATH=$(dotnet --list-sdks | grep -P '5\.\d\.\d+' | head -1 | sed 's/\(5\.[0-9]*\.[0-9]*\).*\[\(.*\)\]/\2\/\1/g')
if grep -q freebsd-x64 $BUNDLEDVERSIONS; then # BUNDLED_VERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props"
echo "Extra platforms already enabled"
else
echo "Enabling extra platform support"
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
fi
}
EnableExtraPlatforms()
{
if grep -qv freebsd-x64 src/Directory.Build.props; then if grep -qv freebsd-x64 src/Directory.Build.props; then
sed -i'' -e "s^<RuntimeIdentifiers>\(.*\)</RuntimeIdentifiers>^<RuntimeIdentifiers>\1;freebsd-x64;linux-x86</RuntimeIdentifiers>^g" src/Directory.Build.props sed -i'' -e "s^<RuntimeIdentifiers>\(.*\)</RuntimeIdentifiers>^<RuntimeIdentifiers>\1;freebsd-x64</RuntimeIdentifiers>^g" src/Directory.Build.props
sed -i'' -e "s^<ExcludedRuntimeFrameworkPairs>\(.*\)</ExcludedRuntimeFrameworkPairs>^<ExcludedRuntimeFrameworkPairs>\1;freebsd-x64:net472</ExcludedRuntimeFrameworkPairs>^g" src/Directory.Build.props
fi fi
} }
@@ -241,32 +234,6 @@ Package()
esac esac
} }
BuildInstaller()
{
local framework="$1"
local runtime="$2"
./_inno/ISCC.exe distribution/windows/setup/prowlarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
}
InstallInno()
{
ProgressStart "Installing portable Inno Setup"
rm -rf _inno
curl -s --output innosetup.exe "https://files.jrsoftware.org/is/6/innosetup-${INNOVERSION:-6.2.2}.exe"
mkdir _inno
./innosetup.exe //portable=1 //silent //currentuser //dir=.\\_inno
rm innosetup.exe
ProgressEnd "Installed portable Inno Setup"
}
RemoveInno()
{
rm -rf _inno
}
PackageTests() PackageTests()
{ {
local framework="$1" local framework="$1"
@@ -298,10 +265,8 @@ if [ $# -eq 0 ]; then
BACKEND=YES BACKEND=YES
FRONTEND=YES FRONTEND=YES
PACKAGES=YES PACKAGES=YES
INSTALLER=NO
LINT=YES LINT=YES
ENABLE_EXTRA_PLATFORMS=NO ENABLE_BSD=NO
ENABLE_EXTRA_PLATFORMS_IN_SDK=NO
fi fi
while [[ $# -gt 0 ]] while [[ $# -gt 0 ]]
@@ -313,12 +278,8 @@ case $key in
BACKEND=YES BACKEND=YES
shift # past argument shift # past argument
;; ;;
--enable-bsd|--enable-extra-platforms) --enable-bsd)
ENABLE_EXTRA_PLATFORMS=YES ENABLE_BSD=YES
shift # past argument
;;
--enable-extra-platforms-in-sdk)
ENABLE_EXTRA_PLATFORMS_IN_SDK=YES
shift # past argument shift # past argument
;; ;;
-r|--runtime) -r|--runtime)
@@ -339,10 +300,6 @@ case $key in
PACKAGES=YES PACKAGES=YES
shift # past argument shift # past argument
;; ;;
--installer)
INSTALLER=YES
shift # past argument
;;
--lint) --lint)
LINT=YES LINT=YES
shift # past argument shift # past argument
@@ -362,17 +319,12 @@ esac
done done
set -- "${POSITIONAL[@]}" # restore positional parameters set -- "${POSITIONAL[@]}" # restore positional parameters
if [ "$ENABLE_EXTRA_PLATFORMS_IN_SDK" = "YES" ];
then
EnableExtraPlatformsInSDK
fi
if [ "$BACKEND" = "YES" ]; if [ "$BACKEND" = "YES" ];
then then
UpdateVersionNumber UpdateVersionNumber
if [ "$ENABLE_EXTRA_PLATFORMS" = "YES" ]; if [ "$ENABLE_BSD" = "YES" ];
then then
EnableExtraPlatforms EnableBsdSupport
fi fi
Build Build
if [[ -z "$RID" || -z "$FRAMEWORK" ]]; if [[ -z "$RID" || -z "$FRAMEWORK" ]];
@@ -382,31 +334,31 @@ then
PackageTests "net6.0" "linux-x64" PackageTests "net6.0" "linux-x64"
PackageTests "net6.0" "linux-musl-x64" PackageTests "net6.0" "linux-musl-x64"
PackageTests "net6.0" "osx-x64" PackageTests "net6.0" "osx-x64"
if [ "$ENABLE_EXTRA_PLATFORMS" = "YES" ]; if [ "$ENABLE_BSD" = "YES" ];
then then
PackageTests "net6.0" "freebsd-x64" PackageTests "net6.0" "freebsd-x64"
PackageTests "net6.0" "linux-x86"
fi fi
else else
PackageTests "$FRAMEWORK" "$RID" PackageTests "$FRAMEWORK" "$RID"
fi fi
fi fi
if [[ "$LINT" = "YES" || "$FRONTEND" = "YES" ]]; if [ "$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
@@ -423,20 +375,11 @@ then
Package "net6.0" "linux-musl-arm" Package "net6.0" "linux-musl-arm"
Package "net6.0" "osx-x64" Package "net6.0" "osx-x64"
Package "net6.0" "osx-arm64" Package "net6.0" "osx-arm64"
if [ "$ENABLE_EXTRA_PLATFORMS" = "YES" ]; if [ "$ENABLE_BSD" = "YES" ];
then then
Package "net6.0" "freebsd-x64" Package "net6.0" "freebsd-x64"
Package "net6.0" "linux-x86"
fi fi
else else
Package "$FRAMEWORK" "$RID" Package "$FRAMEWORK" "$RID"
fi fi
fi fi
if [ "$INSTALLER" = "YES" ];
then
InstallInno
BuildInstaller "net6.0" "win-x64"
BuildInstaller "net6.0" "win-x86"
RemoveInno
fi

View File

@@ -15,7 +15,7 @@
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>prowlarr.icns</string> <string>prowlarr.icns</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.osx.prowlarr.com</string> <string>com.osx.prowlarr.video</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>

View File

@@ -0,0 +1,3 @@
#SET BUILD_NUMBER=1
#SET branch=develop
inno\ISCC.exe prowlarr.iss

View File

@@ -0,0 +1,336 @@
; *** Inno Setup version 5.5.3+ English messages ***
;
; To download user-contributed translations of this file, go to:
; http://www.jrsoftware.org/files/istrans/
;
; Note: When translating this text, do not add periods (.) to the end of
; messages that didn't have them already, because on those messages Inno
; Setup adds the periods automatically (appending a period would result in
; two periods being displayed).
[LangOptions]
; The following three entries are very important. Be sure to read and
; understand the '[LangOptions] section' topic in the help file.
LanguageName=English
LanguageID=$0409
LanguageCodePage=0
; If the language you are translating to requires special font faces or
; sizes, uncomment any of the following entries and change them accordingly.
;DialogFontName=
;DialogFontSize=8
;WelcomeFontName=Verdana
;WelcomeFontSize=12
;TitleFontName=Arial
;TitleFontSize=29
;CopyrightFontName=Arial
;CopyrightFontSize=8
[Messages]
; *** Application titles
SetupAppTitle=Setup
SetupWindowTitle=Setup - %1
UninstallAppTitle=Uninstall
UninstallAppFullTitle=%1 Uninstall
; *** Misc. common
InformationTitle=Information
ConfirmTitle=Confirm
ErrorTitle=Error
; *** SetupLdr messages
SetupLdrStartupMessage=This will install %1. Do you wish to continue?
LdrCannotCreateTemp=Unable to create a temporary file. Setup aborted
LdrCannotExecTemp=Unable to execute file in the temporary directory. Setup aborted
; *** Startup error messages
LastErrorMessage=%1.%n%nError %2: %3
SetupFileMissing=The file %1 is missing from the installation directory. Please correct the problem or obtain a new copy of the program.
SetupFileCorrupt=The setup files are corrupted. Please obtain a new copy of the program.
SetupFileCorruptOrWrongVer=The setup files are corrupted, or are incompatible with this version of Setup. Please correct the problem or obtain a new copy of the program.
InvalidParameter=An invalid parameter was passed on the command line:%n%n%1
SetupAlreadyRunning=Setup is already running.
WindowsVersionNotSupported=This program does not support the version of Windows your computer is running.
WindowsServicePackRequired=This program requires %1 Service Pack %2 or later.
NotOnThisPlatform=This program will not run on %1.
OnlyOnThisPlatform=This program must be run on %1.
OnlyOnTheseArchitectures=This program can only be installed on versions of Windows designed for the following processor architectures:%n%n%1
MissingWOW64APIs=The version of Windows you are running does not include functionality required by Setup to perform a 64-bit installation. To correct this problem, please install Service Pack %1.
WinVersionTooLowError=This program requires %1 version %2 or later.
WinVersionTooHighError=This program cannot be installed on %1 version %2 or later.
AdminPrivilegesRequired=You must be logged in as an administrator when installing this program.
PowerUserPrivilegesRequired=You must be logged in as an administrator or as a member of the Power Users group when installing this program.
SetupAppRunningError=Setup has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
UninstallAppRunningError=Uninstall has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
; *** Misc. errors
ErrorCreatingDir=Setup was unable to create the directory "%1"
ErrorTooManyFilesInDir=Unable to create a file in the directory "%1" because it contains too many files
; *** Setup common messages
ExitSetupTitle=Exit Setup
ExitSetupMessage=Setup is not complete. If you exit now, the program will not be installed.%n%nYou may run Setup again at another time to complete the installation.%n%nExit Setup?
AboutSetupMenuItem=&About Setup...
AboutSetupTitle=About Setup
AboutSetupMessage=%1 version %2%n%3%n%n%1 home page:%n%4
AboutSetupNote=
TranslatorNote=
; *** Buttons
ButtonBack=< &Back
ButtonNext=&Next >
ButtonInstall=&Install
ButtonOK=OK
ButtonCancel=Cancel
ButtonYes=&Yes
ButtonYesToAll=Yes to &All
ButtonNo=&No
ButtonNoToAll=N&o to All
ButtonFinish=&Finish
ButtonBrowse=&Browse...
ButtonWizardBrowse=B&rowse...
ButtonNewFolder=&Make New Folder
; *** "Select Language" dialog messages
SelectLanguageTitle=Select Setup Language
SelectLanguageLabel=Select the language to use during the installation:
; *** Common wizard text
ClickNext=Click Next to continue, or Cancel to exit Setup.
BeveledLabel=
BrowseDialogTitle=Browse For Folder
BrowseDialogLabel=Select a folder in the list below, then click OK.
NewFolderName=New Folder
; *** "Welcome" wizard page
WelcomeLabel1=Welcome to the [name] Setup Wizard
WelcomeLabel2=This will install [name/ver] on your computer.%n%nIt is recommended that you close all other applications before continuing.
; *** "Password" wizard page
WizardPassword=Password
PasswordLabel1=This installation is password protected.
PasswordLabel3=Please provide the password, then click Next to continue. Passwords are case-sensitive.
PasswordEditLabel=&Password:
IncorrectPassword=The password you entered is not correct. Please try again.
; *** "License Agreement" wizard page
WizardLicense=License Agreement
LicenseLabel=Please read the following important information before continuing.
LicenseLabel3=Please read the following License Agreement. You must accept the terms of this agreement before continuing with the installation.
LicenseAccepted=I &accept the agreement
LicenseNotAccepted=I &do not accept the agreement
; *** "Information" wizard pages
WizardInfoBefore=Information
InfoBeforeLabel=Please read the following important information before continuing.
InfoBeforeClickLabel=When you are ready to continue with Setup, click Next.
WizardInfoAfter=Information
InfoAfterLabel=Please read the following important information before continuing.
InfoAfterClickLabel=When you are ready to continue with Setup, click Next.
; *** "User Information" wizard page
WizardUserInfo=User Information
UserInfoDesc=Please enter your information.
UserInfoName=&User Name:
UserInfoOrg=&Organization:
UserInfoSerial=&Serial Number:
UserInfoNameRequired=You must enter a name.
; *** "Select Destination Location" wizard page
WizardSelectDir=Select Destination Location
SelectDirDesc=Where should [name] be installed?
SelectDirLabel3=Setup will install [name] into the following folder.
SelectDirBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse.
DiskSpaceMBLabel=At least [mb] MB of free disk space is required.
CannotInstallToNetworkDrive=Setup cannot install to a network drive.
CannotInstallToUNCPath=Setup cannot install to a UNC path.
InvalidPath=You must enter a full path with drive letter; for example:%n%nC:\APP%n%nor a UNC path in the form:%n%n\\server\share
InvalidDrive=The drive or UNC share you selected does not exist or is not accessible. Please select another.
DiskSpaceWarningTitle=Not Enough Disk Space
DiskSpaceWarning=Setup requires at least %1 KB of free space to install, but the selected drive only has %2 KB available.%n%nDo you want to continue anyway?
DirNameTooLong=The folder name or path is too long.
InvalidDirName=The folder name is not valid.
BadDirName32=Folder names cannot include any of the following characters:%n%n%1
DirExistsTitle=Folder Exists
DirExists=The folder:%n%n%1%n%nalready exists. Would you like to install to that folder anyway?
DirDoesntExistTitle=Folder Does Not Exist
DirDoesntExist=The folder:%n%n%1%n%ndoes not exist. Would you like the folder to be created?
; *** "Select Components" wizard page
WizardSelectComponents=Select Components
SelectComponentsDesc=Which components should be installed?
SelectComponentsLabel2=Select the components you want to install; clear the components you do not want to install. Click Next when you are ready to continue.
FullInstallation=Full installation
; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language)
CompactInstallation=Compact installation
CustomInstallation=Custom installation
NoUninstallWarningTitle=Components Exist
NoUninstallWarning=Setup has detected that the following components are already installed on your computer:%n%n%1%n%nDeselecting these components will not uninstall them.%n%nWould you like to continue anyway?
ComponentSize1=%1 KB
ComponentSize2=%1 MB
ComponentsDiskSpaceMBLabel=Current selection requires at least [mb] MB of disk space.
; *** "Select Additional Tasks" wizard page
WizardSelectTasks=Select Additional Tasks
SelectTasksDesc=Which additional tasks should be performed?
SelectTasksLabel2=Select the additional tasks you would like Setup to perform while installing [name], then click Next.
; *** "Select Start Menu Folder" wizard page
WizardSelectProgramGroup=Select Start Menu Folder
SelectStartMenuFolderDesc=Where should Setup place the program's shortcuts?
SelectStartMenuFolderLabel3=Setup will create the program's shortcuts in the following Start Menu folder.
SelectStartMenuFolderBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse.
MustEnterGroupName=You must enter a folder name.
GroupNameTooLong=The folder name or path is too long.
InvalidGroupName=The folder name is not valid.
BadGroupName=The folder name cannot include any of the following characters:%n%n%1
NoProgramGroupCheck2=&Don't create a Start Menu folder
; *** "Ready to Install" wizard page
WizardReady=Ready to Install
ReadyLabel1=Setup is now ready to begin installing [name] on your computer.
ReadyLabel2a=Click Install to continue with the installation, or click Back if you want to review or change any settings.
ReadyLabel2b=Click Install to continue with the installation.
ReadyMemoUserInfo=User information:
ReadyMemoDir=Destination location:
ReadyMemoType=Setup type:
ReadyMemoComponents=Selected components:
ReadyMemoGroup=Start Menu folder:
ReadyMemoTasks=Additional tasks:
; *** "Preparing to Install" wizard page
WizardPreparing=Preparing to Install
PreparingDesc=Setup is preparing to install [name] on your computer.
PreviousInstallNotCompleted=The installation/removal of a previous program was not completed. You will need to restart your computer to complete that installation.%n%nAfter restarting your computer, run Setup again to complete the installation of [name].
CannotContinue=Setup cannot continue. Please click Cancel to exit.
ApplicationsFound=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications.
ApplicationsFound2=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. After the installation has completed, Setup will attempt to restart the applications.
CloseApplications=&Automatically close the applications
DontCloseApplications=&Do not close the applications
ErrorCloseApplications=Setup was unable to automatically close all applications. It is recommended that you close all applications using files that need to be updated by Setup before continuing.
; *** "Installing" wizard page
WizardInstalling=Installing
InstallingLabel=Please wait while Setup installs [name] on your computer.
; *** "Setup Completed" wizard page
FinishedHeadingLabel=Completing the [name] Setup Wizard
FinishedLabelNoIcons=Setup has finished installing [name] on your computer.
FinishedLabel=Setup has finished installing [name] on your computer. The application may be launched by selecting the installed icons.
ClickFinish=Click Finish to exit Setup.
FinishedRestartLabel=To complete the installation of [name], Setup must restart your computer. Would you like to restart now?
FinishedRestartMessage=To complete the installation of [name], Setup must restart your computer.%n%nWould you like to restart now?
ShowReadmeCheck=Yes, I would like to view the README file
YesRadio=&Yes, restart the computer now
NoRadio=&No, I will restart the computer later
; used for example as 'Run MyProg.exe'
RunEntryExec=Run %1
; used for example as 'View Readme.txt'
RunEntryShellExec=View %1
; *** "Setup Needs the Next Disk" stuff
ChangeDiskTitle=Setup Needs the Next Disk
SelectDiskLabel2=Please insert Disk %1 and click OK.%n%nIf the files on this disk can be found in a folder other than the one displayed below, enter the correct path or click Browse.
PathLabel=&Path:
FileNotInDir2=The file "%1" could not be located in "%2". Please insert the correct disk or select another folder.
SelectDirectoryLabel=Please specify the location of the next disk.
; *** Installation phase messages
SetupAborted=Setup was not completed.%n%nPlease correct the problem and run Setup again.
EntryAbortRetryIgnore=Click Retry to try again, Ignore to proceed anyway, or Abort to cancel installation.
; *** Installation status messages
StatusClosingApplications=Closing applications...
StatusCreateDirs=Creating directories...
StatusExtractFiles=Extracting files...
StatusCreateIcons=Creating shortcuts...
StatusCreateIniEntries=Creating INI entries...
StatusCreateRegistryEntries=Creating registry entries...
StatusRegisterFiles=Registering files...
StatusSavingUninstall=Saving uninstall information...
StatusRunProgram=Finishing installation...
StatusRestartingApplications=Restarting applications...
StatusRollback=Rolling back changes...
; *** Misc. errors
ErrorInternal2=Internal error: %1
ErrorFunctionFailedNoCode=%1 failed
ErrorFunctionFailed=%1 failed; code %2
ErrorFunctionFailedWithMessage=%1 failed; code %2.%n%3
ErrorExecutingProgram=Unable to execute file:%n%1
; *** Registry errors
ErrorRegOpenKey=Error opening registry key:%n%1\%2
ErrorRegCreateKey=Error creating registry key:%n%1\%2
ErrorRegWriteKey=Error writing to registry key:%n%1\%2
; *** INI errors
ErrorIniEntry=Error creating INI entry in file "%1".
; *** File copying errors
FileAbortRetryIgnore=Click Retry to try again, Ignore to skip this file (not recommended), or Abort to cancel installation.
FileAbortRetryIgnore2=Click Retry to try again, Ignore to proceed anyway (not recommended), or Abort to cancel installation.
SourceIsCorrupted=The source file is corrupted
SourceDoesntExist=The source file "%1" does not exist
ExistingFileReadOnly=The existing file is marked as read-only.%n%nClick Retry to remove the read-only attribute and try again, Ignore to skip this file, or Abort to cancel installation.
ErrorReadingExistingDest=An error occurred while trying to read the existing file:
FileExists=The file already exists.%n%nWould you like Setup to overwrite it?
ExistingFileNewer=The existing file is newer than the one Setup is trying to install. It is recommended that you keep the existing file.%n%nDo you want to keep the existing file?
ErrorChangingAttr=An error occurred while trying to change the attributes of the existing file:
ErrorCreatingTemp=An error occurred while trying to create a file in the destination directory:
ErrorReadingSource=An error occurred while trying to read the source file:
ErrorCopying=An error occurred while trying to copy a file:
ErrorReplacingExistingFile=An error occurred while trying to replace the existing file:
ErrorRestartReplace=RestartReplace failed:
ErrorRenamingTemp=An error occurred while trying to rename a file in the destination directory:
ErrorRegisterServer=Unable to register the DLL/OCX: %1
ErrorRegSvr32Failed=RegSvr32 failed with exit code %1
ErrorRegisterTypeLib=Unable to register the type library: %1
; *** Post-installation errors
ErrorOpeningReadme=An error occurred while trying to open the README file.
ErrorRestartingComputer=Setup was unable to restart the computer. Please do this manually.
; *** Uninstaller messages
UninstallNotFound=File "%1" does not exist. Cannot uninstall.
UninstallOpenError=File "%1" could not be opened. Cannot uninstall
UninstallUnsupportedVer=The uninstall log file "%1" is in a format not recognized by this version of the uninstaller. Cannot uninstall
UninstallUnknownEntry=An unknown entry (%1) was encountered in the uninstall log
ConfirmUninstall=Are you sure you want to completely remove %1 and all of its components?
UninstallOnlyOnWin64=This installation can only be uninstalled on 64-bit Windows.
OnlyAdminCanUninstall=This installation can only be uninstalled by a user with administrative privileges.
UninstallStatusLabel=Please wait while %1 is removed from your computer.
UninstalledAll=%1 was successfully removed from your computer.
UninstalledMost=%1 uninstall complete.%n%nSome elements could not be removed. These can be removed manually.
UninstalledAndNeedsRestart=To complete the uninstallation of %1, your computer must be restarted.%n%nWould you like to restart now?
UninstallDataCorrupted="%1" file is corrupted. Cannot uninstall
; *** Uninstallation phase messages
ConfirmDeleteSharedFileTitle=Remove Shared File?
ConfirmDeleteSharedFile2=The system indicates that the following shared file is no longer in use by any programs. Would you like for Uninstall to remove this shared file?%n%nIf any programs are still using this file and it is removed, those programs may not function properly. If you are unsure, choose No. Leaving the file on your system will not cause any harm.
SharedFileNameLabel=File name:
SharedFileLocationLabel=Location:
WizardUninstalling=Uninstall Status
StatusUninstalling=Uninstalling %1...
; *** Shutdown block reasons
ShutdownBlockReasonInstallingApp=Installing %1.
ShutdownBlockReasonUninstallingApp=Uninstalling %1.
; The custom messages below aren't used by Setup itself, but if you make
; use of them in your scripts, you'll want to translate them.
[CustomMessages]
NameAndVersion=%1 version %2
AdditionalIcons=Additional icons:
CreateDesktopIcon=Create a &desktop icon
CreateQuickLaunchIcon=Create a &Quick Launch icon
ProgramOnTheWeb=%1 on the Web
UninstallProgram=Uninstall %1
LaunchProgram=Launch %1
AssocFileExtension=&Associate %1 with the %2 file extension
AssocingFileExtension=Associating %1 with the %2 file extension...
AutoStartProgramGroupDescription=Startup:
AutoStartProgram=Automatically start %1
AddonHostProgramNotFound=%1 could not be located in the folder you selected.%n%nDo you want to continue anyway?

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

View File

@@ -3,12 +3,13 @@
#define AppName "Prowlarr" #define AppName "Prowlarr"
#define AppPublisher "Team Prowlarr" #define AppPublisher "Team Prowlarr"
#define AppURL "https://prowlarr.com/" #define AppURL "https://prowlarr.video/"
#define ForumsURL "https://prowlarr.com/discord/" #define ForumsURL "https://forums.prowlarr.video/"
#define AppExeName "Prowlarr.exe" #define AppExeName "Prowlarr.exe"
#define BaseVersion GetEnv('MAJORVERSION') #define BaseVersion GetEnv('MAJORVERSION')
#define BuildNumber GetEnv('MINORVERSION') #define BuildNumber GetEnv('MINORVERSION')
#define BuildVersion GetEnv('PROWLARRVERSION') #define BuildVersion GetEnv('PROWLARRVERSION')
#define BranchName GetEnv('BUILD_SOURCEBRANCHNAME')
[Setup] [Setup]
; NOTE: The value of AppId uniquely identifies this application. ; NOTE: The value of AppId uniquely identifies this application.
@@ -21,15 +22,15 @@ AppPublisher={#AppPublisher}
AppPublisherURL={#AppURL} AppPublisherURL={#AppURL}
AppSupportURL={#ForumsURL} AppSupportURL={#ForumsURL}
AppUpdatesURL={#AppURL} AppUpdatesURL={#AppURL}
DefaultDirName={commonappdata}\Prowlarr DefaultDirName={commonappdata}\Prowlarr\bin
DisableDirPage=yes DisableDirPage=yes
DefaultGroupName={#AppName} DefaultGroupName={#AppName}
DisableProgramGroupPage=yes DisableProgramGroupPage=yes
OutputBaseFilename=Prowlarr.{#BuildVersion}.{#Runtime} OutputBaseFilename=Prowlarr.{#BranchName}.{#BuildVersion}.windows.{#Framework}
SolidCompression=yes SolidCompression=yes
AppCopyright=Creative Commons 3.0 License AppCopyright=Creative Commons 3.0 License
AllowUNCPath=False AllowUNCPath=False
UninstallDisplayIcon={app}\bin\Prowlarr.exe UninstallDisplayIcon={app}\Prowlarr.exe
DisableReadyPage=True DisableReadyPage=True
CompressionThreads=2 CompressionThreads=2
Compression=lzma2/normal Compression=lzma2/normal
@@ -37,7 +38,6 @@ AppContact={#ForumsURL}
VersionInfoVersion={#BaseVersion}.{#BuildNumber} VersionInfoVersion={#BaseVersion}.{#BuildNumber}
SetupLogging=yes SetupLogging=yes
OutputDir=output OutputDir=output
WizardStyle=modern
[Languages] [Languages]
Name: "english"; MessagesFile: "compiler:Default.isl" Name: "english"; MessagesFile: "compiler:Default.isl"
@@ -48,31 +48,28 @@ Name: "windowsService"; Description: "Install Windows Service (Starts when the c
Name: "startupShortcut"; Description: "Create shortcut in Startup folder (Starts when you log into Windows)"; GroupDescription: "Start automatically"; Flags: exclusive unchecked 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]
Name: "{app}"; Permissions: users-modify
[Files] [Files]
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Prowlarr\Prowlarr.exe"; DestDir: "{app}\bin"; Flags: ignoreversion Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Prowlarr\Prowlarr.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Prowlarr\*"; Excludes: "Prowlarr.Update"; DestDir: "{app}\bin"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Prowlarr\*"; Excludes: "Prowlarr.Update"; DestDir: "{app}"; 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]
Name: "{group}\{#AppName}"; Filename: "{app}\bin\{#AppExeName}"; Parameters: "/icon" Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Parameters: "/icon"
Name: "{commondesktop}\{#AppName}"; Filename: "{app}\bin\{#AppExeName}"; Parameters: "/icon"; Tasks: desktopIcon Name: "{commondesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Parameters: "/icon"; Tasks: desktopIcon
Name: "{userstartup}\{#AppName}"; Filename: "{app}\bin\Prowlarr.exe"; WorkingDir: "{app}\bin"; Tasks: startupShortcut Name: "{userstartup}\{#AppName}"; Filename: "{app}\Prowlarr.exe"; WorkingDir: "{app}"; Tasks: startupShortcut
[InstallDelete] [InstallDelete]
Name: "{app}\bin"; Type: filesandordirs Name: "{app}"; Type: filesandordirs
[Run] [Run]
Filename: "{app}\bin\Prowlarr.Console.exe"; StatusMsg: "Removing previous Windows Service"; Parameters: "/u /exitimmediately"; Flags: runhidden waituntilterminated; Filename: "{app}\Prowlarr.Console.exe"; StatusMsg: "Removing previous Windows Service"; Parameters: "/u /exitimmediately"; Flags: runhidden waituntilterminated;
Filename: "{app}\bin\Prowlarr.Console.exe"; Description: "Enable Access from Other Devices"; StatusMsg: "Enabling Remote access"; Parameters: "/registerurl /exitimmediately"; Flags: postinstall runascurrentuser runhidden waituntilterminated; Tasks: startupShortcut none; Filename: "{app}\Prowlarr.Console.exe"; Description: "Enable Access from Other Devices"; StatusMsg: "Enabling Remote access"; Parameters: "/registerurl /exitimmediately"; Flags: postinstall runascurrentuser runhidden waituntilterminated; Tasks: startupShortcut none;
Filename: "{app}\bin\Prowlarr.Console.exe"; StatusMsg: "Installing Windows Service"; Parameters: "/i /exitimmediately"; Flags: runhidden waituntilterminated; Tasks: windowsService Filename: "{app}\Prowlarr.Console.exe"; StatusMsg: "Installing Windows Service"; Parameters: "/i /exitimmediately"; Flags: runhidden waituntilterminated; Tasks: windowsService
Filename: "{app}\bin\Prowlarr.exe"; Description: "Open Prowlarr Web UI"; Flags: postinstall skipifsilent nowait; Tasks: windowsService; Filename: "{app}\Prowlarr.exe"; Description: "Open Prowlarr Web UI"; Flags: postinstall skipifsilent nowait; Tasks: windowsService;
Filename: "{app}\bin\Prowlarr.exe"; Description: "Start Prowlarr"; Flags: postinstall skipifsilent nowait; Tasks: startupShortcut none; Filename: "{app}\Prowlarr.exe"; Description: "Start Prowlarr"; Flags: postinstall skipifsilent nowait; Tasks: startupShortcut none;
[UninstallRun] [UninstallRun]
Filename: "{app}\bin\prowlarr.console.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist Filename: "{app}\prowlarr.console.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist
[Code] [Code]
function PrepareToInstall(var NeedsRestart: Boolean): String; function PrepareToInstall(var NeedsRestart: Boolean): String;

22
docs.sh Executable file → Normal file
View File

@@ -1,15 +1,11 @@
#!/bin/bash
set -e
FRAMEWORK="net6.0"
PLATFORM=$1 PLATFORM=$1
if [ "$PLATFORM" = "Windows" ]; then if [ "$PLATFORM" = "Windows" ]; then
RUNTIME="win-x64" RUNTIME="win-x64"
elif [ "$PLATFORM" = "Linux" ]; then elif [ "$PLATFORM" = "Linux" ]; then
RUNTIME="linux-x64" WHERE="linux-x64"
elif [ "$PLATFORM" = "Mac" ]; then elif [ "$PLATFORM" = "Mac" ]; then
RUNTIME="osx-x64" WHERE="osx-x64"
else else
echo "Platform must be provided as first arguement: Windows, Linux or Mac" echo "Platform must be provided as first arguement: Windows, Linux or Mac"
exit 1 exit 1
@@ -25,24 +21,18 @@ slnFile=src/Prowlarr.sln
platform=Posix platform=Posix
if [ "$PLATFORM" = "Windows" ]; then
application=Prowlarr.Console.dll
else
application=Prowlarr.dll
fi
dotnet clean $slnFile -c Debug dotnet clean $slnFile -c Debug
dotnet clean $slnFile -c Release dotnet clean $slnFile -c Release
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
dotnet new tool-manifest dotnet new tool-manifest
dotnet tool install --version 6.6.2 Swashbuckle.AspNetCore.Cli dotnet tool install --version 6.2.3 Swashbuckle.AspNetCore.Cli
dotnet tool run swagger tofile --output ./src/Prowlarr.Api.V1/openapi.json "$outputFolder/$FRAMEWORK/$RUNTIME/$application" v1 & dotnet tool run swagger tofile --output ./src/Prowlarr.Api.V1/openapi.json "$outputFolder/net6.0/$RUNTIME/prowlarr.console.dll" v1 &
sleep 45 sleep 10
kill %1 kill %1
exit 0 exit 0

25
frontend/.csscomb.json Normal file
View File

@@ -0,0 +1,25 @@
{
"remove-empty-rulesets": true,
"always-semicolon": true,
"color-case": "lower",
"block-indent": " ",
"color-shorthand": false,
"element-case": "lower",
"eof-newline": true,
"leading-zero": true,
"quotes": "double",
"sort-order-fallback": "abc",
"space-before-colon": "",
"space-after-colon": " ",
"space-before-combinator": " ",
"space-after-combinator": " ",
"space-between-declarations": "\n",
"space-before-opening-brace": " ",
"space-after-opening-brace": "\n",
"space-after-selector-delimiter": " ",
"space-before-selector-delimiter": "",
"space-before-closing-brace": "\n",
"strip-spaces": true,
"tab-size": true,
"unitless-zero": false
}

335
frontend/.esformatter Normal file
View File

@@ -0,0 +1,335 @@
{
"indent": {
"value": " ",
"FunctionExpression": 1,
"ArrayExpression": 1,
"ObjectExpression": 1
},
"lineBreak": {
"value": "\n",
"before": {
"ArrayPatternClosing": 0,
"ArrayPatternComma": 0,
"ArrayPatternOpening": 0,
"ArrowFunctionExpressionArrow": 0,
"ArrowFunctionExpressionClosingBrace": ">=1",
"ArrowFunctionExpressionOpeningBrace": 0,
"AssignmentExpression": ">=1",
"AssignmentOperator": 0,
"BlockStatement": 0,
"BreakKeyword": ">=1",
"CallExpression": -1,
"CallExpressionClosingParentheses": -1,
"CallExpressionOpeningParentheses": 0,
"CatchClosingBrace": ">=1",
"CatchKeyword": 0,
"CatchOpeningBrace": 0,
"ClassDeclaration": ">=1",
"ClassDeclarationClosingBrace": ">=1",
"ClassDeclarationOpeningBrace": 0,
"ConditionalExpression": ">=1",
"DeleteOperator": ">=1",
"DoWhileStatement": ">=1",
"DoWhileStatementClosingBrace": ">=1",
"DoWhileStatementOpeningBrace": 0,
"ElseIfStatement": 0,
"ElseIfStatementClosingBrace": ">=1",
"ElseIfStatementOpeningBrace": 0,
"ElseStatement": 0,
"ElseStatementClosingBrace": ">=1",
"ElseStatementOpeningBrace": 0,
"EmptyStatement": -1,
"EndOfFile": -1,
"FinallyClosingBrace": ">=1",
"FinallyKeyword": -1,
"FinallyOpeningBrace": 0,
"ForInStatement": ">=1",
"ForInStatementClosingBrace": ">=1",
"ForInStatementExpressionClosing": 0,
"ForInStatementExpressionOpening": 0,
"ForInStatementOpeningBrace": 0,
"ForStatement": ">=1",
"ForStatementClosingBrace": ">=1",
"ForStatementExpressionClosing": "<2",
"ForStatementExpressionOpening": 0,
"ForStatementOpeningBrace": 0,
"FunctionDeclaration": ">=1",
"FunctionDeclarationClosingBrace": ">=1",
"FunctionDeclarationOpeningBrace": 0,
"FunctionExpression": 0,
"FunctionExpressionClosingBrace": 1,
"FunctionExpressionOpeningBrace":0,
"IIFEClosingParentheses": 0,
"IfStatement": ">=1",
"IfStatementClosingBrace": ">=1",
"IfStatementOpeningBrace": 0,
"LogicalExpression": -1,
"MemberExpressionClosing": 0,
"MemberExpressionOpening": 0,
"MemberExpressionPeriod": -1,
"MethodDefinition": ">=1",
"ObjectExpressionClosingBrace": "<=1",
"ObjectPatternClosingBrace": 0,
"ObjectPatternComma": 0,
"ObjectPatternOpeningBrace": 0,
"ParameterDefault": 0,
"Property": "<=2",
"PropertyValue": 0,
"ReturnStatement": -1,
"SwitchClosingBrace": ">=1",
"SwitchOpeningBrace": 0,
"ThisExpression": -1,
"ThrowStatement": ">=1",
"TryClosingBrace": ">=1",
"TryKeyword": -1,
"TryOpeningBrace": 0,
"VariableDeclaration": ">=1",
"VariableDeclarationSemiColon": 0,
"VariableDeclarationWithoutInit": ">=1",
"VariableName": ">=1",
"VariableValue": 0,
"WhileStatement": ">=1",
"WhileStatementClosingBrace": ">=1",
"WhileStatementOpeningBrace": 0
},
"after": {
"ArrayPatternClosing": 0,
"ArrayPatternComma": 0,
"ArrayPatternOpening": 0,
"ArrowFunctionExpressionArrow": 0,
"ArrowFunctionExpressionClosingBrace": -1,
"ArrowFunctionExpressionOpeningBrace": ">=1",
"AssignmentExpression": ">=1",
"AssignmentOperator": 0,
"BlockStatement": 0,
"BreakKeyword": -1,
"CallExpression": -1,
"CallExpressionClosingParentheses": -1,
"CallExpressionOpeningParentheses": -1,
"CatchClosingBrace": ">=0",
"CatchKeyword": 0,
"CatchOpeningBrace": ">=1",
"ClassDeclaration": ">=1",
"ClassDeclarationClosingBrace": ">=1",
"ClassDeclarationOpeningBrace": ">=1",
"ConditionalExpression": ">=1",
"DeleteOperator": ">=1",
"DoWhileStatement": ">=1",
"DoWhileStatementClosingBrace": 0,
"DoWhileStatementOpeningBrace": ">=1",
"ElseIfStatement": ">=1",
"ElseIfStatementClosingBrace": ">=1",
"ElseIfStatementOpeningBrace": ">=1",
"ElseStatement": ">=1",
"ElseStatementClosingBrace": ">=1",
"ElseStatementOpeningBrace": ">=1",
"EmptyStatement": -1,
"FinallyClosingBrace": ">=1",
"FinallyKeyword": -1,
"FinallyOpeningBrace": ">=1",
"ForInStatement": ">=1",
"ForInStatementClosingBrace": ">=1",
"ForInStatementExpressionClosing": -1,
"ForInStatementExpressionOpening": "<2",
"ForInStatementOpeningBrace": ">=1",
"ForStatement": ">=1",
"ForStatementClosingBrace": ">=1",
"ForStatementExpressionClosing": -1,
"ForStatementExpressionOpening": "<2",
"ForStatementOpeningBrace": ">=1",
"FunctionDeclaration": ">=1",
"FunctionDeclarationClosingBrace": ">=1",
"FunctionDeclarationOpeningBrace": ">=1",
"FunctionExpression": 0,
"FunctionExpressionClosingBrace": -1,
"FunctionExpressionOpeningBrace": 1,
"IIFEOpeningParentheses": 0,
"IfStatement": ">=1",
"IfStatementClosingBrace": ">=1",
"IfStatementOpeningBrace": ">=1",
"LogicalExpression": -1,
"MemberExpressionClosing": 0,
"MemberExpressionOpening": 0,
"MemberExpressionPeriod": 0,
"MethodDefinition": ">=1",
"ObjectExpressionOpeningBrace": "<=1",
"ObjectPatternClosingBrace": 0,
"ObjectPatternComma": 0,
"ObjectPatternOpeningBrace": 0,
"ParameterDefault": 0,
"Property": -1,
"PropertyName": 0,
"ReturnStatement": -1,
"SwitchCaseColon": ">=1",
"SwitchClosingBrace": ">=1",
"SwitchOpeningBrace": ">=1",
"ThisExpression": 0,
"ThrowStatement": ">=1",
"TryClosingBrace": 0,
"TryKeyword": -1,
"TryOpeningBrace": ">=1",
"VariableDeclaration": ">=1",
"VariableDeclarationSemiColon": ">=1",
"VariableValue": -1,
"WhileStatement": ">=1",
"WhileStatementClosingBrace": ">=1",
"WhileStatementOpeningBrace": ">=1"
}
},
"whiteSpace": {
"value": " ",
"removeTrailing": 1,
"before": {
"ArgumentComma": 0,
"ArgumentList": 0,
"ArgumentListArrayExpression": 0,
"ArgumentListFunctionExpression": 1,
"ArgumentListObjectExpression": 0,
"ArrayExpressionClosing": 0,
"ArrayExpressionComma": 0,
"ArrayExpressionOpening": 1,
"AssignmentOperator": 1,
"BinaryExpression": 0,
"BinaryExpressionOperator": 1,
"BlockComment": 1,
"CallExpression": 1,
"CatchClosingBrace": 1,
"CatchKeyword": 1,
"CatchOpeningBrace": 1,
"CatchParameterList": 0,
"CommaOperator": 0,
"ConditionalExpressionAlternate": 1,
"ConditionalExpressionConsequent": 1,
"DoWhileStatementClosingBrace": 1,
"DoWhileStatementConditional": 1,
"DoWhileStatementOpeningBrace": 1,
"ElseIfStatementClosingBrace": 1,
"ElseIfStatementOpeningBrace": 1,
"ElseStatementClosingBrace": 1,
"ElseStatementOpeningBrace": 1,
"EmptyStatement": 0,
"ExpressionClosingParentheses": 0,
"FinallyClosingBrace": 1,
"FinallyKeyword": -1,
"FinallyOpeningBrace": 1,
"ForInStatement": 1,
"ForInStatementClosingBrace": 1,
"ForInStatementExpressionClosing": 0,
"ForInStatementExpressionOpening": 1,
"ForInStatementOpeningBrace": 1,
"ForStatement": 1,
"ForStatementClosingBrace": 1,
"ForStatementExpressionClosing": 0,
"ForStatementExpressionOpening": 1,
"ForStatementOpeningBrace": 1,
"ForStatementSemicolon": 0,
"FunctionDeclarationClosingBrace": 1,
"FunctionDeclarationOpeningBrace": 1,
"FunctionExpressionClosingBrace": 1,
"FunctionExpressionOpeningBrace": 1,
"IfStatementClosingBrace": 1,
"IfStatementConditionalClosing": 0,
"IfStatementConditionalOpening": 1,
"IfStatementOpeningBrace": 1,
"LineComment": 1,
"LogicalExpressionOperator": 1,
"MemberExpressionClosing": 0,
"ObjectExpressionClosingBrace": 1,
"ParameterComma": 0,
"ParameterList": 0,
"Property": 1,
"PropertyName": 1,
"PropertyValue": 1,
"SwitchDiscriminantClosing": 0,
"SwitchDiscriminantOpening": 1,
"ThrowKeyword": 1,
"TryClosingBrace": 1,
"TryKeyword": -1,
"TryOpeningBrace": 1,
"UnaryExpressionOperator": 0,
"VariableName": 1,
"VariableValue": 1,
"WhileStatementClosingBrace": 1,
"WhileStatementConditionalClosing": 0,
"WhileStatementConditionalOpening": 1,
"WhileStatementOpeningBrace": 1
},
"after": {
"ArgumentComma": 1,
"ArgumentList": 0,
"ArgumentListArrayExpression": 1,
"ArgumentListFunctionExpression": 1,
"ArgumentListObjectExpression": 0,
"ArrayExpressionClosing": 0,
"ArrayExpressionComma": 1,
"ArrayExpressionOpening": 0,
"AssignmentOperator": 1,
"BinaryExpression": 0,
"BinaryExpressionOperator": 1,
"BlockComment": 1,
"CallExpression": 0,
"CatchClosingBrace": 1,
"CatchKeyword": 1,
"CatchOpeningBrace": 1,
"CatchParameterList": 0,
"CommaOperator": 1,
"ConditionalExpressionConsequent": 1,
"ConditionalExpressionTest": 1,
"DoWhileStatementBody": 1,
"DoWhileStatementClosingBrace": 1,
"DoWhileStatementOpeningBrace": 1,
"ElseIfStatementClosingBrace": 1,
"ElseIfStatementOpeningBrace": 1,
"ElseStatementClosingBrace": 1,
"ElseStatementOpeningBrace": 1,
"EmptyStatement": 0,
"ExpressionOpeningParentheses": 0,
"FinallyClosingBrace": 1,
"FinallyKeyword": -1,
"FinallyOpeningBrace": 1,
"ForInStatement": 1,
"ForInStatementClosingBrace": 1,
"ForInStatementExpressionClosing": 1,
"ForInStatementExpressionOpening": 0,
"ForInStatementOpeningBrace": 1,
"ForStatement": 1,
"ForStatementClosingBrace": 1,
"ForStatementExpressionClosing": 1,
"ForStatementExpressionOpening": 0,
"ForStatementOpeningBrace": 1,
"ForStatementSemicolon": 1,
"FunctionDeclarationClosingBrace": 0,
"FunctionDeclarationOpeningBrace": 0,
"FunctionExpressionClosingBrace": 0,
"FunctionExpressionOpeningBrace": 0,
"FunctionName": 0,
"FunctionReservedWord": 0,
"IfStatementClosingBrace": 1,
"IfStatementConditionalClosing": 0,
"IfStatementConditionalOpening": 0,
"IfStatementOpeningBrace": 1,
"LogicalExpressionOperator": 1,
"MemberExpressionOpening": 0,
"ObjectExpressionClosingBrace": 0,
"ObjectExpressionOpeningBrace": 1,
"ParameterComma": 1,
"ParameterList": 0,
"PropertyName": 0,
"PropertyValue": 0,
"SwitchDiscriminantClosing": 1,
"SwitchDiscriminantOpening": 0,
"ThrowKeyword": 1,
"TryClosingBrace": 1,
"TryKeyword": -1,
"TryOpeningBrace": 1,
"UnaryExpressionOperator": 0,
"VariableName": 1,
"WhileStatementClosingBrace": 1,
"WhileStatementConditionalClosing": 1,
"WhileStatementConditionalOpening": 0,
"WhileStatementOpeningBrace": 1
}
}
}

View File

@@ -1,2 +1 @@
**/JsLibraries/** **/JsLibraries/**
**/*.css.d.ts

View File

@@ -1,19 +1,14 @@
// eslint-disable @typescript-eslint/no-var-requires
const fs = require('fs'); const fs = require('fs');
const path = require('path');
const typescriptEslintRecommended = require('@typescript-eslint/eslint-plugin').configs.recommended;
const frontendFolder = __dirname;
const dirs = fs const dirs = fs
.readdirSync(path.join(frontendFolder, 'src'), { withFileTypes: true }) .readdirSync('frontend/src', { withFileTypes: true })
.filter((dirent) => dirent.isDirectory()) .filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name) .map((dirent) => dirent.name)
.join('|'); .join('|');
module.exports = { const frontendFolder = __dirname;
root: true,
module.exports = {
parser: '@babel/eslint-parser', parser: '@babel/eslint-parser',
env: { env: {
@@ -26,8 +21,7 @@ module.exports = {
globals: { globals: {
expect: false, expect: false,
chai: false, chai: false,
sinon: false, sinon: false
JSX: true
}, },
parserOptions: { parserOptions: {
@@ -45,11 +39,8 @@ module.exports = {
plugins: [ plugins: [
'filenames', 'filenames',
'react', 'react',
'react-hooks',
'simple-import-sort', 'simple-import-sort',
'import', 'import'
'@typescript-eslint',
'prettier'
], ],
settings: { settings: {
@@ -232,7 +223,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', { allowArrowFunctions: true }], 'func-style': ['error', 'declaration'],
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 }],
@@ -317,15 +308,11 @@ module.exports = {
'react/react-in-jsx-scope': 2, 'react/react-in-jsx-scope': 2,
'react/self-closing-comp': 2, 'react/self-closing-comp': 2,
'react/sort-comp': 2, 'react/sort-comp': 2,
'react/jsx-wrap-multilines': 2, 'react/jsx-wrap-multilines': 2
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error'
}, },
overrides: [ overrides: [
{ {
files: [ files: ['*.js'],
'*.js'
],
rules: { rules: {
'simple-import-sort/imports': [ 'simple-import-sort/imports': [
'error', 'error',
@@ -340,91 +327,6 @@ module.exports = {
} }
] ]
} }
},
{
files: [
'*.ts',
'*.tsx'
],
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json'
},
extends: [
'prettier'
],
rules: Object.assign(typescriptEslintRecommended.rules, {
'@typescript-eslint/no-unused-vars': [
'error',
{
args: 'after-used',
argsIgnorePattern: '^_',
ignoreRestSiblings: true
}
],
'@typescript-eslint/explicit-function-return-type': 'off',
'no-shadow': 'off',
'prettier/prettier': 'error',
'simple-import-sort/imports': [
'error',
{
groups: [
// Packages
// Absolute Paths
// Relative Paths
// Css
['^@?\\w', `^(${dirs})(/.*|$)`, '^\\.', '^\\..*css$']
]
}
],
// React Hooks
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error',
// React
'react/function-component-definition': 'error',
'react/hook-use-state': 'error',
'react/jsx-boolean-value': ['error', 'always'],
'react/jsx-curly-brace-presence': [
'error',
{ props: 'never', children: 'never' }
],
'react/jsx-fragments': 'error',
'react/jsx-handler-names': [
'error',
{
eventHandlerPrefix: 'on',
eventHandlerPropPrefix: 'on'
}
],
'react/jsx-no-bind': ['error', { ignoreRefs: true }],
'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }],
'react/jsx-pascal-case': ['error', { allowAllCaps: true }],
'react/jsx-sort-props': [
'error',
{
callbacksLast: true,
noSortAlphabetically: true,
reservedFirst: true
}
],
'react/prop-types': 'off',
'react/self-closing-comp': 'error'
})
},
{
files: [
'*.css.d.ts'
],
rules: {
'filenames/match-exported': 'off',
'init-declarations': 'off',
'prettier/prettier': 'off'
}
} }
] ]
}; };

12
frontend/.jsbeautifyrc Normal file
View File

@@ -0,0 +1,12 @@
{
"js": {
"indent_size": 2,
"indent_char": " ",
"indent_level": 2,
"indent_with_tabs": false,
"preserve_newlines": true,
"brace_style": "collapse",
"max_preserve_newlines": 2,
"jslint_happy": true
}
}

View File

@@ -1,10 +0,0 @@
# Ignore everything recursively
*
# But not the .ts files
!*.ts*
*css.d.ts
# Check subdirectories too
!*/

View File

@@ -1,6 +0,0 @@
{
"arrowParens": "always",
"endOfLine": "auto",
"singleQuote": true,
"trailingComma": "es5"
}

View File

@@ -1,12 +1,12 @@
{ {
"plugins": [ "plugins": [
"stylelint-order" "stylelint-order"
], ],
"ignoreFiles": [ "ignoreFiles": [
"frontend/src/Styles/scaffolding.css", "frontend/src/Styles/scaffolding.css",
"**/*.js" "**/*.js"
], ],
"rules": { "rules": {
"at-rule-empty-line-before": [ "at-rule-empty-line-before": [
"always", "always",
{ {
@@ -15,6 +15,9 @@
] ]
} }
], ],
"at-rule-name-case": "lower",
"at-rule-name-newline-after": "always-multi-line",
"at-rule-name-space-after": "always",
"at-rule-no-unknown": [ "at-rule-no-unknown": [
true, true,
{ {
@@ -25,36 +28,83 @@
} }
], ],
"at-rule-no-vendor-prefix": true, "at-rule-no-vendor-prefix": true,
"at-rule-semicolon-newline-after": "always",
"at-rule-semicolon-space-before": "never",
"block-closing-brace-empty-line-before": "never",
"block-closing-brace-newline-after": "always",
"block-closing-brace-newline-before": "always",
"block-closing-brace-space-after": "always-single-line",
"block-closing-brace-space-before": "always-single-line",
"block-no-empty": true, "block-no-empty": true,
"block-opening-brace-newline-after": "always",
"block-opening-brace-newline-before": "never-single-line",
"block-opening-brace-space-after": "always-single-line",
"block-opening-brace-space-before": "always",
"color-hex-case": "lower",
"color-hex-length": "short", "color-hex-length": "short",
"color-named": "never", "color-named": "never",
"color-no-invalid-hex": true, "color-no-invalid-hex": true,
"comment-whitespace-inside": "always", "comment-whitespace-inside": "always",
"declaration-bang-space-after": "never",
"declaration-bang-space-before": "always",
"declaration-block-no-duplicate-properties": [ "declaration-block-no-duplicate-properties": [
true, true,
{ {
"ignoreProperties": [ "ignoreProperties": [
"composes" "composes"
] ]
} }
], ],
"declaration-block-no-redundant-longhand-properties": true, "declaration-block-no-redundant-longhand-properties": true,
"declaration-block-no-shorthand-property-overrides": true, "declaration-block-no-shorthand-property-overrides": true,
"declaration-block-semicolon-newline-after": "always",
"declaration-block-semicolon-newline-before": "never-multi-line",
"declaration-block-semicolon-space-before": "never",
"declaration-block-single-line-max-declarations": 1, "declaration-block-single-line-max-declarations": 1,
"declaration-block-trailing-semicolon": "always",
"declaration-colon-space-after": "always",
"declaration-colon-space-before": "never",
"font-family-name-quotes": "always-unless-keyword", "font-family-name-quotes": "always-unless-keyword",
"function-calc-no-unspaced-operator": true, "function-calc-no-unspaced-operator": true,
"function-comma-newline-after": "never-multi-line",
"function-comma-newline-before": "never-multi-line",
"function-comma-space-after": "always",
"function-comma-space-before": "never",
"function-linear-gradient-no-nonstandard-direction": true, "function-linear-gradient-no-nonstandard-direction": true,
"function-name-case": "lower", "function-name-case": "lower",
"function-parentheses-newline-inside": "never-multi-line",
"function-parentheses-space-inside": "never",
"function-url-quotes": "always", "function-url-quotes": "always",
"function-url-scheme-disallowed-list": [ "function-url-scheme-disallowed-list": [
"data" "data"
], ],
"function-whitespace-after": "always",
"indentation": 2,
"keyframe-declaration-no-important": true, "keyframe-declaration-no-important": true,
"length-zero-no-unit": true, "length-zero-no-unit": true,
"max-empty-lines": 1,
"max-line-length": [
100,
{
"ignore": [
"non-comments"
]
}
],
"max-nesting-depth": 2, "max-nesting-depth": 2,
"media-feature-colon-space-after": "always",
"media-feature-colon-space-before": "never",
"media-feature-name-case": "lower",
"media-feature-name-no-vendor-prefix": true, "media-feature-name-no-vendor-prefix": true,
"media-feature-range-operator-space-after": "always",
"media-feature-range-operator-space-before": "always",
"no-empty-source": true, "no-empty-source": true,
"no-eol-whitespace": true,
"no-extra-semicolons": true,
"no-invalid-double-slash-comments": true, "no-invalid-double-slash-comments": true,
"no-missing-end-of-source-newline": true,
"number-leading-zero": "always",
"number-no-trailing-zeros": true,
"order/order": [ "order/order": [
"custom-properties", "custom-properties",
"dollar-variables", "dollar-variables",
@@ -82,7 +132,6 @@
"right", "right",
"bottom", "bottom",
"left", "left",
"inset",
"z-index", "z-index",
"display", "display",
"visibility", "visibility",
@@ -294,33 +343,54 @@
] ]
} }
], ],
"property-case": "lower",
"property-no-vendor-prefix": true, "property-no-vendor-prefix": true,
"rule-empty-line-before": [ "rule-empty-line-before": [
"always", "always",
{ {
"except": [ "except": [
"first-nested" "first-nested"
], ],
"ignore": [ "ignore": [
"after-comment" "after-comment"
] ]
} }
], ],
"selector-attribute-brackets-space-inside": "never",
"selector-attribute-operator-space-after": "never",
"selector-attribute-operator-space-before": "never",
"selector-attribute-quotes": "never", "selector-attribute-quotes": "never",
"selector-class-pattern": "^[A-Za-z0-9]+$", "selector-class-pattern": "^[A-Za-z0-9]+$",
"selector-combinator-space-after": "always",
"selector-combinator-space-before": "always",
"selector-descendant-combinator-no-non-space": true,
"selector-list-comma-newline-after": "always",
"selector-list-comma-newline-before": "never-multi-line",
"selector-list-comma-space-before": "never",
"selector-max-attribute": 0, "selector-max-attribute": 0,
"selector-max-class": 3, "selector-max-class": 3,
"selector-max-compound-selectors": 3, "selector-max-compound-selectors": 3,
"selector-max-empty-lines": 0,
"selector-max-id": 0, "selector-max-id": 0,
"selector-max-universal": 0, "selector-max-universal": 0,
"selector-pseudo-class-case": "lower",
"selector-pseudo-class-parentheses-space-inside": "never",
"selector-pseudo-element-case": "lower",
"selector-pseudo-element-colon-notation": "double", "selector-pseudo-element-colon-notation": "double",
"selector-pseudo-element-no-unknown": true, "selector-pseudo-element-no-unknown": true,
"selector-type-case": "lower", "selector-type-case": "lower",
"selector-type-no-unknown": true, "selector-type-no-unknown": true,
"shorthand-property-no-redundant-values": true, "shorthand-property-no-redundant-values": true,
"string-no-newline": true, "string-no-newline": true,
"string-quotes": "single",
"time-min-milliseconds": 100, "time-min-milliseconds": 100,
"unit-case": "lower",
"unit-no-unknown": true, "unit-no-unknown": true,
"value-list-comma-newline-after": "never-multi-line",
"value-list-comma-newline-before": "never-multi-line",
"value-list-comma-space-after": "always",
"value-list-comma-space-before": "never",
"value-list-max-empty-lines": 0,
"value-no-vendor-prefix": true "value-no-vendor-prefix": true
} }
} }

View File

@@ -1,7 +0,0 @@
{
"recommendations": [
"stylelint.vscode-stylelint",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}

View File

@@ -1,23 +0,0 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.insertFinalNewline": true,
"files.exclude": {
"**/node_modules": true,
"**/*.d.css": true
},
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll": true
},
"typescript.preferences.quoteStyle": "single",
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
}

View File

@@ -2,25 +2,22 @@ const loose = true;
module.exports = { module.exports = {
plugins: [ plugins: [
'@babel/plugin-transform-logical-assignment-operators',
// Stage 1 // Stage 1
'@babel/plugin-proposal-export-default-from', '@babel/plugin-proposal-export-default-from',
['@babel/plugin-transform-optional-chaining', { loose }], ['@babel/plugin-proposal-optional-chaining', { loose }],
['@babel/plugin-transform-nullish-coalescing-operator', { loose }], ['@babel/plugin-proposal-nullish-coalescing-operator', { loose }],
// Stage 2 // Stage 2
'@babel/plugin-transform-export-namespace-from', '@babel/plugin-proposal-export-namespace-from',
// Stage 3 // Stage 3
['@babel/plugin-transform-class-properties', { loose }], ['@babel/plugin-proposal-class-properties', { loose }],
'@babel/plugin-syntax-dynamic-import' '@babel/plugin-syntax-dynamic-import'
], ],
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'
@@ -28,8 +25,7 @@ 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'

View File

@@ -1,4 +1,3 @@
/* 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');
@@ -6,7 +5,6 @@ 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';
@@ -25,7 +23,6 @@ module.exports = (env) => {
const config = { const config = {
mode: isProduction ? 'production' : 'development', mode: isProduction ? 'production' : 'development',
devtool: isProduction ? 'source-map' : 'eval-source-map', devtool: isProduction ? 'source-map' : 'eval-source-map',
target: 'web',
stats: { stats: {
children: false children: false
@@ -36,22 +33,17 @@ module.exports = (env) => {
}, },
entry: { entry: {
index: 'index.ts' index: 'index.js'
}, },
resolve: { resolve: {
extensions: [
'.ts',
'.tsx',
'.js'
],
modules: [ modules: [
srcFolder, srcFolder,
path.join(srcFolder, 'Shims'), path.join(srcFolder, 'Shims'),
'node_modules' 'node_modules'
], ],
alias: { alias: {
jquery: 'jquery/dist/jquery.min' jquery: 'jquery/src/jquery'
}, },
fallback: { fallback: {
buffer: false, buffer: false,
@@ -66,23 +58,23 @@ module.exports = (env) => {
output: { output: {
path: distFolder, path: distFolder,
publicPath: '/', publicPath: '/',
filename: isProduction ? '[name]-[contenthash].js' : '[name].js', filename: '[name].js',
sourceMapFilename: '[file].map' sourceMapFilename: '[file].map'
}, },
optimization: { optimization: {
moduleIds: 'deterministic', moduleIds: 'deterministic',
chunkIds: isProduction ? 'deterministic' : 'named' chunkIds: '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,
@@ -90,15 +82,13 @@ module.exports = (env) => {
}), }),
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
filename: 'Content/styles.css', filename: 'Content/styles.css'
chunkFilename: isProduction ? 'Content/[id]-[chunkhash].css' : 'Content/[id].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({
@@ -139,8 +129,6 @@ module.exports = (env) => {
} }
}), }),
new ForkTsCheckerWebpackPlugin(),
new LiveReloadPlugin() new LiveReloadPlugin()
], ],
@@ -154,8 +142,8 @@ module.exports = (env) => {
module: { module: {
rules: [ rules: [
{ {
test: [/\.jsx?$/, /\.tsx?$/], test: /\.js?$/,
exclude: /[\\/]node_modules[\\/](?!(@sentry|chart\.js|filesize)[\\/])/, exclude: /(node_modules|JsLibraries)/,
use: [ use: [
{ {
loader: 'babel-loader', loader: 'babel-loader',
@@ -170,7 +158,7 @@ module.exports = (env) => {
loose: true, loose: true,
debug: false, debug: false,
useBuiltIns: 'entry', useBuiltIns: 'entry',
corejs: '3.39' corejs: 3
} }
] ]
] ]
@@ -185,13 +173,12 @@ 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: {
importLoaders: 1, importLoaders: 1,
modules: { modules: {
localIdentName: isProduction ? '[name]/[local]/[hash:base64:5]' : '[name]/[local]' localIdentName: '[name]/[local]/[hash:base64:5]'
} }
} }
}, },
@@ -254,19 +241,18 @@ module.exports = (env) => {
config.resolve.alias['react-dom$'] = 'react-dom/profiling'; config.resolve.alias['react-dom$'] = 'react-dom/profiling';
config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling'; config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling';
config.optimization = { config.optimization.minimizer = [
minimize: true, new TerserPlugin({
minimizer: [ cache: true,
new TerserPlugin({ parallel: true,
terserOptions: { sourceMap: true, // Must be set to true if using source-maps in production
sourceMap: true, // Must be set to true if using source-maps in production terserOptions: {
mangle: false, mangle: false,
keep_classnames: true, keep_classnames: true,
keep_fnames: true keep_fnames: true
} }
}) })
] ];
};
} }
return config; return config;

View File

@@ -1,4 +1,3 @@
// eslint-disable-next-line filenames/match-exported
const loaderUtils = require('loader-utils'); const loaderUtils = require('loader-utils');
module.exports = function cssVariablesLoader(source) { module.exports = function cssVariablesLoader(source) {

View File

@@ -1,6 +1,7 @@
const reload = require('require-nocache')(module); const reload = require('require-nocache')(module);
const cssVarsFiles = [ const cssVarsFiles = [
'./src/Styles/Variables/colors',
'./src/Styles/Variables/dimensions', './src/Styles/Variables/dimensions',
'./src/Styles/Variables/fonts', './src/Styles/Variables/fonts',
'./src/Styles/Variables/animations', './src/Styles/Variables/animations',
@@ -16,7 +17,6 @@ const mixinsFiles = [
module.exports = { module.exports = {
plugins: [ plugins: [
'autoprefixer',
['postcss-mixins', { ['postcss-mixins', {
mixinsFiles mixinsFiles
}], }],
@@ -29,4 +29,4 @@ module.exports = {
'postcss-color-function', 'postcss-color-function',
'postcss-nested' 'postcss-nested'
] ]
}; };

4
frontend/src/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,4 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.insertFinalNewline": true
}

View File

@@ -1,25 +1,18 @@
import { ConnectedRouter, ConnectedRouterProps } from 'connected-react-router'; import { ConnectedRouter } from 'connected-react-router';
import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import DocumentTitle from 'react-document-title'; import DocumentTitle from 'react-document-title';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { Store } from 'redux';
import PageConnector from 'Components/Page/PageConnector'; import PageConnector from 'Components/Page/PageConnector';
import ApplyTheme from './ApplyTheme';
import AppRoutes from './AppRoutes'; import AppRoutes from './AppRoutes';
interface AppProps { function App({ store, history }) {
store: Store;
history: ConnectedRouterProps['history'];
}
function App({ store, history }: AppProps) {
return ( return (
<DocumentTitle title={window.Prowlarr.instanceName}> <DocumentTitle title="Prowlarr">
<Provider store={store}> <Provider store={store}>
<ConnectedRouter history={history}> <ConnectedRouter history={history}>
<ApplyTheme />
<PageConnector> <PageConnector>
<AppRoutes /> <AppRoutes app={App} />
</PageConnector> </PageConnector>
</ConnectedRouter> </ConnectedRouter>
</Provider> </Provider>
@@ -27,4 +20,9 @@ function App({ store, history }: AppProps) {
); );
} }
App.propTypes = {
store: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
};
export default App; export default App;

View File

@@ -0,0 +1,184 @@
import PropTypes from 'prop-types';
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import NotFound from 'Components/NotFound';
import Switch from 'Components/Router/Switch';
import HistoryConnector from 'History/HistoryConnector';
import IndexerIndexConnector from 'Indexer/Index/IndexerIndexConnector';
import StatsConnector from 'Indexer/Stats/StatsConnector';
import SearchIndexConnector from 'Search/SearchIndexConnector';
import ApplicationSettingsConnector from 'Settings/Applications/ApplicationSettingsConnector';
import DevelopmentSettingsConnector from 'Settings/Development/DevelopmentSettingsConnector';
import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector';
import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector';
import IndexerSettings from 'Settings/Indexers/IndexerSettings';
import NotificationSettings from 'Settings/Notifications/NotificationSettings';
import Settings from 'Settings/Settings';
import TagSettings from 'Settings/Tags/TagSettings';
import UISettingsConnector from 'Settings/UI/UISettingsConnector';
import BackupsConnector from 'System/Backup/BackupsConnector';
import LogsTableConnector from 'System/Events/LogsTableConnector';
import Logs from 'System/Logs/Logs';
import Status from 'System/Status/Status';
import Tasks from 'System/Tasks/Tasks';
import UpdatesConnector from 'System/Updates/UpdatesConnector';
import getPathWithUrlBase from 'Utilities/getPathWithUrlBase';
function AppRoutes(props) {
const {
app
} = props;
return (
<Switch>
{/*
Indexers
*/}
<Route
exact={true}
path="/"
component={IndexerIndexConnector}
/>
{
window.Prowlarr.urlBase &&
<Route
exact={true}
path="/"
addUrlBase={false}
render={() => {
return (
<Redirect
to={getPathWithUrlBase('/')}
component={app}
/>
);
}}
/>
}
<Route
path="/indexers/stats"
component={StatsConnector}
/>
{/*
Search
*/}
<Route
path="/search"
component={SearchIndexConnector}
/>
{/*
Activity
*/}
<Route
path="/history"
component={HistoryConnector}
/>
{/*
Settings
*/}
<Route
exact={true}
path="/settings"
component={Settings}
/>
<Route
path="/settings/indexers"
component={IndexerSettings}
/>
<Route
path="/settings/applications"
component={ApplicationSettingsConnector}
/>
<Route
path="/settings/downloadclients"
component={DownloadClientSettingsConnector}
/>
<Route
path="/settings/connect"
component={NotificationSettings}
/>
<Route
path="/settings/tags"
component={TagSettings}
/>
<Route
path="/settings/general"
component={GeneralSettingsConnector}
/>
<Route
path="/settings/ui"
component={UISettingsConnector}
/>
<Route
path="/settings/development"
component={DevelopmentSettingsConnector}
/>
{/*
System
*/}
<Route
path="/system/status"
component={Status}
/>
<Route
path="/system/tasks"
component={Tasks}
/>
<Route
path="/system/backup"
component={BackupsConnector}
/>
<Route
path="/system/updates"
component={UpdatesConnector}
/>
<Route
path="/system/events"
component={LogsTableConnector}
/>
<Route
path="/system/logs/files"
component={Logs}
/>
{/*
Not Found
*/}
<Route
path="*"
component={NotFound}
/>
</Switch>
);
}
AppRoutes.propTypes = {
app: PropTypes.func.isRequired
};
export default AppRoutes;

View File

@@ -1,117 +0,0 @@
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import NotFound from 'Components/NotFound';
import Switch from 'Components/Router/Switch';
import HistoryConnector from 'History/HistoryConnector';
import IndexerIndex from 'Indexer/Index/IndexerIndex';
import IndexerStats from 'Indexer/Stats/IndexerStats';
import SearchIndexConnector from 'Search/SearchIndexConnector';
import ApplicationSettings from 'Settings/Applications/ApplicationSettings';
import DevelopmentSettingsConnector from 'Settings/Development/DevelopmentSettingsConnector';
import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector';
import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector';
import IndexerSettings from 'Settings/Indexers/IndexerSettings';
import NotificationSettings from 'Settings/Notifications/NotificationSettings';
import Settings from 'Settings/Settings';
import TagSettings from 'Settings/Tags/TagSettings';
import UISettingsConnector from 'Settings/UI/UISettingsConnector';
import BackupsConnector from 'System/Backup/BackupsConnector';
import LogsTableConnector from 'System/Events/LogsTableConnector';
import Logs from 'System/Logs/Logs';
import Status from 'System/Status/Status';
import Tasks from 'System/Tasks/Tasks';
import Updates from 'System/Updates/Updates';
import getPathWithUrlBase from 'Utilities/getPathWithUrlBase';
function RedirectWithUrlBase() {
return <Redirect to={getPathWithUrlBase('/')} />;
}
function AppRoutes() {
return (
<Switch>
{/*
Indexers
*/}
<Route exact={true} path="/" component={IndexerIndex} />
{window.Prowlarr.urlBase && (
<Route
exact={true}
path="/"
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
addUrlBase={false}
render={RedirectWithUrlBase}
/>
)}
<Route path="/indexers/stats" component={IndexerStats} />
{/*
Search
*/}
<Route path="/search" component={SearchIndexConnector} />
{/*
Activity
*/}
<Route path="/history" component={HistoryConnector} />
{/*
Settings
*/}
<Route exact={true} path="/settings" component={Settings} />
<Route path="/settings/indexers" component={IndexerSettings} />
<Route path="/settings/applications" component={ApplicationSettings} />
<Route
path="/settings/downloadclients"
component={DownloadClientSettingsConnector}
/>
<Route path="/settings/connect" component={NotificationSettings} />
<Route path="/settings/tags" component={TagSettings} />
<Route path="/settings/general" component={GeneralSettingsConnector} />
<Route path="/settings/ui" component={UISettingsConnector} />
<Route
path="/settings/development"
component={DevelopmentSettingsConnector}
/>
{/*
System
*/}
<Route path="/system/status" component={Status} />
<Route path="/system/tasks" component={Tasks} />
<Route path="/system/backup" component={BackupsConnector} />
<Route path="/system/updates" component={Updates} />
<Route path="/system/events" component={LogsTableConnector} />
<Route path="/system/logs/files" component={Logs} />
{/*
Not Found
*/}
<Route path="*" component={NotFound} />
</Switch>
);
}
export default AppRoutes;

View File

@@ -0,0 +1,30 @@
import PropTypes from 'prop-types';
import React from 'react';
import Modal from 'Components/Modal/Modal';
import AppUpdatedModalContentConnector from './AppUpdatedModalContentConnector';
function AppUpdatedModal(props) {
const {
isOpen,
onModalClose
} = props;
return (
<Modal
isOpen={isOpen}
closeOnBackgroundClick={false}
onModalClose={onModalClose}
>
<AppUpdatedModalContentConnector
onModalClose={onModalClose}
/>
</Modal>
);
}
AppUpdatedModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default AppUpdatedModal;

View File

@@ -1,28 +0,0 @@
import React, { useCallback } from 'react';
import Modal from 'Components/Modal/Modal';
import AppUpdatedModalContent from './AppUpdatedModalContent';
interface AppUpdatedModalProps {
isOpen: boolean;
onModalClose: (...args: unknown[]) => unknown;
}
function AppUpdatedModal(props: AppUpdatedModalProps) {
const { isOpen, onModalClose } = props;
const handleModalClose = useCallback(() => {
location.reload();
}, []);
return (
<Modal
isOpen={isOpen}
closeOnBackgroundClick={false}
onModalClose={onModalClose}
>
<AppUpdatedModalContent onModalClose={handleModalClose} />
</Modal>
);
}
export default AppUpdatedModal;

View File

@@ -0,0 +1,12 @@
import { connect } from 'react-redux';
import AppUpdatedModal from './AppUpdatedModal';
function createMapDispatchToProps(dispatch, props) {
return {
onModalClose() {
location.reload();
}
};
}
export default connect(null, createMapDispatchToProps)(AppUpdatedModal);

View File

@@ -1,7 +1,6 @@
.version { .version {
margin: 0 3px; margin: 0 3px;
font-weight: bold; font-weight: bold;
font-family: var(--defaultFontFamily);
} }
.maintenance { .maintenance {

View File

@@ -1,9 +0,0 @@
// 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;

View File

@@ -0,0 +1,140 @@
import PropTypes from 'prop-types';
import React from 'react';
import Button from 'Components/Link/Button';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { kinds } from 'Helpers/Props';
import UpdateChanges from 'System/Updates/UpdateChanges';
import translate from 'Utilities/String/translate';
import styles from './AppUpdatedModalContent.css';
function mergeUpdates(items, version, prevVersion) {
let installedIndex = items.findIndex((u) => u.version === version);
let installedPreviouslyIndex = items.findIndex((u) => u.version === prevVersion);
if (installedIndex === -1) {
installedIndex = 0;
}
if (installedPreviouslyIndex === -1) {
installedPreviouslyIndex = items.length;
} else if (installedPreviouslyIndex === installedIndex && items.length) {
installedPreviouslyIndex += 1;
}
const appliedUpdates = items.slice(installedIndex, installedPreviouslyIndex);
if (!appliedUpdates.length) {
return null;
}
const appliedChanges = { new: [], fixed: [] };
appliedUpdates.forEach((u) => {
if (u.changes) {
appliedChanges.new.push(... u.changes.new);
appliedChanges.fixed.push(... u.changes.fixed);
}
});
const mergedUpdate = Object.assign({}, appliedUpdates[0], { changes: appliedChanges });
if (!appliedChanges.new.length && !appliedChanges.fixed.length) {
mergedUpdate.changes = null;
}
return mergedUpdate;
}
function AppUpdatedModalContent(props) {
const {
version,
prevVersion,
isPopulated,
error,
items,
onSeeChangesPress,
onModalClose
} = props;
const update = mergeUpdates(items, version, prevVersion);
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
Prowlarr Updated
</ModalHeader>
<ModalBody>
<div>
Version <span className={styles.version}>{version}</span> of Prowlarr has been installed, in order to get the latest changes you'll need to reload Prowlarr.
</div>
{
isPopulated && !error && !!update &&
<div>
{
!update.changes &&
<div className={styles.maintenance}>
{translate('MaintenanceRelease')}
</div>
}
{
!!update.changes &&
<div>
<div className={styles.changes}>
What's new?
</div>
<UpdateChanges
title={translate('New')}
changes={update.changes.new}
/>
<UpdateChanges
title={translate('Fixed')}
changes={update.changes.fixed}
/>
</div>
}
</div>
}
{
!isPopulated && !error &&
<LoadingIndicator />
}
</ModalBody>
<ModalFooter>
<Button
onPress={onSeeChangesPress}
>
Recent Changes
</Button>
<Button
kind={kinds.PRIMARY}
onPress={onModalClose}
>
Reload
</Button>
</ModalFooter>
</ModalContent>
);
}
AppUpdatedModalContent.propTypes = {
version: PropTypes.string.isRequired,
prevVersion: PropTypes.string,
isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
onSeeChangesPress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default AppUpdatedModalContent;

View File

@@ -1,145 +0,0 @@
import React, { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import Button from 'Components/Link/Button';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import usePrevious from 'Helpers/Hooks/usePrevious';
import { kinds } from 'Helpers/Props';
import { fetchUpdates } from 'Store/Actions/systemActions';
import UpdateChanges from 'System/Updates/UpdateChanges';
import Update from 'typings/Update';
import translate from 'Utilities/String/translate';
import AppState from './State/AppState';
import styles from './AppUpdatedModalContent.css';
function mergeUpdates(items: Update[], version: string, prevVersion?: string) {
let installedIndex = items.findIndex((u) => u.version === version);
let installedPreviouslyIndex = items.findIndex(
(u) => u.version === prevVersion
);
if (installedIndex === -1) {
installedIndex = 0;
}
if (installedPreviouslyIndex === -1) {
installedPreviouslyIndex = items.length;
} else if (installedPreviouslyIndex === installedIndex && items.length) {
installedPreviouslyIndex += 1;
}
const appliedUpdates = items.slice(installedIndex, installedPreviouslyIndex);
if (!appliedUpdates.length) {
return null;
}
const appliedChanges: Update['changes'] = { new: [], fixed: [] };
appliedUpdates.forEach((u: Update) => {
if (u.changes) {
appliedChanges.new.push(...u.changes.new);
appliedChanges.fixed.push(...u.changes.fixed);
}
});
const mergedUpdate: Update = Object.assign({}, appliedUpdates[0], {
changes: appliedChanges,
});
if (!appliedChanges.new.length && !appliedChanges.fixed.length) {
mergedUpdate.changes = null;
}
return mergedUpdate;
}
interface AppUpdatedModalContentProps {
onModalClose: () => void;
}
function AppUpdatedModalContent(props: AppUpdatedModalContentProps) {
const dispatch = useDispatch();
const { version, prevVersion } = useSelector((state: AppState) => state.app);
const { isPopulated, error, items } = useSelector(
(state: AppState) => state.system.updates
);
const previousVersion = usePrevious(version);
const { onModalClose } = props;
const update = mergeUpdates(items, version, prevVersion);
const handleSeeChangesPress = useCallback(() => {
window.location.href = `${window.Prowlarr.urlBase}/system/updates`;
}, []);
useEffect(() => {
dispatch(fetchUpdates());
}, [dispatch]);
useEffect(() => {
if (version !== previousVersion) {
dispatch(fetchUpdates());
}
}, [version, previousVersion, dispatch]);
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>{translate('AppUpdated')}</ModalHeader>
<ModalBody>
<div>
<InlineMarkdown
data={translate('AppUpdatedVersion', { version })}
blockClassName={styles.version}
/>
</div>
{isPopulated && !error && !!update ? (
<div>
{update.changes ? (
<div className={styles.maintenance}>
{translate('MaintenanceRelease')}
</div>
) : null}
{update.changes ? (
<div>
<div className={styles.changes}>{translate('WhatsNew')}</div>
<UpdateChanges
title={translate('New')}
changes={update.changes.new}
/>
<UpdateChanges
title={translate('Fixed')}
changes={update.changes.fixed}
/>
</div>
) : null}
</div>
) : null}
{!isPopulated && !error ? <LoadingIndicator /> : null}
</ModalBody>
<ModalFooter>
<Button onPress={handleSeeChangesPress}>
{translate('RecentChanges')}
</Button>
<Button kind={kinds.PRIMARY} onPress={onModalClose}>
{translate('Reload')}
</Button>
</ModalFooter>
</ModalContent>
);
}
export default AppUpdatedModalContent;

View File

@@ -0,0 +1,78 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchUpdates } from 'Store/Actions/systemActions';
import AppUpdatedModalContent from './AppUpdatedModalContent';
function createMapStateToProps() {
return createSelector(
(state) => state.app.version,
(state) => state.app.prevVersion,
(state) => state.system.updates,
(version, prevVersion, updates) => {
const {
isPopulated,
error,
items
} = updates;
return {
version,
prevVersion,
isPopulated,
error,
items
};
}
);
}
function createMapDispatchToProps(dispatch, props) {
return {
dispatchFetchUpdates() {
dispatch(fetchUpdates());
},
onSeeChangesPress() {
window.location = `${window.Prowlarr.urlBase}/system/updates`;
}
};
}
class AppUpdatedModalContentConnector extends Component {
//
// Lifecycle
componentDidMount() {
this.props.dispatchFetchUpdates();
}
componentDidUpdate(prevProps) {
if (prevProps.version !== this.props.version) {
this.props.dispatchFetchUpdates();
}
}
//
// Render
render() {
const {
dispatchFetchUpdates,
...otherProps
} = this.props;
return (
<AppUpdatedModalContent {...otherProps} />
);
}
}
AppUpdatedModalContentConnector.propTypes = {
version: PropTypes.string.isRequired,
dispatchFetchUpdates: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, createMapDispatchToProps)(AppUpdatedModalContentConnector);

View File

@@ -1,33 +0,0 @@
import { useCallback, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import themes from 'Styles/Themes';
import AppState from './State/AppState';
function createThemeSelector() {
return createSelector(
(state: AppState) => state.settings.ui.item.theme || window.Prowlarr.theme,
(theme) => {
return theme;
}
);
}
function ApplyTheme() {
const theme = useSelector(createThemeSelector());
const updateCSSVariables = useCallback(() => {
Object.entries(themes[theme]).forEach(([key, value]) => {
document.documentElement.style.setProperty(`--${key}`, value);
});
}, [theme]);
// On Component Mount and Component Update
useEffect(() => {
updateCSSVariables();
}, [updateCSSVariables, theme]);
return null;
}
export default ApplyTheme;

View File

@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'automatic': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -1,4 +1,5 @@
import React, { useCallback } from 'react'; import PropTypes from 'prop-types';
import React from 'react';
import Button from 'Components/Link/Button'; import Button from 'Components/Link/Button';
import Modal from 'Components/Modal/Modal'; import Modal from 'Components/Modal/Modal';
import ModalBody from 'Components/Modal/ModalBody'; import ModalBody from 'Components/Modal/ModalBody';
@@ -9,31 +10,36 @@ import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import styles from './ConnectionLostModal.css'; import styles from './ConnectionLostModal.css';
interface ConnectionLostModalProps { function ConnectionLostModal(props) {
isOpen: boolean; const {
} isOpen,
onModalClose
function ConnectionLostModal(props: ConnectionLostModalProps) { } = props;
const { isOpen } = props;
const handleModalClose = useCallback(() => {
location.reload();
}, []);
return ( return (
<Modal isOpen={isOpen} onModalClose={handleModalClose}> <Modal
<ModalContent onModalClose={handleModalClose}> isOpen={isOpen}
<ModalHeader>{translate('ConnectionLost')}</ModalHeader> onModalClose={onModalClose}
>
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{translate('ConnectionLost')}
</ModalHeader>
<ModalBody> <ModalBody>
<div>{translate('ConnectionLostToBackend')}</div> <div>
{translate('ConnectionLostMessage')}
</div>
<div className={styles.automatic}> <div className={styles.automatic}>
{translate('ConnectionLostReconnect')} {translate('ConnectionLostAutomaticMessage')}
</div> </div>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button kind={kinds.PRIMARY} onPress={handleModalClose}> <Button
kind={kinds.PRIMARY}
onPress={onModalClose}
>
{translate('Reload')} {translate('Reload')}
</Button> </Button>
</ModalFooter> </ModalFooter>
@@ -42,4 +48,9 @@ function ConnectionLostModal(props: ConnectionLostModalProps) {
); );
} }
ConnectionLostModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default ConnectionLostModal; export default ConnectionLostModal;

View File

@@ -0,0 +1,12 @@
import { connect } from 'react-redux';
import ConnectionLostModal from './ConnectionLostModal';
function createMapDispatchToProps(dispatch, props) {
return {
onModalClose() {
location.reload();
}
};
}
export default connect(undefined, createMapDispatchToProps)(ConnectionLostModal);

View File

@@ -1,5 +0,0 @@
interface ModelBase {
id: number;
}
export default ModelBase;

View File

@@ -1,83 +0,0 @@
import { cloneDeep } from 'lodash';
import React, { useCallback, useEffect } from 'react';
import useSelectState, { SelectState } from 'Helpers/Hooks/useSelectState';
import ModelBase from './ModelBase';
export type SelectContextAction =
| { type: 'reset' }
| { type: 'selectAll' }
| { type: 'unselectAll' }
| {
type: 'toggleSelected';
id: number;
isSelected: boolean;
shiftKey: boolean;
}
| {
type: 'removeItem';
id: number;
}
| {
type: 'updateItems';
items: ModelBase[];
};
export type SelectDispatch = (action: SelectContextAction) => void;
interface SelectProviderOptions<T extends ModelBase> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
children: any;
items: Array<T>;
}
const SelectContext = React.createContext<
[SelectState, SelectDispatch] | undefined
>(cloneDeep(undefined));
export function SelectProvider<T extends ModelBase>(
props: SelectProviderOptions<T>
) {
const { items } = props;
const [state, dispatch] = useSelectState();
const dispatchWrapper = useCallback(
(action: SelectContextAction) => {
switch (action.type) {
case 'reset':
case 'removeItem':
dispatch(action);
break;
default:
dispatch({
...action,
items,
});
break;
}
},
[items, dispatch]
);
const value: [SelectState, SelectDispatch] = [state, dispatchWrapper];
useEffect(() => {
dispatch({ type: 'updateItems', items });
}, [items, dispatch]);
return (
<SelectContext.Provider value={value}>
{props.children}
</SelectContext.Provider>
);
}
export function useSelect() {
const context = React.useContext(SelectContext);
if (context === undefined) {
throw new Error('useSelect must be used within a SelectProvider');
}
return context;
}

View File

@@ -1,63 +0,0 @@
import Column from 'Components/Table/Column';
import SortDirection from 'Helpers/Props/SortDirection';
import { FilterBuilderProp, PropertyFilter } from './AppState';
export interface Error {
responseJSON: {
message: string;
};
}
export interface AppSectionDeleteState {
isDeleting: boolean;
deleteError: Error;
}
export interface AppSectionSaveState {
isSaving: boolean;
saveError: Error;
}
export interface PagedAppSectionState {
page: number;
pageSize: number;
totalPages: number;
totalRecords?: number;
}
export interface TableAppSectionState {
columns: Column[];
}
export interface AppSectionFilterState<T> {
selectedFilterKey: string;
filters: PropertyFilter[];
filterBuilderProps: FilterBuilderProp<T>[];
}
export interface AppSectionSchemaState<T> {
isSchemaFetching: boolean;
isSchemaPopulated: boolean;
schemaError: Error;
schema: {
items: T[];
};
}
export interface AppSectionItemState<T> {
isFetching: boolean;
isPopulated: boolean;
error: Error;
pendingChanges: Partial<T>;
item: T;
}
interface AppSectionState<T> {
isFetching: boolean;
isPopulated: boolean;
error: Error;
items: T[];
sortKey: string;
sortDirection: SortDirection;
}
export default AppSectionState;

View File

@@ -1,71 +0,0 @@
import CommandAppState from './CommandAppState';
import HistoryAppState from './HistoryAppState';
import IndexerAppState, {
IndexerHistoryAppState,
IndexerIndexAppState,
IndexerStatusAppState,
} from './IndexerAppState';
import IndexerStatsAppState from './IndexerStatsAppState';
import SettingsAppState from './SettingsAppState';
import SystemAppState from './SystemAppState';
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[];
}
export interface AppSectionState {
isConnected: boolean;
isReconnecting: boolean;
version: string;
prevVersion?: string;
dimensions: {
isSmallScreen: boolean;
width: number;
height: number;
};
}
interface AppState {
app: AppSectionState;
commands: CommandAppState;
history: HistoryAppState;
indexerHistory: IndexerHistoryAppState;
indexerIndex: IndexerIndexAppState;
indexerStats: IndexerStatsAppState;
indexerStatus: IndexerStatusAppState;
indexers: IndexerAppState;
settings: SettingsAppState;
system: SystemAppState;
tags: TagsAppState;
}
export default AppState;

View File

@@ -1,8 +0,0 @@
import { CustomFilter } from './AppState';
interface ClientSideCollectionAppState {
totalItems: number;
customFilters: CustomFilter[];
}
export default ClientSideCollectionAppState;

View File

@@ -1,6 +0,0 @@
import AppSectionState from 'App/State/AppSectionState';
import Command from 'Commands/Command';
export type CommandAppState = AppSectionState<Command>;
export default CommandAppState;

View File

@@ -1,14 +0,0 @@
import AppSectionState, {
AppSectionFilterState,
} from 'App/State/AppSectionState';
import Column from 'Components/Table/Column';
import History from 'typings/History';
interface HistoryAppState
extends AppSectionState<History>,
AppSectionFilterState<History> {
pageSize: number;
columns: Column[];
}
export default HistoryAppState;

View File

@@ -1,42 +0,0 @@
import Column from 'Components/Table/Column';
import SortDirection from 'Helpers/Props/SortDirection';
import Indexer, { IndexerStatus } from 'Indexer/Indexer';
import History from 'typings/History';
import AppSectionState, {
AppSectionDeleteState,
AppSectionSaveState,
} from './AppSectionState';
import { Filter, FilterBuilderProp } from './AppState';
export interface IndexerIndexAppState {
isTestingAll: boolean;
sortKey: string;
sortDirection: SortDirection;
secondarySortKey: string;
secondarySortDirection: SortDirection;
view: string;
tableOptions: {
showSearchAction: boolean;
};
selectedFilterKey: string;
filterBuilderProps: FilterBuilderProp<Indexer>[];
filters: Filter[];
columns: Column[];
}
interface IndexerAppState
extends AppSectionState<Indexer>,
AppSectionDeleteState,
AppSectionSaveState {
itemMap: Record<number, number>;
isTestingAll: boolean;
}
export type IndexerStatusAppState = AppSectionState<IndexerStatus>;
export type IndexerHistoryAppState = AppSectionState<History>;
export default IndexerAppState;

View File

@@ -1,13 +0,0 @@
import { AppSectionItemState } from 'App/State/AppSectionState';
import { Filter, FilterBuilderProp } from 'App/State/AppState';
import Indexer from 'Indexer/Indexer';
import { IndexerStats } from 'typings/IndexerStats';
export interface IndexerStatsAppState
extends AppSectionItemState<IndexerStats> {
filterBuilderProps: FilterBuilderProp<Indexer>[];
selectedFilterKey: string;
filters: Filter[];
}
export default IndexerStatsAppState;

View File

@@ -1,10 +0,0 @@
import AppSectionState, {
AppSectionDeleteState,
} from 'App/State/AppSectionState';
import Release from 'typings/Release';
interface ReleaseAppState
extends AppSectionState<Release>,
AppSectionDeleteState {}
export default ReleaseAppState;

View File

@@ -1,57 +0,0 @@
import AppSectionState, {
AppSectionDeleteState,
AppSectionItemState,
AppSectionSaveState,
} from 'App/State/AppSectionState';
import { IndexerCategory } from 'Indexer/Indexer';
import Application from 'typings/Application';
import DownloadClient from 'typings/DownloadClient';
import Notification from 'typings/Notification';
import General from 'typings/Settings/General';
import UiSettings from 'typings/Settings/UiSettings';
export interface AppProfileAppState
extends AppSectionState<Application>,
AppSectionDeleteState,
AppSectionSaveState {}
export interface ApplicationAppState
extends AppSectionState<Application>,
AppSectionDeleteState,
AppSectionSaveState {
isTestingAll: boolean;
}
export interface DownloadClientAppState
extends AppSectionState<DownloadClient>,
AppSectionDeleteState,
AppSectionSaveState {
isTestingAll: boolean;
}
export interface GeneralAppState
extends AppSectionItemState<General>,
AppSectionSaveState {}
export interface IndexerCategoryAppState
extends AppSectionState<IndexerCategory>,
AppSectionDeleteState,
AppSectionSaveState {}
export interface NotificationAppState
extends AppSectionState<Notification>,
AppSectionDeleteState {}
export type UiSettingsAppState = AppSectionItemState<UiSettings>;
interface SettingsAppState {
appProfiles: AppProfileAppState;
applications: ApplicationAppState;
downloadClients: DownloadClientAppState;
general: GeneralAppState;
indexerCategories: IndexerCategoryAppState;
notifications: NotificationAppState;
ui: UiSettingsAppState;
}
export default SettingsAppState;

View File

@@ -1,19 +0,0 @@
import Health from 'typings/Health';
import SystemStatus from 'typings/SystemStatus';
import Task from 'typings/Task';
import Update from 'typings/Update';
import AppSectionState, { AppSectionItemState } from './AppSectionState';
export type HealthAppState = AppSectionState<Health>;
export type SystemStatusAppState = AppSectionItemState<SystemStatus>;
export type TaskAppState = AppSectionState<Task>;
export type UpdateAppState = AppSectionState<Update>;
interface SystemAppState {
health: HealthAppState;
status: SystemStatusAppState;
tasks: TaskAppState;
updates: UpdateAppState;
}
export default SystemAppState;

View File

@@ -1,28 +0,0 @@
import ModelBase from 'App/ModelBase';
import AppSectionState, {
AppSectionDeleteState,
AppSectionSaveState,
} from 'App/State/AppSectionState';
export interface Tag extends ModelBase {
label: string;
}
export interface TagDetail extends ModelBase {
label: string;
applicationIds: number[];
indexerIds: number[];
indexerProxyIds: number[];
notificationIds: number[];
}
export interface TagDetailAppState
extends AppSectionState<TagDetail>,
AppSectionDeleteState,
AppSectionSaveState {}
interface TagsAppState extends AppSectionState<Tag>, AppSectionDeleteState {
details: TagDetailAppState;
}
export default TagsAppState;

View File

@@ -1,36 +0,0 @@
import ModelBase from 'App/ModelBase';
export interface CommandBody {
sendUpdatesToClient: boolean;
updateScheduledTask: boolean;
completionMessage: string;
requiresDiskAccess: boolean;
isExclusive: boolean;
isLongRunning: boolean;
name: string;
lastExecutionTime: string;
lastStartTime: string;
trigger: string;
suppressMessages: boolean;
}
interface Command extends ModelBase {
name: string;
commandName: string;
message: string;
body: CommandBody;
priority: string;
status: string;
result: string;
queued: string;
started: string;
ended: string;
duration: string;
trigger: string;
stateChangeTime: string;
sendUpdatesToClient: boolean;
updateScheduledTask: boolean;
lastExecutionTime: string;
}
export default Command;

View File

@@ -7,25 +7,25 @@
} }
.danger { .danger {
border-color: var(--alertDangerBorderColor); border-color: $alertDangerBorderColor;
background-color: var(--alertDangerBackgroundColor); background-color: $alertDangerBackgroundColor;
color: var(--alertDangerColor); color: $alertDangerColor;
} }
.info { .info {
border-color: var(--alertInfoBorderColor); border-color: $alertInfoBorderColor;
background-color: var(--alertInfoBackgroundColor); background-color: $alertInfoBackgroundColor;
color: var(--alertInfoColor); color: $alertInfoColor;
} }
.success { .success {
border-color: var(--alertSuccessBorderColor); border-color: $alertSuccessBorderColor;
background-color: var(--alertSuccessBackgroundColor); background-color: $alertSuccessBackgroundColor;
color: var(--alertSuccessColor); color: $alertSuccessColor;
} }
.warning { .warning {
border-color: var(--alertWarningBorderColor); border-color: $alertWarningBorderColor;
background-color: var(--alertWarningBackgroundColor); background-color: $alertWarningBackgroundColor;
color: var(--alertWarningColor); color: $alertWarningColor;
} }

View File

@@ -1,11 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'alert': string;
'danger': string;
'info': string;
'success': string;
'warning': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -4,9 +4,7 @@ import React from 'react';
import { kinds } from 'Helpers/Props'; import { kinds } from 'Helpers/Props';
import styles from './Alert.css'; import styles from './Alert.css';
function Alert(props) { function Alert({ className, kind, children, ...otherProps }) {
const { className, kind, children, ...otherProps } = props;
return ( return (
<div <div
className={classNames( className={classNames(
@@ -21,8 +19,8 @@ function Alert(props) {
} }
Alert.propTypes = { Alert.propTypes = {
className: PropTypes.string, className: PropTypes.string.isRequired,
kind: PropTypes.oneOf(kinds.all), kind: PropTypes.oneOf(kinds.all).isRequired,
children: PropTypes.node.isRequired children: PropTypes.node.isRequired
}; };

View File

@@ -3,9 +3,9 @@
margin: 10px; margin: 10px;
padding: 10px; padding: 10px;
border-radius: 3px; border-radius: 3px;
background-color: var(--cardBackgroundColor); background-color: $white;
box-shadow: 0 0 10px 1px var(--cardShadowColor); box-shadow: 0 0 10px 1px $cardShadowColor;
color: var(--defaultColor); color: $defaultColor;
} }
.underlay { .underlay {

View File

@@ -1,9 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'card': string;
'overlay': string;
'underlay': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -2,17 +2,15 @@ import Chart from 'chart.js/auto';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { kinds } from 'Helpers/Props'; import { kinds } from 'Helpers/Props';
import { defaultFontFamily } from 'Styles/Variables/fonts'; import colors from 'Styles/Variables/colors';
function getColors(kind) { function getColors(kind) {
const style = getComputedStyle(document.body);
if (kind === kinds.WARNING) { if (kind === kinds.WARNING) {
return style.getPropertyValue('--failedColors').split(','); return colors.failedColors.reverse();
} }
return style.getPropertyValue('--chartColors').split(','); return colors.chartColors;
} }
class BarChart extends Component { class BarChart extends Component {
@@ -40,15 +38,7 @@ class BarChart extends Component {
plugins: { plugins: {
title: { title: {
display: true, display: true,
align: 'start', text: this.props.title
text: this.props.title,
padding: {
bottom: 30
},
font: {
size: 14,
family: defaultFontFamily
}
}, },
legend: { legend: {
display: this.props.legend display: this.props.legend

View File

@@ -1,13 +1,7 @@
import Chart from 'chart.js/auto'; import Chart from 'chart.js/auto';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { defaultFontFamily } from 'Styles/Variables/fonts'; import colors from 'Styles/Variables/colors';
function getColors(kind) {
const style = getComputedStyle(document.body);
return style.getPropertyValue('--chartColorsDiversified').split(',');
}
class DoughnutChart extends Component { class DoughnutChart extends Component {
constructor(props) { constructor(props) {
@@ -23,15 +17,7 @@ class DoughnutChart extends Component {
plugins: { plugins: {
title: { title: {
display: true, display: true,
align: 'start', text: this.props.title
text: this.props.title,
padding: {
bottom: 30
},
font: {
size: 14,
family: defaultFontFamily
}
}, },
legend: { legend: {
position: 'bottom' position: 'bottom'
@@ -43,7 +29,7 @@ class DoughnutChart extends Component {
datasets: [{ datasets: [{
label: this.props.title, label: this.props.title,
data: this.props.data.map((d) => d.value), data: this.props.data.map((d) => d.value),
backgroundColor: getColors() backgroundColor: colors.chartColors
}] }]
} }
}); });

View File

@@ -1,13 +1,7 @@
import Chart from 'chart.js/auto'; import Chart from 'chart.js/auto';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { defaultFontFamily } from 'Styles/Variables/fonts'; import colors from 'Styles/Variables/colors';
function getColors(index) {
const style = getComputedStyle(document.body);
return style.getPropertyValue('--chartColorsDiversified').split(',')[index];
}
class StackedBarChart extends Component { class StackedBarChart extends Component {
constructor(props) { constructor(props) {
@@ -37,19 +31,7 @@ class StackedBarChart extends Component {
plugins: { plugins: {
title: { title: {
display: true, display: true,
align: 'start', text: this.props.title
text: this.props.title,
padding: {
bottom: 30
},
font: {
size: 14,
family: defaultFontFamily
}
},
tooltip: {
mode: 'index',
position: 'average'
} }
} }
}, },
@@ -59,7 +41,7 @@ class StackedBarChart extends Component {
return { return {
label: d.label, label: d.label,
data: d.data, data: d.data,
backgroundColor: getColors(index) backgroundColor: colors.chartColors[index]
}; };
}) })
} }
@@ -72,7 +54,7 @@ class StackedBarChart extends Component {
return { return {
label: d.label, label: d.label,
data: d.data, data: d.data,
backgroundColor: getColors(index) backgroundColor: colors.chartColors[index]
}; };
}); });
this.myChart.update(); this.myChart.update();

View File

@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'descriptionList': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -10,7 +10,6 @@ class DescriptionListItem extends Component {
render() { render() {
const { const {
className,
titleClassName, titleClassName,
descriptionClassName, descriptionClassName,
title, title,
@@ -18,7 +17,7 @@ class DescriptionListItem extends Component {
} = this.props; } = this.props;
return ( return (
<div className={className}> <span>
<DescriptionListItemTitle <DescriptionListItemTitle
className={titleClassName} className={titleClassName}
> >
@@ -30,13 +29,12 @@ class DescriptionListItem extends Component {
> >
{data} {data}
</DescriptionListItemDescription> </DescriptionListItemDescription>
</div> </span>
); );
} }
} }
DescriptionListItem.propTypes = { DescriptionListItem.propTypes = {
className: PropTypes.string,
titleClassName: PropTypes.string, titleClassName: PropTypes.string,
descriptionClassName: PropTypes.string, descriptionClassName: PropTypes.string,
title: PropTypes.string, title: PropTypes.string,

View File

@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'description': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'title': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'dragLayer': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -25,10 +25,6 @@
white-space: pre-wrap; white-space: pre-wrap;
} }
.version {
margin-top: 20px;
}
@media only screen and (max-width: $breakpointMedium) { @media only screen and (max-width: $breakpointMedium) {
.image { .image {
height: 250px; height: 250px;

View File

@@ -1,12 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'container': string;
'details': string;
'image': string;
'imageContainer': string;
'message': string;
'version': string;
}
export const cssExports: CssExports;
export default cssExports;

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