Fixed few layout issues with spinners.

Add a new Test Folder button in settings to test.
Add Docker images.
This commit is contained in:
Roger Far 2020-12-12 14:10:05 -07:00
parent b990a910be
commit 02c154f094
22 changed files with 648 additions and 288 deletions

40
.dockerignore Normal file
View file

@ -0,0 +1,40 @@
### Angular ###
# compiled output
client/dist/
client/tmp/
client/app/**/*.js
client/app/**/*.js.map
# dependencies
client/node_modules/
client/bower_components/
# IDEs and editors
client/.idea/
# misc
client/.sass-cache/
client/connect.lock/
client/coverage/
client/libpeerconnection.log/
client/npm-debug.log
client/testem.log
client/typings/
# e2e
client/e2e/*.js
client/e2e/*.map
#System Files
**/.DS_Store/
### DotnetCore ###
# .NET Core build folders
server/**/bin
server/**/obj
server/**/*.user
server/**/.vs
server/RdtClient.Web/wwwroot/
build/
publish/

28
Dockerfile Normal file
View file

@ -0,0 +1,28 @@
# Stage 1 - Build the frontend
FROM node:12.15-buster AS node-build-env
RUN mkdir /appclient
WORKDIR /appclient
COPY client/. .
RUN npm ci
RUN npx ng build --prod --output-path=out
# Stage 2 - Build the backend
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS dotnet-build-env
RUN mkdir /appserver
WORKDIR /appserver
COPY server/. .
RUN dotnet restore RdtClient.sln
RUN dotnet build -c Release
RUN dotnet publish -c Release -o out
# Stage 3 - Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:5.0.1-buster-slim AS base
RUN mkdir /app
WORKDIR /app
COPY --from=dotnet-build-env /appserver/out .
COPY --from=node-build-env /appclient/out ./wwwroot
ENTRYPOINT ["dotnet", "RdtClient.Web.dll"]

View file

@ -10,21 +10,34 @@ This is a web interface to manage your torrents on Real-Debrid. It supports the
**You will need a Premium service at Real-Debrid!**
## Installation
## Docker Run
### Docker Installation
```console
docker run --cap-add=NET_ADMIN -d \
-v /your/storage/path/:/data/downloads \
--log-driver json-file \
--log-opt max-size=10m \
-p 6500:6500 \
rogerfar/rdtclient
```
### Windows Service Installation
Replace `/your/storage/path/` with your local path to download files to. For Windows i.e. `C:/Downloads`.
## Windows Service Installation
1. Make sure you have the .NET 5.0.1+ runtime installed from [here](https://dotnet.microsoft.com/download).
1. Unpack the latest release from the releases folder and run `startup.bat`. This will start the application on port 6500.
1. Browse to http://127.0.0.1:6500
1. The very first credentials you enter in will be remembered for future logins.
1. Click on Settings on the top and enter your Real-Debrid API key.
1. Change your download path if needed.
1. To install as service on Windows, exit the console and run `serviceinstall.bat` as administrator.
1. To change the default port edit the `appsettings.json` file.
## Setup
1. Browse to [http://127.0.0.1:6500](http://127.0.0.1:6500) (or the path of your server).
1. The very first credentials you enter in will be remembered for future logins.
1. Click on `Settings` on the top and enter your Real-Debrid API key (found here: [https://real-debrid.com/apitoken](https://real-debrid.com/apitoken).
1. Change your download path if needed. When using Docker, this path will be the path on your local machine.
1. Save your settings.
## Removing
1. Run `serviceremove.bat` to remove the service and firewall rules.
@ -35,21 +48,40 @@ This is a web interface to manage your torrents on Real-Debrid. It supports the
## Connecting Sonarr/Radarr
1. Login to Sonarr or Radarr and click Settings
1. Go to the Download Client tab and click the plus to add
1. Click "qBittorrent" in the list
1. Enter the IP or hostname of the RealDebridClient in the Host field
1. Enter the port 6500 in the Port field
RdtClient emulates the qBittorrent web protocol and allow applications to use those APIs. This way you can use Sonarr and Radarr to download directly from RealDebrid.
1. Login to Sonarr or Radarr and click `Settings`.
1. Go to the `Download Client` tab and click the plus to add.
1. Click `qBittorrent` in the list.
1. Enter the IP or hostname of the RealDebridClient in the `Host` field.
1. Enter the 6500 in the `Port` field.
1. Enter your Username/Password you setup in step 3 above in the Username/Password field.
1. Leave the other settings as is.
1. Hit Test and then Save if all is well.
1. Hit `Test` and then `Save` if all is well.
1. Sonarr will now think you have a regular Torrent client hooked up.
Notice: the progress and ETA reported in Sonarr's Activity tab will not be accurate, but it will report the torrent as completed so it can be processed after it is done downloading.
## Build instructions
1. Open the client folder project in VS Code and run `npm install`
2. To debug run `ng serve`, to build run `ng build --prod`
3. Open the Visual Studio 2019 project `RdtClient.sln` and `Publish`
4. The result is found in `\rdt-client\Publish`
### Prerequisites
- NodeJS
- NPM
- (optional) Angular CLI
- .NET 5
- Visual Studio 2019
- (optional) Resharper
1. Open the client folder project in VS Code and run `npm install`.
1. To debug run `ng serve`, to build run `ng build --prod`.
1. Open the Visual Studio 2019 project `RdtClient.sln` and `Publish` the `RdtClient.Web` to the given `PublishFolder` target.
1. When debugging, make sure to run `RdtClient.Web.dll` and not `IISExpress`.
1. The result is found in `Publish`.
## Build docker container
1. In the root of the project run `docker build --tag rdtclient .`
1. To create the docker container run `docker run --publish 6500:6500 --detach --name rdtclientdev rdtclient:latest`
1. To stop: `docker stop rdtclient`
1. To remove: `docker rm rdtclient`

View file

@ -40,17 +40,7 @@
<button
class="button is-success"
(click)="login()"
*ngIf="loggingIn"
>
<span class="icon">
<i class="fas fa-spinner fa-pulse"></i>
</span>
Login
</button>
<button
class="button is-success"
(click)="login()"
*ngIf="!loggingIn"
[ngClass]="{ 'is-loading': loggingIn }"
>
Login
</button>

View file

@ -33,9 +33,7 @@
<span class="file-icon">
<i class="fas fa-upload"></i>
</span>
<span class="file-label">
Pick a torrent file...
</span>
<span class="file-label"> Pick a torrent file... </span>
</span>
<span class="file-name">
{{ fileName }}
@ -59,16 +57,13 @@
</div>
</section>
<footer class="modal-card-foot">
<button class="button is-success" *ngIf="saving">
<span class="icon">
<i class="fas fa-spinner fa-pulse"></i>
</span>
<button
class="button is-success"
[disabled]="saving"
[ngClass]="{ 'is-loading': saving }"
>
<span>Add Torrent</span>
</button>
<button class="button is-success" (click)="ok()" *ngIf="!saving">
Add Torrent
</button>
<button class="button" (click)="cancel()" [disabled]="saving">
Cancel
</button>

View file

@ -36,9 +36,7 @@
[(ngModel)]="settingDownloadFolder"
/>
</div>
<p class="help">
Path on the host where Real Debrid Client is hosted.
</p>
<p class="help">Path on the host where Real Debrid Client is hosted.</p>
</div>
<div class="field">
<label class="label">Maximum parallel downloads</label>
@ -59,18 +57,42 @@
<div class="notification is-danger is-light" *ngIf="error?.length > 0">
Error saving settings: {{ error }}
</div>
<div
class="notification is-danger is-light"
*ngIf="testFolderError?.length > 0"
>
Could not test your download folder<br />
{{ testFolderError }}
</div>
<div class="notification is-success is-light" *ngIf="testFolderSuccess">
Successfully tested your download folder
</div>
</section>
<footer class="modal-card-foot">
<button class="button is-success" *ngIf="saving">
<span class="icon">
<i class="fas fa-spinner fa-pulse"></i>
</span>
<span>Save Settings</span>
</button>
<button class="button is-success" (click)="ok()" *ngIf="!saving">
<button
class="button is-success"
(click)="ok()"
[disabled]="saving"
[ngClass]="{ 'is-loading': saving }"
>
Save Settings
</button>
<button class="button" (click)="cancel()" [disabled]="saving">
<button
class="button is-warning"
(click)="test()"
[disabled]="saving"
[ngClass]="{ 'is-loading': saving }"
>
Test permissions
</button>
<button
class="button"
(click)="cancel()"
[disabled]="saving"
[ngClass]="{ 'is-loading': saving }"
>
Cancel
</button>
</footer>

View file

@ -1,6 +1,6 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { SettingsService } from 'src/app/settings.service';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Setting } from 'src/app/models/setting.model';
import { SettingsService } from 'src/app/settings.service';
@Component({
selector: 'app-settings',
@ -25,6 +25,8 @@ export class SettingsComponent implements OnInit {
public saving = false;
public error: string;
public testFolderError: string;
public testFolderSuccess: boolean;
public settingRealDebridApiKey: string;
public settingDownloadFolder: string;
@ -86,6 +88,24 @@ export class SettingsComponent implements OnInit {
);
}
public test(): void {
this.saving = true;
this.testFolderError = null;
this.testFolderSuccess = false;
this.settingsService.testFolder(this.settingDownloadFolder).subscribe(
() => {
this.saving = false;
this.testFolderSuccess = true;
},
(err) => {
console.log(err);
this.testFolderError = err.error;
this.saving = false;
}
);
}
public cancel(): void {
this.isActive = false;
this.openChange.emit(this.open);

View file

@ -1,8 +1,8 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Setting } from './models/setting.model';
import { Profile } from './models/profile.model';
import { Setting } from './models/setting.model';
@Injectable({
providedIn: 'root',
@ -21,4 +21,8 @@ export class SettingsService {
public getProfile(): Observable<Profile> {
return this.http.get<Profile>(`/Api/Settings/Profile`);
}
public testFolder(folder: string): Observable<void> {
return this.http.post<void>(`/Api/Settings/TestFolder`, { folder });
}
}

4
docker-build.bat Normal file
View file

@ -0,0 +1,4 @@
docker stop rdtclient
docker rm rdtclient
docker build --tag rdtclient .
docker-compose up -d

14
docker-compose.yml Normal file
View file

@ -0,0 +1,14 @@
version: '3.3'
services:
rdtclient:
container_name: rogerfar/rdtclient
volumes:
- 'C:/Downloads/:/data/downloads'
image: rdtclient
restart: always
logging:
driver: json-file
options:
max-size: 10m
ports:
- '6500:6500'

2
docker-connect.bat Normal file
View file

@ -0,0 +1,2 @@
@echo off
docker exec -it rdtclient /bin/bash

View file

@ -11,47 +11,22 @@ namespace RdtClient.Data.Data
public DataContext(DbContextOptions options) : base(options)
{
}
public DataContext()
{
}
public static String ConnectionString => $"Data Source={AppContext.BaseDirectory}database.db";
public DbSet<Download> Downloads { get; set; }
public DbSet<Setting> Settings { get; set; }
public DbSet<Torrent> Torrents { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlite(ConnectionString);
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlite(ConnectionString);
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Setting>()
.HasData(new Setting
{
SettingId = "RealDebridApiKey",
Type = "String",
Value = ""
});
builder.Entity<Setting>()
.HasData(new Setting
{
SettingId = "DownloadFolder",
Type = "String",
Value = @"C:\Downloads"
});
builder.Entity<Setting>()
.HasData(new Setting
{
SettingId = "DownloadLimit",
Type = "Int32",
Value = "10"
});
var cascadeFKs = builder.Model.GetEntityTypes()
.SelectMany(t => t.GetForeignKeys())
.Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade);
@ -61,10 +36,5 @@ namespace RdtClient.Data.Data
fk.DeleteBehavior = DeleteBehavior.Restrict;
}
}
public void Migrate()
{
Database.Migrate();
}
}
}

View file

@ -0,0 +1,63 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using RdtClient.Data.Models.Data;
namespace RdtClient.Data.Data
{
public static class DataMigration
{
private static Boolean InDocker => Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true";
public static void Setup(IServiceScope serviceScope)
{
using var scope = serviceScope.ServiceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope();
var dataContext = scope.ServiceProvider.GetRequiredService<DataContext>();
dataContext.Database.Migrate();
Seed(dataContext);
}
private static void Seed(DataContext dataContext)
{
var defaultDownloadPath = "/data/downloads";
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !InDocker)
{
defaultDownloadPath = @"C:\Downloads";
}
var configuration = dataContext.Settings.FirstOrDefault();
if (configuration == null)
{
dataContext.Settings.Add(new Setting
{
SettingId = "RealDebridApiKey",
Type = "String",
Value = ""
});
dataContext.Settings.Add(new Setting
{
SettingId = "DownloadFolder",
Type = "String",
Value = defaultDownloadPath
});
dataContext.Settings.Add(new Setting
{
SettingId = "DownloadLimit",
Type = "Int32",
Value = "10"
});
dataContext.SaveChanges();
}
}
}
}

View file

@ -9,14 +9,14 @@ using RdtClient.Data.Data;
namespace RdtClient.Data.Migrations
{
[DbContext(typeof(DataContext))]
[Migration("20200408224831_Initial")]
[Migration("20201212204128_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.3");
.HasAnnotation("ProductVersion", "5.0.1");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
@ -28,18 +28,18 @@ namespace RdtClient.Data.Migrations
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasColumnType("TEXT")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
@ -80,8 +80,8 @@ namespace RdtClient.Data.Migrations
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasColumnType("TEXT")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
@ -93,12 +93,12 @@ namespace RdtClient.Data.Migrations
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasColumnType("TEXT")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasColumnType("TEXT")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
@ -116,17 +116,17 @@ namespace RdtClient.Data.Migrations
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasColumnType("TEXT")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers");
});
@ -249,26 +249,6 @@ namespace RdtClient.Data.Migrations
b.HasKey("SettingId");
b.ToTable("Settings");
b.HasData(
new
{
SettingId = "RealDebridApiKey",
Type = "String",
Value = ""
},
new
{
SettingId = "DownloadFolder",
Type = "String",
Value = "C:\\Downloads"
},
new
{
SettingId = "DownloadLimit",
Type = "Int32",
Value = "10"
});
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
@ -391,6 +371,13 @@ namespace RdtClient.Data.Migrations
.HasForeignKey("TorrentId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Torrent");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
{
b.Navigation("Downloads");
});
#pragma warning restore 612, 618
}

View file

@ -11,10 +11,10 @@ namespace RdtClient.Data.Migrations
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Name = table.Column<string>(maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true)
Id = table.Column<string>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
@ -25,21 +25,21 @@ namespace RdtClient.Data.Migrations
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(nullable: false),
UserName = table.Column<string>(maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
Email = table.Column<string>(maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(nullable: false),
PasswordHash = table.Column<string>(nullable: true),
SecurityStamp = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
PhoneNumber = table.Column<string>(nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
TwoFactorEnabled = table.Column<bool>(nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
LockoutEnabled = table.Column<bool>(nullable: false),
AccessFailedCount = table.Column<int>(nullable: false)
Id = table.Column<string>(type: "TEXT", nullable: false),
UserName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
Email = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(type: "INTEGER", nullable: false),
PasswordHash = table.Column<string>(type: "TEXT", nullable: true),
SecurityStamp = table.Column<string>(type: "TEXT", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "TEXT", nullable: true),
PhoneNumber = table.Column<string>(type: "TEXT", nullable: true),
PhoneNumberConfirmed = table.Column<bool>(type: "INTEGER", nullable: false),
TwoFactorEnabled = table.Column<bool>(type: "INTEGER", nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(type: "TEXT", nullable: true),
LockoutEnabled = table.Column<bool>(type: "INTEGER", nullable: false),
AccessFailedCount = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
@ -50,9 +50,9 @@ namespace RdtClient.Data.Migrations
name: "Settings",
columns: table => new
{
SettingId = table.Column<string>(nullable: false),
Value = table.Column<string>(nullable: true),
Type = table.Column<string>(nullable: true)
SettingId = table.Column<string>(type: "TEXT", nullable: false),
Value = table.Column<string>(type: "TEXT", nullable: true),
Type = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
@ -63,24 +63,24 @@ namespace RdtClient.Data.Migrations
name: "Torrents",
columns: table => new
{
TorrentId = table.Column<Guid>(nullable: false),
Hash = table.Column<string>(nullable: true),
Category = table.Column<string>(nullable: true),
AutoDownload = table.Column<bool>(nullable: false),
AutoDelete = table.Column<bool>(nullable: false),
Status = table.Column<int>(nullable: false),
RdId = table.Column<string>(nullable: true),
RdName = table.Column<string>(nullable: true),
RdSize = table.Column<long>(nullable: false),
RdHost = table.Column<string>(nullable: true),
RdSplit = table.Column<long>(nullable: false),
RdProgress = table.Column<long>(nullable: false),
RdStatus = table.Column<string>(nullable: true),
RdAdded = table.Column<DateTimeOffset>(nullable: false),
RdEnded = table.Column<DateTimeOffset>(nullable: true),
RdSpeed = table.Column<long>(nullable: true),
RdSeeders = table.Column<long>(nullable: true),
RdFiles = table.Column<string>(nullable: true)
TorrentId = table.Column<Guid>(type: "TEXT", nullable: false),
Hash = table.Column<string>(type: "TEXT", nullable: true),
Category = table.Column<string>(type: "TEXT", nullable: true),
AutoDownload = table.Column<bool>(type: "INTEGER", nullable: false),
AutoDelete = table.Column<bool>(type: "INTEGER", nullable: false),
Status = table.Column<int>(type: "INTEGER", nullable: false),
RdId = table.Column<string>(type: "TEXT", nullable: true),
RdName = table.Column<string>(type: "TEXT", nullable: true),
RdSize = table.Column<long>(type: "INTEGER", nullable: false),
RdHost = table.Column<string>(type: "TEXT", nullable: true),
RdSplit = table.Column<long>(type: "INTEGER", nullable: false),
RdProgress = table.Column<long>(type: "INTEGER", nullable: false),
RdStatus = table.Column<string>(type: "TEXT", nullable: true),
RdAdded = table.Column<DateTimeOffset>(type: "TEXT", nullable: false),
RdEnded = table.Column<DateTimeOffset>(type: "TEXT", nullable: true),
RdSpeed = table.Column<long>(type: "INTEGER", nullable: true),
RdSeeders = table.Column<long>(type: "INTEGER", nullable: true),
RdFiles = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
@ -91,11 +91,11 @@ namespace RdtClient.Data.Migrations
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
RoleId = table.Column<string>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
RoleId = table.Column<string>(type: "TEXT", nullable: false),
ClaimType = table.Column<string>(type: "TEXT", nullable: true),
ClaimValue = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
@ -112,11 +112,11 @@ namespace RdtClient.Data.Migrations
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
UserId = table.Column<string>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
UserId = table.Column<string>(type: "TEXT", nullable: false),
ClaimType = table.Column<string>(type: "TEXT", nullable: true),
ClaimValue = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
@ -133,10 +133,10 @@ namespace RdtClient.Data.Migrations
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(nullable: false),
ProviderKey = table.Column<string>(nullable: false),
ProviderDisplayName = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: false)
LoginProvider = table.Column<string>(type: "TEXT", nullable: false),
ProviderKey = table.Column<string>(type: "TEXT", nullable: false),
ProviderDisplayName = table.Column<string>(type: "TEXT", nullable: true),
UserId = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
@ -153,8 +153,8 @@ namespace RdtClient.Data.Migrations
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
RoleId = table.Column<string>(nullable: false)
UserId = table.Column<string>(type: "TEXT", nullable: false),
RoleId = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
@ -177,10 +177,10 @@ namespace RdtClient.Data.Migrations
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
LoginProvider = table.Column<string>(nullable: false),
Name = table.Column<string>(nullable: false),
Value = table.Column<string>(nullable: true)
UserId = table.Column<string>(type: "TEXT", nullable: false),
LoginProvider = table.Column<string>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", nullable: false),
Value = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
@ -197,11 +197,11 @@ namespace RdtClient.Data.Migrations
name: "Downloads",
columns: table => new
{
DownloadId = table.Column<Guid>(nullable: false),
TorrentId = table.Column<Guid>(nullable: false),
Link = table.Column<string>(nullable: true),
Added = table.Column<DateTimeOffset>(nullable: false),
Status = table.Column<int>(nullable: false)
DownloadId = table.Column<Guid>(type: "TEXT", nullable: false),
TorrentId = table.Column<Guid>(type: "TEXT", nullable: false),
Link = table.Column<string>(type: "TEXT", nullable: true),
Added = table.Column<DateTimeOffset>(type: "TEXT", nullable: false),
Status = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
@ -214,21 +214,6 @@ namespace RdtClient.Data.Migrations
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.InsertData(
table: "Settings",
columns: new[] { "SettingId", "Type", "Value" },
values: new object[] { "RealDebridApiKey", "String", "" });
migrationBuilder.InsertData(
table: "Settings",
columns: new[] { "SettingId", "Type", "Value" },
values: new object[] { "DownloadFolder", "String", "C:\\Downloads" });
migrationBuilder.InsertData(
table: "Settings",
columns: new[] { "SettingId", "Type", "Value" },
values: new object[] { "DownloadLimit", "Int32", "10" });
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",

View file

@ -2,6 +2,7 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using RdtClient.Data.Data;
namespace RdtClient.Data.Migrations
@ -13,7 +14,7 @@ namespace RdtClient.Data.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.3");
.HasAnnotation("ProductVersion", "5.0.1");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
@ -25,18 +26,18 @@ namespace RdtClient.Data.Migrations
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasColumnType("TEXT")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
@ -77,8 +78,8 @@ namespace RdtClient.Data.Migrations
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasColumnType("TEXT")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
@ -90,12 +91,12 @@ namespace RdtClient.Data.Migrations
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasColumnType("TEXT")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasColumnType("TEXT")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
@ -113,17 +114,17 @@ namespace RdtClient.Data.Migrations
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasColumnType("TEXT")
.HasMaxLength(256);
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers");
});
@ -246,26 +247,6 @@ namespace RdtClient.Data.Migrations
b.HasKey("SettingId");
b.ToTable("Settings");
b.HasData(
new
{
SettingId = "RealDebridApiKey",
Type = "String",
Value = ""
},
new
{
SettingId = "DownloadFolder",
Type = "String",
Value = "C:\\Downloads"
},
new
{
SettingId = "DownloadLimit",
Type = "Int32",
Value = "10"
});
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
@ -388,6 +369,13 @@ namespace RdtClient.Data.Migrations
.HasForeignKey("TorrentId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Torrent");
});
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
{
b.Navigation("Downloads");
});
#pragma warning restore 612, 618
}

View file

@ -7,7 +7,7 @@ namespace RdtClient.Service.Middleware
{
public class AuthorizeMiddleware
{
readonly RequestDelegate _next;
private readonly RequestDelegate _next;
public AuthorizeMiddleware(RequestDelegate next)
{

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using RdtClient.Data.Data;
using RdtClient.Data.Models.Data;
@ -12,6 +13,7 @@ namespace RdtClient.Service.Services
Task Update(IList<Setting> settings);
Task<String> GetString(String key);
Task<Int32> GetNumber(String key);
Task TestFolder(String folder);
}
public class Settings : ISettings
@ -56,5 +58,25 @@ namespace RdtClient.Service.Services
return Int32.Parse(setting.Value);
}
public async Task TestFolder(String folder)
{
if (String.IsNullOrWhiteSpace(folder))
{
throw new Exception("Folder path is not set");
}
folder = folder.TrimEnd('/').TrimEnd('\\');
if (!Directory.Exists(folder))
{
throw new Exception($"Folder {folder} does not exist");
}
var testFile = $"{folder}/test.txt";
await File.WriteAllTextAsync(testFile, "RealDebridClient Test File, you can remove this file.");
File.Delete(testFile);
}
}
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -46,10 +47,24 @@ namespace RdtClient.Web.Controllers
var profile = await _torrents.GetProfile();
return Ok(profile);
}
[HttpPost]
[Route("TestFolder")]
public async Task<ActionResult<Profile>> TestFolder([FromBody] SettingsControllerTestFolderRequest request)
{
await _settings.TestFolder(request.Folder);
return Ok();
}
}
public class SettingsControllerUpdateRequest
{
public IList<Setting> Settings { get; set; }
}
public class SettingsControllerTestFolderRequest
{
public String Folder { get; set; }
}
}

View file

@ -2,9 +2,10 @@ using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using RdtClient.Data.Data;
using RdtClient.Service.Services;
namespace RdtClient.Web
@ -12,6 +13,15 @@ namespace RdtClient.Web
public class Program
{
public static async Task Main(String[] args)
{
var host = CreateHostBuilder(args).Build();
DataMigration.Setup(host.Services.CreateScope());
await host.RunAsync();
}
public static IHostBuilder CreateHostBuilder(String[] args)
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", true, false)
@ -24,47 +34,24 @@ namespace RdtClient.Web
hostUrl = "http://0.0.0.0:6500";
}
await Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<TaskRunner>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
logging.AddFile($"{AppContext.BaseDirectory}app.log");
})
.UseUrls(hostUrl)
.UseKestrel()
.UseStartup<Startup>();
})
.Build()
.RunAsync();
return Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<TaskRunner>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
logging.AddFile($"{AppContext.BaseDirectory}app.log");
})
.UseUrls(hostUrl)
.UseKestrel()
.UseStartup<Startup>();
});
}
}
/*public class Program
{
public static void Main(String[] args)
{
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.CaptureStartupErrors(false)
.UseStartup<Startup>()
.UseKestrel();
});
}
}*/
}
}

View file

@ -123,8 +123,6 @@ namespace RdtClient.Web
spa.Options.DefaultPage = "/index.html";
});
});
dataContext.Migrate();
}
}
}

View file

@ -0,0 +1,194 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/Browsers/Enable/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002Eappxmanifest/@EntryIndexedValue">*.appxmanifest</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002Ed_002Ets/@EntryIndexedValue">*.d.ts</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002Edebug_002Ejs/@EntryIndexedValue">*.debug.js</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002EDesigner_002Ecs/@EntryIndexedValue">*.Designer.cs</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002EDesigner_002Evb/@EntryIndexedValue">*.Designer.vb</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002EGenerated_002Ecs/@EntryIndexedValue">*.Generated.cs</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002EGenerated_002Evb/@EntryIndexedValue">*.Generated.vb</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002Eintellisense_002Ejs/@EntryIndexedValue">*.intellisense.js</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002Emin_002Ecss/@EntryIndexedValue">*.min.css</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002Emin_002Ejs/@EntryIndexedValue">*.min.js</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002A_002EStoreAssociation_002Exml/@EntryIndexedValue">*.StoreAssociation.xml</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=_002Avsdoc_002Ejs/@EntryIndexedValue">*vsdoc.js</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=Component_0020Designer_0020generated_0020code/@EntryIndexedValue"></s:String>
<s:Boolean x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=Component_0020Designer_0020generated_0020code/@EntryIndexRemoved">True</s:Boolean>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=Designer_0020generated_0020code/@EntryIndexedValue"></s:String>
<s:Boolean x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=Designer_0020generated_0020code/@EntryIndexRemoved">True</s:Boolean>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=lib_002Ed_002Ets/@EntryIndexedValue">lib.d.ts</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=Reference_002Evb/@EntryIndexedValue">Reference.vb</s:String>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=Web_0020Form_0020Designer_0020generated_0020code/@EntryIndexedValue"></s:String>
<s:Boolean x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=Web_0020Form_0020Designer_0020generated_0020code/@EntryIndexRemoved">True</s:Boolean>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=Windows_0020Form_0020Designer_0020generated_0020code/@EntryIndexedValue"></s:String>
<s:Boolean x:Key="/Default/CodeInspection/GeneratedCode/GeneratedCodeRegions/=Windows_0020Form_0020Designer_0020generated_0020code/@EntryIndexRemoved">True</s:Boolean>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedFileMasks/=_002A_002Ed_002Ets/@EntryIndexedValue">*.d.ts</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/AnalysisEnabled/@EntryValue">SOLUTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeLocalFunctionBody/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeMethodOrOperatorBody/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeModifiersOrder/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeRedundantParentheses/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeThisQualifier/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeTypeMemberModifiers/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeTypeModifiers/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=BuiltInTypeReferenceStyle/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=BuiltInTypeReferenceStyleForMemberAccess/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CheckNamespace/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassNeverInstantiated_002EGlobal/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CollectionNeverUpdated_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConstantConditionalAccessQualifier/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertIfStatementToConditionalTernaryExpression/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertIfStatementToNullCoalescingExpression/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertIfStatementToReturnStatement/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToUsingDeclaration/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EmptyStatement/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceDoWhileStatementBraces/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceFixedStatementBraces/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceForeachStatementBraces/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceForStatementBraces/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceIfStatementBraces/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceLockStatementBraces/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceUsingStatementBraces/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceWhileStatementBraces/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=FieldCanBeMadeReadOnly_002ELocal/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=Html_002ECssClassNotUsed/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=Html_002EObsolete/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=Html_002EPathError/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ImplicitAnyTypeWarning/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=InlineOutVariableDeclaration/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=InvalidValue/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=InvertIf/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=LocalizableElement/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=LoopCanBeConvertedToQuery/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=NotAccessedField_002ELocal/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=OptionalParameterHierarchyMismatch/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ParameterTypeCanBeEnumerable_002ELocal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PossibleInvalidOperationException/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PossibleNullReferenceException/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantAnonymousTypePropertyName/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantArgumentDefaultValue/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantCast/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantExplicitArrayCreation/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantNameQualifier/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantStringInterpolation/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantTypeArgumentsOfMethod/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantUsingDirective/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantVariableTypeSpecification/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RemoveRedundantBraces/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SpecifyVariableTypeExplicitly/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=StatementIsNotTerminated/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=StringConcatenationToTemplateString/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=StringLiteralWrongQuotes/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestBaseTypeForParameter/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FBuiltInTypes/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FElsewhere/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FSimpleTypes/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=TailRecursiveCall/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnknownCssClass/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedAutoPropertyAccessor_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedMember_002EGlobal/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedParameter_002ELocal/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedVariable/@EntryIndexedValue">SUGGESTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseStringInterpolation/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=VariableCanBeMadeConst/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=WrongPublicModifierSpecification/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=Xaml_002EBindingWithContextNotResolved/@EntryIndexedValue">ERROR</s:String>
<s:Boolean x:Key="/Default/CodeInspection/Highlighting/SuppressVsSquiggles/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CommonFormatter/END_OF_LINE/@EntryValue">CRLF</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CommonFormatter/ENFORCE_LINE_ENDING_STYLE/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BUILTIN_TYPE_REFERENCE_FOR_MEMBER_ACCESS_STYLE/@EntryValue">UseClrName</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BUILTIN_TYPE_REFERENCE_STYLE/@EntryValue">UseClrName</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/PARENTHESES_NON_OBVIOUS_OPERATIONS/@EntryValue">Arithmetic, Shift, Bitwise</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_FIRST_ARG_BY_PAREN/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_LINQ_QUERY/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_CALLS_CHAIN/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_EXTENDS_LIST/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_PARAMETER/@EntryValue">True</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AFTER_CONTROL_TRANSFER_STATEMENTS/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AFTER_MULTILINE_STATEMENTS/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AFTER_START_COMMENT/@EntryValue">0</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BEFORE_BLOCK_STATEMENTS/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BEFORE_CONTROL_TRANSFER_STATEMENTS/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BEFORE_MULTILINE_STATEMENTS/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BEFORE_SINGLE_LINE_COMMENT/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_INSIDE_REGION/@EntryValue">0</s:Int64>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_FIXED_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_FOR_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_FOREACH_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_IFELSE_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_USING_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_WHILE_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_NESTED_FIXED_STMT/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_NESTED_USINGS_STMT/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_PREPROCESSOR_REGION/@EntryValue">NO_INDENT</s:String>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_DECLARATION_PARENS_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_EMBEDDED_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_INITIALIZER_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_INVOCATION_PARENS_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_SWITCH_EXPRESSION_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/MAX_ENUM_MEMBERS_ON_LINE/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/MAX_INITIALIZER_ELEMENTS_ON_LINE/@EntryValue">1</s:Int64>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSOR_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ANONYMOUSMETHOD_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_EMBEDDED_STATEMENT_ON_SAME_LINE/@EntryValue">NEVER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_INITIALIZER_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_WHILE_ON_NEW_LINE/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SIMPLE_EMBEDDED_STATEMENT_STYLE/@EntryValue">LINE_BREAK</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/STICK_COMMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/USE_INDENT_FROM_VS/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARGUMENTS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARRAY_INITIALIZER_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_FIRST_TYPE_PARAMETER_CONSTRAINT/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_CHAINED_METHOD_CALLS/@EntryValue">CHOP_IF_LONG</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_EXTENDS_LIST_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">180</s:Int64>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LINQ_EXPRESSIONS/@EntryValue">CHOP_ALWAYS</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_MULTIPLE_DECLARATION_STYLE/@EntryValue">WRAP_IF_LONG</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_MULTIPLE_TYPE_PARAMEER_CONSTRAINTS_STYLE/@EntryValue">CHOP_ALWAYS</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_OBJECT_AND_COLLECTION_INITIALIZER_STYLE/@EntryValue">CHOP_ALWAYS</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_PARAMETERS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:Boolean x:Key="/Default/CodeStyle/EditorConfig/EnableClangFormatSupport/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/EditorConfig/SyncToVisualStudio/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/Generate/=DisposePattern/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Generate/=DisposePattern/Options/=ChangeFinalize/@EntryIndexedValue">Replace</s:String>
<s:Boolean x:Key="/Default/CodeStyle/Generate/=Implementations/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Generate/=Implementations/Options/=Async/@EntryIndexedValue">True</s:String>
<s:String x:Key="/Default/CodeStyle/Generate/=Implementations/Options/=Mutable/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Generate/=Implementations/Options/=PropertyBody/@EntryIndexedValue">Default body (from options)</s:String>
<s:Boolean x:Key="/Default/CodeStyle/Generate/=Overrides/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Async/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=PropertyBody/@EntryIndexedValue">Default body (from options)</s:String>
<s:Boolean x:Key="/Default/CodeStyle/Naming/CSharpAutoNaming/IsNotificationDisabled/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AWS/@EntryIndexedValue">AWS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FQL/@EntryIndexedValue">FQL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HCP/@EntryIndexedValue">HCP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=QA/@EntryIndexedValue">QA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=QB/@EntryIndexedValue">QB</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XML/@EntryIndexedValue">XML</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Constants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=LocalConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FHTML_005FCONTROL/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FTAG_005FNAME/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FTAG_005FPREFIX/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=NAMESPACE_005FALIAS/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>