mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-13 08:42:57 -07:00
Merge remote-tracking branch 'upstream/develop' into original-language
This commit is contained in:
commit
18742776da
24 changed files with 145 additions and 197 deletions
13
.github/workflows/pr.yml
vendored
13
.github/workflows/pr.yml
vendored
|
@ -28,6 +28,8 @@ jobs:
|
||||||
|
|
||||||
unit-test:
|
unit-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
checks: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-dotnet@v1
|
- uses: actions/setup-dotnet@v1
|
||||||
|
@ -45,7 +47,16 @@ jobs:
|
||||||
- name: Run Unit Tests
|
- name: Run Unit Tests
|
||||||
run: |
|
run: |
|
||||||
cd src
|
cd src
|
||||||
dotnet test --logger trx --results-directory "TestResults"
|
dotnet test --logger "trx;LogFileName=test-results.trx" || true
|
||||||
|
|
||||||
|
- name: Test Report
|
||||||
|
uses: dorny/test-reporter@v1
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: DotNET Tests
|
||||||
|
path: "**/test-results.trx"
|
||||||
|
reporter: dotnet-trx
|
||||||
|
fail-on-error: true
|
||||||
|
|
||||||
analysis:
|
analysis:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
44
CHANGELOG.md
44
CHANGELOG.md
|
@ -1,3 +1,20 @@
|
||||||
|
## [4.16.17](https://github.com/Ombi-app/Ombi/compare/v4.16.16...v4.16.17) (2022-04-25)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.16.16](https://github.com/Ombi-app/Ombi/compare/v4.16.15...v4.16.16) (2022-04-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **4616:** :bug: fixed mandatory fields ([d8f2260](https://github.com/Ombi-app/Ombi/commit/d8f2260c7ae3ed48386743b7adbd06e284487034))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.16.15](https://github.com/Ombi-app/Ombi/compare/v4.16.14...v4.16.15) (2022-04-24)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.16.14](https://github.com/Ombi-app/Ombi/compare/v4.16.13...v4.16.14) (2022-04-19)
|
## [4.16.14](https://github.com/Ombi-app/Ombi/compare/v4.16.13...v4.16.14) (2022-04-19)
|
||||||
|
|
||||||
|
|
||||||
|
@ -321,30 +338,3 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [4.12.0](https://github.com/Ombi-app/Ombi/compare/v4.11.8...v4.12.0) (2022-02-14)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **radarr:** 4K Requests and Radarr 4K support ([ba88848](https://github.com/Ombi-app/Ombi/commit/ba88848866b0a9dedb1e79b55c4d81a0fd453843))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.11.8](https://github.com/Ombi-app/Ombi/compare/v4.11.7...v4.11.8) (2022-02-13)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **settings:** :bug: Fixed an issue where we were not displaying the excluded keyworks correctly in the TheMovieDbSettings page ([d3b3316](https://github.com/Ombi-app/Ombi/commit/d3b3316cbac18356b2f6b0912a3deb2c183e6534))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.11.7](https://github.com/Ombi-app/Ombi/compare/v4.11.6...v4.11.7) (2022-02-12)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **notifications:** :bug: This is a fix for some of the duplicate notification issues [#3825](https://github.com/Ombi-app/Ombi/issues/3825) ([22bb422](https://github.com/Ombi-app/Ombi/commit/22bb4226ead2d62e8c2c2c05be47d7da621402e2))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
23
README.md
23
README.md
|
@ -665,14 +665,21 @@ Here are some of the features Ombi has:
|
||||||
<sub><b>Shoghi</b></sub>
|
<sub><b>Shoghi</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/Teifun2">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/7461832?v=4" width="50;" alt="Teifun2"/>
|
||||||
|
<br />
|
||||||
|
<sub><b>Teifun2</b></sub>
|
||||||
|
</a>
|
||||||
|
</td></tr>
|
||||||
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/thomasvt1">
|
<a href="https://github.com/thomasvt1">
|
||||||
<img src="https://avatars.githubusercontent.com/u/2271011?v=4" width="50;" alt="thomasvt1"/>
|
<img src="https://avatars.githubusercontent.com/u/2271011?v=4" width="50;" alt="thomasvt1"/>
|
||||||
<br />
|
<br />
|
||||||
<sub><b>Thomas Van Tilburg</b></sub>
|
<sub><b>Thomas Van Tilburg</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td></tr>
|
</td>
|
||||||
<tr>
|
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/Tim-Trott">
|
<a href="https://github.com/Tim-Trott">
|
||||||
<img src="https://avatars.githubusercontent.com/u/8249434?v=4" width="50;" alt="Tim-Trott"/>
|
<img src="https://avatars.githubusercontent.com/u/8249434?v=4" width="50;" alt="Tim-Trott"/>
|
||||||
|
@ -707,15 +714,15 @@ Here are some of the features Ombi has:
|
||||||
<br />
|
<br />
|
||||||
<sub><b>Xirg</b></sub>
|
<sub><b>Xirg</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td></tr>
|
||||||
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/bazhip">
|
<a href="https://github.com/bazhip">
|
||||||
<img src="https://avatars.githubusercontent.com/u/10350445?v=4" width="50;" alt="bazhip"/>
|
<img src="https://avatars.githubusercontent.com/u/10350445?v=4" width="50;" alt="bazhip"/>
|
||||||
<br />
|
<br />
|
||||||
<sub><b>Tim OBrien</b></sub>
|
<sub><b>Tim OBrien</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td></tr>
|
</td>
|
||||||
<tr>
|
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/x-limitless-x">
|
<a href="https://github.com/x-limitless-x">
|
||||||
<img src="https://avatars.githubusercontent.com/u/17127926?v=4" width="50;" alt="x-limitless-x"/>
|
<img src="https://avatars.githubusercontent.com/u/17127926?v=4" width="50;" alt="x-limitless-x"/>
|
||||||
|
@ -750,15 +757,15 @@ Here are some of the features Ombi has:
|
||||||
<br />
|
<br />
|
||||||
<sub><b>M4tta</b></sub>
|
<sub><b>M4tta</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td></tr>
|
||||||
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/maartenheebink">
|
<a href="https://github.com/maartenheebink">
|
||||||
<img src="https://avatars.githubusercontent.com/u/28894544?v=4" width="50;" alt="maartenheebink"/>
|
<img src="https://avatars.githubusercontent.com/u/28894544?v=4" width="50;" alt="maartenheebink"/>
|
||||||
<br />
|
<br />
|
||||||
<sub><b>Maartenheebink</b></sub>
|
<sub><b>Maartenheebink</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td></tr>
|
</td>
|
||||||
<tr>
|
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/masterhuck">
|
<a href="https://github.com/masterhuck">
|
||||||
<img src="https://avatars.githubusercontent.com/u/4671442?v=4" width="50;" alt="masterhuck"/>
|
<img src="https://avatars.githubusercontent.com/u/4671442?v=4" width="50;" alt="masterhuck"/>
|
||||||
|
|
|
@ -533,7 +533,10 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
x.ShowSubscribe = true;
|
if (!x.Available && !x.Available4K && (!x.Denied ?? true) && (!x.Denied4K ?? true))
|
||||||
|
{
|
||||||
|
x.ShowSubscribe = true;
|
||||||
|
}
|
||||||
var hasSub = sub.FirstOrDefault(r => r.RequestId == x.Id);
|
var hasSub = sub.FirstOrDefault(r => r.RequestId == x.Id);
|
||||||
x.Subscribed = hasSub != null;
|
x.Subscribed = hasSub != null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -270,7 +270,10 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
x.ShowSubscribe = true;
|
if (!x.Available && (!x.Denied ?? false))
|
||||||
|
{
|
||||||
|
x.ShowSubscribe = true;
|
||||||
|
}
|
||||||
var hasSub = sub.FirstOrDefault(r => r.RequestId == x.Id);
|
var hasSub = sub.FirstOrDefault(r => r.RequestId == x.Id);
|
||||||
x.Subscribed = hasSub != null;
|
x.Subscribed = hasSub != null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -886,7 +886,10 @@ namespace Ombi.Core.Engine
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
x.ShowSubscribe = true;
|
if (!x.Available && (!x.Denied ?? true))
|
||||||
|
{
|
||||||
|
x.ShowSubscribe = true;
|
||||||
|
}
|
||||||
var result = relevantSubs.FirstOrDefault(s => s.RequestId == x.Id);
|
var result = relevantSubs.FirstOrDefault(s => s.RequestId == x.Id);
|
||||||
x.Subscribed = result != null;
|
x.Subscribed = result != null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ namespace Ombi.Core.Senders
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return new SenderResult { Success = false, Sent = false, Message = "Something went wrong!" };
|
return new SenderResult { Success = false, Sent = false };
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SenderResult> SendToLidarr(AlbumRequest model, LidarrSettings settings)
|
private async Task<SenderResult> SendToLidarr(AlbumRequest model, LidarrSettings settings)
|
||||||
|
|
|
@ -133,8 +133,7 @@ namespace Ombi.Core.Senders
|
||||||
|
|
||||||
return new SenderResult
|
return new SenderResult
|
||||||
{
|
{
|
||||||
Success = false,
|
Success = false
|
||||||
Message = "Something went wrong!"
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,8 +342,6 @@ namespace Ombi.Core.Senders
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
var seriesChanges = false;
|
|
||||||
|
|
||||||
foreach (var season in model.SeasonRequests)
|
foreach (var season in model.SeasonRequests)
|
||||||
{
|
{
|
||||||
foreach (var ep in season.Episodes)
|
foreach (var ep in season.Episodes)
|
||||||
|
@ -359,72 +356,36 @@ namespace Ombi.Core.Senders
|
||||||
}
|
}
|
||||||
|
|
||||||
existingSeason = result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber);
|
existingSeason = result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber);
|
||||||
var sonarrEpisodeList = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber).ToList();
|
|
||||||
var sonarrEpCount = sonarrEpisodeList.Count;
|
|
||||||
var ourRequestCount = season.Episodes.Count;
|
|
||||||
|
|
||||||
var ourEpisodes = season.Episodes.Select(x => x.EpisodeNumber).ToList();
|
|
||||||
var unairedEpisodes = sonarrEpisodeList.Where(x => x.airDateUtc > DateTime.UtcNow).Select(x => x.episodeNumber).ToList();
|
|
||||||
|
|
||||||
//// Check if we have requested all the latest episodes, if we have then monitor
|
|
||||||
//// NOTE, not sure if needed since ombi ui displays future episodes anyway...
|
|
||||||
//ourEpisodes.AddRange(unairedEpisodes);
|
|
||||||
//var distinctEpisodes = ourEpisodes.Distinct().ToList();
|
|
||||||
//var missingEpisodes = Enumerable.Range(distinctEpisodes.Min(), distinctEpisodes.Count).Except(distinctEpisodes);
|
|
||||||
|
|
||||||
|
|
||||||
if (sonarrEpCount == ourRequestCount /*|| !missingEpisodes.Any()*/)
|
// Make sure this season is set to monitored
|
||||||
|
if (!existingSeason.monitored)
|
||||||
{
|
{
|
||||||
// We have the same amount of requests as all of the episodes in the season.
|
// We need to monitor it, problem being is all episodes will now be monitored
|
||||||
|
// So we need to monitor the series but unmonitor every episode
|
||||||
existingSeason.monitored = true;
|
existingSeason.monitored = true;
|
||||||
seriesChanges = true;
|
var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber);
|
||||||
|
sea.monitored = true;
|
||||||
|
|
||||||
// We do not need to update the episodes as marking the season as monitored will mark the episodes as monitored.
|
result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri);
|
||||||
var seasonToUpdate = result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber);
|
var epToUnmonitored = new List<Episode>();
|
||||||
seasonToUpdate.monitored = true; // Update by ref
|
var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the original member
|
||||||
}
|
foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList())
|
||||||
else
|
|
||||||
{
|
|
||||||
// Make sure this season is set to monitored
|
|
||||||
if (!existingSeason.monitored)
|
|
||||||
{
|
{
|
||||||
// We need to monitor it, problem being is all episodes will now be monitored
|
ep.monitored = false;
|
||||||
// So we need to monitor the series but unmonitor every episode
|
epToUnmonitored.Add(ep);
|
||||||
// Except the episodes that are already monitored before we update the series (we do not want to unmonitored episodes that are monitored beforehand)
|
|
||||||
existingSeason.monitored = true;
|
|
||||||
var sea = result.seasons.FirstOrDefault(x => x.seasonNumber == existingSeason.seasonNumber);
|
|
||||||
sea.monitored = true;
|
|
||||||
//var previouslyMonitoredEpisodes = sonarrEpList.Where(x =>
|
|
||||||
// x.seasonNumber == existingSeason.seasonNumber && x.monitored).Select(x => x.episodeNumber).ToList(); // We probably don't actually care about this
|
|
||||||
result = await SonarrApi.UpdateSeries(result, s.ApiKey, s.FullUri);
|
|
||||||
var epToUnmonitored = new List<Episode>();
|
|
||||||
var newEpList = sonarrEpList.ConvertAll(ep => new Episode(ep)); // Clone it so we don't modify the original member
|
|
||||||
foreach (var ep in newEpList.Where(x => x.seasonNumber == existingSeason.seasonNumber).ToList())
|
|
||||||
{
|
|
||||||
//if (previouslyMonitoredEpisodes.Contains(ep.episodeNumber))
|
|
||||||
//{
|
|
||||||
// // This was previously monitored.
|
|
||||||
// continue;
|
|
||||||
//}
|
|
||||||
ep.monitored = false;
|
|
||||||
epToUnmonitored.Add(ep);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var epToUpdate in epToUnmonitored)
|
|
||||||
{
|
|
||||||
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Now update the episodes that need updating
|
|
||||||
foreach (var epToUpdate in episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber))
|
foreach (var epToUpdate in epToUnmonitored)
|
||||||
{
|
{
|
||||||
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
|
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// Now update the episodes that need updating
|
||||||
if (seriesChanges)
|
foreach (var epToUpdate in episodesToUpdate.Where(x => x.seasonNumber == season.SeasonNumber))
|
||||||
{
|
{
|
||||||
await SonarrApi.SeasonPass(s.ApiKey, s.FullUri, result);
|
await SonarrApi.UpdateEpisode(epToUpdate, s.ApiKey, s.FullUri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!s.AddOnly)
|
if (!s.AddOnly)
|
||||||
|
|
|
@ -212,7 +212,7 @@ namespace Ombi.Store.Context
|
||||||
notificationToAdd = new NotificationTemplates
|
notificationToAdd = new NotificationTemplates
|
||||||
{
|
{
|
||||||
NotificationType = notificationType,
|
NotificationType = notificationType,
|
||||||
Message = "Your TV request is now partially available! Season {PartiallyAvailableSeasonNumber} Episodes {PartiallyAvailableEpisodeNumbers}!",
|
Message = "Your TV request for {Title} is now partially available! Season {PartiallyAvailableSeasonNumber} Episodes {PartiallyAvailableEpisodeNumbers}!",
|
||||||
Subject = "{ApplicationName}: Partially Available Request!",
|
Subject = "{ApplicationName}: Partially Available Request!",
|
||||||
Agent = agent,
|
Agent = agent,
|
||||||
Enabled = true,
|
Enabled = true,
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<ng-container matColumnDef="actions">
|
<ng-container matColumnDef="actions">
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
<th mat-header-cell *matHeaderCellDef> </th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
<button mat-raised-button color="accent" [routerLink]="[element.providerId]">{{ 'Issues.Details' | translate}}</button>
|
<a mat-raised-button color="accent" [routerLink]="[element.providerId]">{{ 'Issues.Details' | translate}}</a>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ export class TvRequestGridComponent {
|
||||||
// Make sure something has been selected
|
// Make sure something has been selected
|
||||||
const selected = this.selection.hasValue();
|
const selected = this.selection.hasValue();
|
||||||
if (!selected && !this.tv.requestAll && !this.tv.firstSeason && !this.tv.latestSeason) {
|
if (!selected && !this.tv.requestAll && !this.tv.firstSeason && !this.tv.latestSeason) {
|
||||||
this.notificationService.send("You need to select some episodes!", "OK");
|
this.notificationService.send(this.translate.instant("Requests.NeedToSelectEpisodes"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,70 +80,6 @@ export class TvRequestGridComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async approve(request: IChildRequests) {
|
|
||||||
const result = await this.requestService.approveChild({
|
|
||||||
id: request.id
|
|
||||||
}).toPromise();
|
|
||||||
|
|
||||||
if (result.result) {
|
|
||||||
request.approved = true;
|
|
||||||
request.denied = false;
|
|
||||||
request.seasonRequests.forEach((season) => {
|
|
||||||
season.episodes.forEach((ep) => {
|
|
||||||
ep.approved = true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.notificationService.send("Request has been approved", "Ok");
|
|
||||||
} else {
|
|
||||||
this.notificationService.send(result.errorMessage, "Ok");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public changeAvailability(request: IChildRequests, available: boolean) {
|
|
||||||
request.available = available;
|
|
||||||
request.seasonRequests.forEach((season) => {
|
|
||||||
season.episodes.forEach((ep) => {
|
|
||||||
ep.available = available;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (available) {
|
|
||||||
this.requestService.markTvAvailable({ id: request.id }).subscribe(x => {
|
|
||||||
if (x.result) {
|
|
||||||
this.notificationService.send(
|
|
||||||
`This request is now available`);
|
|
||||||
} else {
|
|
||||||
this.notificationService.send("Request Available", x.message ? x.message : x.errorMessage);
|
|
||||||
request.approved = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.requestService.markTvUnavailable({ id: request.id }).subscribe(x => {
|
|
||||||
if (x.result) {
|
|
||||||
this.notificationService.send(
|
|
||||||
`This request is now unavailable`);
|
|
||||||
} else {
|
|
||||||
this.notificationService.send("Request Available", x.message ? x.message : x.errorMessage);
|
|
||||||
request.approved = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public async deny(request: IChildRequests) {
|
|
||||||
const dialogRef = this.dialog.open(DenyDialogComponent, {
|
|
||||||
width: '250px',
|
|
||||||
data: {requestId: request.id, requestType: RequestType.tvShow}
|
|
||||||
});
|
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
|
||||||
request.denied = true;
|
|
||||||
request.seasonRequests.forEach((season) => {
|
|
||||||
season.episodes.forEach((ep) => {
|
|
||||||
ep.approved = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async requestAllSeasons() {
|
public async requestAllSeasons() {
|
||||||
this.tv.requestAll = true;
|
this.tv.requestAll = true;
|
||||||
await this.submitRequests();
|
await this.submitRequests();
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { MatDialog } from "@angular/material/dialog";
|
||||||
import { MessageService } from "../../../../../services";
|
import { MessageService } from "../../../../../services";
|
||||||
import { RequestService } from "../../../../../services/request.service";
|
import { RequestService } from "../../../../../services/request.service";
|
||||||
import { RequestServiceV2 } from "../../../../../services/requestV2.service";
|
import { RequestServiceV2 } from "../../../../../services/requestV2.service";
|
||||||
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./tv-requests-panel.component.html",
|
templateUrl: "./tv-requests-panel.component.html",
|
||||||
|
@ -24,7 +25,8 @@ export class TvRequestsPanelComponent {
|
||||||
constructor(private requestService: RequestService,
|
constructor(private requestService: RequestService,
|
||||||
private requestService2: RequestServiceV2,
|
private requestService2: RequestServiceV2,
|
||||||
private messageService: MessageService,
|
private messageService: MessageService,
|
||||||
public dialog: MatDialog) {
|
public dialog: MatDialog,
|
||||||
|
private translateService: TranslateService) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +43,7 @@ export class TvRequestsPanelComponent {
|
||||||
ep.approved = true;
|
ep.approved = true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.messageService.send("Request has been approved", "Ok");
|
this.messageService.send(this.translateService.instant("Requests.SuccessfullyApproved"));
|
||||||
} else {
|
} else {
|
||||||
this.messageService.sendRequestEngineResultError(result);
|
this.messageService.sendRequestEngineResultError(result);
|
||||||
}
|
}
|
||||||
|
@ -52,7 +54,7 @@ export class TvRequestsPanelComponent {
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
this.tvRequest.splice(this.tvRequest.indexOf(request),1);
|
this.tvRequest.splice(this.tvRequest.indexOf(request),1);
|
||||||
this.messageService.send("Request has been Deleted", "Ok");
|
this.messageService.send(this.translateService.instant("Requests.SuccessfullyDeleted"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,9 +69,9 @@ export class TvRequestsPanelComponent {
|
||||||
this.requestService.markTvAvailable({ id: request.id }).subscribe(x => {
|
this.requestService.markTvAvailable({ id: request.id }).subscribe(x => {
|
||||||
if (x.result) {
|
if (x.result) {
|
||||||
this.messageService.send(
|
this.messageService.send(
|
||||||
`This request is now available`);
|
this.translateService.instant("Requests.NowAvailable"));
|
||||||
} else {
|
} else {
|
||||||
this.messageService.send("Request Available", x.message ? x.message : x.errorMessage);
|
this.messageService.sendRequestEngineResultError(x);
|
||||||
request.approved = false;
|
request.approved = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -77,9 +79,9 @@ export class TvRequestsPanelComponent {
|
||||||
this.requestService.markTvUnavailable({ id: request.id }).subscribe(x => {
|
this.requestService.markTvUnavailable({ id: request.id }).subscribe(x => {
|
||||||
if (x.result) {
|
if (x.result) {
|
||||||
this.messageService.send(
|
this.messageService.send(
|
||||||
`This request is now unavailable`);
|
this.translateService.instant("Requests.NowUnavailable"));
|
||||||
} else {
|
} else {
|
||||||
this.messageService.send("Request Available", x.message ? x.message : x.errorMessage);
|
this.messageService.sendRequestEngineResultError(x);
|
||||||
request.approved = false;
|
request.approved = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -102,11 +104,11 @@ export class TvRequestsPanelComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
public reProcessRequest(request: IChildRequests) {
|
public reProcessRequest(request: IChildRequests) {
|
||||||
this.requestService2.reprocessRequest(request.id, RequestType.tvShow, false).subscribe(result => {
|
this.requestService2.reprocessRequest(request.id, RequestType.tvShow, false).subscribe(x => {
|
||||||
if (result.result) {
|
if (x.result) {
|
||||||
this.messageService.send(result.message ? result.message : "Successfully Re-processed the request", "Ok");
|
this.messageService.send(this.translateService.instant("Requests.SuccessfullyReprocessed"));
|
||||||
} else {
|
} else {
|
||||||
this.messageService.sendRequestEngineResultError(result);
|
this.messageService.sendRequestEngineResultError(x);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
<ng-container matColumnDef="actions">
|
<ng-container matColumnDef="actions">
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
<th mat-header-cell *matHeaderCellDef> </th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
<button mat-raised-button color="accent" [routerLink]="'/details/artist/' + element.foreignArtistId">{{ 'Requests.Details' | translate}}</button>
|
<a mat-raised-button color="accent" [routerLink]="'/details/artist/' + element.foreignArtistId">{{ 'Requests.Details' | translate}}</a>
|
||||||
<button mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || ( manageOwnRequests && element.requestedUser?.userName == userName )"> {{ 'Requests.Options' | translate}}</button>
|
<button mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || ( manageOwnRequests && element.requestedUser?.userName == userName )"> {{ 'Requests.Options' | translate}}</button>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
|
@ -83,8 +83,8 @@
|
||||||
<ng-container matColumnDef="actions">
|
<ng-container matColumnDef="actions">
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
<th mat-header-cell *matHeaderCellDef> </th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
<button id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">{{ 'Requests.Details' | translate}}</button>
|
<a id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">{{ 'Requests.Details' | translate}}</a>
|
||||||
<button id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || ( manageOwnRequests && element.requestedUser?.userName == userName ) "> {{ 'Requests.Options' | translate}}</button>
|
<a id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || ( manageOwnRequests && element.requestedUser?.userName == userName ) "> {{ 'Requests.Options' | translate}}</a>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -103,4 +103,4 @@
|
||||||
<button id="denyFabButton" mat-menu-item (click)="bulkDeny()">{{'Requests.RequestPanel.Deny' | translate}}</button>
|
<button id="denyFabButton" mat-menu-item (click)="bulkDeny()">{{'Requests.RequestPanel.Deny' | translate}}</button>
|
||||||
<button *ngIf="is4kEnabled" id="deny4kFabButton" mat-menu-item (click)="bulkDeny4K()">{{'Requests.RequestPanel.Deny4K' | translate}}</button>
|
<button *ngIf="is4kEnabled" id="deny4kFabButton" mat-menu-item (click)="bulkDeny4K()">{{'Requests.RequestPanel.Deny4K' | translate}}</button>
|
||||||
<button id="deleteFabButton" mat-menu-item (click)="bulkDelete()">{{'Requests.RequestPanel.Delete' | translate}}</button>
|
<button id="deleteFabButton" mat-menu-item (click)="bulkDelete()">{{'Requests.RequestPanel.Delete' | translate}}</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
<ng-container matColumnDef="actions">
|
<ng-container matColumnDef="actions">
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
<th mat-header-cell *matHeaderCellDef> </th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
<button id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/tv/' + element.parentRequest.externalProviderId">{{'Requests.Details' | translate}}</button>
|
<a id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/tv/' + element.parentRequest.externalProviderId">{{'Requests.Details' | translate}}</a>
|
||||||
<button id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin">{{'Requests.Options' | translate}}</button>
|
<button id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin">{{'Requests.Options' | translate}}</button>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
|
@ -22,11 +22,15 @@ export class MessageService {
|
||||||
}
|
}
|
||||||
public sendRequestEngineResultError(result: IRequestEngineResult, action: string = "Ok") {
|
public sendRequestEngineResultError(result: IRequestEngineResult, action: string = "Ok") {
|
||||||
const textKey = 'Requests.ErrorCodes.' + result.errorCode;
|
const textKey = 'Requests.ErrorCodes.' + result.errorCode;
|
||||||
const text = this.translate.instant(textKey);
|
var text = this.translate.instant(textKey);
|
||||||
if (text !== textKey) {
|
if (text === textKey) { // Error code on backend may not exist in frontend
|
||||||
this.send(text, action);
|
if (result.errorMessage || result.message) {
|
||||||
} else {
|
text = result.errorMessage ? result.errorMessage : result.message;
|
||||||
this.send(result.errorMessage ? result.errorMessage : result.message, action);
|
} else {
|
||||||
|
text = this.translate.instant('ErrorPages.SomethingWentWrong');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.send(text, action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { TesterService, NotificationService, RadarrService } from "../../../serv
|
||||||
selector: "ombi-settings-radarr-form",
|
selector: "ombi-settings-radarr-form",
|
||||||
templateUrl: "./radarr-form.component.html",
|
templateUrl: "./radarr-form.component.html",
|
||||||
styleUrls: ["./radarr-form.component.scss"],
|
styleUrls: ["./radarr-form.component.scss"],
|
||||||
// changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class RadarrFormComponent implements OnInit {
|
export class RadarrFormComponent implements OnInit {
|
||||||
|
|
||||||
|
@ -23,11 +23,11 @@ export class RadarrFormComponent implements OnInit {
|
||||||
constructor(private radarrService: RadarrService,
|
constructor(private radarrService: RadarrService,
|
||||||
private notificationService: NotificationService,
|
private notificationService: NotificationService,
|
||||||
private testerService: TesterService,
|
private testerService: TesterService,
|
||||||
private controlContainer: ControlContainer) { }
|
private controlContainer: ControlContainer) {
|
||||||
|
}
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.form = <FormGroup>this.controlContainer.control;
|
this.form = <FormGroup>this.controlContainer.control;
|
||||||
// this.toggleValidators();
|
|
||||||
|
|
||||||
this.qualities = [];
|
this.qualities = [];
|
||||||
this.qualities.push({ name: "Please Select", id: -1 });
|
this.qualities.push({ name: "Please Select", id: -1 });
|
||||||
|
|
|
@ -8,11 +8,13 @@
|
||||||
<mat-tab-group dynamicHeight>
|
<mat-tab-group dynamicHeight>
|
||||||
<mat-tab label="Radarr">
|
<mat-tab label="Radarr">
|
||||||
<ombi-settings-radarr-form
|
<ombi-settings-radarr-form
|
||||||
|
#normalForm
|
||||||
formGroupName="radarr">
|
formGroupName="radarr">
|
||||||
</ombi-settings-radarr-form>
|
</ombi-settings-radarr-form>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
<mat-tab *ngIf="is4kEnabled" label="Radarr 4K">
|
<mat-tab *ngIf="is4kEnabled" label="Radarr 4K">
|
||||||
<ombi-settings-radarr-form
|
<ombi-settings-radarr-form
|
||||||
|
#4kForm
|
||||||
formGroupName="radarr4K">
|
formGroupName="radarr4K">
|
||||||
</ombi-settings-radarr-form>
|
</ombi-settings-radarr-form>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit, QueryList, ViewChild, ViewChildren } from "@angular/core";
|
||||||
import { FormBuilder, FormGroup } from "@angular/forms";
|
import { FormBuilder, FormGroup } from "@angular/forms";
|
||||||
|
|
||||||
import { IMinimumAvailability, IRadarrCombined, IRadarrProfile, IRadarrRootFolder } from "../../interfaces";
|
import { IMinimumAvailability, IRadarrCombined, IRadarrProfile, IRadarrRootFolder } from "../../interfaces";
|
||||||
import { NotificationService, SettingsService } from "../../services";
|
import { NotificationService, SettingsService } from "../../services";
|
||||||
import { FeaturesFacade } from "../../state/features/features.facade";
|
import { FeaturesFacade } from "../../state/features/features.facade";
|
||||||
|
import { RadarrFormComponent } from "./components/radarr-form.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: "./radarr.component.html",
|
templateUrl: "./radarr.component.html",
|
||||||
|
@ -19,11 +20,15 @@ export class RadarrComponent implements OnInit {
|
||||||
public form: FormGroup;
|
public form: FormGroup;
|
||||||
public is4kEnabled: boolean = false;
|
public is4kEnabled: boolean = false;
|
||||||
|
|
||||||
|
@ViewChildren('4kForm') public form4k: QueryList<RadarrFormComponent>;
|
||||||
|
@ViewChildren('normalForm') public normalForm: QueryList<RadarrFormComponent>;
|
||||||
|
|
||||||
constructor(private settingsService: SettingsService,
|
constructor(private settingsService: SettingsService,
|
||||||
private notificationService: NotificationService,
|
private notificationService: NotificationService,
|
||||||
private featureFacade: FeaturesFacade,
|
private featureFacade: FeaturesFacade,
|
||||||
private fb: FormBuilder) { }
|
private fb: FormBuilder) { }
|
||||||
|
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.is4kEnabled = this.featureFacade.is4kEnabled();
|
this.is4kEnabled = this.featureFacade.is4kEnabled();
|
||||||
this.settingsService.getRadarr()
|
this.settingsService.getRadarr()
|
||||||
|
@ -56,7 +61,16 @@ export class RadarrComponent implements OnInit {
|
||||||
scanForAvailability: [x.radarr4K.scanForAvailability]
|
scanForAvailability: [x.radarr4K.scanForAvailability]
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
this.normalForm.changes.forEach((comp => {
|
||||||
|
comp.first.toggleValidators();
|
||||||
|
}))
|
||||||
|
if (this.is4kEnabled) {
|
||||||
|
this.form4k.changes.forEach((comp => {
|
||||||
|
comp.first.toggleValidators();
|
||||||
|
}))
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
|
|
||||||
|
|
||||||
<button type="button" mat-raised-button color="primary" data-test="adduserbtn" [routerLink]="['/usermanagement/user']">Add User To Ombi</button>
|
<a type="button" mat-raised-button color="primary" data-test="adduserbtn" [routerLink]="['/usermanagement/user']">Add User To Ombi</a>
|
||||||
|
|
||||||
<button type="button" style="float:right;" mat-raised-button color="primary" (click)="showBulkEdit = !showBulkEdit" [disabled]="this.selection.selected.length <= 0">Bulk Edit</button>
|
<button type="button" style="float:right;" mat-raised-button color="primary" (click)="showBulkEdit = !showBulkEdit" [disabled]="this.selection.selected.length <= 0">Bulk Edit</button>
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@
|
||||||
<ng-container matColumnDef="actions">
|
<ng-container matColumnDef="actions">
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
<th mat-header-cell *matHeaderCellDef> </th>
|
||||||
<td mat-cell *matCellDef="let u">
|
<td mat-cell *matCellDef="let u">
|
||||||
<button id="edit{{u.userName}}" mat-raised-button color="accent" [routerLink]="['/usermanagement/user/' + u.id]">Edit</button>
|
<a id="edit{{u.userName}}" mat-raised-button color="accent" [routerLink]="['/usermanagement/user/' + u.id]">Edit</a>
|
||||||
<button *ngIf="!u.hasLoggedIn" mat-raised-button color="accent" (click)="welcomeEmail(u)" [disabled]="!applicationUrl"><i class="far fa-paper-plane"></i> Welcome</button>
|
<button *ngIf="!u.hasLoggedIn" mat-raised-button color="accent" (click)="welcomeEmail(u)" [disabled]="!applicationUrl"><i class="far fa-paper-plane"></i> Welcome</button>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
|
@ -142,6 +142,10 @@
|
||||||
background-color: $ombi-active;
|
background-color: $ombi-active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.mat-raised-button {
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
border-top: 1px solid $accent-dark;
|
border-top: 1px solid $accent-dark;
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,11 +282,18 @@ namespace Ombi.Controllers.V1.External
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var currentUser = await GetCurrentUserAsync();
|
||||||
|
|
||||||
|
if (!currentUser.Email.HasValue())
|
||||||
|
{
|
||||||
|
throw new Exception($"User '{currentUser.UserName}' has no email address set on their user profile.");
|
||||||
|
}
|
||||||
|
|
||||||
var message = new NotificationMessage
|
var message = new NotificationMessage
|
||||||
{
|
{
|
||||||
Message = "This is just a test! Success!",
|
Message = "This is just a test! Success!",
|
||||||
Subject = $"Ombi: Test",
|
Subject = $"Ombi: Test",
|
||||||
To = (await GetCurrentUserAsync()).Email,
|
To = currentUser.Email,
|
||||||
};
|
};
|
||||||
|
|
||||||
message.Other.Add("PlainTextBody", "This is just a test! Success!");
|
message.Other.Add("PlainTextBody", "This is just a test! Success!");
|
||||||
|
@ -539,7 +546,7 @@ namespace Ombi.Controllers.V1.External
|
||||||
{
|
{
|
||||||
|
|
||||||
var user = await UserManager.Users.Include(x => x.UserNotificationPreferences).FirstOrDefaultAsync(x => x.UserName == HttpContext.User.Identity.Name);
|
var user = await UserManager.Users.Include(x => x.UserNotificationPreferences).FirstOrDefaultAsync(x => x.UserName == HttpContext.User.Identity.Name);
|
||||||
|
|
||||||
|
|
||||||
var status = await WhatsAppApi.SendMessage(new WhatsAppModel {
|
var status = await WhatsAppApi.SendMessage(new WhatsAppModel {
|
||||||
From = settings.From,
|
From = settings.From,
|
||||||
|
|
|
@ -60,7 +60,8 @@
|
||||||
"CheckPageForUpdates": "Check this page for continuous site updates."
|
"CheckPageForUpdates": "Check this page for continuous site updates."
|
||||||
},
|
},
|
||||||
"ErrorPages": {
|
"ErrorPages": {
|
||||||
"NotFound": "Page not found"
|
"NotFound": "Page not found",
|
||||||
|
"SomethingWentWrong": "Something went wrong!"
|
||||||
},
|
},
|
||||||
"NavigationBar": {
|
"NavigationBar": {
|
||||||
"Discover": "Discover",
|
"Discover": "Discover",
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"version": "4.16.14"
|
"version": "4.16.17"
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue