diff --git a/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs index c1ad70613..4012e2ffc 100644 --- a/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs +++ b/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs @@ -9,6 +9,7 @@ namespace Ombi.Core.Engine.Interfaces { public interface IMovieEngineV2 { + Task GetMovieInfoByRequestId(int requestId, CancellationToken cancellationToken, string langCode = null); Task GetFullMovieInformation(int theMovieDbId, CancellationToken cancellationToken, string langCode = null); Task> SimilarMovies(int theMovieDbId, string langCode); Task> PopularMovies(); diff --git a/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs index f318290d8..f9889ec9c 100644 --- a/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs @@ -7,6 +7,7 @@ namespace Ombi.Core.Engine.Interfaces public interface IMusicSearchEngineV2 { Task GetArtistInformation(string artistId); + Task GetArtistInformationByRequestId(int requestId); Task GetReleaseGroupArt(string musicBrainzId, CancellationToken token); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs index 0a18a32bf..a8a27aa19 100644 --- a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs @@ -6,5 +6,6 @@ namespace Ombi.Core public interface ITVSearchEngineV2 { Task GetShowInformation(int tvdbid); + Task GetShowByRequest(int requestId); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs index f33a7f3a6..796e0d194 100644 --- a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs @@ -48,6 +48,16 @@ namespace Ombi.Core.Engine.V2 return await ProcessSingleMovie(movieInfo); } + public async Task GetMovieInfoByRequestId(int requestId, CancellationToken cancellationToken, string langCode = null) + { + langCode = await DefaultLanguageCode(langCode); + var request = await RequestService.MovieRequestService.Find(requestId); + var movieInfo = await Cache.GetOrAdd(nameof(GetFullMovieInformation) + request.TheMovieDbId + langCode, + async () => await MovieApi.GetFullMovieInfo(request.TheMovieDbId, cancellationToken, langCode), DateTime.Now.AddHours(12), cancellationToken); + + return await ProcessSingleMovie(movieInfo); + } + public async Task GetCollection(int collectionId, CancellationToken cancellationToken, string langCode = null) { langCode = await DefaultLanguageCode(langCode); diff --git a/src/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs index dd132a460..fd885c914 100644 --- a/src/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs @@ -118,6 +118,12 @@ namespace Ombi.Core.Engine.V2 return new AlbumArt(); } + + public async Task GetArtistInformationByRequestId(int requestId) + { + var request = await RequestService.MusicRequestRepository.Find(requestId); + return await GetArtistInformation(request.ForeignArtistId); + } private List GetBandMembers(Artist artist) { diff --git a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs index aa8bcc9b3..5af022a8d 100644 --- a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs @@ -21,6 +21,7 @@ using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Store.Repository; using TraktSharp.Entities; +using Microsoft.EntityFrameworkCore; namespace Ombi.Core.Engine.V2 { @@ -48,7 +49,12 @@ namespace Ombi.Core.Engine.V2 private IEmbyContentRepository EmbyContentRepo { get; } private ITraktApi TraktApi { get; } - + public async Task GetShowByRequest(int requestId) + { + var request = await RequestService.TvRequestService.GetChild().Include(x => x.ParentRequest).FirstOrDefaultAsync(x => x.Id == requestId); + return await GetShowInformation(request.ParentRequest.TvDbId); + } + public async Task GetShowInformation(int tvdbid) { var tvdbshow = await Cache.GetOrAdd(nameof(GetShowInformation) + tvdbid, @@ -60,11 +66,11 @@ namespace Ombi.Core.Engine.V2 var show = await Cache.GetOrAdd("GetTvFullInformation" + tvdbshow.id, async () => await TvMazeApi.GetTvFullInformation(tvdbshow.id), DateTime.Now.AddHours(12)); if (show == null) - { + { // We don't have enough information return null; } - + // Setup the task so we can get the data later on if we have a IMDBID Task traktInfoTask = new Task(() => null); if (show.externals?.imdb.HasValue() ?? false) @@ -147,7 +153,7 @@ namespace Ombi.Core.Engine.V2 private async Task GetExtraInfo(Task showInfoTask, SearchFullInfoTvShowViewModel model) { var result = await showInfoTask; - if(result == null) + if (result == null) { return model; } diff --git a/src/Ombi/ClientApp/src/app/app.component.html b/src/Ombi/ClientApp/src/app/app.component.html index e356c06d2..9cd684ec1 100644 --- a/src/Ombi/ClientApp/src/app/app.component.html +++ b/src/Ombi/ClientApp/src/app/app.component.html @@ -1,5 +1,4 @@ - - - - -
-
-
{{element.requestId}}
-
-
- -
+ + +
+
+
{{element.requestId}}
+
+
+ +
- - - - - \ No newline at end of file + + + + + --> \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/issues/components/issues-list/issues-list.component.ts b/src/Ombi/ClientApp/src/app/issues/components/issues-list/issues-list.component.ts index 5528401ff..82883a4f2 100644 --- a/src/Ombi/ClientApp/src/app/issues/components/issues-list/issues-list.component.ts +++ b/src/Ombi/ClientApp/src/app/issues/components/issues-list/issues-list.component.ts @@ -6,6 +6,7 @@ import { IIssueCount, IIssues, IPagenator, IssueStatus } from "../../../interfac import { COLUMNS } from "./issues-list.constants"; @Component({ + selector: "issues-list", templateUrl: "issues-list.component.html", }) export class IssuesListComponent implements OnInit { diff --git a/src/Ombi/ClientApp/src/app/issues/issues.component.html b/src/Ombi/ClientApp/src/app/issues/issues.component.html index 242427b8b..ff8440569 100644 --- a/src/Ombi/ClientApp/src/app/issues/issues.component.html +++ b/src/Ombi/ClientApp/src/app/issues/issues.component.html @@ -1,28 +1,25 @@ -

- - - - {{'Issues.PendingTitle' | translate}} {{count.pending}} - -
- -
-
-
- - {{'Issues.InProgressTitle' | translate}} {{count.inProgress}} - -
- -
-
-
- - {{'Issues.ResolvedTitle' | translate}} {{count.resolved}} - -
- -
-
-
-
\ No newline at end of file +
+ + + +
+ +
+
+
+ + +
+ +
+
+
+ + +
+ +
+
+
+
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/issues/issues.component.scss b/src/Ombi/ClientApp/src/app/issues/issues.component.scss new file mode 100644 index 000000000..0de21e45d --- /dev/null +++ b/src/Ombi/ClientApp/src/app/issues/issues.component.scss @@ -0,0 +1,4 @@ +.small-middle-container { + margin: auto; + width: 95%; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/issues/issues.component.ts b/src/Ombi/ClientApp/src/app/issues/issues.component.ts index 55e4fe9be..c97eb4eff 100644 --- a/src/Ombi/ClientApp/src/app/issues/issues.component.ts +++ b/src/Ombi/ClientApp/src/app/issues/issues.component.ts @@ -6,6 +6,7 @@ import { IIssueCount, IIssues, IPagenator, IssueStatus } from "../interfaces"; @Component({ templateUrl: "issues.component.html", + styleUrls: ['issues.component.scss'] }) export class IssuesComponent implements OnInit { diff --git a/src/Ombi/ClientApp/src/app/issues/issues.module.ts b/src/Ombi/ClientApp/src/app/issues/issues.module.ts index cc7240553..cf357d269 100644 --- a/src/Ombi/ClientApp/src/app/issues/issues.module.ts +++ b/src/Ombi/ClientApp/src/app/issues/issues.module.ts @@ -21,7 +21,7 @@ import { IssuesListComponent } from "./components/issues-list/issues-list.compon import * as fromComponents from "./components"; const routes: Routes = [ - { path: "", component: IssuesListComponent, canActivate: [AuthGuard] }, + { path: "", component: IssuesComponent, canActivate: [AuthGuard] }, { path: ":id", component: IssueDetailsComponent, canActivate: [AuthGuard] }, ]; diff --git a/src/Ombi/ClientApp/src/app/issues/issuestable.component.html b/src/Ombi/ClientApp/src/app/issues/issuestable.component.html index 83dc8a1db..d48b267e3 100644 --- a/src/Ombi/ClientApp/src/app/issues/issuestable.component.html +++ b/src/Ombi/ClientApp/src/app/issues/issuestable.component.html @@ -1,4 +1,68 @@ - +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ 'Issues.ColumnTitle' | translate}} {{element.title}} {{ 'Issues.Category' | translate}} {{element.issueCategory.value}} {{ 'Issues.Subject' | translate}} {{element.subject}} {{ 'Issues.Status' | translate}} {{IssueStatus[element.status] | humanize}} {{ 'Issues.ReportedBy' | translate}} {{element.userReported.userAlias}} + + + + +
+ + \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/issues/issuestable.component.ts b/src/Ombi/ClientApp/src/app/issues/issuestable.component.ts index aadfd546a..d1c656662 100644 --- a/src/Ombi/ClientApp/src/app/issues/issuestable.component.ts +++ b/src/Ombi/ClientApp/src/app/issues/issuestable.component.ts @@ -13,6 +13,7 @@ export class IssuesTableComponent { @Output() public changePage = new EventEmitter(); + public displayedColumns = ["title", "category", "subject", "status", "reportedBy", "actions"] public IssueStatus = IssueStatus; public order: string = "id"; diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts index 20dae078e..1d2600550 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts @@ -30,14 +30,36 @@ export class MovieDetailsComponent { public dialog: MatDialog, private requestService: RequestService, public messageService: MessageService, private auth: AuthService) { this.route.params.subscribe((params: any) => { - this.theMovidDbId = params.movieDbId; - this.load(); + + this.theMovidDbId = params.movieDbId; + if (params.requestId) { + this.load(+params.requestId); + } else { + this.load(undefined); + } + }); } - public load() { + public async load(requestId: number|undefined) { this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + + if (requestId) { + var result = await this.searchService.getFullMovieDetailsByRequestId(requestId); + this.theMovidDbId = result.id + + this.movie = result; + if (this.movie.requestId > 0) { + // Load up this request + this.hasRequest = true; + this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId); + } + this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe(x => { + this.movie.background = this.sanitizer.bypassSecurityTrustStyle + ("url(" + x + ")"); + }); + } else { this.searchService.getFullMovieDetails(this.theMovidDbId).subscribe(async x => { this.movie = x; if (this.movie.requestId > 0) { @@ -50,7 +72,7 @@ export class MovieDetailsComponent { ("url(" + x + ")"); }); }); - + } } public async request() { @@ -87,7 +109,7 @@ export class MovieDetailsComponent { public async issue() { const dialogRef = this.dialog.open(NewIssueComponent, { width: '500px', - data: {requestId: this.movieRequest ? this.movieRequest.id : null, requestType: RequestType.movie, imdbid: this.movie.imdbId} + data: {requestId: this.movieRequest ? this.movieRequest.id : null, requestType: RequestType.movie, imdbid: this.movie.imdbId, title: this.movie.title} }); } diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/interfaces/interfaces.ts b/src/Ombi/ClientApp/src/app/media-details/components/shared/interfaces/interfaces.ts index 2f2b54cbc..89b19e453 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/interfaces/interfaces.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/interfaces/interfaces.ts @@ -10,4 +10,5 @@ export interface IIssueDialogData { requestType: RequestType; requestId: number; imdbId: string; + title: string; } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/new-issue/new-issue.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/shared/new-issue/new-issue.component.ts index 636a4a1cf..3baf1fd57 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/new-issue/new-issue.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/new-issue/new-issue.component.ts @@ -31,7 +31,7 @@ export class NewIssueComponent implements OnInit { comments: [], requestId: data.requestId, requestType: data.requestType, - title: "", + title: data.title, providerId: data.imdbId, userReported: undefined, }; diff --git a/src/Ombi/ClientApp/src/app/media-details/media-details.module.ts b/src/Ombi/ClientApp/src/app/media-details/media-details.module.ts index 3a1f6e466..e494319c4 100644 --- a/src/Ombi/ClientApp/src/app/media-details/media-details.module.ts +++ b/src/Ombi/ClientApp/src/app/media-details/media-details.module.ts @@ -18,6 +18,7 @@ import { ArtistDetailsComponent } from "./components/artist/artist-details.compo const routes: Routes = [ { path: "movie/:movieDbId", component: MovieDetailsComponent, canActivate: [AuthGuard] }, + { path: "movie/request/:requestId", component: MovieDetailsComponent, canActivate: [AuthGuard] }, { path: "tv/:tvdbId/:search", component: TvDetailsComponent, canActivate: [AuthGuard] }, { path: "tv/:tvdbId", component: TvDetailsComponent, canActivate: [AuthGuard] }, { path: "artist/:artistId", component: ArtistDetailsComponent, canActivate: [AuthGuard] }, diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html index e4bf0cfe8..8e79c3a74 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html @@ -3,7 +3,7 @@ {{applicationName}} - {{nav.icon}}  {{nav.name | translate}} diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts index 908966363..1f7b5d696 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.ts @@ -4,6 +4,7 @@ import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { INavBar } from '../interfaces/ICommon'; import { StorageService } from '../shared/storage/storage-service'; +import { SettingsService } from '../services'; @Component({ selector: 'app-my-nav', @@ -24,27 +25,33 @@ export class MyNavComponent implements OnInit { @Output() public logoutClick = new EventEmitter(); @Output() public themeChange = new EventEmitter(); public theme: string; + public issuesEnabled: boolean = false; + public navItems: INavBar[]; constructor(private breakpointObserver: BreakpointObserver, + private settingsService: SettingsService, private store: StorageService) { } - public ngOnInit(): void { + public async ngOnInit() { + + this.issuesEnabled = await this.settingsService.issueEnabled().toPromise(); + console.log("issues enabled: " + this.issuesEnabled); this.theme = this.store.get("theme"); if(!this.theme) { this.store.save("theme","dark"); } + this.navItems = [ + { name: "NavigationBar.Discover", icon: "find_replace", link: "/discover", requiresAdmin: false, enabled: true }, + { name: "NavigationBar.Requests", icon: "list", link: "/requests-list", requiresAdmin: false, enabled: true }, + { name: "NavigationBar.Issues", icon: "notification_important", link: "/issues", requiresAdmin: false, enabled: this.issuesEnabled }, + { name: "NavigationBar.UserManagement", icon: "account_circle", link: "/usermanagement", requiresAdmin: true, enabled: true }, + { name: "NavigationBar.Calendar", icon: "calendar_today", link: "/calendar", requiresAdmin: false, enabled: true }, + { name: "NavigationBar.Settings", icon: "settings", link: "/Settings/About", requiresAdmin: true, enabled: true }, + { name: "NavigationBar.UserPreferences", icon: "person", link: "/user-preferences", requiresAdmin: false, enabled: true }, + ]; } - public navItems: INavBar[] = [ - { name: "NavigationBar.Discover", icon: "find_replace", link: "/discover", requiresAdmin: false }, - { name: "NavigationBar.Requests", icon: "list", link: "/requests-list", requiresAdmin: false }, - { name: "NavigationBar.UserManagement", icon: "account_circle", link: "/usermanagement", requiresAdmin: true }, - { name: "NavigationBar.Calendar", icon: "calendar_today", link: "/calendar", requiresAdmin: false }, - { name: "NavigationBar.Settings", icon: "settings", link: "/Settings/About", requiresAdmin: true }, - { name: "NavigationBar.UserPreferences", icon: "person", link: "/user-preferences", requiresAdmin: false }, - ] - public logOut() { this.logoutClick.emit(); } diff --git a/src/Ombi/ClientApp/src/app/services/searchV2.service.ts b/src/Ombi/ClientApp/src/app/services/searchV2.service.ts index 59dd71917..2b8fda367 100644 --- a/src/Ombi/ClientApp/src/app/services/searchV2.service.ts +++ b/src/Ombi/ClientApp/src/app/services/searchV2.service.ts @@ -23,6 +23,11 @@ export class SearchV2Service extends ServiceHelpers { public getFullMovieDetails(theMovieDbId: number): Observable { return this.http.get(`${this.url}/Movie/${theMovieDbId}`); } + + public getFullMovieDetailsByRequestId(requestId: number): Promise { + return this.http.get(`${this.url}/Movie/request/${requestId}`).toPromise(); + } + public getFullMovieDetailsPromise(theMovieDbId: number): Promise { return this.http.get(`${this.url}/Movie/${theMovieDbId}`).toPromise(); } diff --git a/src/Ombi/Controllers/V1/IssuesController.cs b/src/Ombi/Controllers/V1/IssuesController.cs index e65f19fe3..7f3c901d8 100644 --- a/src/Ombi/Controllers/V1/IssuesController.cs +++ b/src/Ombi/Controllers/V1/IssuesController.cs @@ -246,7 +246,7 @@ namespace Ombi.Controllers.V1 /// /// Deletes a comment on a issue /// - [HttpDelete("comments/{id:int}")] + [HttpDelete("comments/{id}")] [PowerUser] public async Task DeleteComment(int id) { @@ -256,6 +256,16 @@ namespace Ombi.Controllers.V1 return true; } + [HttpDelete("{id}")] + [PowerUser] + public async Task DeleteIssue(int id) + { + var issue = await _issues.GetAll().FirstOrDefaultAsync(x => x.Id == id); + + await _issues.Delete(issue); + return true; + } + [HttpPost("status")] public async Task UpdateStatus([FromBody] IssueStateViewModel model) { diff --git a/src/Ombi/Controllers/V2/SearchController.cs b/src/Ombi/Controllers/V2/SearchController.cs index 929dbdf57..ff80dc2f9 100644 --- a/src/Ombi/Controllers/V2/SearchController.cs +++ b/src/Ombi/Controllers/V2/SearchController.cs @@ -61,6 +61,15 @@ namespace Ombi.Controllers.V2 return await _movieEngineV2.GetFullMovieInformation(movieDbId, Request.HttpContext.RequestAborted); } + /// + /// Returns details for a single movie + /// + [HttpGet("movie/request/{requestId}")] + public async Task GetMovieByRequest(int requestId) + { + return await _movieEngineV2.GetMovieInfoByRequestId(requestId, Request.HttpContext.RequestAborted); + } + /// /// Returns basic information about the provided collection /// @@ -83,6 +92,17 @@ namespace Ombi.Controllers.V2 return await _tvEngineV2.GetShowInformation(tvdbid); } + /// + /// Returns details for a single show + /// + /// TVMaze is the TV Show Provider + /// + [HttpGet("tv/request/{requestId}")] + public async Task GetTvInfoByRequest(int requestId) + { + return await _tvEngineV2.GetShowByRequest(requestId); + } + /// /// Returns details for a single show /// @@ -357,6 +377,14 @@ namespace Ombi.Controllers.V2 return await _musicEngine.GetArtistInformation(artistId); } + [HttpGet("artist/request/{requestId}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesDefaultResponseType] + public async Task GetArtistInformationByRequestId(int requestId) + { + return await _musicEngine.GetArtistInformationByRequestId(requestId); + } + [HttpGet("releasegroupart/{musicBrainzId}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesDefaultResponseType]