diff --git a/Ombi/Ombi.Api/Api.cs b/Ombi/Ombi.Api/Api.cs index adeb34bfc..931598b50 100644 --- a/Ombi/Ombi.Api/Api.cs +++ b/Ombi/Ombi.Api/Api.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; @@ -16,6 +17,7 @@ namespace Ombi.Api public async Task Get(Uri uri) { var h = new HttpClient(); + //h.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); var response = await h.GetAsync(uri); if (!response.IsSuccessStatusCode) diff --git a/Ombi/Ombi.Core/Engine/Interfaces/IRequestEngine.cs b/Ombi/Ombi.Core/Engine/Interfaces/IRequestEngine.cs index f23604468..ace321444 100644 --- a/Ombi/Ombi.Core/Engine/Interfaces/IRequestEngine.cs +++ b/Ombi/Ombi.Core/Engine/Interfaces/IRequestEngine.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading.Tasks; +using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; using Ombi.Store.Entities; @@ -8,5 +10,6 @@ namespace Ombi.Core.Engine { Task RequestMovie(SearchMovieViewModel model); bool ShouldAutoApprove(RequestType requestType); + Task> GetRequests(); } } \ No newline at end of file diff --git a/Ombi/Ombi.Core/Engine/MovieEngine.cs b/Ombi/Ombi.Core/Engine/MovieEngine.cs index 219f65d11..fa6eae68a 100644 --- a/Ombi/Ombi.Core/Engine/MovieEngine.cs +++ b/Ombi/Ombi.Core/Engine/MovieEngine.cs @@ -7,6 +7,7 @@ using Ombi.Core.Models.Search; using Ombi.Core.Requests.Models; using Ombi.Helpers; using Ombi.Store.Entities; +using Ombi.TheMovieDbApi; using Ombi.TheMovieDbApi.Models; namespace Ombi.Core.Engine @@ -14,11 +15,14 @@ namespace Ombi.Core.Engine public class MovieEngine : IMovieEngine { - public MovieEngine(IRequestService service) + public MovieEngine(IRequestService service, IMovieDbApi movApi) { RequestService = service; + MovieApi = movApi; } private IRequestService RequestService { get; } + private IMovieDbApi MovieApi { get; } + public async Task> ProcessMovieSearch(string search) { var api = new TheMovieDbApi.TheMovieDbApi(); @@ -99,24 +103,25 @@ namespace Ombi.Core.Engine }; viewMovies.Add(viewMovie); - //if (counter <= 5) // Let's only do it for the first 5 items - //{ - // var movieInfo = MovieApi.GetMovieInformationWithVideos(movie.Id); + var counter = 0; + if (counter <= 5) // Let's only do it for the first 5 items + { + var movieInfo = await MovieApi.GetMovieInformationWithVideo(movie.id); - // // TODO needs to be careful about this, it's adding extra time to search... - // // https://www.themoviedb.org/talk/5807f4cdc3a36812160041f2 - // viewMovie.ImdbId = movieInfo?.imdb_id; - // viewMovie.Homepage = movieInfo?.homepage; - // var videoId = movieInfo?.video ?? false - // ? movieInfo?.videos?.results?.FirstOrDefault()?.key - // : string.Empty; + // TODO needs to be careful about this, it's adding extra time to search... + // https://www.themoviedb.org/talk/5807f4cdc3a36812160041f2 + viewMovie.ImdbId = movieInfo?.imdb_id; + viewMovie.Homepage = movieInfo?.homepage; + //var videoId = movieInfo?.video ?? false + // ? movieInfo?.videos?.results?.FirstOrDefault()?.key + // : string.Empty; - // viewMovie.Trailer = string.IsNullOrEmpty(videoId) - // ? string.Empty - // : $"https://www.youtube.com/watch?v={videoId}"; + //viewMovie.Trailer = string.IsNullOrEmpty(videoId) + // ? string.Empty + // : $"https://www.youtube.com/watch?v={videoId}"; - // counter++; - //} + counter++; + } // var canSee = CanUserSeeThisRequest(viewMovie.Id, Security.HasPermissions(User, Permissions.UsersCanViewOnlyOwnRequests), dbMovies); diff --git a/Ombi/Ombi.Core/Engine/RequestEngine.cs b/Ombi/Ombi.Core/Engine/RequestEngine.cs index eeaecfa86..f4f02e2f7 100644 --- a/Ombi/Ombi.Core/Engine/RequestEngine.cs +++ b/Ombi/Ombi.Core/Engine/RequestEngine.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; @@ -228,5 +229,38 @@ namespace Ombi.Core.Engine return new RequestEngineResult{RequestAdded = true}; } + + public async Task> GetRequests() + { + var allRequests = await RequestService.GetAllAsync(); + var viewModel = allRequests.Select(movie => new RequestViewModel + { + ProviderId = movie.ProviderId, + Type = movie.Type, + Status = movie.Status, + ImdbId = movie.ImdbId, + Id = movie.Id, + PosterPath = movie.PosterPath, + ReleaseDate = movie.ReleaseDate, + RequestedDate = movie.RequestedDate, + Released = DateTime.Now > movie.ReleaseDate, + Approved = movie.Available || movie.Approved, + Title = movie.Title, + Overview = movie.Overview, + RequestedUsers = movie.AllUsers.ToArray(), + ReleaseYear = movie.ReleaseDate.Year.ToString(), + Available = movie.Available, + Admin = false, + IssueId = movie.IssueId, + Denied = movie.Denied, + DeniedReason = movie.DeniedReason, + //Qualities = qualities.ToArray(), + //HasRootFolders = rootFolders.Any(), + //RootFolders = rootFolders.ToArray(), + //CurrentRootPath = radarr.Enabled ? GetRootPath(movie.RootFolderSelected, radarr).Result : null + }).ToList(); + return viewModel; + } + } } \ No newline at end of file diff --git a/Ombi/Ombi.Core/Models/QualityModel.cs b/Ombi/Ombi.Core/Models/QualityModel.cs new file mode 100644 index 000000000..0e6be341e --- /dev/null +++ b/Ombi/Ombi.Core/Models/QualityModel.cs @@ -0,0 +1,8 @@ +namespace Ombi.Core.Models +{ + public class QualityModel + { + public string Id { get; set; } + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Core/Models/Requests/RequestModel.cs b/Ombi/Ombi.Core/Models/Requests/RequestModel.cs index 1eb9442e6..92ca73a68 100644 --- a/Ombi/Ombi.Core/Models/Requests/RequestModel.cs +++ b/Ombi/Ombi.Core/Models/Requests/RequestModel.cs @@ -33,10 +33,7 @@ namespace Ombi.Core.Models.Requests public int[] SeasonList { get; set; } public int SeasonCount { get; set; } public string SeasonsRequested { get; set; } - public string MusicBrainzId { get; set; } public List RequestedUsers { get; set; } - public string ArtistName { get; set; } - public string ArtistId { get; set; } public int IssueId { get; set; } public List Episodes { get; set; } public bool Denied { get; set; } @@ -86,8 +83,6 @@ namespace Ombi.Core.Models.Requests return "Movie"; case RequestType.TvShow: return "TV Show"; - case RequestType.Album: - return "Album"; default: return string.Empty; } diff --git a/Ombi/Ombi.Core/Models/Requests/RequestViewModel.cs b/Ombi/Ombi.Core/Models/Requests/RequestViewModel.cs new file mode 100644 index 000000000..a7816f777 --- /dev/null +++ b/Ombi/Ombi.Core/Models/Requests/RequestViewModel.cs @@ -0,0 +1,33 @@ +using System; +using Ombi.Store.Entities; + +namespace Ombi.Core.Models.Requests +{ + public class RequestViewModel + { + public int Id { get; set; } + public int ProviderId { get; set; } + public string ImdbId { get; set; } + public string Overview { get; set; } + public string Title { get; set; } + public string PosterPath { get; set; } + public DateTime ReleaseDate { get; set; } + public bool Released { get; set; } + public RequestType Type { get; set; } + public string Status { get; set; } + public bool Approved { get; set; } + public string[] RequestedUsers { get; set; } + public DateTime RequestedDate { get; set; } + public string ReleaseYear { get; set; } + public bool Available { get; set; } + public bool Admin { get; set; } + public int IssueId { get; set; } + public QualityModel[] Qualities { get; set; } + public EpisodesModel[] Episodes { get; set; } + public bool Denied { get; set; } + public string DeniedReason { get; set; } + public RootFolderModel[] RootFolders { get; set; } + public bool HasRootFolders { get; set; } + public string CurrentRootPath { get; set; } + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Core/Models/RootFolderModel.cs b/Ombi/Ombi.Core/Models/RootFolderModel.cs new file mode 100644 index 000000000..209d301b8 --- /dev/null +++ b/Ombi/Ombi.Core/Models/RootFolderModel.cs @@ -0,0 +1,9 @@ +namespace Ombi.Core.Models.Requests +{ + public class RootFolderModel + { + public string Id { get; set; } + public string Path { get; set; } + public long FreeSpace { get; set; } + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Core/Settings/ISettingsService.cs b/Ombi/Ombi.Core/Settings/ISettingsService.cs index f781676a0..7fe6b4f6f 100644 --- a/Ombi/Ombi.Core/Settings/ISettingsService.cs +++ b/Ombi/Ombi.Core/Settings/ISettingsService.cs @@ -8,7 +8,7 @@ namespace Ombi.Core.Settings Task GetSettingsAsync(); bool SaveSettings(T model); Task SaveSettingsAsync(T model); - bool Delete(T model); - Task DeleteAsync(T model); + void Delete(T model); + Task DeleteAsync(T model); } } \ No newline at end of file diff --git a/Ombi/Ombi.Store/Entities/RequestBlobs.cs b/Ombi/Ombi.Store/Entities/RequestBlobs.cs index 16ecff989..21c60a13e 100644 --- a/Ombi/Ombi.Store/Entities/RequestBlobs.cs +++ b/Ombi/Ombi.Store/Entities/RequestBlobs.cs @@ -13,8 +13,7 @@ namespace Ombi.Store.Entities } public enum RequestType { - Movie, - TvShow, - Album + Movie = 1, + TvShow = 2 } } \ No newline at end of file diff --git a/Ombi/Ombi.TheMovieDbApi/IMovieDbApi.cs b/Ombi/Ombi.TheMovieDbApi/IMovieDbApi.cs index 219bc5a41..13f4a65f6 100644 --- a/Ombi/Ombi.TheMovieDbApi/IMovieDbApi.cs +++ b/Ombi/Ombi.TheMovieDbApi/IMovieDbApi.cs @@ -7,6 +7,7 @@ namespace Ombi.TheMovieDbApi public interface IMovieDbApi { Task GetMovieInformation(int movieId); + Task GetMovieInformationWithVideo(int movieId); Task> NowPlaying(); Task> PopularMovies(); Task> SearchMovie(string searchTerm); diff --git a/Ombi/Ombi.TheMovieDbApi/TheMovieDbApi.cs b/Ombi/Ombi.TheMovieDbApi/TheMovieDbApi.cs index dc68c5f52..33787f911 100644 --- a/Ombi/Ombi.TheMovieDbApi/TheMovieDbApi.cs +++ b/Ombi/Ombi.TheMovieDbApi/TheMovieDbApi.cs @@ -23,6 +23,14 @@ namespace Ombi.TheMovieDbApi return await Api.Get(url); } + public async Task GetMovieInformationWithVideo(int movieId) + { + var url = BaseUri.ChangePath("movie/{0}", movieId.ToString()); + url = AddHeaders(url); + url = url.AddQueryParameter("append_to_response", "videos"); + return await Api.Get(url); + } + public async Task> SearchMovie(string searchTerm) { var url = BaseUri.ChangePath("search/movie/"); diff --git a/Ombi/Ombi/Controllers/RequestController.cs b/Ombi/Ombi/Controllers/RequestController.cs index 34e4465d2..6fa16fca6 100644 --- a/Ombi/Ombi/Controllers/RequestController.cs +++ b/Ombi/Ombi/Controllers/RequestController.cs @@ -1,6 +1,8 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Ombi.Core.Engine; +using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; namespace Ombi.Controllers @@ -14,6 +16,12 @@ namespace Ombi.Controllers private IRequestEngine RequestEngine { get; } + [HttpGet] + public async Task> GetRequests() + { + return await RequestEngine.GetRequests(); + } + [HttpPost("movie")] public async Task SearchMovie([FromBody]SearchMovieViewModel movie) { diff --git a/Ombi/Ombi/Styles/base.scss b/Ombi/Ombi/Styles/base.scss index c750dc353..095d7d2ab 100644 --- a/Ombi/Ombi/Styles/base.scss +++ b/Ombi/Ombi/Styles/base.scss @@ -701,4 +701,16 @@ body { line-height: 1.42857143; color: #333; background-color: #fff; +} + +.ui-datatable-odd { + background-color: $form-color $i; +} +.ui-datatable-even { + background-color: $form-color-lighter $i; +} + +.ui-widget-content { + border: 1px solid $form-color-lighter $i; + background: $form-color $i; } \ No newline at end of file diff --git a/Ombi/Ombi/app/app.component.html b/Ombi/Ombi/app/app.component.html index 4311d459d..d75a0c200 100644 --- a/Ombi/Ombi/app/app.component.html +++ b/Ombi/Ombi/app/app.component.html @@ -1,4 +1,5 @@ - + + -
diff --git a/Ombi/Ombi/app/app.component.ts b/Ombi/Ombi/app/app.component.ts index af6818c55..36b29087e 100644 --- a/Ombi/Ombi/app/app.component.ts +++ b/Ombi/Ombi/app/app.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { NotificationService } from './services/notification.service'; @Component({ selector: 'ombi', @@ -7,4 +8,5 @@ }) export class AppComponent { + constructor(public notificationService: NotificationService) { }; } \ No newline at end of file diff --git a/Ombi/Ombi/app/app.module.ts b/Ombi/Ombi/app/app.module.ts index ea5d2123c..c7a1dee52 100644 --- a/Ombi/Ombi/app/app.module.ts +++ b/Ombi/Ombi/app/app.module.ts @@ -9,21 +9,25 @@ import { RouterModule, Routes } from '@angular/router'; import { HttpModule } from '@angular/http'; import { SearchComponent } from './search/search.component'; +import { RequestComponent } from './requests/request.component'; import { PageNotFoundComponent } from './errors/not-found.component'; // Services import { SearchService } from './services/search.service'; import { RequestService } from './services/request.service'; +import { NotificationService } from './services/notification.service'; // Modules import { SettingsModule } from './settings/settings.module'; import { ButtonModule } from 'primeng/primeng'; import { GrowlModule } from 'primeng/components/growl/growl'; +import { DataTableModule, SharedModule } from 'primeng/primeng'; const routes: Routes = [ { path: '*', component: PageNotFoundComponent }, - { path: 'search', component: SearchComponent } + { path: 'search', component: SearchComponent }, + { path: 'requests', component: RequestComponent }, ]; @NgModule({ @@ -35,16 +39,20 @@ const routes: Routes = [ GrowlModule, ButtonModule, FormsModule, - SettingsModule + SettingsModule, + DataTableModule, + SharedModule ], declarations: [ AppComponent, PageNotFoundComponent, - SearchComponent + SearchComponent, + RequestComponent ], providers: [ SearchService, - RequestService + RequestService, + NotificationService ], bootstrap: [AppComponent] }) diff --git a/Ombi/Ombi/app/interfaces/IRequestModel.ts b/Ombi/Ombi/app/interfaces/IRequestModel.ts new file mode 100644 index 000000000..31db39bf7 --- /dev/null +++ b/Ombi/Ombi/app/interfaces/IRequestModel.ts @@ -0,0 +1,27 @@ +export interface IRequestModel { + id: number, + providerId: number, + imdbId: string, + overview: string, + title: string, + posterPath: string, + releaseDate: Date, + released: boolean, + type: RequestType, + status: string, + approved: boolean, + requestedUsers: string[], + requestedDate: Date, + releaseYear: string, + available: boolean, + issueId: number, + denied: boolean, + deniedReason: string, + + +} + +export enum RequestType { + movie = 1, + tvShow = 2 +} \ No newline at end of file diff --git a/Ombi/Ombi/app/interfaces/ISearchMovieResult.ts b/Ombi/Ombi/app/interfaces/ISearchMovieResult.ts index 4a308438f..99af3b533 100644 --- a/Ombi/Ombi/app/interfaces/ISearchMovieResult.ts +++ b/Ombi/Ombi/app/interfaces/ISearchMovieResult.ts @@ -16,5 +16,9 @@ alreadyInCp: boolean, trailer: string, homepage: string, - imdbId:string + imdbId: string, + approved: boolean, + requested: boolean, + available: boolean, + plexUrl: string } \ No newline at end of file diff --git a/Ombi/Ombi/app/requests/request.component.html b/Ombi/Ombi/app/requests/request.component.html new file mode 100644 index 000000000..b2654cbdc --- /dev/null +++ b/Ombi/Ombi/app/requests/request.component.html @@ -0,0 +1,46 @@ +

Requests

+ + + + + + + + + + + + + + +
+
+
+ + + +
+
+
+
+
Type:
+
{{request.type}}
+
+
+
Status:
+
{{request.status}}
+
+
+
Approved:
+
{{request.approved}}
+
+
+
Available:
+
{{request.available}}
+
+
+
+
+
+
+
diff --git a/Ombi/Ombi/app/requests/request.component.ts b/Ombi/Ombi/app/requests/request.component.ts new file mode 100644 index 000000000..65a733b77 --- /dev/null +++ b/Ombi/Ombi/app/requests/request.component.ts @@ -0,0 +1,24 @@ +import { Component, OnInit } from '@angular/core'; +import 'rxjs/add/operator/debounceTime'; +import 'rxjs/add/operator/distinctUntilChanged'; +import 'rxjs/add/operator/map'; + +import { RequestService } from '../services/request.service'; + +import { IRequestModel } from '../interfaces/IRequestModel'; + +@Component({ + selector: 'ombi', + moduleId: module.id, + templateUrl: './request.component.html', + providers: [RequestService] +}) +export class RequestComponent implements OnInit { + constructor(private requestService: RequestService) { } + + requests: IRequestModel[]; + + ngOnInit() { + this.requestService.getAllRequests().subscribe(x => this.requests = x); + } +} \ No newline at end of file diff --git a/Ombi/Ombi/app/search/search.component.html b/Ombi/Ombi/app/search/search.component.html index 5178a9f71..1ef35faf1 100644 --- a/Ombi/Ombi/app/search/search.component.html +++ b/Ombi/Ombi/app/search/search.component.html @@ -128,14 +128,14 @@
-

{{result.title}} ({{result.releaseDate}})

+

{{result.title}} ({{result.releaseDate | date: 'yyyy'}})

- Air Date: {{result.firstAired}} + Air Date: {{result.firstAired | date: 'dd/MM/yyyy'}} - Release Date: {{result.releaseDate}} + Release Date: {{result.releaseDate | date: 'dd/MM/yyyy'}} @UI.Search_Available diff --git a/Ombi/Ombi/app/search/search.component.ts b/Ombi/Ombi/app/search/search.component.ts index c7152dcea..20a5a6a7a 100644 --- a/Ombi/Ombi/app/search/search.component.ts +++ b/Ombi/Ombi/app/search/search.component.ts @@ -6,6 +6,7 @@ import 'rxjs/add/operator/map'; import { SearchService } from '../services/search.service'; import { RequestService } from '../services/request.service'; +import { NotificationService } from '../services/notification.service'; import { ISearchMovieResult } from '../interfaces/ISearchMovieResult'; import { IRequestEngineResult } from '../interfaces/IRequestEngineResult'; @@ -14,7 +15,6 @@ import { IRequestEngineResult } from '../interfaces/IRequestEngineResult'; selector: 'ombi', moduleId: module.id, templateUrl: './search.component.html', - providers: [SearchService, RequestService] }) export class SearchComponent implements OnInit { @@ -23,7 +23,7 @@ export class SearchComponent implements OnInit { movieResults: ISearchMovieResult[]; result: IRequestEngineResult; - constructor(private searchService: SearchService, private requestService: RequestService) { + constructor(private searchService: SearchService, private requestService: RequestService, private notificationService : NotificationService) { this.searchChanged .debounceTime(600) // Wait Xms afterthe last event before emitting last event .distinctUntilChanged() // only emit if value is different from previous value @@ -51,7 +51,17 @@ export class SearchComponent implements OnInit { } request(searchResult: ISearchMovieResult) { - this.requestService.requestMovie(searchResult).subscribe(x => this.result = x); + searchResult.requested = true; + this.requestService.requestMovie(searchResult).subscribe(x => { + this.result = x; + + if (this.result.requestAdded) { + this.notificationService.success("Request Added", + `Request for ${searchResult.title} has been added successfully`); + } else { + this.notificationService.warning("Request Added", this.result.message); + } + }); } popularMovies() { diff --git a/Ombi/Ombi/app/services/notification.service.ts b/Ombi/Ombi/app/services/notification.service.ts new file mode 100644 index 000000000..edf17ae44 --- /dev/null +++ b/Ombi/Ombi/app/services/notification.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { Message } from 'primeng/components/common/api'; + +@Injectable() +export class NotificationService { + messages: Message[] = []; + public addMessage(message: Message) { + this.messages.push(message); + } + + public success(title: string, body: string) { + this.addMessage({ severity: 'success', detail: body, summary: title }); + } + + public info(title: string, body: string) { + this.addMessage({ severity: 'info', detail: body, summary: title }); + } + + public warning(title: string, body: string) { + this.addMessage({ severity: 'warning', detail: body, summary: title }); + } + + public error(title: string, body: string) { + this.addMessage({ severity: 'danger', detail: body, summary: title }); + } + + public clearMessages() { + this.messages = []; + } +} \ No newline at end of file diff --git a/Ombi/Ombi/app/services/request.service.ts b/Ombi/Ombi/app/services/request.service.ts index 4af6c70b7..fe8d188f0 100644 --- a/Ombi/Ombi/app/services/request.service.ts +++ b/Ombi/Ombi/app/services/request.service.ts @@ -5,6 +5,7 @@ import { Observable } from 'rxjs/Rx'; import { ServiceHelpers } from './service.helpers'; import { IRequestEngineResult } from '../interfaces/IRequestEngineResult'; import { ISearchMovieResult } from '../interfaces/ISearchMovieResult'; +import { IRequestModel } from '../interfaces/IRequestModel'; @Injectable() export class RequestService { @@ -15,4 +16,8 @@ export class RequestService { return this.http.post('/api/Request/Movie/', JSON.stringify(movie), ServiceHelpers.RequestOptions).map(ServiceHelpers.extractData); } + getAllRequests(): Observable { + return this.http.get('/api/request').map(ServiceHelpers.extractData); + } + } \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index fa3c25e50..f97cdb039 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,11 +11,11 @@ before_build: - cmd: cd ombi/ombi - appveyor-retry dotnet restore - appveyor-retry npm install bower -g -- appveyor-retry npm install -g gulp +#- appveyor-retry npm install -g gulp - appveyor-retry npm install -g typescript - appveyor-retry npm install - appveyor-retry bower install -- gulp publish +#- gulp publish build_script: - dotnet build after_build: