pull/1358/head
Jamie.Rees 7 years ago
parent 8ce7ff07fc
commit d040d06976

@ -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<T> Get<T>(Uri uri)
{
var h = new HttpClient();
//h.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
var response = await h.GetAsync(uri);
if (!response.IsSuccessStatusCode)

@ -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<RequestEngineResult> RequestMovie(SearchMovieViewModel model);
bool ShouldAutoApprove(RequestType requestType);
Task<IEnumerable<RequestViewModel>> GetRequests();
}
}

@ -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<IEnumerable<SearchMovieViewModel>> 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);

@ -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<IEnumerable<RequestViewModel>> 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;
}
}
}

@ -0,0 +1,8 @@
namespace Ombi.Core.Models
{
public class QualityModel
{
public string Id { get; set; }
public string Name { get; set; }
}
}

@ -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<string> RequestedUsers { get; set; }
public string ArtistName { get; set; }
public string ArtistId { get; set; }
public int IssueId { get; set; }
public List<EpisodesModel> 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;
}

@ -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; }
}
}

@ -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; }
}
}

@ -8,7 +8,7 @@ namespace Ombi.Core.Settings
Task<T> GetSettingsAsync();
bool SaveSettings(T model);
Task<bool> SaveSettingsAsync(T model);
bool Delete(T model);
Task<bool> DeleteAsync(T model);
void Delete(T model);
Task DeleteAsync(T model);
}
}

@ -13,8 +13,7 @@ namespace Ombi.Store.Entities
}
public enum RequestType
{
Movie,
TvShow,
Album
Movie = 1,
TvShow = 2
}
}

@ -7,6 +7,7 @@ namespace Ombi.TheMovieDbApi
public interface IMovieDbApi
{
Task<MovieResponse> GetMovieInformation(int movieId);
Task<MovieResponse> GetMovieInformationWithVideo(int movieId);
Task<TheMovieDbContainer<SearchResult>> NowPlaying();
Task<TheMovieDbContainer<SearchResult>> PopularMovies();
Task<TheMovieDbContainer<SearchResult>> SearchMovie(string searchTerm);

@ -23,6 +23,14 @@ namespace Ombi.TheMovieDbApi
return await Api.Get<MovieResponse>(url);
}
public async Task<MovieResponse> 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<MovieResponse>(url);
}
public async Task<TheMovieDbContainer<SearchResult>> SearchMovie(string searchTerm)
{
var url = BaseUri.ChangePath("search/movie/");

@ -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<IEnumerable<RequestViewModel>> GetRequests()
{
return await RequestEngine.GetRequests();
}
[HttpPost("movie")]
public async Task<RequestEngineResult> SearchMovie([FromBody]SearchMovieViewModel movie)
{

@ -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;
}

@ -1,4 +1,5 @@

<p-growl [value]="notificationService.messages" style="margin-top: 35px;"></p-growl>
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
@ -13,17 +14,19 @@
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li ><a [routerLink]="['/search']"><i class="fa fa-search"></i> Search</a></li>
<li><a [routerLink]="['/search']"><i class="fa fa-search"></i> Search</a></li>
</ul>
<ul class="nav navbar-nav">
<li><a [routerLink]="['/requests']"><i class="fa fa-plus"></i> Requests</a></li>
</ul>
<ul class="nav navbar-nav">
<li ><a [routerLink]="['/settings/ombi']"><i class="fa fa-search"></i> Settings</a></li>
<li><a [routerLink]="['/settings/ombi']"><i class="fa fa-cog"></i> Settings</a></li>
</ul>
</div>
</div>
</nav>
<div class="container" style="padding-top: 5%">
<router-outlet></router-outlet>
</div>

@ -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) { };
}

@ -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]
})

@ -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
}

@ -16,5 +16,9 @@
alreadyInCp: boolean,
trailer: string,
homepage: string,
imdbId:string
imdbId: string,
approved: boolean,
requested: boolean,
available: boolean,
plexUrl: string
}

@ -0,0 +1,46 @@
<h1 id="searchTitle">Requests</h1>
<input #gb type="text" pInputText size="50" placeholder="Search">
<p-dataTable [value]="requests" expandableRows="true" [rows]="15" [paginator]="true" [pageLinks]="3" [rowsPerPageOptions]="[5,10,20,50]" [globalFilter]="gb">
<p-column expander="true" styleClass="col-icon"></p-column>
<p-column field="title" header="Title" [sortable]="true"></p-column>
<p-column field="requestedDate" header="Requested Date" [sortable]="true"></p-column>
<p-column field="approved" header="Approved" [sortable]="true">
<ng-template let-col let-request="rowData" pTemplate="body">
<span *ngIf="requests[col]" class="fa fa-check"></span>
<span *ngIf="!requests[col]" class="fa fa-times"></span>
</ng-template>
</p-column>
<ng-template let-request pTemplate="rowexpansion">
<div class="ui-grid ui-grid-responsive ui-fluid" style="font-size:16px;padding:20px">
<div class="ui-grid-row">
<div class="ui-grid-col-3" style="text-align:center">
<i class="fa fa-search" (click)="showCar(car)" style="cursor:pointer;float:left;margin-top:40px"></i>
<img src="https://image.tmdb.org/t/p/w150/{{request.posterPath}}">
<img *ngIf="request.type === 2" src="{{request.posterPath}}">
</div>
<div class="ui-grid-col-9">
<div class="ui-grid ui-grid-responsive ui-grid-pad">
<div class="ui-grid-row">
<div class="ui-grid-col-2 label">Type: </div>
<div class="ui-grid-col-10">{{request.type}}</div>
</div>
<div class="ui-grid-row">
<div class="ui-grid-col-2 label">Status: </div>
<div class="ui-grid-col-10">{{request.status}}</div>
</div>
<div class="ui-grid-row">
<div class="ui-grid-col-2 label">Approved: </div>
<div class="ui-grid-col-10">{{request.approved}}</div>
</div>
<div class="ui-grid-row">
<div class="ui-grid-col-2 label">Available: </div>
<div class="ui-grid-col-10">{{request.available}}</div>
</div>
</div>
</div>
</div>
</div>
</ng-template>
</p-dataTable>

@ -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);
}
}

@ -128,14 +128,14 @@
<div class="col-sm-8">
<div>
<a href="https://www.themoviedb.org/movie/{{result.id}}/" target="_blank">
<h4>{{result.title}} ({{result.releaseDate}})</h4>
<h4>{{result.title}} ({{result.releaseDate | date: 'yyyy'}})</h4>
</a>
<span *ngIf="result.firstAired" class="label label-info" target="_blank">Air Date: {{result.firstAired}}</span>
<span *ngIf="result.firstAired" class="label label-info" target="_blank">Air Date: {{result.firstAired | date: 'dd/MM/yyyy'}}</span>
<span *ngIf="result.releaseDate" class="label label-info" target="_blank">Release Date: {{result.releaseDate}}</span>
<span *ngIf="result.releaseDate" class="label label-info" target="_blank">Release Date: {{result.releaseDate | date: 'dd/MM/yyyy'}}</span>
<span *ngIf="result.available" class="label label-success">@UI.Search_Available</span>

@ -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() {

@ -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 = [];
}
}

@ -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<IRequestModel[]> {
return this.http.get('/api/request').map(ServiceHelpers.extractData);
}
}

@ -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:

Loading…
Cancel
Save